diff --git a/.gitattributes b/.gitattributes
deleted file mode 100644
index a6344aac8c09253b3b630fb776ae94478aa0275b..0000000000000000000000000000000000000000
--- a/.gitattributes
+++ /dev/null
@@ -1,35 +0,0 @@
-*.7z filter=lfs diff=lfs merge=lfs -text
-*.arrow filter=lfs diff=lfs merge=lfs -text
-*.bin filter=lfs diff=lfs merge=lfs -text
-*.bz2 filter=lfs diff=lfs merge=lfs -text
-*.ckpt filter=lfs diff=lfs merge=lfs -text
-*.ftz filter=lfs diff=lfs merge=lfs -text
-*.gz filter=lfs diff=lfs merge=lfs -text
-*.h5 filter=lfs diff=lfs merge=lfs -text
-*.joblib filter=lfs diff=lfs merge=lfs -text
-*.lfs.* filter=lfs diff=lfs merge=lfs -text
-*.mlmodel filter=lfs diff=lfs merge=lfs -text
-*.model filter=lfs diff=lfs merge=lfs -text
-*.msgpack filter=lfs diff=lfs merge=lfs -text
-*.npy filter=lfs diff=lfs merge=lfs -text
-*.npz filter=lfs diff=lfs merge=lfs -text
-*.onnx filter=lfs diff=lfs merge=lfs -text
-*.ot filter=lfs diff=lfs merge=lfs -text
-*.parquet filter=lfs diff=lfs merge=lfs -text
-*.pb filter=lfs diff=lfs merge=lfs -text
-*.pickle filter=lfs diff=lfs merge=lfs -text
-*.pkl filter=lfs diff=lfs merge=lfs -text
-*.pt filter=lfs diff=lfs merge=lfs -text
-*.pth filter=lfs diff=lfs merge=lfs -text
-*.rar filter=lfs diff=lfs merge=lfs -text
-*.safetensors filter=lfs diff=lfs merge=lfs -text
-saved_model/**/* filter=lfs diff=lfs merge=lfs -text
-*.tar.* filter=lfs diff=lfs merge=lfs -text
-*.tar filter=lfs diff=lfs merge=lfs -text
-*.tflite filter=lfs diff=lfs merge=lfs -text
-*.tgz filter=lfs diff=lfs merge=lfs -text
-*.wasm filter=lfs diff=lfs merge=lfs -text
-*.xz filter=lfs diff=lfs merge=lfs -text
-*.zip filter=lfs diff=lfs merge=lfs -text
-*.zst filter=lfs diff=lfs merge=lfs -text
-*tfevents* filter=lfs diff=lfs merge=lfs -text
diff --git a/.gitignore b/.gitignore
index e4d9b211af4b4c08c2dce016474d567ad0dabac2..3538bb8f93e6308eee0977b39a7824a12e941b98 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,28 +1,38 @@
-# Dependencies
-node_modules/
-frontend/node_modules/
+# Python
__pycache__/
*.py[cod]
*$py.class
+*.so
+.Python
+env/
+venv/
+.venv/
+ENV/
+env.bak/
+venv.bak/
+.pytest_cache/
+*.egg-info/
+dist/
+build/
+
+# Node.js
+node_modules/
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+.npm
+.eslintcache
# Build outputs
frontend/dist/
frontend/build/
-# Environment
-.env
-.env.local
-.env.development.local
-.env.test.local
-.env.production.local
-.venv/
-venv/
-
# IDE
.vscode/
.idea/
*.swp
*.swo
+*~
# OS
.DS_Store
@@ -32,15 +42,20 @@ Thumbs.db
*.log
logs/
-# Cache
-.cache/
-.pytest_cache/
-.mypy_cache/
-
-# Model cache (uncomment to ignore downloaded models)
-# models/
-# .cache/huggingface/
+# Environment variables
+.env
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
# Temporary files
*.tmp
-*.temp
\ No newline at end of file
+*.temp
+test_*.py
+debug_*.py
+quick_*.py
+
+# Model cache (optional - uncomment if you don't want to track downloaded models)
+# .cache/
+# models/
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index 1e129e2a8ff0da825127000133d3add92e0827ee..0000000000000000000000000000000000000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,230 +0,0 @@
-# Contributing to Edge LLM ๐ค
-
-Thank you for your interest in contributing to Edge LLM! This guide will help you get started with development and contributions.
-
-## ๐ Quick Setup for Contributors
-
-### 1. Fork and Clone
-```bash
-# Fork the repository on Hugging Face Spaces
-# Then clone your fork
-git clone https://huggingface.co/spaces/[your-username]/EdgeLLM
-cd EdgeLLM
-```
-
-### 2. Install Dependencies
-```bash
-# Install Python dependencies
-pip install -r requirements.txt
-
-# Install Node.js dependencies
-cd frontend && npm install && cd ..
-
-# Optional: Install root package for scripts
-npm install
-```
-
-### 3. Start Development
-```bash
-# Option 1: Use npm scripts
-npm run dev
-
-# Option 2: Use Python script
-python scripts/start_platform.py
-
-# Option 3: Start manually
-npm run backend # Terminal 1
-npm run frontend # Terminal 2
-```
-
-## ๐ Project Structure
-
-```
-EdgeLLM/ # Main project directory
-โโโ ๐ง Backend
-โ โโโ backend/
-โ โ โโโ api/ # API routes
-โ โ โโโ services/ # Business logic
-โ โ โโโ models.py # Data models
-โ โ โโโ config.py # Configuration
-โ โ โโโ main.py # FastAPI app
-โ โโโ app.py # Entry point
-โ โโโ requirements.txt # Python dependencies
-โโโ ๐ป Frontend
-โ โโโ frontend/
-โ โ โโโ src/
-โ โ โ โโโ components/ # React components
-โ โ โ โโโ pages/ # Page components
-โ โ โ โโโ hooks/ # Custom hooks
-โ โ โ โโโ types/ # TypeScript types
-โ โ โโโ package.json # Frontend dependencies
-โ โ โโโ vite.config.ts # Build configuration
-โ โโโ static/ # Built assets (auto-generated)
-โโโ ๐จ Development
-โ โโโ scripts/ # Development scripts
-โ โโโ package.json # Root scripts
-โ โโโ .gitignore # Git ignore rules
-โโโ ๐ Documentation
- โโโ README.md # Main documentation
- โโโ CONTRIBUTING.md # This file
-```
-
-## ๐ ๏ธ Development Workflow
-
-### Frontend Development
-```bash
-cd frontend
-npm run dev # Start dev server (hot reload)
-npm run build # Build for production
-npm run preview # Preview production build
-```
-
-### Backend Development
-```bash
-# Start with auto-reload
-uvicorn app:app --host 0.0.0.0 --port 8000 --reload
-
-# Or use npm script
-npm run backend
-```
-
-### Full Stack Development
-```bash
-# Start both frontend and backend
-npm run dev
-
-# Build everything
-npm run build
-```
-
-## ๐งช Testing Your Changes
-
-### 1. Frontend Testing
-```bash
-cd frontend
-npm run test # Run tests
-npm run build # Ensure build works
-```
-
-### 2. Backend Testing
-```bash
-# Start backend and test API endpoints
-curl http://localhost:8000/health
-curl http://localhost:8000/models
-```
-
-### 3. Integration Testing
-```bash
-# Build and test full application
-npm run build
-python app.py # Test production build
-```
-
-## ๐ Code Style Guidelines
-
-### Frontend (TypeScript/React)
-- Use TypeScript for type safety
-- Follow React best practices
-- Use ShadCN UI components when possible
-- Keep components small and focused
-- Use custom hooks for reusable logic
-
-### Backend (Python/FastAPI)
-- Use type hints everywhere
-- Follow PEP 8 style guide
-- Keep services modular
-- Add docstrings to functions
-- Use Pydantic models for data validation
-
-### General
-- Write descriptive commit messages
-- Keep functions small and focused
-- Add comments for complex logic
-- Update documentation for new features
-
-## ๐ Contribution Process
-
-### 1. Create a Feature Branch
-```bash
-git checkout -b feature/your-feature-name
-```
-
-### 2. Make Your Changes
-- Follow the code style guidelines
-- Add tests if applicable
-- Update documentation
-
-### 3. Test Your Changes
-```bash
-npm run build # Ensure everything builds
-npm run dev # Test in development
-```
-
-### 4. Commit and Push
-```bash
-git add .
-git commit -m "feat: add your feature description"
-git push origin feature/your-feature-name
-```
-
-### 5. Create a Pull Request
-- Describe your changes clearly
-- Include screenshots if UI changes
-- Reference any related issues
-
-## ๐ฏ Areas for Contribution
-
-### ๐ง Backend Improvements
-- Add new model support
-- Improve error handling
-- Add model caching optimizations
-- Create API tests
-
-### ๐ป Frontend Enhancements
-- Add new UI components
-- Improve chat interface
-- Add dark mode support
-- Enhance accessibility
-
-### ๐ Documentation
-- Improve README
-- Add code comments
-- Create tutorials
-- Update API documentation
-
-### ๐ DevOps & Deployment
-- Improve Docker configuration
-- Add CI/CD workflows
-- Optimize build process
-- Add monitoring
-
-## ๐ Bug Reports
-
-When reporting bugs, please include:
-- Steps to reproduce
-- Expected behavior
-- Actual behavior
-- Browser/OS information
-- Console error messages
-
-## ๐ก Feature Requests
-
-When requesting features, please include:
-- Clear description of the feature
-- Use case and motivation
-- Proposed implementation approach
-- Any relevant examples
-
-## ๐ Getting Help
-
-- **Issues**: Create a GitHub issue for bugs or questions
-- **Discussions**: Use GitHub discussions for general questions
-- **Documentation**: Check the README and API docs first
-
-## ๐ Thank You!
-
-Every contribution, no matter how small, helps make Edge LLM better for everyone. We appreciate your time and effort!
-
----
-
-**Happy coding!** ๐
diff --git a/Dockerfile b/Dockerfile
index 3919f1af443ecb3898c7e65e5ee12670d1820fb8..321e4d35a71e19bca4e764ab1a4b74edfa8b76a9 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -13,4 +13,4 @@ COPY --chown=user ./requirements.txt requirements.txt
RUN pip install --no-cache-dir --upgrade -r requirements.txt
COPY --chown=user . /app
-CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
+CMD ["python", "app.py"]
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index b84d80f33e718b7e1b235b8025c0a7ba2cb041c4..0000000000000000000000000000000000000000
--- a/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2025 ZEKUN WU
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/README.md b/README.md
index c674c7aac17f7ec177539fec8f328be60931a24e..d4b7a724a4003f831a08a12e115249286a764e9f 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,137 @@
----
-title: EdgeLLM
-emoji: ๐
-colorFrom: blue
-colorTo: yellow
-sdk: docker
-pinned: false
----
-
-Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
+# ๐ Edge LLM Platform
+
+A lightweight, local LLM inference platform with a modern web interface.
+
+> **Note**: All development now happens directly in this repository (EdgeLLM_HF). This is both the development environment and the production Hugging Face Space.
+
+## โจ Features
+
+### ๐ค **Hybrid Model Support**
+- **Local Models**: Run Qwen models locally for privacy
+- **API Models**: Access powerful cloud models via [AiHubMix API](https://docs.aihubmix.com/en/api/Qwen)
+- **Seamless Switching**: Switch between local and API models effortlessly
+- **Thinking Models**: Support for models with visible reasoning process
+
+### ๐ **Available Models**
+
+#### Local Models (Privacy-First)
+- `Qwen/Qwen3-4B-Thinking-2507` - Local model with thinking process (~8GB)
+- `Qwen/Qwen3-4B-Instruct-2507` - Local direct instruction model (~8GB)
+
+#### API Models (Cloud-Powered)
+- `Qwen/Qwen3-30B-A3B` - Advanced Qwen3 with dynamic thinking modes
+- `qwen2.5-vl-72b-instruct` - Multimodal model with vision capabilities
+- `Qwen/QVQ-72B-Preview` - Visual reasoning with thinking process
+
+### ๐จ **Modern UI/UX**
+- **Responsive Design**: Works on desktop and mobile
+- **Chat Interface**: Beautiful conversation bubbles with session management
+- **Model Management**: Easy switching between local and API models
+- **Parameter Controls**: Temperature, max tokens, and system prompts
+- **Session History**: Persistent conversations with localStorage
+
+## ๐ Project Structure
+
+```
+EdgeLLM/
+โโโ frontend/ # ๐จ React frontend with ShadCN UI
+โโโ backend/ # ๐ง FastAPI backend
+โโโ static/ # ๐ฑ Built frontend assets
+โโโ app.py # ๐ Production entry point
+โโโ requirements.txt # ๐ Python dependencies
+โโโ README.md # ๐ Documentation
+```
+
+## ๐ฏ Quick Start
+
+1. **Clone the repository**
+ ```bash
+ git clone https://huggingface.co/spaces/wu981526092/EdgeLLM
+ cd EdgeLLM
+ ```
+
+2. **Set up environment variables**
+ ```bash
+ # Create .env file with your API credentials
+ echo 'api_key="your-aihubmix-api-key"' > .env
+ echo 'base_url="https://aihubmix.com/v1"' >> .env
+ ```
+
+3. **Install dependencies**
+ ```bash
+ pip install -r requirements.txt
+ cd frontend && npm install && cd ..
+ ```
+
+4. **Run locally**
+ ```bash
+ python app.py
+ ```
+
+5. **Deploy changes**
+ ```bash
+ # Build frontend if needed
+ cd frontend && npm run build && cd ..
+
+ # Push to Hugging Face
+ git add .
+ git commit -m "Update: your changes"
+ git push
+ ```
+
+## ๐ Live Demo
+
+Visit the live demo at: [https://huggingface.co/spaces/wu981526092/EdgeLLM](https://huggingface.co/spaces/wu981526092/EdgeLLM)
+
+## ๐ง Configuration
+
+### Environment Variables
+
+For local development, create a `.env` file:
+```bash
+api_key="your-aihubmix-api-key"
+base_url="https://aihubmix.com/v1"
+```
+
+For production (Hugging Face Spaces), set these as secrets:
+- `api_key`: Your AiHubMix API key
+- `base_url`: API endpoint (https://aihubmix.com/v1)
+
+### API Integration
+
+This platform integrates with [AiHubMix API](https://docs.aihubmix.com/en/api/Qwen) for cloud-based model access. Features include:
+
+- OpenAI-compatible API interface
+- Support for Qwen 3 series models
+- Multimodal capabilities (text + vision)
+- Streaming and non-streaming responses
+
+## ๐ ๏ธ Development Workflow
+
+1. **Frontend development**: Work in `frontend/`
+2. **Backend development**: Work in `backend/`
+3. **Build frontend**: `cd frontend && npm run build`
+4. **Deploy**: Standard git workflow
+ ```bash
+ git add .
+ git commit -m "Your changes"
+ git push
+ ```
+
+## ๐๏ธ Architecture
+
+### Backend (FastAPI)
+- **Models Service**: Handles both local model loading and API client management
+- **Chat Service**: Routes requests to appropriate generation method (local/API)
+- **API Routes**: RESTful endpoints for model management and text generation
+- **Configuration**: Environment-based settings for API credentials
+
+### Frontend (React + TypeScript)
+- **Modern UI**: Built with ShadCN components and Tailwind CSS
+- **Chat Interface**: Real-time conversation with message bubbles
+- **Model Management**: Easy switching between available models
+- **Session Management**: Persistent chat history and settings
+
+## ๐ License
+
+MIT License - see `LICENSE` for details.
diff --git a/app.py b/app.py
index e5a7cdee7b2a6eee7769a1213eb9aaa1b1e95ac2..f6f0188ac5b6c9c68569eb43dad0dab132d9c4ea 100644
--- a/app.py
+++ b/app.py
@@ -1,292 +1,195 @@
-from fastapi import FastAPI, HTTPException
-from fastapi.middleware.cors import CORSMiddleware
-from fastapi.staticfiles import StaticFiles
-from fastapi.responses import FileResponse
-from pydantic import BaseModel
-from transformers import AutoModelForCausalLM, AutoTokenizer
-import torch
-from typing import Optional, Dict, Any
+"""
+Edge LLM API - Main application entry point with integrated frontend
+
+This entry point handles both backend API and frontend serving,
+with automatic port detection and process management.
+"""
+import uvicorn
+import socket
+import subprocess
+import sys
import os
-
-app = FastAPI(title="Edge LLM API")
-
-# Enable CORS for Hugging Face Space
-app.add_middleware(
- CORSMiddleware,
- allow_origins=["*"], # Allow all origins for HF Space
- allow_credentials=True,
- allow_methods=["*"],
- allow_headers=["*"],
-)
-
-# Mount static files
-app.mount("/assets", StaticFiles(directory="static/assets"), name="assets")
-
-# Available models
-AVAILABLE_MODELS = {
- "Qwen/Qwen3-4B-Thinking-2507": {
- "name": "Qwen3-4B-Thinking-2507",
- "supports_thinking": True,
- "description": "Shows thinking process",
- "size_gb": "~8GB"
- },
- "Qwen/Qwen3-4B-Instruct-2507": {
- "name": "Qwen3-4B-Instruct-2507",
- "supports_thinking": False,
- "description": "Direct instruction following",
- "size_gb": "~8GB"
- }
-}
-
-# Global model cache
-models_cache: Dict[str, Dict[str, Any]] = {}
-current_model_name = None # No model loaded by default
-
-class PromptRequest(BaseModel):
- prompt: str
- system_prompt: Optional[str] = None
- model_name: Optional[str] = None
- temperature: Optional[float] = 0.7
- max_new_tokens: Optional[int] = 1024
-
-class PromptResponse(BaseModel):
- thinking_content: str
- content: str
- model_used: str
- supports_thinking: bool
-
-class ModelInfo(BaseModel):
- model_name: str
- name: str
- supports_thinking: bool
- description: str
- size_gb: str
- is_loaded: bool
-
-class ModelsResponse(BaseModel):
- models: list[ModelInfo]
- current_model: str
-
-class ModelLoadRequest(BaseModel):
- model_name: str
-
-class ModelUnloadRequest(BaseModel):
- model_name: str
-
-def load_model_by_name(model_name: str):
- """Load a model into the cache"""
- global models_cache
+import time
+import signal
+import webbrowser
+from backend.main import app
+
+def find_free_port(start_port=8000, max_attempts=50):
+ """Find a free port starting from start_port"""
+ for port in range(start_port, start_port + max_attempts):
+ try:
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
+ s.bind(('localhost', port))
+ return port
+ except OSError:
+ continue
+ raise RuntimeError(f"Could not find a free port in range {start_port}-{start_port + max_attempts}")
+
+def kill_processes_on_port(port):
+ """Kill processes using the specified port"""
+ try:
+ if os.name == 'nt': # Windows
+ result = subprocess.run(['netstat', '-ano'], capture_output=True, text=True)
+ lines = result.stdout.split('\n')
+ for line in lines:
+ if f':{port}' in line and 'LISTENING' in line:
+ parts = line.split()
+ if len(parts) >= 5:
+ pid = parts[-1]
+ try:
+ subprocess.run(['taskkill', '/pid', pid, '/f'],
+ capture_output=True, check=True)
+ print(f"โ
Killed process {pid} on port {port}")
+ except subprocess.CalledProcessError:
+ pass
+ else: # Unix/Linux/macOS
+ try:
+ result = subprocess.run(['lsof', '-ti', f':{port}'],
+ capture_output=True, text=True)
+ pids = result.stdout.strip().split('\n')
+ for pid in pids:
+ if pid:
+ subprocess.run(['kill', '-9', pid], capture_output=True)
+ print(f"โ
Killed process {pid} on port {port}")
+ except subprocess.CalledProcessError:
+ pass
+ except Exception as e:
+ print(f"โ ๏ธ Warning: Could not kill processes on port {port}: {e}")
+
+def update_frontend_config(port):
+ """Update frontend configuration to use the correct backend port"""
+ frontend_files = [
+ 'frontend/src/pages/Models.tsx',
+ 'frontend/src/pages/Playground.tsx'
+ ]
- if model_name in models_cache:
+ for file_path in frontend_files:
+ if os.path.exists(file_path):
+ try:
+ with open(file_path, 'r', encoding='utf-8') as f:
+ content = f.read()
+
+ # Update the baseUrl to use the current port (no longer needed with dynamic ports)
+ old_pattern = "window.location.hostname === 'localhost' ? `${window.location.protocol}//${window.location.host}` : ''"
+ new_pattern = old_pattern # No change needed since it's already dynamic
+
+ # No need to update frontend files since they use dynamic origins now
+ print(f"โ
Frontend uses dynamic origins - no port updates needed")
+ except Exception as e:
+ print(f"โ ๏ธ Warning: Could not update {file_path}: {e}")
+
+def build_frontend():
+ """Build the frontend if needed"""
+ if not os.path.exists('frontend/dist') or not os.listdir('frontend/dist'):
+ print("๐จ Building frontend...")
+ try:
+ os.chdir('frontend')
+ subprocess.run(['npm', 'install'], check=True, capture_output=True)
+ subprocess.run(['npm', 'run', 'build'], check=True, capture_output=True)
+ os.chdir('..')
+ print("โ
Frontend built successfully")
+ except subprocess.CalledProcessError as e:
+ print(f"โ Frontend build failed: {e}")
+ os.chdir('..')
+ return False
+ except FileNotFoundError:
+ print("โ npm not found. Please install Node.js")
+ return False
+ return True
+
+def should_rebuild_frontend():
+ """Check if frontend needs to be rebuilt"""
+ # Check if build exists
+ if not (os.path.exists('frontend/dist/index.html') and os.path.exists('frontend/dist/assets')):
+ print("โ ๏ธ Frontend build not found - will build it")
return True
- if model_name not in AVAILABLE_MODELS:
- return False
-
+ # Check if source is newer than build
try:
- print(f"Loading model: {model_name}")
- tokenizer = AutoTokenizer.from_pretrained(model_name)
- model = AutoModelForCausalLM.from_pretrained(
- model_name,
- torch_dtype=torch.float16,
- device_map="auto"
- )
+ dist_time = os.path.getmtime('frontend/dist/index.html')
- models_cache[model_name] = {
- "model": model,
- "tokenizer": tokenizer
- }
- print(f"Model {model_name} loaded successfully")
- return True
- except Exception as e:
- print(f"Error loading model {model_name}: {e}")
+ # Check key source files
+ source_files = [
+ 'frontend/src',
+ 'frontend/package.json',
+ 'frontend/vite.config.ts',
+ 'frontend/tsconfig.json'
+ ]
+
+ for src_path in source_files:
+ if os.path.exists(src_path):
+ if os.path.isdir(src_path):
+ # Check all files in directory
+ for root, dirs, files in os.walk(src_path):
+ for file in files:
+ file_path = os.path.join(root, file)
+ if os.path.getmtime(file_path) > dist_time:
+ print(f"๐ Source files changed - will rebuild frontend")
+ return True
+ else:
+ if os.path.getmtime(src_path) > dist_time:
+ print(f"๐ {src_path} changed - will rebuild frontend")
+ return True
+
+ print("โ
Frontend build is up to date")
return False
-
-def unload_model_by_name(model_name: str):
- """Unload a model from the cache"""
- global models_cache, current_model_name
-
- if model_name in models_cache:
- del models_cache[model_name]
- if current_model_name == model_name:
- current_model_name = None
- print(f"Model {model_name} unloaded")
+
+ except Exception as e:
+ print(f"โ ๏ธ Error checking build status: {e} - will rebuild")
return True
- return False
-
-@app.on_event("startup")
-async def startup_event():
- """Startup event - don't load models by default"""
- print("๐ Edge LLM API is starting up...")
- print("๐ก Models will be loaded on demand")
-@app.get("/")
-async def read_index():
- """Serve the React app"""
- return FileResponse('static/index.html')
+def cleanup_handler(signum, frame):
+ """Handle cleanup on exit"""
+ print("\n๐ Shutting down Edge LLM...")
+ sys.exit(0)
-@app.get("/health")
-async def health_check():
- return {"status": "healthy", "message": "Edge LLM API is running"}
-
-@app.get("/models", response_model=ModelsResponse)
-async def get_models():
- """Get available models and their status"""
- global current_model_name
-
- models = []
- for model_name, info in AVAILABLE_MODELS.items():
- models.append(ModelInfo(
- model_name=model_name,
- name=info["name"],
- supports_thinking=info["supports_thinking"],
- description=info["description"],
- size_gb=info["size_gb"],
- is_loaded=model_name in models_cache
- ))
-
- return ModelsResponse(
- models=models,
- current_model=current_model_name or ""
- )
-
-@app.post("/load-model")
-async def load_model(request: ModelLoadRequest):
- """Load a specific model"""
- global current_model_name
-
- if request.model_name not in AVAILABLE_MODELS:
- raise HTTPException(
- status_code=400,
- detail=f"Model {request.model_name} not available"
- )
+if __name__ == "__main__":
+ # Set up signal handlers
+ signal.signal(signal.SIGINT, cleanup_handler)
+ signal.signal(signal.SIGTERM, cleanup_handler)
- success = load_model_by_name(request.model_name)
- if success:
- current_model_name = request.model_name
- return {
- "message": f"Model {request.model_name} loaded successfully",
- "current_model": current_model_name
- }
- else:
- raise HTTPException(
- status_code=500,
- detail=f"Failed to load model {request.model_name}"
- )
-
-@app.post("/unload-model")
-async def unload_model(request: ModelUnloadRequest):
- """Unload a specific model"""
- global current_model_name
+ print("๐ Starting Edge LLM with auto-build frontend...")
- success = unload_model_by_name(request.model_name)
- if success:
- return {
- "message": f"Model {request.model_name} unloaded successfully",
- "current_model": current_model_name or ""
- }
+ # Find available port
+ import os
+ original_port = int(os.getenv("PORT", "0")) # Use env var or auto-assign
+ if original_port == 0:
+ # Auto-assign a free port starting from 8000
+ original_port = find_free_port(8000)
+ print(f"๐ Auto-assigned port: {original_port}")
else:
- raise HTTPException(
- status_code=404,
- detail=f"Model {request.model_name} not found in cache"
- )
-
-@app.post("/set-current-model")
-async def set_current_model(request: ModelLoadRequest):
- """Set the current active model"""
- global current_model_name
-
- if request.model_name not in models_cache:
- raise HTTPException(
- status_code=400,
- detail=f"Model {request.model_name} is not loaded. Please load it first."
- )
-
- current_model_name = request.model_name
- return {
- "message": f"Current model set to {current_model_name}",
- "current_model": current_model_name
- }
-
-@app.post("/generate", response_model=PromptResponse)
-async def generate_text(request: PromptRequest):
- """Generate text using the loaded model"""
- global current_model_name
-
- # Use the model specified in request, or fall back to current model
- model_to_use = request.model_name if request.model_name else current_model_name
-
- if not model_to_use:
- raise HTTPException(
- status_code=400,
- detail="No model specified. Please load a model first."
- )
-
- if model_to_use not in models_cache:
- raise HTTPException(
- status_code=400,
- detail=f"Model {model_to_use} is not loaded. Please load it first."
- )
+ kill_processes_on_port(original_port)
try:
- model = models_cache[model_to_use]["model"]
- tokenizer = models_cache[model_to_use]["tokenizer"]
- model_info = AVAILABLE_MODELS[model_to_use]
-
- # Build the prompt
- messages = []
- if request.system_prompt:
- messages.append({"role": "system", "content": request.system_prompt})
- messages.append({"role": "user", "content": request.prompt})
-
- # Apply chat template
- formatted_prompt = tokenizer.apply_chat_template(
- messages,
- tokenize=False,
- add_generation_prompt=True
- )
+ port = find_free_port(original_port)
+ print(f"๐ก Using port: {port}")
- # Tokenize
- inputs = tokenizer(formatted_prompt, return_tensors="pt").to(model.device)
+ if port != original_port:
+ print(f"โ ๏ธ Port {original_port} was busy, switched to {port}")
+ update_frontend_config(port)
- # Generate
- with torch.no_grad():
- outputs = model.generate(
- **inputs,
- max_new_tokens=request.max_new_tokens,
- temperature=request.temperature,
- do_sample=True,
- pad_token_id=tokenizer.eos_token_id
- )
+ # Auto-build frontend if needed
+ if should_rebuild_frontend():
+ print("๐จ Building frontend...")
+ build_frontend()
- # Decode
- generated_tokens = outputs[0][inputs['input_ids'].shape[1]:]
- generated_text = tokenizer.decode(generated_tokens, skip_special_tokens=True)
+ # Start the backend server
+ print(f"๐ Starting server on http://localhost:{port}")
+ print("๐ฏ Frontend and Backend integrated - ready to use!")
- # Parse thinking vs final content for thinking models
- thinking_content = ""
- final_content = generated_text
+ # Auto-open browser after a short delay
+ def open_browser():
+ time.sleep(2)
+ webbrowser.open(f'http://localhost:{port}')
- if model_info["supports_thinking"] and "" in generated_text:
- parts = generated_text.split("")
- if len(parts) > 1:
- thinking_part = parts[1]
- if " " in thinking_part:
- thinking_content = thinking_part.split(" ")[0].strip()
- remaining = thinking_part.split("", 1)[1] if "" in thinking_part else ""
- final_content = remaining.strip()
+ import threading
+ browser_thread = threading.Thread(target=open_browser)
+ browser_thread.daemon = True
+ browser_thread.start()
- return PromptResponse(
- thinking_content=thinking_content,
- content=final_content,
- model_used=model_to_use,
- supports_thinking=model_info["supports_thinking"]
- )
+ # Start the server
+ uvicorn.run(app, host="0.0.0.0", port=port)
except Exception as e:
- print(f"Generation error: {e}")
- raise HTTPException(status_code=500, detail=f"Generation failed: {str(e)}")
-
-if __name__ == "__main__":
- import uvicorn
- uvicorn.run(app, host="0.0.0.0", port=7860)
+ print(f"โ Error starting server: {e}")
+ sys.exit(1)
diff --git a/backend/__init__.py b/backend/__init__.py
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/backend/api/__init__.py b/backend/api/__init__.py
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/backend/api/endpoints/__init__.py b/backend/api/endpoints/__init__.py
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/backend/api/routes.py b/backend/api/routes.py
index bdbf8c58f281ec56f0960472c8de1ceb7e9b7ee9..ca7bdf8a5aae46acece21f5246a58e378c549beb 100644
--- a/backend/api/routes.py
+++ b/backend/api/routes.py
@@ -1,7 +1,7 @@
"""
API routes for Edge LLM
"""
-from fastapi import APIRouter, HTTPException
+from fastapi import APIRouter, HTTPException, Request
from fastapi.responses import FileResponse
from ..models import (
PromptRequest, PromptResponse, ModelInfo, ModelsResponse,
@@ -18,7 +18,8 @@ router = APIRouter()
@router.get("/")
async def read_index():
"""Serve the React app"""
- return FileResponse('static/index.html')
+ from ..config import FRONTEND_DIST_DIR
+ return FileResponse(f'{FRONTEND_DIST_DIR}/index.html')
@router.get("/health")
@@ -38,7 +39,8 @@ async def get_models():
supports_thinking=info["supports_thinking"],
description=info["description"],
size_gb=info["size_gb"],
- is_loaded=model_service.is_model_loaded(model_name)
+ is_loaded=model_service.is_model_loaded(model_name),
+ type=info["type"]
))
return ModelsResponse(
@@ -124,6 +126,7 @@ async def generate_text(request: PromptRequest):
thinking_content, final_content, model_used, supports_thinking = chat_service.generate_response(
prompt=request.prompt,
model_name=model_to_use,
+ messages=[msg.dict() for msg in request.messages] if request.messages else [],
system_prompt=request.system_prompt,
temperature=request.temperature,
max_new_tokens=request.max_new_tokens
@@ -139,3 +142,14 @@ async def generate_text(request: PromptRequest):
except Exception as e:
print(f"Generation error: {e}")
raise HTTPException(status_code=500, detail=f"Generation failed: {str(e)}")
+
+
+# Catch-all route for SPA - must be last
+@router.get("/{full_path:path}")
+async def catch_all(request: Request, full_path: str):
+ """
+ Catch-all route to serve index.html for any unmatched paths.
+ This enables client-side routing for the React SPA.
+ """
+ from ..config import FRONTEND_DIST_DIR
+ return FileResponse(f'{FRONTEND_DIST_DIR}/index.html')
diff --git a/backend/app.py b/backend/app.py
deleted file mode 100644
index 2dda420e9d79d8f7c6642e01bd6467ed71fc33c9..0000000000000000000000000000000000000000
--- a/backend/app.py
+++ /dev/null
@@ -1,243 +0,0 @@
-from fastapi import FastAPI, HTTPException
-from fastapi.middleware.cors import CORSMiddleware
-from pydantic import BaseModel
-from transformers import AutoModelForCausalLM, AutoTokenizer
-import torch
-from typing import Optional, Dict, Any
-
-app = FastAPI(title="Edge LLM API")
-
-# Enable CORS for frontend
-app.add_middleware(
- CORSMiddleware,
- allow_origins=["http://localhost:5173", "http://localhost:5174"], # Vite ports
- allow_credentials=True,
- allow_methods=["*"],
- allow_headers=["*"],
-)
-
-# Available models
-AVAILABLE_MODELS = {
- "Qwen/Qwen3-4B-Thinking-2507": {
- "name": "Qwen3-4B-Thinking-2507",
- "supports_thinking": True,
- "description": "Shows thinking process",
- "size_gb": "~8GB"
- },
- "Qwen/Qwen3-4B-Instruct-2507": {
- "name": "Qwen3-4B-Instruct-2507",
- "supports_thinking": False,
- "description": "Direct instruction following",
- "size_gb": "~8GB"
- }
-}
-
-# Global model cache
-models_cache: Dict[str, Dict[str, Any]] = {}
-current_model_name = None # No model loaded by default
-
-class PromptRequest(BaseModel):
- prompt: str
- system_prompt: Optional[str] = None
- model_name: Optional[str] = None
- temperature: Optional[float] = 0.7
- max_new_tokens: Optional[int] = 1024
-
-class PromptResponse(BaseModel):
- thinking_content: str
- content: str
- model_used: str
- supports_thinking: bool
-
-class ModelInfo(BaseModel):
- model_name: str
- name: str
- supports_thinking: bool
- description: str
- size_gb: str
- is_loaded: bool
-
-class ModelLoadRequest(BaseModel):
- model_name: str
-
-class ModelUnloadRequest(BaseModel):
- model_name: str
-
-async def load_model_by_name(model_name: str):
- """Load a specific model and cache it (without setting as current)"""
- global models_cache
-
- if model_name not in AVAILABLE_MODELS:
- raise HTTPException(status_code=400, detail=f"Model {model_name} not available")
-
- if model_name not in models_cache:
- print(f"Loading model: {model_name}...")
- tokenizer = AutoTokenizer.from_pretrained(model_name)
- model = AutoModelForCausalLM.from_pretrained(
- model_name,
- torch_dtype="auto",
- device_map="auto"
- )
- models_cache[model_name] = {
- "model": model,
- "tokenizer": tokenizer
- }
- print(f"Model {model_name} loaded successfully!")
-
- return models_cache[model_name]
-
-def unload_model_by_name(model_name: str):
- """Unload a specific model from cache"""
- global models_cache, current_model_name
-
- if model_name in models_cache:
- del models_cache[model_name]
- print(f"Model {model_name} unloaded from cache")
-
- # If current model was unloaded, reset current model
- if current_model_name == model_name:
- current_model_name = None
-
-@app.on_event("startup")
-async def startup_event():
- """Startup without loading any models"""
- print("Backend started. Models will be loaded on demand.")
-
-@app.get("/")
-async def root():
- return {"message": "Edge LLM API is running"}
-
-@app.get("/models")
-async def get_available_models():
- """Get list of available models with their status"""
- models_info = []
- for model_name, info in AVAILABLE_MODELS.items():
- models_info.append(ModelInfo(
- model_name=model_name,
- name=info["name"],
- supports_thinking=info["supports_thinking"],
- description=info["description"],
- size_gb=info["size_gb"],
- is_loaded=model_name in models_cache
- ))
- return {
- "models": models_info,
- "current_model": current_model_name
- }
-
-@app.post("/load-model")
-async def load_model(request: ModelLoadRequest):
- """Load a model into memory"""
- try:
- model_data = await load_model_by_name(request.model_name)
- return {
- "message": f"Model loaded: {request.model_name}",
- "model_name": request.model_name,
- "supports_thinking": AVAILABLE_MODELS[request.model_name]["supports_thinking"]
- }
- except Exception as e:
- raise HTTPException(status_code=500, detail=str(e))
-
-@app.post("/unload-model")
-async def unload_model(request: ModelUnloadRequest):
- """Unload a model from memory"""
- try:
- unload_model_by_name(request.model_name)
- return {
- "message": f"Model unloaded: {request.model_name}",
- "model_name": request.model_name
- }
- except Exception as e:
- raise HTTPException(status_code=500, detail=str(e))
-
-@app.post("/set-current-model")
-async def set_current_model(request: ModelLoadRequest):
- """Set the current active model (must be loaded first)"""
- global current_model_name
-
- if request.model_name not in models_cache:
- raise HTTPException(status_code=400, detail=f"Model {request.model_name} is not loaded. Please load it first.")
-
- current_model_name = request.model_name
- return {
- "message": f"Current model set to: {request.model_name}",
- "model_name": request.model_name,
- "supports_thinking": AVAILABLE_MODELS[request.model_name]["supports_thinking"]
- }
-
-@app.post("/generate", response_model=PromptResponse)
-async def generate_response(request: PromptRequest):
- global current_model_name
-
- # Determine which model to use
- target_model = request.model_name if request.model_name else current_model_name
-
- if not target_model:
- raise HTTPException(status_code=400, detail="No model specified and no current model set")
-
- # Check if the target model is loaded
- if target_model not in models_cache:
- raise HTTPException(
- status_code=400,
- detail=f"Model {target_model} is not loaded. Please load the model first using the load button."
- )
-
- # Set as current model if it's different
- if target_model != current_model_name:
- current_model_name = target_model
-
- # Get model and tokenizer
- model_data = models_cache[current_model_name]
- model = model_data["model"]
- tokenizer = model_data["tokenizer"]
- supports_thinking = AVAILABLE_MODELS[current_model_name]["supports_thinking"]
-
- # Prepare the model input with optional system prompt
- messages = []
- if request.system_prompt:
- messages.append({"role": "system", "content": request.system_prompt})
- messages.append({"role": "user", "content": request.prompt})
-
- text = tokenizer.apply_chat_template(
- messages,
- tokenize=False,
- add_generation_prompt=True,
- )
- model_inputs = tokenizer([text], return_tensors="pt").to(model.device)
-
- # Generate response with parameters
- generated_ids = model.generate(
- **model_inputs,
- max_new_tokens=request.max_new_tokens,
- temperature=request.temperature,
- do_sample=True if request.temperature > 0 else False,
- pad_token_id=tokenizer.eos_token_id
- )
- output_ids = generated_ids[0][len(model_inputs.input_ids[0]):].tolist()
-
- thinking_content = ""
- content = ""
-
- if supports_thinking:
- # Parse thinking content for thinking models
- try:
- index = len(output_ids) - output_ids[::-1].index(151668)
- except ValueError:
- index = 0
-
- thinking_content = tokenizer.decode(output_ids[:index], skip_special_tokens=True).strip("\n")
- content = tokenizer.decode(output_ids[index:], skip_special_tokens=True).strip("\n")
- else:
- # For non-thinking models, everything is content
- content = tokenizer.decode(output_ids, skip_special_tokens=True).strip("\n")
-
- return PromptResponse(
- thinking_content=thinking_content,
- content=content,
- model_used=current_model_name,
- supports_thinking=supports_thinking
- )
-
-if __name__ == "__main__":
- import uvicorn
- uvicorn.run("app:app", host="0.0.0.0", port=8000, reload=False)
diff --git a/backend/config.py b/backend/config.py
index 96ca313095497d82d1692abd3ef61ae078a482d9..3d139f9164a46278d29c76fae7debb5164240ac4 100644
--- a/backend/config.py
+++ b/backend/config.py
@@ -1,30 +1,64 @@
"""
Configuration settings for the Edge LLM API
"""
+import os
+from dotenv import load_dotenv
+
+# Load environment variables
+load_dotenv()
+
+# API Configuration
+API_KEY = os.getenv("api_key", "")
+BASE_URL = os.getenv("base_url", "https://aihubmix.com/v1")
# Available models configuration
AVAILABLE_MODELS = {
+ # API models (AiHubMix) - Prioritized first
+ "Qwen/Qwen3-30B-A3B": {
+ "name": "Qwen3-30B-A3B",
+ "supports_thinking": True,
+ "description": "API: Qwen3 with dynamic thinking modes",
+ "size_gb": "API",
+ "type": "api"
+ },
+ # Local models (for local development)
"Qwen/Qwen3-4B-Thinking-2507": {
"name": "Qwen3-4B-Thinking-2507",
"supports_thinking": True,
- "description": "Shows thinking process",
- "size_gb": "~8GB"
+ "description": "Local: Shows thinking process",
+ "size_gb": "~8GB",
+ "type": "local"
},
"Qwen/Qwen3-4B-Instruct-2507": {
- "name": "Qwen3-4B-Instruct-2507",
+ "name": "Qwen3-4B-Instruct-2507",
"supports_thinking": False,
- "description": "Direct instruction following",
- "size_gb": "~8GB"
+ "description": "Local: Direct instruction following",
+ "size_gb": "~8GB",
+ "type": "local"
+ },
+ "qwen2.5-vl-72b-instruct": {
+ "name": "Qwen2.5-VL-72B-Instruct",
+ "supports_thinking": False,
+ "description": "API: Multimodal model with vision",
+ "size_gb": "API",
+ "type": "api"
+ },
+ "Qwen/QVQ-72B-Preview": {
+ "name": "QVQ-72B-Preview",
+ "supports_thinking": True,
+ "description": "API: Visual reasoning with thinking",
+ "size_gb": "API",
+ "type": "api"
}
}
# CORS settings
CORS_ORIGINS = ["*"] # Allow all origins for HF Space
-# Static files directory
-STATIC_DIR = "static"
-ASSETS_DIR = "static/assets"
+# Static files directory - point directly to frontend build
+FRONTEND_DIST_DIR = "frontend/dist"
+ASSETS_DIR = "frontend/dist/assets"
-# Server settings
+# Server settings (port will be dynamically determined)
HOST = "0.0.0.0"
-PORT = 7860
+DEFAULT_PORT = int(os.getenv("PORT", "0")) # 0 means auto-assign a free port
diff --git a/backend/core/__init__.py b/backend/core/__init__.py
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/backend/main.py b/backend/main.py
index b2b3ced81ad84fedce9a975f563eafb5d280b7a9..f5e9f7c1bc8fc69f531c3aad764df71bfe087d86 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -5,7 +5,7 @@ from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from .api.routes import router
-from .config import CORS_ORIGINS, ASSETS_DIR
+from .config import CORS_ORIGINS, ASSETS_DIR, FRONTEND_DIST_DIR
def create_app() -> FastAPI:
diff --git a/backend/models.py b/backend/models.py
index 8395f16ff632d45b6585c81856379fd742a0ed15..74831313f507fefcb1b48ae2c4d457d6ca11fbc2 100644
--- a/backend/models.py
+++ b/backend/models.py
@@ -5,8 +5,13 @@ from pydantic import BaseModel
from typing import Optional, List
+class ChatMessage(BaseModel):
+ role: str # 'user', 'assistant', 'system'
+ content: str
+
class PromptRequest(BaseModel):
prompt: str
+ messages: Optional[List[ChatMessage]] = [] # Full conversation history
system_prompt: Optional[str] = None
model_name: Optional[str] = None
temperature: Optional[float] = 0.7
@@ -27,6 +32,7 @@ class ModelInfo(BaseModel):
description: str
size_gb: str
is_loaded: bool
+ type: str
class ModelsResponse(BaseModel):
diff --git a/backend/services/__init__.py b/backend/services/__init__.py
deleted file mode 100644
index 0557eb635c5522686a57e633065437599726336a..0000000000000000000000000000000000000000
--- a/backend/services/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# Services module
diff --git a/backend/services/chat_service.py b/backend/services/chat_service.py
index 2a02f0a358b0b95a927a48b6850f5e51ff396f94..24c8b74c2b94bc4fc41ae624d8a3901c8a40b752 100644
--- a/backend/services/chat_service.py
+++ b/backend/services/chat_service.py
@@ -1,26 +1,94 @@
"""
-Chat generation service
+Chat generation service supporting both local models and API calls
"""
import torch
from typing import Tuple
+from openai import OpenAI
from .model_service import model_service
-from ..config import AVAILABLE_MODELS
+from ..config import AVAILABLE_MODELS, API_KEY, BASE_URL
class ChatService:
+ def __init__(self):
+ # Initialize OpenAI client for API calls
+ self.api_client = OpenAI(
+ api_key=API_KEY,
+ base_url=BASE_URL
+ ) if API_KEY else None
- @staticmethod
- def generate_response(
+ def _generate_api_response(
+ self,
prompt: str,
model_name: str,
+ messages: list = None,
system_prompt: str = None,
temperature: float = 0.7,
max_new_tokens: int = 1024
) -> Tuple[str, str, str, bool]:
- """
- Generate chat response
- Returns: (thinking_content, final_content, model_used, supports_thinking)
- """
+ """Generate response using API"""
+ if not self.api_client:
+ raise ValueError("API client not configured. Please check API_KEY.")
+
+ # Build messages with conversation history
+ api_messages = []
+ if system_prompt:
+ api_messages.append({"role": "system", "content": system_prompt})
+
+ # Add conversation history
+ if messages:
+ for msg in messages:
+ api_messages.append({"role": msg.get("role"), "content": msg.get("content")})
+
+ # Add current prompt as the latest user message
+ api_messages.append({"role": "user", "content": prompt})
+
+ model_info = AVAILABLE_MODELS[model_name]
+
+ try:
+ # Make API call
+ completion = self.api_client.chat.completions.create(
+ model=model_name,
+ messages=api_messages,
+ temperature=temperature,
+ max_tokens=max_new_tokens,
+ stream=False
+ )
+
+ generated_text = completion.choices[0].message.content
+
+ # Parse thinking vs final content for thinking models
+ thinking_content = ""
+ final_content = generated_text
+
+ if model_info["supports_thinking"] and "" in generated_text:
+ parts = generated_text.split("")
+ if len(parts) > 1:
+ thinking_part = parts[1]
+ if " " in thinking_part:
+ thinking_content = thinking_part.split(" ")[0].strip()
+ remaining = thinking_part.split("", 1)[1] if "" in thinking_part else ""
+ final_content = remaining.strip()
+
+ return (
+ thinking_content,
+ final_content,
+ model_name,
+ model_info["supports_thinking"]
+ )
+
+ except Exception as e:
+ raise ValueError(f"API call failed: {str(e)}")
+
+ def _generate_local_response(
+ self,
+ prompt: str,
+ model_name: str,
+ messages: list = None,
+ system_prompt: str = None,
+ temperature: float = 0.7,
+ max_new_tokens: int = 1024
+ ) -> Tuple[str, str, str, bool]:
+ """Generate response using local model"""
if not model_service.is_model_loaded(model_name):
raise ValueError(f"Model {model_name} is not loaded")
@@ -30,15 +98,22 @@ class ChatService:
tokenizer = model_data["tokenizer"]
model_info = AVAILABLE_MODELS[model_name]
- # Build the prompt
- messages = []
+ # Build the conversation with full history
+ conversation = []
if system_prompt:
- messages.append({"role": "system", "content": system_prompt})
- messages.append({"role": "user", "content": prompt})
+ conversation.append({"role": "system", "content": system_prompt})
+
+ # Add conversation history
+ if messages:
+ for msg in messages:
+ conversation.append({"role": msg.get("role"), "content": msg.get("content")})
+
+ # Add current prompt as the latest user message
+ conversation.append({"role": "user", "content": prompt})
# Apply chat template
formatted_prompt = tokenizer.apply_chat_template(
- messages,
+ conversation,
tokenize=False,
add_generation_prompt=True
)
@@ -79,6 +154,33 @@ class ChatService:
model_name,
model_info["supports_thinking"]
)
+
+ def generate_response(
+ self,
+ prompt: str,
+ model_name: str,
+ messages: list = None,
+ system_prompt: str = None,
+ temperature: float = 0.7,
+ max_new_tokens: int = 1024
+ ) -> Tuple[str, str, str, bool]:
+ """
+ Generate chat response using appropriate method (API or local)
+ Returns: (thinking_content, final_content, model_used, supports_thinking)
+ """
+ model_info = AVAILABLE_MODELS.get(model_name)
+ if not model_info:
+ raise ValueError(f"Unknown model: {model_name}")
+
+ # Route to appropriate generation method
+ if model_info["type"] == "api":
+ return self._generate_api_response(
+ prompt, model_name, messages, system_prompt, temperature, max_new_tokens
+ )
+ else:
+ return self._generate_local_response(
+ prompt, model_name, messages, system_prompt, temperature, max_new_tokens
+ )
# Global chat service instance
diff --git a/backend/services/model_service.py b/backend/services/model_service.py
index d017cced53766c06b5805d96df49934697b0f28b..9cdfa5591ddbc34a795ababd36c62d8830461b8d 100644
--- a/backend/services/model_service.py
+++ b/backend/services/model_service.py
@@ -1,46 +1,60 @@
"""
Model loading and management service
"""
-import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
-from typing import Dict, Any, Optional
+import torch
+from typing import Dict, Any
from ..config import AVAILABLE_MODELS
class ModelService:
def __init__(self):
self.models_cache: Dict[str, Dict[str, Any]] = {}
- self.current_model_name: Optional[str] = None
-
+ self.current_model_name: str = None
+
def load_model(self, model_name: str) -> bool:
- """Load a model into the cache"""
- if model_name in self.models_cache:
- return True
-
+ """Load a model into memory"""
if model_name not in AVAILABLE_MODELS:
+ print(f"Model {model_name} not available.")
return False
+ model_info = AVAILABLE_MODELS[model_name]
+
+ # API models don't need to be "loaded" - they're always available
+ if model_info["type"] == "api":
+ print(f"API model {model_name} is always available")
+ return True
+
+ # Handle local models
+ if model_name in self.models_cache:
+ print(f"Model {model_name} already loaded.")
+ return True
+
try:
- print(f"Loading model: {model_name}")
+ print(f"Loading local model: {model_name}")
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype=torch.float16,
device_map="auto"
)
-
- self.models_cache[model_name] = {
- "model": model,
- "tokenizer": tokenizer
- }
+ self.models_cache[model_name] = {"model": model, "tokenizer": tokenizer}
print(f"Model {model_name} loaded successfully")
return True
except Exception as e:
print(f"Error loading model {model_name}: {e}")
return False
-
+
def unload_model(self, model_name: str) -> bool:
- """Unload a model from the cache"""
+ """Unload a model from memory"""
+ model_info = AVAILABLE_MODELS.get(model_name, {})
+
+ # API models can't be "unloaded"
+ if model_info.get("type") == "api":
+ print(f"API model {model_name} cannot be unloaded")
+ return True
+
+ # Handle local models
if model_name in self.models_cache:
del self.models_cache[model_name]
if self.current_model_name == model_name:
@@ -48,27 +62,47 @@ class ModelService:
print(f"Model {model_name} unloaded")
return True
return False
-
+
def set_current_model(self, model_name: str) -> bool:
"""Set the current active model"""
- if model_name in self.models_cache:
+ if model_name not in AVAILABLE_MODELS:
+ return False
+
+ model_info = AVAILABLE_MODELS[model_name]
+
+ # API models are always "available"
+ if model_info["type"] == "api":
self.current_model_name = model_name
return True
- return False
-
- def get_model_info(self, model_name: str) -> Dict[str, Any]:
- """Get model configuration info"""
- return AVAILABLE_MODELS.get(model_name, {})
-
+
+ # Local models need to be loaded first
+ if model_name not in self.models_cache:
+ if not self.load_model(model_name):
+ return False
+
+ self.current_model_name = model_name
+ return True
+
def is_model_loaded(self, model_name: str) -> bool:
- """Check if a model is loaded"""
+ """Check if a model is loaded/available"""
+ model_info = AVAILABLE_MODELS.get(model_name, {})
+
+ # API models are always available
+ if model_info.get("type") == "api":
+ return True
+
+ # Local models need to be in cache
return model_name in self.models_cache
-
+
def get_loaded_models(self) -> list:
- """Get list of currently loaded models"""
- return list(self.models_cache.keys())
-
- def get_current_model(self) -> Optional[str]:
+ """Get list of currently loaded/available models"""
+ loaded = []
+ for model_name, model_info in AVAILABLE_MODELS.items():
+ if model_info["type"] == "api" or model_name in self.models_cache:
+ loaded.append(model_name)
+ return loaded
+
+ def get_current_model(self) -> str:
"""Get the current active model"""
return self.current_model_name
diff --git a/backend/utils/__init__.py b/backend/utils/__init__.py
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/frontend/components.json b/frontend/components.json
new file mode 100644
index 0000000000000000000000000000000000000000..70e291640dfc568bc5e5cd89d0be82659005ed50
--- /dev/null
+++ b/frontend/components.json
@@ -0,0 +1,21 @@
+{
+ "$schema": "https://ui.shadcn.com/schema.json",
+ "style": "default",
+ "rsc": false,
+ "tsx": true,
+ "tailwind": {
+ "config": "tailwind.config.js",
+ "css": "src/index.css",
+ "baseColor": "slate",
+ "cssVariables": true,
+ "prefix": ""
+ },
+ "aliases": {
+ "components": "@/components",
+ "utils": "@/lib/utils",
+ "ui": "@/components/ui",
+ "lib": "@/lib",
+ "hooks": "@/hooks"
+ },
+ "iconLibrary": "lucide"
+}
diff --git a/frontend/index.html b/frontend/index.html
index 28fc7629932327eeb2d923ff7a2a5f57c2077233..84ff93b2a1fd2e20c8282aa2943c2132a2cea4ec 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -4,7 +4,7 @@
-
Edge LLM Platform
+ Edge LLM
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index e14c4d6c5877cc3d3028689bf4511d7096049f7a..34aa80848f93ef2a10c260533546f6a6ad81e10b 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -16,9 +16,10 @@
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-switch": "^1.2.6",
"@tailwindcss/typography": "^0.5.16",
+ "ai": "^5.0.27",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
- "lucide-react": "^0.263.1",
+ "lucide-react": "^0.542.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-markdown": "^10.1.0",
@@ -37,6 +38,51 @@
"vite": "^4.4.5"
}
},
+ "node_modules/@ai-sdk/gateway": {
+ "version": "1.0.15",
+ "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-1.0.15.tgz",
+ "integrity": "sha512-xySXoQ29+KbGuGfmDnABx+O6vc7Gj7qugmj1kGpn0rW0rQNn6UKUuvscKMzWyv1Uv05GyC1vqHq8ZhEOLfXscQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@ai-sdk/provider": "2.0.0",
+ "@ai-sdk/provider-utils": "3.0.7"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.76 || ^4"
+ }
+ },
+ "node_modules/@ai-sdk/provider": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.0.tgz",
+ "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "json-schema": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@ai-sdk/provider-utils": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.7.tgz",
+ "integrity": "sha512-o3BS5/t8KnBL3ubP8k3w77AByOypLm+pkIL/DCw0qKkhDbvhCy+L3hRTGPikpdb8WHcylAeKsjgwOxhj4cqTUA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@ai-sdk/provider": "2.0.0",
+ "@standard-schema/spec": "^1.0.0",
+ "eventsource-parser": "^3.0.5"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.76 || ^4"
+ }
+ },
"node_modules/@alloc/quick-lru": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
@@ -844,6 +890,15 @@
"node": ">= 8"
}
},
+ "node_modules/@opentelemetry/api": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
+ "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@@ -1569,6 +1624,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@standard-schema/spec": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz",
+ "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==",
+ "license": "MIT"
+ },
"node_modules/@tailwindcss/typography": {
"version": "0.5.16",
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.16.tgz",
@@ -1736,6 +1797,24 @@
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
}
},
+ "node_modules/ai": {
+ "version": "5.0.27",
+ "resolved": "https://registry.npmjs.org/ai/-/ai-5.0.27.tgz",
+ "integrity": "sha512-V7I9Rvrap5+3ozAjOrETA5Mv9Z1LmQobyY13U88IkFRahFp0xrEwjvYTwjQa4q5lPgLxwKgbIZRLnZSbUQwnUg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@ai-sdk/gateway": "1.0.15",
+ "@ai-sdk/provider": "2.0.0",
+ "@ai-sdk/provider-utils": "3.0.7",
+ "@opentelemetry/api": "1.9.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.76 || ^4"
+ }
+ },
"node_modules/ansi-regex": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz",
@@ -2277,6 +2356,15 @@
"url": "https://opencollective.com/unified"
}
},
+ "node_modules/eventsource-parser": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.5.tgz",
+ "integrity": "sha512-bSRG85ZrMdmWtm7qkF9He9TNRzc/Bm99gEJMaQoHJ9E6Kv9QBbsldh2oMj7iXmYNEAVvNgvv5vPorG6W+XtBhQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
"node_modules/extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
@@ -2675,6 +2763,12 @@
"node": ">=6"
}
},
+ "node_modules/json-schema": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
+ "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
+ "license": "(AFL-2.1 OR BSD-3-Clause)"
+ },
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@@ -2757,12 +2851,12 @@
}
},
"node_modules/lucide-react": {
- "version": "0.263.1",
- "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.263.1.tgz",
- "integrity": "sha512-keqxAx97PlaEN89PXZ6ki1N8nRjGWtDa4021GFYLNj0RgruM5odbpl8GHTExj0hhPq3sF6Up0gnxt6TSHu+ovw==",
+ "version": "0.542.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.542.0.tgz",
+ "integrity": "sha512-w3hD8/SQB7+lzU2r4VdFyzzOzKnUjTZIF/MQJGSSvni7Llewni4vuViRppfRAa2guOsY5k4jZyxw/i9DQHv+dw==",
"license": "ISC",
"peerDependencies": {
- "react": "^16.5.1 || ^17.0.0 || ^18.0.0"
+ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/mdast-util-from-markdown": {
@@ -4819,6 +4913,16 @@
"node": ">= 14.6"
}
},
+ "node_modules/zod": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.5.tgz",
+ "integrity": "sha512-rcUUZqlLJgBC33IT3PNMgsCq6TzLQEG/Ei/KTCU0PedSWRMAXoOUN+4t/0H+Q8bdnLPdqUYnvboJT0bn/229qg==",
+ "license": "MIT",
+ "peer": true,
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
"node_modules/zwitch": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 233dc8b69704c303f6e8dc18183486296a521d10..947ab9cfbd67db342d0eeaa2a76e116c7fc1ba0d 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -6,6 +6,7 @@
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
+ "build:watch": "tsc && vite build --watch",
"preview": "vite preview"
},
"dependencies": {
@@ -17,9 +18,10 @@
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-switch": "^1.2.6",
"@tailwindcss/typography": "^0.5.16",
+ "ai": "^5.0.27",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
- "lucide-react": "^0.263.1",
+ "lucide-react": "^0.542.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-markdown": "^10.1.0",
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 49ed8148ac1db557fdcdb4879fe91c730dff7b2d..69d1a8768f6bfb6d211009dbea1b7bde4d5b1be1 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -21,4 +21,3 @@ function App() {
}
export default App
-
diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..123fa931e6eded834885a39d7db82c21d613b109
--- /dev/null
+++ b/frontend/src/components/Layout.tsx
@@ -0,0 +1,18 @@
+import { Outlet } from 'react-router-dom'
+import { Sidebar } from './Sidebar'
+
+export function Layout() {
+ return (
+
+ {/* Sidebar */}
+
+
+
+
+ {/* Main content */}
+
+
+
+
+ )
+}
diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx
index 62e2155d4c3366d200acc6d69777cdddeb16daf6..ed2360dcb3f417ebf43c6f771c7daf818be3c180 100644
--- a/frontend/src/components/Sidebar.tsx
+++ b/frontend/src/components/Sidebar.tsx
@@ -63,24 +63,24 @@ export function Sidebar() {
const location = useLocation()
return (
-
- {/* Logo/Brand */}
-
+
+ {/* Header */}
+
-
-
+
+
-
Edge LLM
+
Edge LLM
Local AI Platform
{/* Navigation */}
-
-
-
+
+
+
Get started
@@ -115,8 +115,8 @@ export function Sidebar() {
-
- Tools
+
+ Advanced
{tools.map((item) => {
@@ -140,14 +140,6 @@ export function Sidebar() {
-
- {/* Footer */}
-
-
-
Local Model Platform
-
Privacy-focused AI
-
-
)
}
diff --git a/frontend/src/components/chat/ChatContainer.tsx b/frontend/src/components/chat/ChatContainer.tsx
index 8d2488393df0741d5743146dbf81533da8cfe575..9a990fbba24b99db4cfd7192db6bf6604bff336d 100644
--- a/frontend/src/components/chat/ChatContainer.tsx
+++ b/frontend/src/components/chat/ChatContainer.tsx
@@ -1,113 +1,185 @@
-import { useEffect, useRef } from 'react'
-import { ChatMessage } from './ChatMessage'
-import { ChatInput } from './ChatInput'
+import React from 'react'
+import ReactMarkdown from 'react-markdown'
+import { Button } from '@/components/ui/button'
+import { Textarea } from '@/components/ui/textarea'
+import { Card } from '@/components/ui/card'
+import { Badge } from '@/components/ui/badge'
import { Message } from '@/types/chat'
-import { Loader2 } from 'lucide-react'
-import { cn } from '@/lib/utils'
+import { Send, Square, Eye, EyeOff, Brain, User, Bot } from 'lucide-react'
interface ChatContainerProps {
messages: Message[]
input: string
- onInputChange: (value: string) => void
+ setInput: (value: string) => void
onSubmit: () => void
- onStop?: () => void
- isLoading?: boolean
+ onStop: () => void
+ isLoading: boolean
disabled?: boolean
- className?: string
placeholder?: string
}
export function ChatContainer({
messages,
input,
- onInputChange,
+ setInput,
onSubmit,
onStop,
- isLoading = false,
+ isLoading,
disabled = false,
- className,
- placeholder = "Ask me anything..."
+ placeholder = "Type your message..."
}: ChatContainerProps) {
- const messagesEndRef = useRef
(null)
- const messagesContainerRef = useRef(null)
+ const [showThinking, setShowThinking] = React.useState<{ [key: string]: boolean }>({})
- // Auto-scroll to bottom when new messages arrive
- useEffect(() => {
- if (messagesEndRef.current) {
- messagesEndRef.current.scrollIntoView({ behavior: 'smooth' })
+ const handleKeyPress = (e: React.KeyboardEvent) => {
+ if (e.key === 'Enter' && !e.shiftKey) {
+ e.preventDefault()
+ if (!isLoading && !disabled) {
+ onSubmit()
+ }
}
- }, [messages, isLoading])
+ }
- const handleCopyMessage = (content: string) => {
- navigator.clipboard.writeText(content)
- // Could add a toast notification here
+ const toggleThinking = (messageId: string) => {
+ setShowThinking(prev => ({
+ ...prev,
+ [messageId]: !prev[messageId]
+ }))
}
return (
-
- {/* Messages Area */}
-
+
+ {/* Messages */}
+
{messages.length === 0 ? (
-
-
-
-
Start a conversation
-
- Ask me anything! I can help with coding, writing, analysis, and more.
-
-
+
+
+
+
Start a conversation
+
+ Ask me anything and I'll help you out!
+
) : (
- <>
- {messages.map((message) => (
-
-
-
- ))}
-
- {/* Loading indicator */}
- {isLoading && (
-
- {/* Assistant avatar */}
-
-
-
+ messages.map((message) => (
+
+
+ {message.role !== 'user' && (
+
+
+
+ )}
- {/* Loading message */}
-
-
-
-
-
Thinking...
+
+
+
+ {message.role === 'user' ? (
+
+ ) : (
+
+ )}
+
+ {message.role === 'user' ? 'You' : 'Assistant'}
+
+ {message.model_used && (
+
+ {message.model_used}
+
+ )}
+
+ {/* Thinking content */}
+ {message.thinking_content && message.supports_thinking && (
+
+
toggleThinking(message.id)}
+ className="p-1 h-auto"
+ >
+
+ {showThinking[message.id] ? (
+ <>
+
+ Hide thinking
+ >
+ ) : (
+ <>
+
+ Show thinking
+ >
+ )}
+
+
+ {showThinking[message.id] && (
+
+
Thinking process:
+
{children}
+ }}
+ >
+ {message.thinking_content}
+
+
+ )}
+
+ )}
+
+ {/* Main content */}
+
{children}
+ }}
+ >
+ {message.content}
+
-
+
+
+ {message.role === 'user' && (
+
+
+
+ )}
- )}
- >
+
+ ))
)}
-
- {/* Scroll anchor */}
-
- {/* Input Area */}
-
+ {/* Input area */}
+
)
}
diff --git a/frontend/src/components/chat/ChatInput.tsx b/frontend/src/components/chat/ChatInput.tsx
deleted file mode 100644
index 9969c602364f0c40ea7a9e6cb3610b5075497acf..0000000000000000000000000000000000000000
--- a/frontend/src/components/chat/ChatInput.tsx
+++ /dev/null
@@ -1,138 +0,0 @@
-import { useState, useRef, useEffect } from 'react'
-import { Button } from '@/components/ui/button'
-import { Textarea } from '@/components/ui/textarea'
-import {
- Send,
- Square,
- Paperclip
-} from 'lucide-react'
-import { cn } from '@/lib/utils'
-
-interface ChatInputProps {
- value: string
- onChange: (value: string) => void
- onSubmit: () => void
- onStop?: () => void
- isLoading?: boolean
- disabled?: boolean
- placeholder?: string
- maxRows?: number
-}
-
-export function ChatInput({
- value,
- onChange,
- onSubmit,
- onStop,
- isLoading = false,
- disabled = false,
- placeholder = "Type your message...",
- maxRows = 6
-}: ChatInputProps) {
- const textareaRef = useRef
(null)
- const [rows, setRows] = useState(1)
-
- // Auto-resize textarea
- useEffect(() => {
- if (textareaRef.current) {
- textareaRef.current.style.height = 'auto'
- const scrollHeight = textareaRef.current.scrollHeight
- const rowHeight = 24 // Approximate line height
- const newRows = Math.min(Math.max(Math.ceil(scrollHeight / rowHeight), 1), maxRows)
- setRows(newRows)
- }
- }, [value, maxRows])
-
- const handleKeyDown = (e: React.KeyboardEvent) => {
- if (e.key === 'Enter' && !e.shiftKey) {
- e.preventDefault()
- if (!isLoading && value.trim() && !disabled) {
- onSubmit()
- }
- }
- }
-
- const handleSubmit = (e: React.FormEvent) => {
- e.preventDefault()
- if (!isLoading && value.trim() && !disabled) {
- onSubmit()
- }
- }
-
- const canSend = value.trim() && !disabled && !isLoading
-
- return (
-
- )
-}
diff --git a/frontend/src/components/chat/ChatMessage.tsx b/frontend/src/components/chat/ChatMessage.tsx
deleted file mode 100644
index b328a0ba3758e5619db09190a2cc70652ebacdc6..0000000000000000000000000000000000000000
--- a/frontend/src/components/chat/ChatMessage.tsx
+++ /dev/null
@@ -1,192 +0,0 @@
-import { useState } from 'react'
-import { Card, CardContent } from '@/components/ui/card'
-import { Button } from '@/components/ui/button'
-import { Badge } from '@/components/ui/badge'
-import { Message } from '@/types/chat'
-import ReactMarkdown from 'react-markdown'
-import {
- Copy,
- User,
- Bot,
- Brain,
- Zap,
- ChevronDown,
- ChevronUp,
- MessageSquare
-} from 'lucide-react'
-import { cn } from '@/lib/utils'
-
-interface ChatMessageProps {
- message: Message
- onCopy?: (content: string) => void
-}
-
-export function ChatMessage({ message, onCopy }: ChatMessageProps) {
- const [showThinking, setShowThinking] = useState(false)
- const isUser = message.role === 'user'
- const isSystem = message.role === 'system'
-
- const handleCopy = () => {
- if (onCopy) {
- onCopy(message.content)
- } else {
- navigator.clipboard.writeText(message.content)
- }
- }
-
- const formatTime = (timestamp: number) => {
- return new Date(timestamp).toLocaleTimeString([], {
- hour: '2-digit',
- minute: '2-digit'
- })
- }
-
- if (isSystem) {
- return (
-
-
-
- System prompt set
-
-
- )
- }
-
- return (
-
- {/* Avatar */}
-
- {isUser ? (
-
- ) : message.supports_thinking ? (
-
- ) : (
-
- )}
-
-
- {/* Message Content */}
-
- {/* Message Bubble */}
-
-
- {/* Model info for assistant messages */}
- {!isUser && message.model_used && (
-
- {message.supports_thinking ? : }
- {message.model_used}
-
- {message.supports_thinking ? "Thinking" : "Instruct"}
-
-
- )}
-
- {/* Thinking Content Toggle */}
- {!isUser && message.thinking_content && (
-
-
setShowThinking(!showThinking)}
- className="h-auto p-2 text-xs font-normal"
- >
-
- Thinking Process
- {showThinking ? (
-
- ) : (
-
- )}
-
-
- {showThinking && (
-
-
-
- {message.thinking_content}
-
-
-
- )}
-
- )}
-
- {/* Main Message Content */}
-
- {isUser ? (
-
{message.content}
- ) : (
-
-
{children} ,
- h2: ({children}) => {children} ,
- h3: ({children}) => {children} ,
- p: ({children}) => {children}
,
- strong: ({children}) => {children} ,
- em: ({children}) => {children} ,
- code: ({children}) => {children},
- ul: ({children}) => ,
- ol: ({children}) => {children} ,
- li: ({children}) => {children} ,
- }}
- >
- {message.content}
-
-
- )}
-
-
-
- {/* Message Actions */}
- {!isUser && (
-
-
-
-
-
- )}
-
-
- {/* Timestamp */}
-
- {formatTime(message.timestamp)}
-
-
-
- )
-}
diff --git a/frontend/src/components/chat/ChatSessions.tsx b/frontend/src/components/chat/ChatSessions.tsx
index fe0801e47231c67d32256c486b94ea9cd88c8187..00b205d216786d953d77932126f8619cd1c59e00 100644
--- a/frontend/src/components/chat/ChatSessions.tsx
+++ b/frontend/src/components/chat/ChatSessions.tsx
@@ -1,16 +1,16 @@
-import { useState } from 'react'
+import React from 'react'
import { Button } from '@/components/ui/button'
-import { Card, CardContent } from '@/components/ui/card'
+import { Card } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
+import { ChatSession } from '@/types/chat'
import {
Plus,
MessageSquare,
Trash2,
- Edit3,
- Calendar
+ Edit2,
+ Check,
+ X
} from 'lucide-react'
-import { ChatSession } from '@/types/chat'
-import { cn } from '@/lib/utils'
interface ChatSessionsProps {
sessions: ChatSession[]
@@ -18,7 +18,7 @@ interface ChatSessionsProps {
onSelectSession: (sessionId: string) => void
onNewSession: () => void
onDeleteSession: (sessionId: string) => void
- onRenameSession?: (sessionId: string, newTitle: string) => void
+ onRenameSession: (sessionId: string, newTitle: string) => void
}
export function ChatSessions({
@@ -29,182 +29,141 @@ export function ChatSessions({
onDeleteSession,
onRenameSession
}: ChatSessionsProps) {
- const [editingSession, setEditingSession] = useState(null)
- const [editTitle, setEditTitle] = useState('')
+ const [editingId, setEditingId] = React.useState(null)
+ const [editTitle, setEditTitle] = React.useState('')
- const handleStartEdit = (session: ChatSession) => {
- setEditingSession(session.id)
+ const startEditing = (session: ChatSession) => {
+ setEditingId(session.id)
setEditTitle(session.title)
}
- const handleSaveEdit = () => {
- if (editingSession && editTitle.trim() && onRenameSession) {
- onRenameSession(editingSession, editTitle.trim())
+ const finishEditing = () => {
+ if (editingId && editTitle.trim()) {
+ onRenameSession(editingId, editTitle.trim())
}
- setEditingSession(null)
+ setEditingId(null)
setEditTitle('')
}
- const handleCancelEdit = () => {
- setEditingSession(null)
+ const cancelEditing = () => {
+ setEditingId(null)
setEditTitle('')
}
- const formatDate = (timestamp: number) => {
- const date = new Date(timestamp)
- const now = new Date()
- const diffTime = now.getTime() - date.getTime()
- const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24))
-
- if (diffDays === 0) {
- return 'Today'
- } else if (diffDays === 1) {
- return 'Yesterday'
- } else if (diffDays < 7) {
- return `${diffDays} days ago`
- } else {
- return date.toLocaleDateString()
- }
- }
-
- const groupedSessions = sessions.reduce((groups, session) => {
- const date = formatDate(session.updated_at)
- if (!groups[date]) {
- groups[date] = []
- }
- groups[date].push(session)
- return groups
- }, {} as Record)
-
return (
{/* Header */}
-
-
Chat Sessions
-
-
+
+
Chat Sessions
+
+
+ New
-
-
-
- New Chat
-
- {/* Sessions List */}
-
- {Object.keys(groupedSessions).length === 0 ? (
-
-
-
No chat sessions yet
-
Start a new conversation
+ {/* Sessions list */}
+
+ {sessions.length === 0 ? (
+
+
+
No conversations yet
+
+ Start your first chat
+
) : (
- Object.entries(groupedSessions).map(([date, sessionGroup]) => (
-
- {/* Date Group Header */}
-
-
-
- {date}
-
-
-
- {/* Sessions in this date group */}
-
- {sessionGroup.map((session) => (
-
onSelectSession(session.id)}
- >
-
- {editingSession === session.id ? (
-
-
setEditTitle(e.target.value)}
- className="w-full text-sm bg-transparent border border-input rounded px-2 py-1"
- onKeyDown={(e) => {
- if (e.key === 'Enter') handleSaveEdit()
- if (e.key === 'Escape') handleCancelEdit()
- }}
- autoFocus
- />
-
-
- Save
-
-
- Cancel
-
-
-
- ) : (
-
-
-
- {session.title}
-
-
-
- {onRenameSession && (
- {
- e.stopPropagation()
- handleStartEdit(session)
- }}
- className="h-6 w-6 p-0"
- >
-
-
- )}
- {
- e.stopPropagation()
- onDeleteSession(session.id)
- }}
- className="h-6 w-6 p-0 text-destructive hover:text-destructive"
- >
-
-
-
-
-
-
- {session.messages.length} messages
- {session.model_name && (
-
- {session.model_name.split('/').pop()?.split('-')[0]}
-
- )}
-
-
- )}
-
-
- ))}
+ sessions.map((session) => (
+
onSelectSession(session.id)}
+ >
+
+ {/* Title */}
+ {editingId === session.id ? (
+
+ setEditTitle(e.target.value)}
+ onKeyPress={(e) => {
+ if (e.key === 'Enter') finishEditing()
+ if (e.key === 'Escape') cancelEditing()
+ }}
+ className="flex-1 text-sm bg-background border rounded px-2 py-1"
+ autoFocus
+ onClick={(e) => e.stopPropagation()}
+ />
+ {
+ e.stopPropagation()
+ finishEditing()
+ }}
+ >
+
+
+ {
+ e.stopPropagation()
+ cancelEditing()
+ }}
+ >
+
+
+
+ ) : (
+
+
+ {session.title}
+
+
+ {
+ e.stopPropagation()
+ startEditing(session)
+ }}
+ className="h-6 w-6 p-0"
+ >
+
+
+ {
+ e.stopPropagation()
+ onDeleteSession(session.id)
+ }}
+ className="h-6 w-6 p-0 text-destructive hover:text-destructive"
+ >
+
+
+
+
+ )}
+
+ {/* Metadata */}
+
+ {session.messages.length} messages
+ {new Date(session.updatedAt).toLocaleDateString()}
+
+
+ {/* Model info */}
+ {session.model && (
+
+ {session.model}
+
+ )}
-
+
))
)}
diff --git a/frontend/src/components/chat/index.ts b/frontend/src/components/chat/index.ts
deleted file mode 100644
index d9ccf4073764fda4404f5c7c877641c653c04b49..0000000000000000000000000000000000000000
--- a/frontend/src/components/chat/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export { ChatContainer } from './ChatContainer'
-export { ChatInput } from './ChatInput'
-export { ChatMessage } from './ChatMessage'
-export { ChatSessions } from './ChatSessions'
diff --git a/frontend/src/components/ui/alert-dialog.tsx b/frontend/src/components/ui/alert-dialog.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..a97230a5f557481fa39e997a78a2fefb9eb3f07d
--- /dev/null
+++ b/frontend/src/components/ui/alert-dialog.tsx
@@ -0,0 +1,138 @@
+import * as React from "react"
+import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
+import { cn } from "@/lib/utils"
+import { buttonVariants } from "@/components/ui/button"
+
+const AlertDialog = AlertDialogPrimitive.Root
+
+const AlertDialogTrigger = AlertDialogPrimitive.Trigger
+
+const AlertDialogPortal = AlertDialogPrimitive.Portal
+
+const AlertDialogOverlay = React.forwardRef<
+ React.ElementRef
,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
+
+const AlertDialogContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+
+))
+AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
+
+const AlertDialogHeader = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+)
+AlertDialogHeader.displayName = "AlertDialogHeader"
+
+const AlertDialogFooter = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+)
+AlertDialogFooter.displayName = "AlertDialogFooter"
+
+const AlertDialogTitle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
+
+const AlertDialogDescription = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AlertDialogDescription.displayName =
+ AlertDialogPrimitive.Description.displayName
+
+const AlertDialogAction = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
+
+const AlertDialogCancel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
+
+export {
+ AlertDialog,
+ AlertDialogPortal,
+ AlertDialogOverlay,
+ AlertDialogTrigger,
+ AlertDialogContent,
+ AlertDialogHeader,
+ AlertDialogFooter,
+ AlertDialogTitle,
+ AlertDialogDescription,
+ AlertDialogAction,
+ AlertDialogCancel,
+}
diff --git a/frontend/src/components/ui/badge.tsx b/frontend/src/components/ui/badge.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..cc9774f6192db2f5a6e77ac4017d400045432215
--- /dev/null
+++ b/frontend/src/components/ui/badge.tsx
@@ -0,0 +1,35 @@
+import * as React from "react"
+import { cva, type VariantProps } from "class-variance-authority"
+import { cn } from "@/lib/utils"
+
+const badgeVariants = cva(
+ "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
+ {
+ variants: {
+ variant: {
+ default:
+ "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
+ secondary:
+ "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
+ destructive:
+ "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
+ outline: "text-foreground",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ }
+)
+
+export interface BadgeProps
+ extends React.HTMLAttributes,
+ VariantProps {}
+
+function Badge({ className, variant, ...props }: BadgeProps) {
+ return (
+
+ )
+}
+
+export { Badge, badgeVariants }
diff --git a/frontend/src/components/ui/button.tsx b/frontend/src/components/ui/button.tsx
index 65d4fcd9ca74240125c5f72cf84c873781141fea..6042281e398d2e8095e35b061bc66c5e2fa9af0e 100644
--- a/frontend/src/components/ui/button.tsx
+++ b/frontend/src/components/ui/button.tsx
@@ -1,30 +1,28 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
-
import { cn } from "@/lib/utils"
const buttonVariants = cva(
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
+ "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
- default:
- "bg-primary text-primary-foreground shadow hover:bg-primary/90",
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
- "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
+ "bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
- "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
+ "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary:
- "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
- default: "h-9 px-4 py-2",
- sm: "h-8 rounded-md px-3 text-xs",
- lg: "h-10 rounded-md px-8",
- icon: "h-9 w-9",
+ default: "h-10 px-4 py-2",
+ sm: "h-9 rounded-md px-3",
+ lg: "h-11 rounded-md px-8",
+ icon: "h-10 w-10",
},
},
defaultVariants: {
diff --git a/frontend/src/components/ui/card.tsx b/frontend/src/components/ui/card.tsx
index cabfbfc59d955db9efc2049783cad4a71db55442..53fe59fa88b32d0db9dd3b4bd3cf962e9e45e6e6 100644
--- a/frontend/src/components/ui/card.tsx
+++ b/frontend/src/components/ui/card.tsx
@@ -1,5 +1,4 @@
import * as React from "react"
-
import { cn } from "@/lib/utils"
const Card = React.forwardRef<
@@ -9,7 +8,7 @@ const Card = React.forwardRef<
>(({ className, ...props }, ref) => (
-
+
))
CardHeader.displayName = "CardHeader"
const CardTitle = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes
+ HTMLParagraphElement,
+ React.HTMLAttributes
>(({ className, ...props }, ref) => (
-
))
CardTitle.displayName = "CardTitle"
const CardDescription = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes
+ HTMLParagraphElement,
+ React.HTMLAttributes
>(({ className, ...props }, ref) => (
-
>(({ className, ...props }, ref) => (
-
+
))
CardFooter.displayName = "CardFooter"
diff --git a/frontend/src/components/ui/chat.tsx b/frontend/src/components/ui/chat.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..9fa6c5cbcc2ff9ea71d2b60188d745c235e222bb
--- /dev/null
+++ b/frontend/src/components/ui/chat.tsx
@@ -0,0 +1,123 @@
+import * as React from 'react'
+import { cn } from '@/lib/utils'
+import { Button } from '@/components/ui/button'
+import { Textarea } from '@/components/ui/textarea'
+import { Send, Square, User, Bot } from 'lucide-react'
+
+export interface ChatProps extends React.HTMLAttributes
{
+ messages: Array<{
+ id: string
+ role: 'user' | 'assistant' | 'system'
+ content: string
+ createdAt?: Date
+ }>
+ input: string
+ handleInputChange: (e: React.ChangeEvent) => void
+ handleSubmit: (e: React.FormEvent) => void
+ isGenerating?: boolean
+ stop?: () => void
+}
+
+const Chat = React.forwardRef(
+ ({ className, messages, input, handleInputChange, handleSubmit, isGenerating, stop, ...props }, ref) => {
+ const messagesEndRef = React.useRef(null)
+
+ const scrollToBottom = () => {
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
+ }
+
+ React.useEffect(() => {
+ console.log('Chat component - messages updated:', messages.length, messages.map(m => ({ id: m.id, role: m.role, content: m.content.slice(0, 50) + '...' })))
+ scrollToBottom()
+ }, [messages])
+
+ return (
+
+ {/* Messages */}
+
+ {messages.length === 0 ? (
+
+
No messages yet. Start a conversation!
+
+ ) : (
+ messages.map((message, index) => (
+
+ {/* Avatar for assistant */}
+ {message.role !== 'user' && (
+
+
+
+ )}
+
+ {/* Message content */}
+
+
+ {message.role === 'user' ? 'You' : 'Assistant'} โข #{index + 1}
+
+
+ {message.content}
+
+
+
+ {/* Avatar for user */}
+ {message.role === 'user' && (
+
+
+
+ )}
+
+ ))
+ )}
+
+
+
+ {/* Input */}
+
+
+ {
+ if (e.key === 'Enter' && !e.shiftKey) {
+ e.preventDefault()
+ handleSubmit(e as any)
+ }
+ }}
+ />
+ {isGenerating ? (
+
+
+
+ ) : (
+
+
+
+ )}
+
+
+
+ )
+ }
+)
+Chat.displayName = 'Chat'
+
+export { Chat }
diff --git a/frontend/src/components/ui/collapsible.tsx b/frontend/src/components/ui/collapsible.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..a23e7a281287e18b1c332498491b6bcc8d8e2b70
--- /dev/null
+++ b/frontend/src/components/ui/collapsible.tsx
@@ -0,0 +1,9 @@
+import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"
+
+const Collapsible = CollapsiblePrimitive.Root
+
+const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger
+
+const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent
+
+export { Collapsible, CollapsibleTrigger, CollapsibleContent }
diff --git a/frontend/src/components/ui/label.tsx b/frontend/src/components/ui/label.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..e0d8e049fbb199cf4eafb5f54197af14f0379b19
--- /dev/null
+++ b/frontend/src/components/ui/label.tsx
@@ -0,0 +1,23 @@
+import * as React from "react"
+import * as LabelPrimitive from "@radix-ui/react-label"
+import { cva, type VariantProps } from "class-variance-authority"
+import { cn } from "@/lib/utils"
+
+const labelVariants = cva(
+ "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
+)
+
+const Label = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef &
+ VariantProps
+>(({ className, ...props }, ref) => (
+
+))
+Label.displayName = LabelPrimitive.Root.displayName
+
+export { Label }
diff --git a/frontend/src/components/ui/select.tsx b/frontend/src/components/ui/select.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..365102ba09713dc30eb04dde582b522188dd97ca
--- /dev/null
+++ b/frontend/src/components/ui/select.tsx
@@ -0,0 +1,156 @@
+import * as React from "react"
+import * as SelectPrimitive from "@radix-ui/react-select"
+import { Check, ChevronDown, ChevronUp } from "lucide-react"
+import { cn } from "@/lib/utils"
+
+const Select = SelectPrimitive.Root
+
+const SelectGroup = SelectPrimitive.Group
+
+const SelectValue = SelectPrimitive.Value
+
+const SelectTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+ span]:line-clamp-1",
+ className
+ )}
+ {...props}
+ >
+ {children}
+
+
+
+
+))
+SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
+
+const SelectScrollUpButton = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+))
+SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
+
+const SelectScrollDownButton = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+))
+SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName
+
+const SelectContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, position = "popper", ...props }, ref) => (
+
+
+
+
+ {children}
+
+
+
+
+))
+SelectContent.displayName = SelectPrimitive.Content.displayName
+
+const SelectLabel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+SelectLabel.displayName = SelectPrimitive.Label.displayName
+
+const SelectItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+
+
+
+
+ {children}
+
+))
+SelectItem.displayName = SelectPrimitive.Item.displayName
+
+const SelectSeparator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+SelectSeparator.displayName = SelectPrimitive.Separator.displayName
+
+export {
+ Select,
+ SelectGroup,
+ SelectValue,
+ SelectTrigger,
+ SelectContent,
+ SelectLabel,
+ SelectItem,
+ SelectSeparator,
+ SelectScrollUpButton,
+ SelectScrollDownButton,
+}
diff --git a/frontend/src/components/ui/slider.tsx b/frontend/src/components/ui/slider.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..fccdf6e9b124f9b0bf3e3ef94edfd19293b67e42
--- /dev/null
+++ b/frontend/src/components/ui/slider.tsx
@@ -0,0 +1,25 @@
+import * as React from "react"
+import * as SliderPrimitive from "@radix-ui/react-slider"
+import { cn } from "@/lib/utils"
+
+const Slider = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+
+
+
+))
+Slider.displayName = SliderPrimitive.Root.displayName
+
+export { Slider }
diff --git a/frontend/src/components/ui/switch.tsx b/frontend/src/components/ui/switch.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..fbb280ca8c828f45d4c31327b33ff083a44dcb03
--- /dev/null
+++ b/frontend/src/components/ui/switch.tsx
@@ -0,0 +1,26 @@
+import * as React from "react"
+import * as SwitchPrimitives from "@radix-ui/react-switch"
+import { cn } from "@/lib/utils"
+
+const Switch = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+))
+Switch.displayName = SwitchPrimitives.Root.displayName
+
+export { Switch }
diff --git a/frontend/src/components/ui/textarea.tsx b/frontend/src/components/ui/textarea.tsx
index e56b0affd7e03bd8359092a81d81c606ab09972d..c61fd6eff91df86112e1426e32a868f919380e2f 100644
--- a/frontend/src/components/ui/textarea.tsx
+++ b/frontend/src/components/ui/textarea.tsx
@@ -1,22 +1,23 @@
import * as React from "react"
-
import { cn } from "@/lib/utils"
-const Textarea = React.forwardRef<
- HTMLTextAreaElement,
- React.ComponentProps<"textarea">
->(({ className, ...props }, ref) => {
- return (
-
- )
-})
+export interface TextareaProps
+ extends React.TextareaHTMLAttributes {}
+
+const Textarea = React.forwardRef(
+ ({ className, ...props }, ref) => {
+ return (
+
+ )
+ }
+)
Textarea.displayName = "Textarea"
export { Textarea }
diff --git a/frontend/src/hooks/useChat.ts b/frontend/src/hooks/useChat.ts
index 55f1e94f3c4de26e9275736844d5dfc48430ae29..7048585d38969c814f68ca86effcf1ec0905d3ed 100644
--- a/frontend/src/hooks/useChat.ts
+++ b/frontend/src/hooks/useChat.ts
@@ -1,4 +1,4 @@
-import { useState, useEffect, useCallback } from 'react'
+import React, { useState, useEffect, useCallback } from 'react'
import { Message, ChatSession, MessageStatus } from '@/types/chat'
import { chatStorage } from '@/lib/chat-storage'
@@ -17,8 +17,8 @@ interface ApiResponse {
export function useChat(options: UseChatOptions = {}) {
const {
- api_endpoint = 'http://localhost:8000/generate',
- defaultModel = 'Qwen/Qwen3-4B-Instruct-2507',
+ api_endpoint = `${window.location.origin}/generate`,
+ defaultModel = 'Qwen/Qwen3-30B-A3B',
defaultSystemPrompt = ''
} = options
@@ -37,9 +37,22 @@ export function useChat(options: UseChatOptions = {}) {
const [temperature, setTemperature] = useState(0.7)
const [maxTokens, setMaxTokens] = useState(1024)
- // Current session
- const currentSession = sessions.find(s => s.id === currentSessionId) || null
- const messages = currentSession?.messages || []
+ // Current session - add dependency on sessions to force re-render
+ const currentSession = React.useMemo(() => {
+ const session = sessions.find((s: any) => s.id === currentSessionId) || null
+ console.log('useChat - currentSession updated:', {
+ sessionId: currentSessionId,
+ found: !!session,
+ messageCount: session?.messages?.length || 0
+ })
+ return session
+ }, [sessions, currentSessionId])
+
+ const messages = React.useMemo(() => {
+ const msgs = currentSession?.messages || []
+ console.log('useChat - messages computed:', msgs.length, msgs.map(m => ({ id: m.id, role: m.role })))
+ return msgs
+ }, [currentSession?.messages])
// Load sessions on mount
useEffect(() => {
@@ -66,7 +79,8 @@ export function useChat(options: UseChatOptions = {}) {
)
// Update React state with all sessions from localStorage
- setSessions(chatStorage.getAllSessions())
+ const updatedSessions = chatStorage.getAllSessions()
+ setSessions([...updatedSessions]) // Force update with new array reference
setCurrentSessionId(newSession.id)
chatStorage.setCurrentSession(newSession.id)
@@ -83,7 +97,7 @@ export function useChat(options: UseChatOptions = {}) {
const deleteSession = useCallback((sessionId: string) => {
chatStorage.deleteSession(sessionId)
const updatedSessions = chatStorage.getAllSessions()
- setSessions(updatedSessions)
+ setSessions([...updatedSessions]) // Force update with new array reference
if (currentSessionId === sessionId) {
if (updatedSessions.length > 0) {
@@ -98,7 +112,8 @@ export function useChat(options: UseChatOptions = {}) {
// Rename session
const renameSession = useCallback((sessionId: string, newTitle: string) => {
chatStorage.updateSession(sessionId, { title: newTitle })
- setSessions(chatStorage.getAllSessions())
+ const updatedSessions = chatStorage.getAllSessions()
+ setSessions([...updatedSessions]) // Force update with new array reference
}, [])
// Add message to current session
@@ -106,7 +121,9 @@ export function useChat(options: UseChatOptions = {}) {
if (!currentSessionId) return
chatStorage.addMessageToSession(currentSessionId, message)
- setSessions(chatStorage.getAllSessions())
+ // Force update with new array reference
+ const updatedSessions = chatStorage.getAllSessions()
+ setSessions([...updatedSessions])
}, [currentSessionId])
// Send message
@@ -130,7 +147,9 @@ export function useChat(options: UseChatOptions = {}) {
role: 'user',
content: userMessage
})
- setSessions(chatStorage.getAllSessions())
+ // Force update sessions state with fresh data
+ const updatedSessions = chatStorage.getAllSessions()
+ setSessions([...updatedSessions]) // Create new array reference to force re-render
}
// Add system message if system prompt is set
@@ -143,10 +162,21 @@ export function useChat(options: UseChatOptions = {}) {
role: 'system',
content: systemPrompt
})
- setSessions(chatStorage.getAllSessions())
+ // Force update sessions state with fresh data
+ const updatedSessions = chatStorage.getAllSessions()
+ setSessions([...updatedSessions]) // Create new array reference to force re-render
}
try {
+ // Get conversation history (excluding system messages for the API)
+ const actualSession = chatStorage.getSession(sessionId!)
+ const conversationHistory = actualSession?.messages
+ ?.filter((msg: any) => msg.role !== 'system')
+ ?.map((msg: any) => ({
+ role: msg.role,
+ content: msg.content
+ })) || []
+
const response = await fetch(api_endpoint, {
method: 'POST',
headers: {
@@ -154,6 +184,7 @@ export function useChat(options: UseChatOptions = {}) {
},
body: JSON.stringify({
prompt: userMessage,
+ messages: conversationHistory,
system_prompt: systemPrompt || null,
model_name: selectedModel,
temperature,
@@ -177,7 +208,9 @@ export function useChat(options: UseChatOptions = {}) {
model_used: data.model_used,
supports_thinking: data.supports_thinking
})
- setSessions(chatStorage.getAllSessions())
+ // Force update sessions state with fresh data
+ const updatedSessions = chatStorage.getAllSessions()
+ setSessions([...updatedSessions]) // Create new array reference to force re-render
}
setStatus({ isLoading: false, error: null })
@@ -191,7 +224,9 @@ export function useChat(options: UseChatOptions = {}) {
role: 'assistant',
content: `Sorry, I encountered an error: ${errorMessage}`
})
- setSessions(chatStorage.getAllSessions())
+ // Force update sessions state with fresh data
+ const updatedSessions = chatStorage.getAllSessions()
+ setSessions([...updatedSessions]) // Create new array reference to force re-render
}
}
}, [
diff --git a/frontend/src/index.css b/frontend/src/index.css
index 73b350f31090131c2b496f2588380c6844df7e05..7b06b6dc6774151b7ce74a5b7c850c0aafa4d36b 100644
--- a/frontend/src/index.css
+++ b/frontend/src/index.css
@@ -3,115 +3,52 @@
@tailwind utilities;
@layer base {
- body {
- @apply bg-gray-50 text-gray-900;
- }
:root {
-
--background: 0 0% 100%;
-
- --foreground: 0 0% 3.9%;
-
+ --foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
-
- --card-foreground: 0 0% 3.9%;
-
+ --card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
-
- --popover-foreground: 0 0% 3.9%;
-
- --primary: 0 0% 9%;
-
- --primary-foreground: 0 0% 98%;
-
- --secondary: 0 0% 96.1%;
-
- --secondary-foreground: 0 0% 9%;
-
- --muted: 0 0% 96.1%;
-
- --muted-foreground: 0 0% 45.1%;
-
- --accent: 0 0% 96.1%;
-
- --accent-foreground: 0 0% 9%;
-
+ --popover-foreground: 222.2 84% 4.9%;
+ --primary: 221.2 83.2% 53.3%;
+ --primary-foreground: 210 40% 98%;
+ --secondary: 210 40% 96%;
+ --secondary-foreground: 222.2 84% 4.9%;
+ --muted: 210 40% 96%;
+ --muted-foreground: 215.4 16.3% 46.9%;
+ --accent: 210 40% 96%;
+ --accent-foreground: 222.2 84% 4.9%;
--destructive: 0 84.2% 60.2%;
-
- --destructive-foreground: 0 0% 98%;
-
- --border: 0 0% 89.8%;
-
- --input: 0 0% 89.8%;
-
- --ring: 0 0% 3.9%;
-
- --chart-1: 12 76% 61%;
-
- --chart-2: 173 58% 39%;
-
- --chart-3: 197 37% 24%;
-
- --chart-4: 43 74% 66%;
-
- --chart-5: 27 87% 67%;
-
- --radius: 0.5rem
+ --destructive-foreground: 210 40% 98%;
+ --border: 214.3 31.8% 91.4%;
+ --input: 214.3 31.8% 91.4%;
+ --ring: 221.2 83.2% 53.3%;
+ --radius: 0.5rem;
}
- .dark {
-
- --background: 0 0% 3.9%;
-
- --foreground: 0 0% 98%;
-
- --card: 0 0% 3.9%;
-
- --card-foreground: 0 0% 98%;
-
- --popover: 0 0% 3.9%;
-
- --popover-foreground: 0 0% 98%;
-
- --primary: 0 0% 98%;
-
- --primary-foreground: 0 0% 9%;
-
- --secondary: 0 0% 14.9%;
-
- --secondary-foreground: 0 0% 98%;
-
- --muted: 0 0% 14.9%;
-
- --muted-foreground: 0 0% 63.9%;
-
- --accent: 0 0% 14.9%;
-
- --accent-foreground: 0 0% 98%;
+ .dark {
+ --background: 222.2 84% 4.9%;
+ --foreground: 210 40% 98%;
+ --card: 222.2 84% 4.9%;
+ --card-foreground: 210 40% 98%;
+ --popover: 222.2 84% 4.9%;
+ --popover-foreground: 210 40% 98%;
+ --primary: 217.2 91.2% 59.8%;
+ --primary-foreground: 222.2 84% 4.9%;
+ --secondary: 217.2 32.6% 17.5%;
+ --secondary-foreground: 210 40% 98%;
+ --muted: 217.2 32.6% 17.5%;
+ --muted-foreground: 215 20.2% 65.1%;
+ --accent: 217.2 32.6% 17.5%;
+ --accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
-
- --destructive-foreground: 0 0% 98%;
-
- --border: 0 0% 14.9%;
-
- --input: 0 0% 14.9%;
-
- --ring: 0 0% 83.1%;
-
- --chart-1: 220 70% 50%;
-
- --chart-2: 160 60% 45%;
-
- --chart-3: 30 80% 55%;
-
- --chart-4: 280 65% 60%;
-
- --chart-5: 340 75% 55%
+ --destructive-foreground: 210 40% 98%;
+ --border: 217.2 32.6% 17.5%;
+ --input: 217.2 32.6% 17.5%;
+ --ring: 224.3 76.3% 94.1%;
}
}
-
-
@layer base {
* {
@apply border-border;
@@ -120,19 +57,3 @@
@apply bg-background text-foreground;
}
}
-
-@layer utilities {
- .line-clamp-2 {
- display: -webkit-box;
- -webkit-line-clamp: 2;
- -webkit-box-orient: vertical;
- overflow: hidden;
- }
-
- .line-clamp-3 {
- display: -webkit-box;
- -webkit-line-clamp: 3;
- -webkit-box-orient: vertical;
- overflow: hidden;
- }
-}
diff --git a/frontend/src/lib/chat-storage.ts b/frontend/src/lib/chat-storage.ts
index e23dbdb7c4dff67c4c5630d5e79fcd207b8e78e1..175af44308f344cd4604abac63349b78f3c340e0 100644
--- a/frontend/src/lib/chat-storage.ts
+++ b/frontend/src/lib/chat-storage.ts
@@ -1,132 +1,105 @@
-import { ChatSession, Message, ChatStore } from '@/types/chat'
+import { ChatSession, Message } from '@/types/chat'
-const STORAGE_KEY = 'edge-llm-chat-store'
+const STORAGE_KEYS = {
+ sessions: 'edge-llm-sessions',
+ currentSession: 'edge-llm-current-session'
+}
-export const chatStorage = {
- // Load all chat data from localStorage
- load(): ChatStore {
- try {
- const stored = localStorage.getItem(STORAGE_KEY)
- if (!stored) {
- return { sessions: [], current_session_id: null }
- }
- return JSON.parse(stored)
- } catch (error) {
- console.error('Failed to load chat store:', error)
- return { sessions: [], current_session_id: null }
- }
- },
+function generateId(): string {
+ return Math.random().toString(36).substring(2) + Date.now().toString(36)
+}
- // Save chat data to localStorage
- save(store: ChatStore): void {
- try {
- localStorage.setItem(STORAGE_KEY, JSON.stringify(store))
- } catch (error) {
- console.error('Failed to save chat store:', error)
- }
- },
+function generateMessageId(): string {
+ return generateId()
+}
+
+class ChatStorageManager {
+ getAllSessions(): ChatSession[] {
+ const stored = localStorage.getItem(STORAGE_KEYS.sessions)
+ return stored ? JSON.parse(stored) : []
+ }
+
+ getSession(sessionId: string): ChatSession | null {
+ const sessions = this.getAllSessions()
+ return sessions.find(s => s.id === sessionId) || null
+ }
- // Create a new chat session
- createSession(title?: string, model_name?: string, system_prompt?: string): ChatSession {
- const now = Date.now()
+ createSession(title?: string, model?: string, systemPrompt?: string): ChatSession {
const newSession: ChatSession = {
- id: `session_${now}_${Math.random().toString(36).substr(2, 9)}`,
- title: title || `New Chat ${new Date(now).toLocaleDateString()}`,
+ id: generateId(),
+ title: title || `New Chat ${new Date().toLocaleString()}`,
messages: [],
- created_at: now,
- updated_at: now,
- model_name,
- system_prompt,
+ model,
+ systemPrompt,
+ createdAt: Date.now(),
+ updatedAt: Date.now()
}
-
- // Save the new session to localStorage immediately
- const store = this.load()
- store.sessions.unshift(newSession) // Add to beginning of array
- this.save(store)
-
- return newSession
- },
- // Add message to session
- addMessageToSession(sessionId: string, message: Omit): void {
- const store = this.load()
- const session = store.sessions.find(s => s.id === sessionId)
+ const sessions = this.getAllSessions()
+ sessions.unshift(newSession)
+ localStorage.setItem(STORAGE_KEYS.sessions, JSON.stringify(sessions))
- if (session) {
- const newMessage: Message = {
- ...message,
- id: `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
- timestamp: Date.now(),
- }
-
- session.messages.push(newMessage)
- session.updated_at = Date.now()
-
- // Update session title based on first user message
- if (session.messages.length === 1 && message.role === 'user') {
- session.title = message.content.slice(0, 50) + (message.content.length > 50 ? '...' : '')
- }
-
- this.save(store)
- }
- },
-
- // Get session by ID
- getSession(sessionId: string): ChatSession | null {
- const store = this.load()
- return store.sessions.find(s => s.id === sessionId) || null
- },
+ return newSession
+ }
- // Update session
updateSession(sessionId: string, updates: Partial): void {
- const store = this.load()
- const sessionIndex = store.sessions.findIndex(s => s.id === sessionId)
+ const sessions = this.getAllSessions()
+ const sessionIndex = sessions.findIndex(s => s.id === sessionId)
if (sessionIndex !== -1) {
- store.sessions[sessionIndex] = {
- ...store.sessions[sessionIndex],
+ sessions[sessionIndex] = {
+ ...sessions[sessionIndex],
...updates,
- updated_at: Date.now(),
+ updatedAt: Date.now()
}
- this.save(store)
+ localStorage.setItem(STORAGE_KEYS.sessions, JSON.stringify(sessions))
}
- },
+ }
- // Delete session
deleteSession(sessionId: string): void {
- const store = this.load()
- store.sessions = store.sessions.filter(s => s.id !== sessionId)
+ const sessions = this.getAllSessions()
+ const filtered = sessions.filter(s => s.id !== sessionId)
+ localStorage.setItem(STORAGE_KEYS.sessions, JSON.stringify(filtered))
- // If deleting current session, clear current_session_id
- if (store.current_session_id === sessionId) {
- store.current_session_id = store.sessions.length > 0 ? store.sessions[0].id : null
+ if (this.getCurrentSessionId() === sessionId) {
+ localStorage.removeItem(STORAGE_KEYS.currentSession)
}
+ }
+
+ addMessageToSession(sessionId: string, message: Omit): void {
+ const sessions = this.getAllSessions()
+ const sessionIndex = sessions.findIndex(s => s.id === sessionId)
- this.save(store)
- },
+ if (sessionIndex !== -1) {
+ const newMessage: Message = {
+ ...message,
+ id: generateMessageId(),
+ timestamp: Date.now()
+ }
+
+ sessions[sessionIndex].messages.push(newMessage)
+ sessions[sessionIndex].updatedAt = Date.now()
+ localStorage.setItem(STORAGE_KEYS.sessions, JSON.stringify(sessions))
+ }
+ }
- // Set current session
- setCurrentSession(sessionId: string): void {
- const store = this.load()
- store.current_session_id = sessionId
- this.save(store)
- },
+ getCurrentSessionId(): string | null {
+ return localStorage.getItem(STORAGE_KEYS.currentSession)
+ }
- // Get current session
getCurrentSession(): ChatSession | null {
- const store = this.load()
- if (!store.current_session_id) return null
- return this.getSession(store.current_session_id)
- },
+ const currentId = this.getCurrentSessionId()
+ return currentId ? this.getSession(currentId) : null
+ }
- // Get all sessions sorted by updated_at
- getAllSessions(): ChatSession[] {
- const store = this.load()
- return store.sessions.sort((a, b) => b.updated_at - a.updated_at)
- },
+ setCurrentSession(sessionId: string): void {
+ localStorage.setItem(STORAGE_KEYS.currentSession, sessionId)
+ }
- // Clear all data
clear(): void {
- localStorage.removeItem(STORAGE_KEY)
- },
+ localStorage.removeItem(STORAGE_KEYS.sessions)
+ localStorage.removeItem(STORAGE_KEYS.currentSession)
+ }
}
+
+export const chatStorage = new ChatStorageManager()
diff --git a/frontend/src/lib/utils.ts b/frontend/src/lib/utils.ts
index bd0c391ddd1088e9067844c48835bf4abcd61783..d084ccade0d8b5bd77fd5174993bcef7b57644c9 100644
--- a/frontend/src/lib/utils.ts
+++ b/frontend/src/lib/utils.ts
@@ -1,4 +1,4 @@
-import { clsx, type ClassValue } from "clsx"
+import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx
index 696bc3f8a146d3209ac46b94aa2b22acc90ee2c8..9fb36b3ff610b8f3e4a5451fbd890aae680f06b2 100644
--- a/frontend/src/pages/Home.tsx
+++ b/frontend/src/pages/Home.tsx
@@ -8,8 +8,7 @@ import {
Zap,
Shield,
Cpu,
- ArrowRight,
- Download
+ ArrowRight
} from 'lucide-react'
import { Link } from 'react-router-dom'
@@ -67,115 +66,106 @@ const quickActions = [
export function Home() {
return (
- {/* Header */}
-
-
-
-
-
- {/* Hero Section */}
-
-
-
- Local AI Platform
+
+ {/* Hero Section */}
+
+
+
+
-
- Welcome to Edge LLM
-
-
- A powerful local AI platform for running language models privately on your machine.
- Experience the future of AI without compromising your privacy.
-
-
- {/* Quick Actions */}
-
- {quickActions.map((action) => (
-
-
-
-
-
-
{action.title}
-
{action.description}
-
-
-
-
- ))}
+
Edge LLM
+
+ Your local AI companion. Run powerful language models on your own hardware with complete privacy and control.
+
+
+
+
+
+ Start Chatting
+
+
+
+
+
+ Browse Models
+
+
+
- {/* Features Grid */}
-
-
-
Key Features
-
- Everything you need for local AI development and experimentation
-
-
-
-
- {features.map((feature, index) => (
-
-
-
-
-
{feature.title}
-
{feature.description}
-
-
-
- ))}
-
-
+ {/* Features Grid */}
+
+ {features.map((feature, index) => (
+
+
+
+ {feature.title}
+ {feature.description}
+
+
+ ))}
+
- {/* Getting Started */}
+ {/* Quick Actions */}
+
+ {quickActions.map((action, index) => (
+
+
+
+ {action.description}
+
+
+ Get Started
+
+
+
+
+
+ ))}
+
+
+ {/* Getting Started */}
+
-
-
- Getting Started
-
+ Getting Started
-
-
+
+
-
+
1
-
Choose a Model
+
Browse Available Models
- Browse the model catalog and select a model that fits your needs.
+ Check out our model catalog to see what's available for your use case.
-
+
2
-
Load the Model
+
Load a Model
- Click the load button to download and prepare the model for use.
+ Select and load a model that fits your hardware and requirements.
-
+
3
Start Chatting
@@ -203,29 +193,34 @@ export function Home() {
System Status
-
-
-
-
+
+
+
Backend Status
+
+
+ Online
+
-
-
-
-
Models
-
Ready to load
-
+
+
+ Models Loaded
+ Ready
-
-
-
+
+
+ Memory Usage
+ Optimized
+
+
+
+
+
+ View Settings
+
+
+
diff --git a/frontend/src/pages/Models.tsx b/frontend/src/pages/Models.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..8b72e4fc2753e7b079f4197aa89d0b04ae7f1149
--- /dev/null
+++ b/frontend/src/pages/Models.tsx
@@ -0,0 +1,339 @@
+import { useState, useEffect } from 'react'
+import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'
+import { Button } from '@/components/ui/button'
+import { Badge } from '@/components/ui/badge'
+import {
+ BookOpen,
+ Brain,
+ Zap,
+ Download,
+ Trash2,
+ Loader2,
+ Info,
+ CheckCircle,
+ Cloud,
+ HardDrive
+} from 'lucide-react'
+
+interface ModelInfo {
+ model_name: string
+ name: string
+ supports_thinking: boolean
+ description: string
+ size_gb: string
+ is_loaded: boolean
+ type: 'local' | 'api'
+}
+
+interface ModelsResponse {
+ models: ModelInfo[]
+ current_model: string
+}
+
+export function Models() {
+ const [models, setModels] = useState
([])
+ const [loading, setLoading] = useState(true)
+ const [modelLoading, setModelLoading] = useState(null)
+
+ useEffect(() => {
+ fetchModels()
+ }, [])
+
+ const fetchModels = async () => {
+ try {
+ const baseUrl = window.location.hostname === 'localhost' ? `${window.location.protocol}//${window.location.host}` : ''
+ const res = await fetch(`${baseUrl}/models`)
+ if (res.ok) {
+ const data: ModelsResponse = await res.json()
+ setModels(data.models)
+ }
+ } catch (err) {
+ console.error('Failed to fetch models:', err)
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ const handleLoadModel = async (modelName: string) => {
+ setModelLoading(modelName)
+ try {
+ const baseUrl = window.location.hostname === 'localhost' ? `${window.location.protocol}//${window.location.host}` : ''
+ const res = await fetch(`${baseUrl}/load-model`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ model_name: modelName }),
+ })
+
+ if (res.ok) {
+ await fetchModels()
+ }
+ } catch (err) {
+ console.error('Failed to load model:', err)
+ } finally {
+ setModelLoading(null)
+ }
+ }
+
+ const handleUnloadModel = async (modelName: string) => {
+ try {
+ const baseUrl = window.location.hostname === 'localhost' ? `${window.location.protocol}//${window.location.host}` : ''
+ const res = await fetch(`${baseUrl}/unload-model`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ model_name: modelName }),
+ })
+
+ if (res.ok) {
+ await fetchModels()
+ }
+ } catch (err) {
+ console.error('Failed to unload model:', err)
+ }
+ }
+
+ if (loading) {
+ return (
+
+ )
+ }
+
+ return (
+
+ {/* Header */}
+
+
+
+
+
Model Catalog
+
+
+
+ Refresh
+
+
+
+
+
+
+
+
+ {/* Info Card */}
+
+
+
+
+
+
Model Management
+
+ Load models to use them in the playground. Models are cached locally for faster access.
+ Each model requires significant storage space and initial download time.
+
+
+
+
+
+
+ {/* API Models Section */}
+
+
+
+ API Models
+ Cloud-Powered
+
+
+ {models.filter(m => m.type === 'api').map((model) => (
+
+ ))}
+
+
+
+ {/* Local Models Section */}
+
+
+
+ Local Models
+ Privacy-First
+
+
+ {models.filter(m => m.type === 'local').map((model) => (
+
+ ))}
+
+
+
+ {/* Stats Card */}
+
+
+ Model Statistics
+
+
+
+
+
{models.length}
+
Available Models
+
+
+
+ {models.filter(m => m.is_loaded).length}
+
+
Loaded Models
+
+
+
+ {models.filter(m => m.supports_thinking).length}
+
+
Thinking Models
+
+
+
+
+
+
+
+ )
+}
+
+// ModelCard component for reusability
+interface ModelCardProps {
+ model: ModelInfo
+ modelLoading: string | null
+ onLoad: (modelName: string) => void
+ onUnload: (modelName: string) => void
+}
+
+function ModelCard({ model, modelLoading, onLoad, onUnload }: ModelCardProps) {
+ const isApiModel = model.type === 'api'
+
+ return (
+
+
+
+
+ {isApiModel ? (
+
+ ) : model.supports_thinking ? (
+
+ ) : (
+
+ )}
+
+
{model.name}
+
+ {isApiModel ? (
+
+
+ API Model
+
+ ) : (
+
+
+ {model.supports_thinking ? "Thinking Model" : "Instruction Model"}
+
+ )}
+ {model.is_loaded && (
+
+
+ {isApiModel ? "Ready" : "Loaded"}
+
+ )}
+
+
+
+
+
+
+
+
{model.description}
+
+ Size: {model.size_gb}
+ {!isApiModel && Format: Safetensors }
+ {isApiModel && Type: Cloud API }
+
+
+
+
+
Capabilities
+
+ Text Generation
+ Conversation
+ Code
+ {model.supports_thinking && (
+ Reasoning
+ )}
+ {isApiModel && model.model_name.includes('vl') && (
+ Vision
+ )}
+
+
+
+
+ {model.is_loaded ? (
+
+ {!isApiModel && (
+
onUnload(model.model_name)}
+ className="flex-1"
+ >
+
+ Unload
+
+ )}
+
+ Use in Playground
+
+
+ ) : (
+
onLoad(model.model_name)}
+ disabled={modelLoading === model.model_name}
+ className="w-full"
+ size="sm"
+ >
+ {modelLoading === model.model_name ? (
+ <>
+
+ {isApiModel ? "Connecting..." : "Loading..."}
+ >
+ ) : (
+ <>
+ {isApiModel ? (
+
+ ) : (
+
+ )}
+ {isApiModel ? "Connect" : "Load Model"}
+ >
+ )}
+
+ )}
+
+
+
+ )
+}
diff --git a/frontend/src/pages/Playground.tsx b/frontend/src/pages/Playground.tsx
index 762ac19c0b3b9d800bbbf595d56afa7d2cc585d6..b882bd2b13c3be60805f55a39a5bc437dfa5e1ec 100644
--- a/frontend/src/pages/Playground.tsx
+++ b/frontend/src/pages/Playground.tsx
@@ -5,29 +5,35 @@ import { Slider } from '@/components/ui/slider'
import { Label } from '@/components/ui/label'
import { Badge } from '@/components/ui/badge'
import {
- AlertDialog,
- AlertDialogAction,
- AlertDialogCancel,
- AlertDialogContent,
- AlertDialogDescription,
- AlertDialogFooter,
- AlertDialogHeader,
- AlertDialogTitle
-} from '@/components/ui/alert-dialog'
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectLabel,
+ SelectTrigger,
+ SelectValue,
+} from '@/components/ui/select'
+
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger
} from '@/components/ui/collapsible'
-import { ChatContainer } from '@/components/chat/ChatContainer'
-import { ChatSessions } from '@/components/chat/ChatSessions'
+import {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+} from '@/components/ui/alert-dialog'
+import { Chat } from '@/components/ui/chat'
import { useChat } from '@/hooks/useChat'
import {
- Loader2,
Brain,
Zap,
- Download,
- Trash2,
ChevronDown,
MessageSquare,
RotateCcw,
@@ -37,7 +43,13 @@ import {
History,
Settings,
PanelLeftOpen,
- PanelLeftClose
+ PanelLeftClose,
+ Cloud,
+ BookOpen,
+ Download,
+ AlertTriangle,
+ Plus,
+ Trash2
} from 'lucide-react'
interface ModelInfo {
@@ -47,6 +59,7 @@ interface ModelInfo {
description: string
size_gb: string
is_loaded: boolean
+ type: 'local' | 'api'
}
interface ModelsResponse {
@@ -63,7 +76,7 @@ export function Playground() {
createNewSession,
selectSession,
deleteSession,
- renameSession,
+
messages,
input,
setInput,
@@ -83,16 +96,12 @@ export function Playground() {
// UI state
const [showSessions, setShowSessions] = useState(false)
const [isSystemPromptOpen, setIsSystemPromptOpen] = useState(false)
+ const [autoLoadingModel, setAutoLoadingModel] = useState(null)
+ const [showLoadConfirm, setShowLoadConfirm] = useState(false)
+ const [pendingModelToLoad, setPendingModelToLoad] = useState(null)
// Model management state
const [models, setModels] = useState([])
- const [modelLoading, setModelLoading] = useState(null)
- const [showLoadConfirm, setShowLoadConfirm] = useState(false)
- const [showUnloadConfirm, setShowUnloadConfirm] = useState(false)
- const [pendingModelAction, setPendingModelAction] = useState<{
- action: 'load' | 'unload'
- model: ModelInfo | null
- }>({ action: 'load', model: null })
// Preset system prompts
const systemPromptPresets = [
@@ -153,108 +162,168 @@ export function Playground() {
// Update selected model when models change
useEffect(() => {
- if (selectedModel && !models.find(m => m.model_name === selectedModel && m.is_loaded)) {
- const loadedModel = models.find(m => m.is_loaded)
- if (loadedModel) {
- setSelectedModel(loadedModel.model_name)
+ // Only reset if the selected model no longer exists in the models list
+ if (selectedModel && !models.find(m => m.model_name === selectedModel)) {
+ const firstModel = models[0]
+ if (firstModel) {
+ setSelectedModel(firstModel.model_name)
}
}
}, [models, selectedModel, setSelectedModel])
- const fetchModels = async () => {
- try {
- const res = await fetch('http://localhost:8000/models')
- if (res.ok) {
- const data: ModelsResponse = await res.json()
- setModels(data.models)
-
- // Set selected model to current model if available, otherwise first loaded model
- if (data.current_model && selectedModel !== data.current_model) {
- setSelectedModel(data.current_model)
- } else if (!selectedModel) {
- const loadedModel = data.models.find(m => m.is_loaded)
- if (loadedModel) {
- setSelectedModel(loadedModel.model_name)
+ // Auto-load/unload local models when selection changes
+ useEffect(() => {
+ const handleModelChange = async () => {
+ if (!selectedModel || !models.length) return
+
+ const selectedModelInfo = models.find(m => m.model_name === selectedModel)
+ if (!selectedModelInfo) return
+
+ const baseUrl = window.location.hostname === 'localhost' ? `${window.location.protocol}//${window.location.host}` : ''
+
+ // If selected model is a local model and not loaded, show confirmation
+ if (selectedModelInfo.type === 'local' && !selectedModelInfo.is_loaded) {
+ setPendingModelToLoad(selectedModelInfo)
+ setShowLoadConfirm(true)
+ return // Don't auto-load, wait for user confirmation
+ }
+
+ // Unload other local models that are loaded but not selected
+ const loadedLocalModels = models.filter(m =>
+ m.type === 'local' &&
+ m.is_loaded &&
+ m.model_name !== selectedModel
+ )
+
+ for (const model of loadedLocalModels) {
+ try {
+ const response = await fetch(`${baseUrl}/unload-model`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ model_name: model.model_name })
+ })
+
+ if (response.ok) {
+ console.log(`โ
Auto-unloaded local model: ${model.model_name}`)
}
+ } catch (error) {
+ console.error(`Error auto-unloading model ${model.model_name}:`, error)
}
}
- } catch (err) {
- console.error('Failed to fetch models:', err)
+
+ // Refresh models after any unloading
+ if (loadedLocalModels.length > 0) {
+ fetchModels()
+ }
}
- }
- const handleLoadModelClick = (model: ModelInfo) => {
- setPendingModelAction({ action: 'load', model })
- setShowLoadConfirm(true)
- }
-
- const handleUnloadModelClick = (model: ModelInfo) => {
- setPendingModelAction({ action: 'unload', model })
- setShowUnloadConfirm(true)
- }
+ handleModelChange()
+ }, [selectedModel, models])
- const confirmLoadModel = async () => {
- const model = pendingModelAction.model
- if (!model) return
-
- setModelLoading(model.model_name)
+ const handleLoadModelConfirm = async () => {
+ if (!pendingModelToLoad) return
+
setShowLoadConfirm(false)
+ setAutoLoadingModel(pendingModelToLoad.model_name)
try {
- const res = await fetch('http://localhost:8000/load-model', {
+ const baseUrl = window.location.hostname === 'localhost' ? `${window.location.protocol}//${window.location.host}` : ''
+ const response = await fetch(`${baseUrl}/load-model`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ model_name: model.model_name }),
+ body: JSON.stringify({ model_name: pendingModelToLoad.model_name })
})
- if (res.ok) {
- await fetchModels()
-
- // Set as selected model
- setSelectedModel(model.model_name)
+ if (response.ok) {
+ console.log(`โ
User confirmed and loaded: ${pendingModelToLoad.model_name}`)
+ fetchModels() // Refresh model states
} else {
- const errorData = await res.json()
- console.error(`Failed to load model: ${errorData.detail || 'Unknown error'}`)
+ console.error(`โ Failed to load model: ${pendingModelToLoad.model_name}`)
+ // Revert to an API model if load failed
+ const apiModel = models.find(m => m.type === 'api')
+ if (apiModel) {
+ setSelectedModel(apiModel.model_name)
+ }
+ }
+ } catch (error) {
+ console.error('Error loading model:', error)
+ // Revert to an API model if error
+ const apiModel = models.find(m => m.type === 'api')
+ if (apiModel) {
+ setSelectedModel(apiModel.model_name)
}
- } catch (err) {
- console.error(`Failed to load model: ${err instanceof Error ? err.message : 'Unknown error'}`)
} finally {
- setModelLoading(null)
+ setAutoLoadingModel(null)
+ setPendingModelToLoad(null)
}
}
- const confirmUnloadModel = async () => {
- const model = pendingModelAction.model
- if (!model) return
-
- setShowUnloadConfirm(false)
+ const handleLoadModelCancel = () => {
+ setShowLoadConfirm(false)
+ setPendingModelToLoad(null)
- try {
- const res = await fetch('http://localhost:8000/unload-model', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ model_name: model.model_name }),
- })
+ // Revert to an API model
+ const apiModel = models.find(m => m.type === 'api')
+ if (apiModel) {
+ setSelectedModel(apiModel.model_name)
+ }
+ }
+
+ // Cleanup: unload all local models when component unmounts or user leaves
+ useEffect(() => {
+ const handlePageUnload = async () => {
+ const baseUrl = window.location.hostname === 'localhost' ? `${window.location.protocol}//${window.location.host}` : ''
+ const loadedLocalModels = models.filter(m => m.type === 'local' && m.is_loaded)
+ for (const model of loadedLocalModels) {
+ try {
+ await fetch(`${baseUrl}/unload-model`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ model_name: model.model_name })
+ })
+ console.log(`โ
Cleanup: unloaded ${model.model_name}`)
+ } catch (error) {
+ console.error(`Error cleaning up model ${model.model_name}:`, error)
+ }
+ }
+ }
+
+ // Cleanup on component unmount
+ return () => {
+ handlePageUnload()
+ }
+ }, [models])
+
+ const fetchModels = async () => {
+ try {
+ const baseUrl = window.location.hostname === 'localhost' ? `${window.location.protocol}//${window.location.host}` : ''
+ const res = await fetch(`${baseUrl}/models`)
if (res.ok) {
- await fetchModels()
+ const data: ModelsResponse = await res.json()
+ setModels(data.models)
- // If we unloaded the selected model, find another loaded model
- if (selectedModel === model.model_name) {
- const remainingLoaded = models.find(m => m.is_loaded && m.model_name !== model.model_name)
- if (remainingLoaded) {
- setSelectedModel(remainingLoaded.model_name)
- }
+ // Set selected model to current model if available, otherwise first API model
+ if (data.current_model && selectedModel !== data.current_model) {
+ setSelectedModel(data.current_model)
+ } else if (!selectedModel && data.models.length > 0) {
+ // Prefer API models as default
+ const apiModel = data.models.find(m => m.type === 'api')
+ const defaultModel = apiModel || data.models[0]
+ setSelectedModel(defaultModel.model_name)
}
- } else {
- const errorData = await res.json()
- console.error(`Failed to unload model: ${errorData.detail || 'Unknown error'}`)
}
} catch (err) {
- console.error(`Failed to unload model: ${err instanceof Error ? err.message : 'Unknown error'}`)
+ console.error('Failed to fetch models:', err)
}
}
+
+
+
+
+
+
const handleSamplePromptClick = (samplePrompt: string) => {
setInput(samplePrompt)
}
@@ -267,14 +336,44 @@ export function Playground() {
fixed inset-y-0 left-0 z-50 w-80 bg-background border-r transition-transform duration-300 ease-in-out
lg:translate-x-0 lg:static lg:inset-0
`}>
-
+
+
+
Chat Sessions
+
+
+ New
+
+
+
+ {sessions.map((session) => (
+
selectSession(session.id)}
+ >
+
+ {session.title}
+ {
+ e.stopPropagation()
+ deleteSession(session.id)
+ }}
+ className="h-6 w-6 p-0"
+ >
+
+
+
+
+ {session.messages.length} messages
+
+
+ ))}
+
+
{/* Overlay for mobile */}
@@ -368,19 +467,22 @@ export function Playground() {
)}
{/* Chat Messages and Input */}
-
({
+ id: msg.id,
+ role: msg.role as 'user' | 'assistant' | 'system',
+ content: msg.content,
+ createdAt: new Date(msg.timestamp)
+ }))}
input={input}
- onInputChange={setInput}
- onSubmit={sendMessage}
- onStop={stopGeneration}
- isLoading={isLoading}
- disabled={!selectedModel || !models.find(m => m.model_name === selectedModel)?.is_loaded}
- placeholder={
- !selectedModel || !models.find(m => m.model_name === selectedModel)?.is_loaded
- ? "Please load a model first..."
- : "Ask me anything..."
- }
+ handleInputChange={(e) => setInput(e.target.value)}
+ handleSubmit={async (e) => {
+ e.preventDefault()
+ if (!selectedModel || !models.find(m => m.model_name === selectedModel)) return
+ await sendMessage()
+ }}
+ isGenerating={isLoading}
+ stop={stopGeneration}
className="flex-1"
/>
@@ -393,87 +495,94 @@ export function Playground() {
Configuration
- {/* Model Management */}
+ {/* Model Selection */}
- Model Management
+ Model Selection
- {models.map((model) => (
-
-
- {/* Model Header */}
-
- {model.supports_thinking ?
:
}
-
-
- {model.name}
- {model.model_name === selectedModel && (
- Active
- )}
- {model.is_loaded && model.model_name !== selectedModel && (
- Loaded
- )}
-
-
- {model.description} โข {model.size_gb}
-
-
-
-
- {/* Model Selection */}
- {model.is_loaded && (
-
- setSelectedModel(model.model_name)}
- className="h-3 w-3 flex-shrink-0"
- />
- Use for generation
-
- )}
+ {/* Simple Model Dropdown */}
+
+
Active Model
+
+
+
+ {selectedModel && (() => {
+ const model = models.find(m => m.model_name === selectedModel)
+ if (!model) return selectedModel
+ const isApiModel = model.type === 'api'
+ return (
+
+ {isApiModel ? (
+
+ ) : model.supports_thinking ? (
+
+ ) : (
+
+ )}
+ {model.name}
+ {autoLoadingModel === selectedModel ? (
+
+ Loading...
+
+ ) : (
+
+ {isApiModel ? "API" : model.is_loaded ? "Loaded" : "Available"}
+
+ )}
+
+ )
+ })()}
+
+
+
+
+ ๐ API Models
+ {models.filter(m => m.type === 'api').map((model) => (
+
+
+
+ {model.name}
+ API
+
+
+ ))}
+
+
+ ๐ป Local Models
+ {models.filter(m => m.type === 'local').map((model) => (
+
+
+ {model.supports_thinking ? (
+
+ ) : (
+
+ )}
+ {model.name}
+ {autoLoadingModel === model.model_name ? (
+ Loading...
+ ) : model.is_loaded ? (
+ Loaded
+ ) : (
+ Available
+ )}
+
+
+ ))}
+
+
+
+
- {/* Action Button */}
-
- {model.is_loaded ? (
- handleUnloadModelClick(model)}
- disabled={isLoading}
- className="h-8 px-3 text-xs flex-shrink-0"
- >
-
- Unload
-
- ) : (
- handleLoadModelClick(model)}
- disabled={isLoading || modelLoading === model.model_name}
- className="h-8 px-3 text-xs flex-shrink-0 min-w-[80px]"
- >
- {modelLoading === model.model_name ? (
- <>
-
- Loading...
- >
- ) : (
- <>
-
- Load
- >
- )}
-
- )}
-
-
-
- ))}
+ {/* Model Catalog Link */}
+
@@ -490,7 +599,7 @@ export function Playground() {
setTemperature(value[0])}
+ onValueChange={(value: number[]) => setTemperature(value[0])}
min={0}
max={2}
step={0.01}
@@ -509,7 +618,7 @@ export function Playground() {
setMaxTokens(value[0])}
+ onValueChange={(value: number[]) => setMaxTokens(value[0])}
min={100}
max={4096}
step={100}
@@ -581,7 +690,7 @@ export function Playground() {
setSystemPrompt(e.target.value)}
+ onChange={(e: React.ChangeEvent) => setSystemPrompt(e.target.value)}
placeholder="Enter custom system prompt to define how the model should behave..."
className="w-full min-h-[80px] text-xs p-2 border rounded-md bg-background"
disabled={isLoading}
@@ -599,47 +708,59 @@ export function Playground() {
- {/* Load Model Confirmation Dialog */}
+ {/* Model Load Confirmation Dialog */}
- Load Model
-
- Do you want to load {pendingModelAction.model?.name} ?
-
- Size: {pendingModelAction.model?.size_gb}
-
- Note: This will download the model if it's not already cached locally.
- This may take several minutes and use significant bandwidth and storage.
-
-
-
- Cancel
-
- Load Model
-
-
-
-
+
+
+ Load Local Model
+
+
+
+
+ You're about to load {pendingModelToLoad?.name} locally.
+
+
+
+
+
+
+
Resource Requirements:
+
+ โข Storage: {pendingModelToLoad?.size_gb}
+ โข RAM: ~{pendingModelToLoad?.size_gb} (while running)
+ โข Download: First-time loading will download the model
+
+
+
+
- {/* Unload Model Confirmation Dialog */}
-
-
-
- Unload Model
-
- Are you sure you want to unload {pendingModelAction.model?.name} ?
-
- This will free up memory but you'll need to reload it to use it again.
- {pendingModelAction.model?.model_name === selectedModel && (
- <>Warning: This is the currently active model.>
- )}
+
+
+
Model Features:
+
{pendingModelToLoad?.description}
+ {pendingModelToLoad?.supports_thinking && (
+
+
+ Supports thinking process
+
+ )}
+
+
+
+
+ The model will be cached locally for faster future access. You can unload it anytime to free up memory.
+
+
- Cancel
-
- Unload Model
+
+ Cancel
+
+
+ Load Model
diff --git a/frontend/src/pages/Settings.tsx b/frontend/src/pages/Settings.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..8f648092ee52ae2823de1f1da48d4c9d285c1a7d
--- /dev/null
+++ b/frontend/src/pages/Settings.tsx
@@ -0,0 +1,228 @@
+import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'
+import { Button } from '@/components/ui/button'
+import { Label } from '@/components/ui/label'
+import { Switch } from '@/components/ui/switch'
+import { Badge } from '@/components/ui/badge'
+import {
+ Settings as SettingsIcon,
+ Server,
+ Brain,
+ Palette,
+ Shield,
+ Info
+} from 'lucide-react'
+
+export function Settings() {
+ return (
+
+ {/* Header */}
+
+
+
+
+
+ {/* Server Settings */}
+
+
+
+
+ Server Configuration
+
+
+
+
+
+
Backend URL
+
+ {window.location.origin}
+
+
+
+
Frontend URL
+
+ {window.location.origin}
+
+
+
+
+
+
+
Auto-connect on startup
+
Automatically connect to backend when app starts
+
+
+
+
+
+
+
+ {/* Model Settings */}
+
+
+
+
+ Model Preferences
+
+
+
+
+
+
Auto-load last used model
+
Automatically load the last used model on startup
+
+
+
+
+
+
Show model thinking process
+
Display thinking content for supported models
+
+
+
+
+
+
Confirm model loading
+
Show confirmation dialog before downloading models
+
+
+
+
+
+
+ {/* Interface Settings */}
+
+
+
+
+ Interface
+
+
+
+
+
+
Dark mode
+
Use dark theme for the interface
+
+
+
+
+
+
Compact mode
+
Use smaller spacing for more content
+
+
+
+
+
+
Show sample prompts
+
Display quick start prompts in playground
+
+
+
+
+
+
+ {/* Privacy Settings */}
+
+
+
+
+ Privacy & Data
+
+
+
+
+
+
+ Local Processing
+
+
+ All AI processing happens locally on your machine. No data is sent to external servers.
+
+
+
+
+
Save conversation history
+
Store conversations locally for reference
+
+
+
+
+
+
Save system prompts
+
Remember custom system prompts
+
+
+
+
+
+
+ {/* System Information */}
+
+
+
+
+ System Information
+
+
+
+
+
+
Platform
+
+ Local
+ Privacy-focused AI
+
+
+
+
+
+
+
+
Storage Location
+
~/.edge-llm/models/
+
+
+ Open Folder
+
+
+
+
+
+
+ {/* Actions */}
+
+
+ Actions
+
+
+
+
+ Export Settings
+
+
+ Import Settings
+
+
+ Reset to Defaults
+
+
+
+
+
+
+
+ )
+}
diff --git a/frontend/src/types/chat.ts b/frontend/src/types/chat.ts
index bbe504a96abc504b2ddbf6efe68285f74b326a6b..5d4e8fd67eb3f1e10cc94573252950e127151773 100644
--- a/frontend/src/types/chat.ts
+++ b/frontend/src/types/chat.ts
@@ -2,8 +2,8 @@ export interface Message {
id: string
role: 'user' | 'assistant' | 'system'
content: string
- thinking_content?: string
timestamp: number
+ thinking_content?: string
model_used?: string
supports_thinking?: boolean
}
@@ -12,18 +12,13 @@ export interface ChatSession {
id: string
title: string
messages: Message[]
- created_at: number
- updated_at: number
- model_name?: string
- system_prompt?: string
-}
-
-export interface ChatStore {
- sessions: ChatSession[]
- current_session_id: string | null
+ model?: string
+ systemPrompt?: string
+ createdAt: number
+ updatedAt: number
}
export interface MessageStatus {
isLoading: boolean
error: string | null
-}
+}
\ No newline at end of file
diff --git a/frontend/src/vite-env.d.ts b/frontend/src/vite-env.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ef86892202f8436908a29ecc4cf24a92d8de23f1
--- /dev/null
+++ b/frontend/src/vite-env.d.ts
@@ -0,0 +1,13 @@
+///
+
+interface ImportMetaEnv {
+ readonly MODE: string
+ readonly BASE_URL: string
+ readonly PROD: boolean
+ readonly DEV: boolean
+ // more env variables...
+}
+
+interface ImportMeta {
+ readonly env: ImportMetaEnv
+}
diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js
index 045b95a856ac894fe0cde397ec74f2b20f8cc9c1..e5db970cc33d931710c00e30556c552736048667 100644
--- a/frontend/tailwind.config.js
+++ b/frontend/tailwind.config.js
@@ -1,91 +1,56 @@
/** @type {import('tailwindcss').Config} */
-module.exports = {
- darkMode: ["class"],
+export default {
content: [
- './pages/**/*.{ts,tsx}',
- './components/**/*.{ts,tsx}',
- './app/**/*.{ts,tsx}',
- './src/**/*.{ts,tsx}',
+ "./index.html",
+ "./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
- container: {
- center: true,
- padding: '2rem',
- screens: {
- '2xl': '1400px'
- }
- },
- extend: {
- colors: {
- border: 'hsl(var(--border))',
- input: 'hsl(var(--input))',
- ring: 'hsl(var(--ring))',
- background: 'hsl(var(--background))',
- foreground: 'hsl(var(--foreground))',
- primary: {
- DEFAULT: 'hsl(var(--primary))',
- foreground: 'hsl(var(--primary-foreground))'
- },
- secondary: {
- DEFAULT: 'hsl(var(--secondary))',
- foreground: 'hsl(var(--secondary-foreground))'
- },
- destructive: {
- DEFAULT: 'hsl(var(--destructive))',
- foreground: 'hsl(var(--destructive-foreground))'
- },
- muted: {
- DEFAULT: 'hsl(var(--muted))',
- foreground: 'hsl(var(--muted-foreground))'
- },
- accent: {
- DEFAULT: 'hsl(var(--accent))',
- foreground: 'hsl(var(--accent-foreground))'
- },
- popover: {
- DEFAULT: 'hsl(var(--popover))',
- foreground: 'hsl(var(--popover-foreground))'
- },
- card: {
- DEFAULT: 'hsl(var(--card))',
- foreground: 'hsl(var(--card-foreground))'
- },
- chart: {
- '1': 'hsl(var(--chart-1))',
- '2': 'hsl(var(--chart-2))',
- '3': 'hsl(var(--chart-3))',
- '4': 'hsl(var(--chart-4))',
- '5': 'hsl(var(--chart-5))'
- }
- },
- borderRadius: {
- lg: 'var(--radius)',
- md: 'calc(var(--radius) - 2px)',
- sm: 'calc(var(--radius) - 4px)'
- },
- keyframes: {
- 'accordion-down': {
- from: {
- height: 0
- },
- to: {
- height: 'var(--radix-accordion-content-height)'
- }
- },
- 'accordion-up': {
- from: {
- height: 'var(--radix-accordion-content-height)'
- },
- to: {
- height: 0
- }
- }
- },
- animation: {
- 'accordion-down': 'accordion-down 0.2s ease-out',
- 'accordion-up': 'accordion-up 0.2s ease-out'
- }
- }
+ extend: {
+ colors: {
+ border: "hsl(var(--border))",
+ input: "hsl(var(--input))",
+ ring: "hsl(var(--ring))",
+ background: "hsl(var(--background))",
+ foreground: "hsl(var(--foreground))",
+ primary: {
+ DEFAULT: "hsl(var(--primary))",
+ foreground: "hsl(var(--primary-foreground))",
+ },
+ secondary: {
+ DEFAULT: "hsl(var(--secondary))",
+ foreground: "hsl(var(--secondary-foreground))",
+ },
+ destructive: {
+ DEFAULT: "hsl(var(--destructive))",
+ foreground: "hsl(var(--destructive-foreground))",
+ },
+ muted: {
+ DEFAULT: "hsl(var(--muted))",
+ foreground: "hsl(var(--muted-foreground))",
+ },
+ accent: {
+ DEFAULT: "hsl(var(--accent))",
+ foreground: "hsl(var(--accent-foreground))",
+ },
+ popover: {
+ DEFAULT: "hsl(var(--popover))",
+ foreground: "hsl(var(--popover-foreground))",
+ },
+ card: {
+ DEFAULT: "hsl(var(--card))",
+ foreground: "hsl(var(--card-foreground))",
+ },
+ },
+ borderRadius: {
+ lg: "var(--radius)",
+ md: "calc(var(--radius) - 2px)",
+ sm: "calc(var(--radius) - 4px)",
+ },
+ animation: {
+ "accordion-down": "accordion-down 0.2s ease-out",
+ "accordion-up": "accordion-up 0.2s ease-out",
+ },
+ },
},
- plugins: [require("tailwindcss-animate"), require("@tailwindcss/typography")],
+ plugins: [require("tailwindcss-animate")],
}
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
index 13e485dec31782176d37337c1c104f5466cfd402..964d907b49f308cef32fecd8467c3b86c1f5d27f 100644
--- a/frontend/vite.config.ts
+++ b/frontend/vite.config.ts
@@ -2,7 +2,6 @@ import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'
-// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
resolve: {
@@ -10,4 +9,12 @@ export default defineConfig({
"@": path.resolve(__dirname, "./src"),
},
},
+ server: {
+ port: 5173,
+ host: true
+ },
+ build: {
+ outDir: 'dist',
+ sourcemap: true
+ }
})
diff --git a/package.json b/package.json
deleted file mode 100644
index 12098d1826d005c1e4a22ffbf274b1771ba8e417..0000000000000000000000000000000000000000
--- a/package.json
+++ /dev/null
@@ -1,42 +0,0 @@
-{
- "name": "edge-llm",
- "version": "1.0.0",
- "description": "Local AI Chat Platform with Modern UI",
- "scripts": {
- "dev": "concurrently \"npm run backend\" \"npm run frontend\"",
- "backend": "uvicorn app:app --host 0.0.0.0 --port 8000 --reload",
- "frontend": "cd frontend && npm run dev",
- "build": "cd frontend && npm run build && cp -r dist/* ../static/",
- "build:frontend": "cd frontend && npm run build",
- "build:docker": "docker build -t edge-llm .",
- "preview": "cd frontend && npm run preview",
- "install:all": "pip install -r requirements.txt && cd frontend && npm install",
- "start": "python scripts/start_platform.py",
- "stop": "python scripts/stop_platform.py",
- "test": "cd frontend && npm run test",
- "deploy": "npm run build && echo 'Ready for deployment'",
- "clean": "rm -rf frontend/dist frontend/node_modules __pycache__ .cache"
- },
- "repository": {
- "type": "git",
- "url": "https://huggingface.co/spaces/wu981526092/EdgeLLM"
- },
- "keywords": [
- "ai",
- "llm",
- "chat",
- "fastapi",
- "react",
- "local",
- "privacy"
- ],
- "author": "EdgeLLM Contributors",
- "license": "MIT",
- "devDependencies": {
- "concurrently": "^8.2.0"
- },
- "engines": {
- "node": ">=18.0.0",
- "python": ">=3.9.0"
- }
-}
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 458fccd49bcc96713605c7da900ebdbee2f3e262..3137454be667df0b335be7c8b5f368530c34505f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,3 +5,5 @@ torch>=2.1.0
accelerate>=0.24.0
pydantic>=2.5.0
python-multipart>=0.0.6
+openai>=1.0.0
+python-dotenv>=1.0.0
diff --git a/scripts/start_both.bat b/scripts/start_both.bat
deleted file mode 100644
index 927cca495fbca0b408d098f7e4d291478057947e..0000000000000000000000000000000000000000
--- a/scripts/start_both.bat
+++ /dev/null
@@ -1,51 +0,0 @@
-@echo off
-echo.
-echo ========================================
-echo Edge LLM Platform Startup Script
-echo ========================================
-echo.
-
-echo [1/4] Checking prerequisites...
-
-REM Check if virtual environment exists
-if not exist ".venv\Scripts\activate.bat" (
- echo ERROR: Virtual environment not found!
- echo Please run: python -m venv .venv
- echo Then: pip install -r requirements.txt
- pause
- exit /b 1
-)
-
-REM Check if frontend dependencies exist
-if not exist "frontend\node_modules" (
- echo ERROR: Frontend dependencies not found!
- echo Please run: cd frontend && npm install
- pause
- exit /b 1
-)
-
-echo [2/4] Starting backend server...
-start "Edge LLM Backend" cmd /k "call .venv\Scripts\activate.bat && cd backend && python app.py"
-
-echo [3/4] Waiting for backend to initialize...
-timeout /t 3 /nobreak >nul
-
-echo [4/4] Starting frontend development server...
-start "Edge LLM Frontend" cmd /k "cd frontend && npm run dev"
-
-echo.
-echo ========================================
-echo ๐ Edge LLM Platform Starting...
-echo ========================================
-echo.
-echo Backend: http://localhost:8000
-echo Frontend: http://localhost:5173
-echo.
-echo Both services are starting in separate windows.
-echo Close this window to keep services running.
-echo.
-echo To stop services:
-echo - Close the backend and frontend windows, OR
-echo - Run: stop_both.bat
-echo.
-pause
diff --git a/scripts/start_platform.py b/scripts/start_platform.py
deleted file mode 100644
index cf463c878a7077b5c8d8d3a0ab592fbda698ad8b..0000000000000000000000000000000000000000
--- a/scripts/start_platform.py
+++ /dev/null
@@ -1,279 +0,0 @@
-#!/usr/bin/env python3
-"""
-Edge LLM Platform Startup Script
-Starts both backend and frontend services simultaneously
-"""
-
-import os
-import sys
-import subprocess
-import time
-import signal
-import platform
-import webbrowser
-from pathlib import Path
-
-class EdgeLLMStarter:
- def __init__(self):
- self.processes = []
- self.is_windows = platform.system() == "Windows"
- self.project_root = Path(__file__).parent
-
- def print_banner(self):
- print("\n" + "="*50)
- print(" ๐ค Edge LLM Platform Startup")
- print("="*50)
-
- def check_prerequisites(self):
- print("\n[1/5] ๐ Checking prerequisites...")
-
- # Check virtual environment
- venv_path = self.project_root / ".venv"
- if self.is_windows:
- venv_python = venv_path / "Scripts" / "python.exe"
- venv_activate = venv_path / "Scripts" / "activate.bat"
- else:
- venv_python = venv_path / "bin" / "python"
- venv_activate = venv_path / "bin" / "activate"
-
- if not venv_path.exists() or not venv_python.exists():
- print("โ Virtual environment not found!")
- print("Please run: python -m venv .venv")
- print("Then: pip install -r requirements.txt")
- return False
-
- # Check backend dependencies
- requirements_file = self.project_root / "requirements.txt"
- if not requirements_file.exists():
- print("โ requirements.txt not found!")
- return False
-
- # Check frontend dependencies
- node_modules = self.project_root / "frontend" / "node_modules"
- package_json = self.project_root / "frontend" / "package.json"
-
- if not package_json.exists():
- print("โ Frontend package.json not found!")
- return False
-
- if not node_modules.exists():
- print("โ Frontend dependencies not installed!")
- print("Please run: cd frontend && npm install")
- return False
-
- print("โ
All prerequisites satisfied")
- return True
-
- def start_backend(self):
- print("\n[2/5] ๐ Starting backend server...")
-
- backend_dir = self.project_root / "backend"
- if not backend_dir.exists():
- print("โ Backend directory not found!")
- return None
-
- try:
- if self.is_windows:
- # Windows: use call to activate venv and run python
- cmd = [
- "cmd", "/c",
- f"call {self.project_root}/.venv/Scripts/activate.bat && "
- f"cd {backend_dir} && python app.py"
- ]
- process = subprocess.Popen(
- cmd,
- creationflags=subprocess.CREATE_NEW_CONSOLE,
- cwd=str(self.project_root)
- )
- else:
- # Unix: use source to activate venv
- cmd = f"source {self.project_root}/.venv/bin/activate && cd {backend_dir} && python app.py"
- process = subprocess.Popen(
- cmd,
- shell=True,
- cwd=str(self.project_root)
- )
-
- self.processes.append(("Backend", process))
- print("โ
Backend starting...")
- return process
-
- except Exception as e:
- print(f"โ Failed to start backend: {e}")
- return None
-
- def start_frontend(self):
- print("\n[3/5] โ๏ธ Starting frontend development server...")
-
- frontend_dir = self.project_root / "frontend"
- if not frontend_dir.exists():
- print("โ Frontend directory not found!")
- return None
-
- try:
- if self.is_windows:
- cmd = ["cmd", "/c", "npm run dev"]
- process = subprocess.Popen(
- cmd,
- creationflags=subprocess.CREATE_NEW_CONSOLE,
- cwd=str(frontend_dir)
- )
- else:
- cmd = ["npm", "run", "dev"]
- process = subprocess.Popen(
- cmd,
- cwd=str(frontend_dir)
- )
-
- self.processes.append(("Frontend", process))
- print("โ
Frontend starting...")
- return process
-
- except Exception as e:
- print(f"โ Failed to start frontend: {e}")
- return None
-
- def wait_for_services(self):
- print("\n[4/5] โณ Waiting for services to initialize...")
-
- # Wait a bit for services to start
- for i in range(5, 0, -1):
- print(f" Waiting {i} seconds...", end="\r")
- time.sleep(1)
- print(" Services should be ready! ")
-
- def check_services(self):
- print("\n[5/5] ๐ Checking service status...")
-
- try:
- import requests
-
- # Check backend
- try:
- response = requests.get("http://localhost:8000/", timeout=5)
- if response.status_code == 200:
- print("โ
Backend: Running on http://localhost:8000")
- else:
- print(f"โ ๏ธ Backend: HTTP {response.status_code}")
- except:
- print("โณ Backend: Still starting up...")
-
- # Check frontend
- try:
- response = requests.get("http://localhost:5173/", timeout=5)
- if response.status_code == 200:
- print("โ
Frontend: Running on http://localhost:5173")
- else:
- print(f"โ ๏ธ Frontend: HTTP {response.status_code}")
- except:
- print("โณ Frontend: Still starting up...")
-
- except ImportError:
- print("โน๏ธ Install 'requests' package to check service status")
- print(" pip install requests")
-
- def open_browser(self):
- """Open the application in default browser"""
- try:
- print("\n๐ Opening Edge LLM in your browser...")
- webbrowser.open("http://localhost:5173")
- except:
- pass
-
- def show_info(self):
- print("\n" + "="*50)
- print(" ๐ Edge LLM Platform Started!")
- print("="*50)
- print("\n๐ Access URLs:")
- print(" Frontend: http://localhost:5173")
- print(" Backend: http://localhost:8000")
- print(" API Docs: http://localhost:8000/docs")
-
- print("\n๐ก Usage:")
- print(" 1. Go to http://localhost:5173/playground")
- print(" 2. Load a model from the right panel")
- print(" 3. Start chatting!")
-
- print("\n๐ To stop services:")
- if self.is_windows:
- print(" - Close the backend and frontend windows, OR")
- print(" - Run: stop_both.bat, OR")
- print(" - Press Ctrl+C in this window")
-
- def cleanup(self):
- """Clean up processes when shutting down"""
- print("\n๐ Shutting down Edge LLM Platform...")
-
- for name, process in self.processes:
- try:
- print(f" Stopping {name}...")
- if self.is_windows:
- subprocess.run(["taskkill", "/F", "/T", "/PID", str(process.pid)],
- capture_output=True)
- else:
- process.terminate()
- process.wait(timeout=5)
- except:
- pass
-
- print("โ
All services stopped")
-
- def run(self):
- """Main execution function"""
- try:
- self.print_banner()
-
- if not self.check_prerequisites():
- input("\nPress Enter to exit...")
- return 1
-
- backend_process = self.start_backend()
- if not backend_process:
- return 1
-
- frontend_process = self.start_frontend()
- if not frontend_process:
- return 1
-
- self.wait_for_services()
- self.check_services()
- self.show_info()
-
- # Open browser after a short delay
- time.sleep(2)
- self.open_browser()
-
- print("\nโจ๏ธ Press Ctrl+C to stop all services...")
-
- # Keep the script running
- while True:
- time.sleep(1)
-
- # Check if processes are still running
- for name, process in self.processes:
- if process.poll() is not None:
- print(f"\nโ ๏ธ {name} process stopped unexpectedly")
-
- except KeyboardInterrupt:
- print("\n\nโจ๏ธ Received stop signal...")
-
- except Exception as e:
- print(f"\nโ Unexpected error: {e}")
-
- finally:
- self.cleanup()
-
- return 0
-
-def signal_handler(signum, frame):
- """Handle Ctrl+C gracefully"""
- print("\n\nโจ๏ธ Received stop signal...")
- sys.exit(0)
-
-if __name__ == "__main__":
- # Handle Ctrl+C gracefully
- signal.signal(signal.SIGINT, signal_handler)
-
- starter = EdgeLLMStarter()
- exit_code = starter.run()
- sys.exit(exit_code)
diff --git a/scripts/start_platform.sh b/scripts/start_platform.sh
deleted file mode 100644
index d50db5e0af8f00b674a97d62f27a7144bfaa89bd..0000000000000000000000000000000000000000
--- a/scripts/start_platform.sh
+++ /dev/null
@@ -1,216 +0,0 @@
-#!/bin/bash
-
-# Edge LLM Platform Startup Script for Linux/macOS
-# Starts both backend and frontend services
-
-# Colors for output
-RED='\033[0;31m'
-GREEN='\033[0;32m'
-YELLOW='\033[1;33m'
-BLUE='\033[0;34m'
-NC='\033[0m' # No Color
-
-# Function to print colored output
-print_status() {
- echo -e "${GREEN}[INFO]${NC} $1"
-}
-
-print_error() {
- echo -e "${RED}[ERROR]${NC} $1"
-}
-
-print_warning() {
- echo -e "${YELLOW}[WARNING]${NC} $1"
-}
-
-print_banner() {
- echo ""
- echo "=================================================="
- echo " ๐ค Edge LLM Platform Startup Script"
- echo "=================================================="
- echo ""
-}
-
-# Cleanup function
-cleanup() {
- echo ""
- print_status "๐ Shutting down Edge LLM Platform..."
-
- # Kill background jobs
- jobs -p | xargs -r kill 2>/dev/null
-
- # Kill any remaining python/node processes related to our project
- pkill -f "backend/app.py" 2>/dev/null
- pkill -f "npm run dev" 2>/dev/null
-
- print_status "โ
All services stopped"
- exit 0
-}
-
-# Set up signal handlers
-trap cleanup SIGINT SIGTERM
-
-check_prerequisites() {
- print_status "[1/5] ๐ Checking prerequisites..."
-
- # Check if virtual environment exists
- if [ ! -d ".venv" ] || [ ! -f ".venv/bin/python" ]; then
- print_error "Virtual environment not found!"
- echo "Please run: python -m venv .venv"
- echo "Then: source .venv/bin/activate && pip install -r requirements.txt"
- return 1
- fi
-
- # Check if frontend dependencies exist
- if [ ! -d "frontend/node_modules" ] || [ ! -f "frontend/package.json" ]; then
- print_error "Frontend dependencies not found!"
- echo "Please run: cd frontend && npm install"
- return 1
- fi
-
- print_status "โ
All prerequisites satisfied"
- return 0
-}
-
-start_backend() {
- print_status "[2/5] ๐ Starting backend server..."
-
- if [ ! -d "backend" ]; then
- print_error "Backend directory not found!"
- return 1
- fi
-
- # Start backend in background
- (
- source .venv/bin/activate
- cd backend
- python app.py
- ) &
-
- BACKEND_PID=$!
- print_status "โ
Backend starting (PID: $BACKEND_PID)..."
- return 0
-}
-
-start_frontend() {
- print_status "[3/5] โ๏ธ Starting frontend development server..."
-
- if [ ! -d "frontend" ]; then
- print_error "Frontend directory not found!"
- return 1
- fi
-
- # Start frontend in background
- (
- cd frontend
- npm run dev
- ) &
-
- FRONTEND_PID=$!
- print_status "โ
Frontend starting (PID: $FRONTEND_PID)..."
- return 0
-}
-
-wait_for_services() {
- print_status "[4/5] โณ Waiting for services to initialize..."
-
- for i in {5..1}; do
- printf "\r Waiting %d seconds..." $i
- sleep 1
- done
- printf "\r Services should be ready! \n"
-}
-
-check_services() {
- print_status "[5/5] ๐ Checking service status..."
-
- # Check if curl is available
- if command -v curl &> /dev/null; then
- # Check backend
- if curl -s http://localhost:8000/ >/dev/null 2>&1; then
- print_status "โ
Backend: Running on http://localhost:8000"
- else
- print_warning "โณ Backend: Still starting up..."
- fi
-
- # Check frontend
- if curl -s http://localhost:5173/ >/dev/null 2>&1; then
- print_status "โ
Frontend: Running on http://localhost:5173"
- else
- print_warning "โณ Frontend: Still starting up..."
- fi
- else
- print_warning "Install 'curl' to check service status"
- fi
-}
-
-open_browser() {
- print_status "๐ Opening Edge LLM in your browser..."
-
- # Try to open browser (works on most Linux distributions and macOS)
- if command -v xdg-open &> /dev/null; then
- xdg-open http://localhost:5173 >/dev/null 2>&1 &
- elif command -v open &> /dev/null; then
- open http://localhost:5173 >/dev/null 2>&1 &
- fi
-}
-
-show_info() {
- echo ""
- echo "=================================================="
- echo " ๐ Edge LLM Platform Started!"
- echo "=================================================="
- echo ""
- echo "๐ Access URLs:"
- echo " Frontend: http://localhost:5173"
- echo " Backend: http://localhost:8000"
- echo " API Docs: http://localhost:8000/docs"
- echo ""
- echo "๐ก Usage:"
- echo " 1. Go to http://localhost:5173/playground"
- echo " 2. Load a model from the right panel"
- echo " 3. Start chatting!"
- echo ""
- echo "๐ To stop services:"
- echo " - Press Ctrl+C in this terminal"
- echo ""
-}
-
-main() {
- print_banner
-
- # Check prerequisites
- if ! check_prerequisites; then
- read -p "Press Enter to exit..."
- exit 1
- fi
-
- # Start services
- if ! start_backend; then
- exit 1
- fi
-
- if ! start_frontend; then
- exit 1
- fi
-
- # Wait and check
- wait_for_services
- check_services
- show_info
-
- # Open browser after a short delay
- sleep 2
- open_browser
-
- print_status "โจ๏ธ Press Ctrl+C to stop all services..."
-
- # Keep script running and wait for services
- wait
-}
-
-# Make sure we're in the right directory
-cd "$(dirname "$0")"
-
-# Run main function
-main
diff --git a/scripts/stop_both.bat b/scripts/stop_both.bat
deleted file mode 100644
index 2b14328bc935a0697358a971c6da6a136e642140..0000000000000000000000000000000000000000
--- a/scripts/stop_both.bat
+++ /dev/null
@@ -1,34 +0,0 @@
-@echo off
-echo.
-echo ========================================
-echo Edge LLM Platform Stop Script
-echo ========================================
-echo.
-
-echo [1/3] Stopping backend processes...
-taskkill /F /IM python.exe 2>nul
-if %errorlevel% == 0 (
- echo โ
Backend processes stopped
-) else (
- echo โ ๏ธ No backend processes found
-)
-
-echo [2/3] Stopping frontend processes...
-taskkill /F /IM node.exe 2>nul
-if %errorlevel% == 0 (
- echo โ
Frontend processes stopped
-) else (
- echo โ ๏ธ No frontend processes found
-)
-
-echo [3/3] Stopping any remaining Edge LLM processes...
-for /f "tokens=2" %%i in ('tasklist /FI "WINDOWTITLE eq Edge LLM*" /FO CSV ^| find /v "PID"') do (
- taskkill /F /PID %%i 2>nul
-)
-
-echo.
-echo ========================================
-echo ๐ All Edge LLM services stopped
-echo ========================================
-echo.
-pause
diff --git a/static/assets/index-286a4216.js b/static/assets/index-286a4216.js
deleted file mode 100644
index 8fa0bb15af136333eca845da25449247476b243f..0000000000000000000000000000000000000000
--- a/static/assets/index-286a4216.js
+++ /dev/null
@@ -1,342 +0,0 @@
-function Ev(e,t){for(var n=0;n
r[i]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))r(i);new MutationObserver(i=>{for(const l of i)if(l.type==="childList")for(const o of l.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&r(o)}).observe(document,{childList:!0,subtree:!0});function n(i){const l={};return i.integrity&&(l.integrity=i.integrity),i.referrerPolicy&&(l.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?l.credentials="include":i.crossOrigin==="anonymous"?l.credentials="omit":l.credentials="same-origin",l}function r(i){if(i.ep)return;i.ep=!0;const l=n(i);fetch(i.href,l)}})();var Xl=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function To(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var Ep={exports:{}},Ro={},bp={exports:{}},q={};/**
- * @license React
- * react.production.min.js
- *
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */var qi=Symbol.for("react.element"),bv=Symbol.for("react.portal"),jv=Symbol.for("react.fragment"),Pv=Symbol.for("react.strict_mode"),_v=Symbol.for("react.profiler"),Tv=Symbol.for("react.provider"),Rv=Symbol.for("react.context"),Iv=Symbol.for("react.forward_ref"),Mv=Symbol.for("react.suspense"),Av=Symbol.for("react.memo"),Lv=Symbol.for("react.lazy"),Qc=Symbol.iterator;function Dv(e){return e===null||typeof e!="object"?null:(e=Qc&&e[Qc]||e["@@iterator"],typeof e=="function"?e:null)}var jp={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},Pp=Object.assign,_p={};function Vr(e,t,n){this.props=e,this.context=t,this.refs=_p,this.updater=n||jp}Vr.prototype.isReactComponent={};Vr.prototype.setState=function(e,t){if(typeof e!="object"&&typeof e!="function"&&e!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")};Vr.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function Tp(){}Tp.prototype=Vr.prototype;function xu(e,t,n){this.props=e,this.context=t,this.refs=_p,this.updater=n||jp}var wu=xu.prototype=new Tp;wu.constructor=xu;Pp(wu,Vr.prototype);wu.isPureReactComponent=!0;var Yc=Array.isArray,Rp=Object.prototype.hasOwnProperty,ku={current:null},Ip={key:!0,ref:!0,__self:!0,__source:!0};function Mp(e,t,n){var r,i={},l=null,o=null;if(t!=null)for(r in t.ref!==void 0&&(o=t.ref),t.key!==void 0&&(l=""+t.key),t)Rp.call(t,r)&&!Ip.hasOwnProperty(r)&&(i[r]=t[r]);var s=arguments.length-2;if(s===1)i.children=n;else if(1>>1,J=_[W];if(0>>1;Wi(lt,k))oei(ot,lt)?(_[W]=ot,_[oe]=k,W=oe):(_[W]=lt,_[ke]=k,W=ke);else if(oei(ot,k))_[W]=ot,_[oe]=k,W=oe;else break e}}return O}function i(_,O){var k=_.sortIndex-O.sortIndex;return k!==0?k:_.id-O.id}if(typeof performance=="object"&&typeof performance.now=="function"){var l=performance;e.unstable_now=function(){return l.now()}}else{var o=Date,s=o.now();e.unstable_now=function(){return o.now()-s}}var a=[],c=[],d=1,f=null,p=3,h=!1,w=!1,y=!1,S=typeof setTimeout=="function"?setTimeout:null,m=typeof clearTimeout=="function"?clearTimeout:null,g=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function x(_){for(var O=n(c);O!==null;){if(O.callback===null)r(c);else if(O.startTime<=_)r(c),O.sortIndex=O.expirationTime,t(a,O);else break;O=n(c)}}function N(_){if(y=!1,x(_),!w)if(n(a)!==null)w=!0,Q(b);else{var O=n(c);O!==null&&Y(N,O.startTime-_)}}function b(_,O){w=!1,y&&(y=!1,m(R),R=-1),h=!0;var k=p;try{for(x(O),f=n(a);f!==null&&(!(f.expirationTime>O)||_&&!A());){var W=f.callback;if(typeof W=="function"){f.callback=null,p=f.priorityLevel;var J=W(f.expirationTime<=O);O=e.unstable_now(),typeof J=="function"?f.callback=J:f===n(a)&&r(a),x(O)}else r(a);f=n(a)}if(f!==null)var C=!0;else{var ke=n(c);ke!==null&&Y(N,ke.startTime-O),C=!1}return C}finally{f=null,p=k,h=!1}}var E=!1,j=null,R=-1,z=5,L=-1;function A(){return!(e.unstable_now()-L_||125<_?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):z=0<_?Math.floor(1e3/_):5},e.unstable_getCurrentPriorityLevel=function(){return p},e.unstable_getFirstCallbackNode=function(){return n(a)},e.unstable_next=function(_){switch(p){case 1:case 2:case 3:var O=3;break;default:O=p}var k=p;p=O;try{return _()}finally{p=k}},e.unstable_pauseExecution=function(){},e.unstable_requestPaint=function(){},e.unstable_runWithPriority=function(_,O){switch(_){case 1:case 2:case 3:case 4:case 5:break;default:_=3}var k=p;p=_;try{return O()}finally{p=k}},e.unstable_scheduleCallback=function(_,O,k){var W=e.unstable_now();switch(typeof k=="object"&&k!==null?(k=k.delay,k=typeof k=="number"&&0W?(_.sortIndex=k,t(c,_),n(a)===null&&_===n(c)&&(y?(m(R),R=-1):y=!0,Y(N,k-W))):(_.sortIndex=J,t(a,_),w||h||(w=!0,Q(b))),_},e.unstable_shouldYield=A,e.unstable_wrapCallback=function(_){var O=p;return function(){var k=p;p=O;try{return _.apply(this,arguments)}finally{p=k}}}})(Op);zp.exports=Op;var Kv=zp.exports;/**
- * @license React
- * react-dom.production.min.js
- *
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */var Qv=v,pt=Kv;function I(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Xs=Object.prototype.hasOwnProperty,Yv=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,Xc={},Jc={};function qv(e){return Xs.call(Jc,e)?!0:Xs.call(Xc,e)?!1:Yv.test(e)?Jc[e]=!0:(Xc[e]=!0,!1)}function Xv(e,t,n,r){if(n!==null&&n.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return r?!1:n!==null?!n.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function Jv(e,t,n,r){if(t===null||typeof t>"u"||Xv(e,t,n,r))return!0;if(r)return!1;if(n!==null)switch(n.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function Ye(e,t,n,r,i,l,o){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=r,this.attributeNamespace=i,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=l,this.removeEmptyString=o}var Oe={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){Oe[e]=new Ye(e,0,!1,e,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];Oe[t]=new Ye(t,1,!1,e[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(e){Oe[e]=new Ye(e,2,!1,e.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){Oe[e]=new Ye(e,2,!1,e,null,!1,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){Oe[e]=new Ye(e,3,!1,e.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(e){Oe[e]=new Ye(e,3,!0,e,null,!1,!1)});["capture","download"].forEach(function(e){Oe[e]=new Ye(e,4,!1,e,null,!1,!1)});["cols","rows","size","span"].forEach(function(e){Oe[e]=new Ye(e,6,!1,e,null,!1,!1)});["rowSpan","start"].forEach(function(e){Oe[e]=new Ye(e,5,!1,e.toLowerCase(),null,!1,!1)});var Nu=/[\-:]([a-z])/g;function Eu(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(Nu,Eu);Oe[t]=new Ye(t,1,!1,e,null,!1,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(Nu,Eu);Oe[t]=new Ye(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)});["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(Nu,Eu);Oe[t]=new Ye(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(e){Oe[e]=new Ye(e,1,!1,e.toLowerCase(),null,!1,!1)});Oe.xlinkHref=new Ye("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(e){Oe[e]=new Ye(e,1,!1,e.toLowerCase(),null,!0,!0)});function bu(e,t,n,r){var i=Oe.hasOwnProperty(t)?Oe[t]:null;(i!==null?i.type!==0:r||!(2s||i[o]!==l[s]){var a=`
-`+i[o].replace(" at new "," at ");return e.displayName&&a.includes("")&&(a=a.replace("",e.displayName)),a}while(1<=o&&0<=s);break}}}finally{os=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?di(e):""}function Zv(e){switch(e.tag){case 5:return di(e.type);case 16:return di("Lazy");case 13:return di("Suspense");case 19:return di("SuspenseList");case 0:case 2:case 15:return e=ss(e.type,!1),e;case 11:return e=ss(e.type.render,!1),e;case 1:return e=ss(e.type,!0),e;default:return""}}function ta(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case mr:return"Fragment";case hr:return"Portal";case Js:return"Profiler";case ju:return"StrictMode";case Zs:return"Suspense";case ea:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case Up:return(e.displayName||"Context")+".Consumer";case Bp:return(e._context.displayName||"Context")+".Provider";case Pu:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case _u:return t=e.displayName||null,t!==null?t:ta(e.type)||"Memo";case pn:t=e._payload,e=e._init;try{return ta(e(t))}catch{}}return null}function ex(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return ta(t);case 8:return t===ju?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function _n(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function Vp(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function tx(e){var t=Vp(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&typeof n.get=="function"&&typeof n.set=="function"){var i=n.get,l=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return i.call(this)},set:function(o){r=""+o,l.call(this,o)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(o){r=""+o},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function cl(e){e._valueTracker||(e._valueTracker=tx(e))}function Hp(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=Vp(e)?e.checked?"true":"false":e.value),e=r,e!==n?(t.setValue(e),!0):!1}function Jl(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function na(e,t){var n=t.checked;return xe({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function ed(e,t){var n=t.defaultValue==null?"":t.defaultValue,r=t.checked!=null?t.checked:t.defaultChecked;n=_n(t.value!=null?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function Wp(e,t){t=t.checked,t!=null&&bu(e,"checked",t,!1)}function ra(e,t){Wp(e,t);var n=_n(t.value),r=t.type;if(n!=null)r==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if(r==="submit"||r==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?ia(e,t.type,n):t.hasOwnProperty("defaultValue")&&ia(e,t.type,_n(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function td(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!(r!=="submit"&&r!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}n=e.name,n!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,n!==""&&(e.name=n)}function ia(e,t,n){(t!=="number"||Jl(e.ownerDocument)!==e)&&(n==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var fi=Array.isArray;function br(e,t,n,r){if(e=e.options,t){t={};for(var i=0;i"+t.valueOf().toString()+"",t=dl.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function Pi(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeType===3){n.nodeValue=t;return}}e.textContent=t}var mi={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},nx=["Webkit","ms","Moz","O"];Object.keys(mi).forEach(function(e){nx.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),mi[t]=mi[e]})});function Yp(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typeof t!="number"||t===0||mi.hasOwnProperty(e)&&mi[e]?(""+t).trim():t+"px"}function qp(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var r=n.indexOf("--")===0,i=Yp(n,t[n],r);n==="float"&&(n="cssFloat"),r?e.setProperty(n,i):e[n]=i}}var rx=xe({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function sa(e,t){if(t){if(rx[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(I(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(I(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(I(61))}if(t.style!=null&&typeof t.style!="object")throw Error(I(62))}}function aa(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var ua=null;function Tu(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var ca=null,jr=null,Pr=null;function id(e){if(e=Zi(e)){if(typeof ca!="function")throw Error(I(280));var t=e.stateNode;t&&(t=Do(t),ca(e.stateNode,e.type,t))}}function Xp(e){jr?Pr?Pr.push(e):Pr=[e]:jr=e}function Jp(){if(jr){var e=jr,t=Pr;if(Pr=jr=null,id(e),t)for(e=0;e>>=0,e===0?32:31-(hx(e)/mx|0)|0}var fl=64,pl=4194304;function pi(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function no(e,t){var n=e.pendingLanes;if(n===0)return 0;var r=0,i=e.suspendedLanes,l=e.pingedLanes,o=n&268435455;if(o!==0){var s=o&~i;s!==0?r=pi(s):(l&=o,l!==0&&(r=pi(l)))}else o=n&~i,o!==0?r=pi(o):l!==0&&(r=pi(l));if(r===0)return 0;if(t!==0&&t!==r&&!(t&i)&&(i=r&-r,l=t&-t,i>=l||i===16&&(l&4194240)!==0))return t;if(r&4&&(r|=n&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=r;0n;n++)t.push(e);return t}function Xi(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-Rt(t),e[t]=n}function xx(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0=yi),pd=String.fromCharCode(32),hd=!1;function vh(e,t){switch(e){case"keyup":return Kx.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function xh(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var gr=!1;function Yx(e,t){switch(e){case"compositionend":return xh(t);case"keypress":return t.which!==32?null:(hd=!0,pd);case"textInput":return e=t.data,e===pd&&hd?null:e;default:return null}}function qx(e,t){if(gr)return e==="compositionend"||!Ou&&vh(e,t)?(e=gh(),zl=Lu=yn=null,gr=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=vd(n)}}function Ch(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Ch(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Nh(){for(var e=window,t=Jl();t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=Jl(e.document)}return t}function Fu(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function l1(e){var t=Nh(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&Ch(n.ownerDocument.documentElement,n)){if(r!==null&&Fu(n)){if(t=r.start,e=r.end,e===void 0&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if(e=(t=n.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var i=n.textContent.length,l=Math.min(r.start,i);r=r.end===void 0?l:Math.min(r.end,i),!e.extend&&l>r&&(i=r,r=l,l=i),i=xd(n,l);var o=xd(n,r);i&&o&&(e.rangeCount!==1||e.anchorNode!==i.node||e.anchorOffset!==i.offset||e.focusNode!==o.node||e.focusOffset!==o.offset)&&(t=t.createRange(),t.setStart(i.node,i.offset),e.removeAllRanges(),l>r?(e.addRange(t),e.extend(o.node,o.offset)):(t.setEnd(o.node,o.offset),e.addRange(t)))}}for(t=[],e=n;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof n.focus=="function"&&n.focus(),n=0;n=document.documentMode,yr=null,ga=null,xi=null,ya=!1;function wd(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;ya||yr==null||yr!==Jl(r)||(r=yr,"selectionStart"in r&&Fu(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),xi&&Ai(xi,r)||(xi=r,r=lo(ga,"onSelect"),0wr||(e.current=Ca[wr],Ca[wr]=null,wr--)}function ue(e,t){wr++,Ca[wr]=e.current,e.current=t}var Tn={},$e=Mn(Tn),et=Mn(!1),Yn=Tn;function zr(e,t){var n=e.type.contextTypes;if(!n)return Tn;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var i={},l;for(l in n)i[l]=t[l];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=i),i}function tt(e){return e=e.childContextTypes,e!=null}function so(){pe(et),pe($e)}function jd(e,t,n){if($e.current!==Tn)throw Error(I(168));ue($e,t),ue(et,n)}function Mh(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,typeof r.getChildContext!="function")return n;r=r.getChildContext();for(var i in r)if(!(i in t))throw Error(I(108,ex(e)||"Unknown",i));return xe({},n,r)}function ao(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||Tn,Yn=$e.current,ue($e,e),ue(et,et.current),!0}function Pd(e,t,n){var r=e.stateNode;if(!r)throw Error(I(169));n?(e=Mh(e,t,Yn),r.__reactInternalMemoizedMergedChildContext=e,pe(et),pe($e),ue($e,e)):pe(et),ue(et,n)}var Jt=null,zo=!1,ks=!1;function Ah(e){Jt===null?Jt=[e]:Jt.push(e)}function y1(e){zo=!0,Ah(e)}function An(){if(!ks&&Jt!==null){ks=!0;var e=0,t=le;try{var n=Jt;for(le=1;e>=o,i-=o,Zt=1<<32-Rt(t)+i|n<R?(z=j,j=null):z=j.sibling;var L=p(m,j,x[R],N);if(L===null){j===null&&(j=z);break}e&&j&&L.alternate===null&&t(m,j),g=l(L,g,R),E===null?b=L:E.sibling=L,E=L,j=z}if(R===x.length)return n(m,j),me&&Bn(m,R),b;if(j===null){for(;RR?(z=j,j=null):z=j.sibling;var A=p(m,j,L.value,N);if(A===null){j===null&&(j=z);break}e&&j&&A.alternate===null&&t(m,j),g=l(A,g,R),E===null?b=A:E.sibling=A,E=A,j=z}if(L.done)return n(m,j),me&&Bn(m,R),b;if(j===null){for(;!L.done;R++,L=x.next())L=f(m,L.value,N),L!==null&&(g=l(L,g,R),E===null?b=L:E.sibling=L,E=L);return me&&Bn(m,R),b}for(j=r(m,j);!L.done;R++,L=x.next())L=h(j,m,R,L.value,N),L!==null&&(e&&L.alternate!==null&&j.delete(L.key===null?R:L.key),g=l(L,g,R),E===null?b=L:E.sibling=L,E=L);return e&&j.forEach(function(T){return t(m,T)}),me&&Bn(m,R),b}function S(m,g,x,N){if(typeof x=="object"&&x!==null&&x.type===mr&&x.key===null&&(x=x.props.children),typeof x=="object"&&x!==null){switch(x.$$typeof){case ul:e:{for(var b=x.key,E=g;E!==null;){if(E.key===b){if(b=x.type,b===mr){if(E.tag===7){n(m,E.sibling),g=i(E,x.props.children),g.return=m,m=g;break e}}else if(E.elementType===b||typeof b=="object"&&b!==null&&b.$$typeof===pn&&Rd(b)===E.type){n(m,E.sibling),g=i(E,x.props),g.ref=ii(m,E,x),g.return=m,m=g;break e}n(m,E);break}else t(m,E);E=E.sibling}x.type===mr?(g=Qn(x.props.children,m.mode,N,x.key),g.return=m,m=g):(N=Wl(x.type,x.key,x.props,null,m.mode,N),N.ref=ii(m,g,x),N.return=m,m=N)}return o(m);case hr:e:{for(E=x.key;g!==null;){if(g.key===E)if(g.tag===4&&g.stateNode.containerInfo===x.containerInfo&&g.stateNode.implementation===x.implementation){n(m,g.sibling),g=i(g,x.children||[]),g.return=m,m=g;break e}else{n(m,g);break}else t(m,g);g=g.sibling}g=_s(x,m.mode,N),g.return=m,m=g}return o(m);case pn:return E=x._init,S(m,g,E(x._payload),N)}if(fi(x))return w(m,g,x,N);if(Zr(x))return y(m,g,x,N);wl(m,x)}return typeof x=="string"&&x!==""||typeof x=="number"?(x=""+x,g!==null&&g.tag===6?(n(m,g.sibling),g=i(g,x),g.return=m,m=g):(n(m,g),g=Ps(x,m.mode,N),g.return=m,m=g),o(m)):n(m,g)}return S}var Fr=Oh(!0),Fh=Oh(!1),fo=Mn(null),po=null,Cr=null,Vu=null;function Hu(){Vu=Cr=po=null}function Wu(e){var t=fo.current;pe(fo),e._currentValue=t}function ba(e,t,n){for(;e!==null;){var r=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,r!==null&&(r.childLanes|=t)):r!==null&&(r.childLanes&t)!==t&&(r.childLanes|=t),e===n)break;e=e.return}}function Tr(e,t){po=e,Vu=Cr=null,e=e.dependencies,e!==null&&e.firstContext!==null&&(e.lanes&t&&(Ze=!0),e.firstContext=null)}function St(e){var t=e._currentValue;if(Vu!==e)if(e={context:e,memoizedValue:t,next:null},Cr===null){if(po===null)throw Error(I(308));Cr=e,po.dependencies={lanes:0,firstContext:e}}else Cr=Cr.next=e;return t}var Hn=null;function Gu(e){Hn===null?Hn=[e]:Hn.push(e)}function Bh(e,t,n,r){var i=t.interleaved;return i===null?(n.next=n,Gu(t)):(n.next=i.next,i.next=n),t.interleaved=n,ln(e,r)}function ln(e,t){e.lanes|=t;var n=e.alternate;for(n!==null&&(n.lanes|=t),n=e,e=e.return;e!==null;)e.childLanes|=t,n=e.alternate,n!==null&&(n.childLanes|=t),n=e,e=e.return;return n.tag===3?n.stateNode:null}var hn=!1;function Ku(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Uh(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function tn(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function Nn(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,ne&2){var i=r.pending;return i===null?t.next=t:(t.next=i.next,i.next=t),r.pending=t,ln(e,n)}return i=r.interleaved,i===null?(t.next=t,Gu(r)):(t.next=i.next,i.next=t),r.interleaved=t,ln(e,n)}function Fl(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194240)!==0)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,Iu(e,n)}}function Id(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var i=null,l=null;if(n=n.firstBaseUpdate,n!==null){do{var o={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};l===null?i=l=o:l=l.next=o,n=n.next}while(n!==null);l===null?i=l=t:l=l.next=t}else i=l=t;n={baseState:r.baseState,firstBaseUpdate:i,lastBaseUpdate:l,shared:r.shared,effects:r.effects},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function ho(e,t,n,r){var i=e.updateQueue;hn=!1;var l=i.firstBaseUpdate,o=i.lastBaseUpdate,s=i.shared.pending;if(s!==null){i.shared.pending=null;var a=s,c=a.next;a.next=null,o===null?l=c:o.next=c,o=a;var d=e.alternate;d!==null&&(d=d.updateQueue,s=d.lastBaseUpdate,s!==o&&(s===null?d.firstBaseUpdate=c:s.next=c,d.lastBaseUpdate=a))}if(l!==null){var f=i.baseState;o=0,d=c=a=null,s=l;do{var p=s.lane,h=s.eventTime;if((r&p)===p){d!==null&&(d=d.next={eventTime:h,lane:0,tag:s.tag,payload:s.payload,callback:s.callback,next:null});e:{var w=e,y=s;switch(p=t,h=n,y.tag){case 1:if(w=y.payload,typeof w=="function"){f=w.call(h,f,p);break e}f=w;break e;case 3:w.flags=w.flags&-65537|128;case 0:if(w=y.payload,p=typeof w=="function"?w.call(h,f,p):w,p==null)break e;f=xe({},f,p);break e;case 2:hn=!0}}s.callback!==null&&s.lane!==0&&(e.flags|=64,p=i.effects,p===null?i.effects=[s]:p.push(s))}else h={eventTime:h,lane:p,tag:s.tag,payload:s.payload,callback:s.callback,next:null},d===null?(c=d=h,a=f):d=d.next=h,o|=p;if(s=s.next,s===null){if(s=i.shared.pending,s===null)break;p=s,s=p.next,p.next=null,i.lastBaseUpdate=p,i.shared.pending=null}}while(1);if(d===null&&(a=f),i.baseState=a,i.firstBaseUpdate=c,i.lastBaseUpdate=d,t=i.shared.interleaved,t!==null){i=t;do o|=i.lane,i=i.next;while(i!==t)}else l===null&&(i.shared.lanes=0);Jn|=o,e.lanes=o,e.memoizedState=f}}function Md(e,t,n){if(e=t.effects,t.effects=null,e!==null)for(t=0;tn?n:4,e(!0);var r=Cs.transition;Cs.transition={};try{e(!1),t()}finally{le=n,Cs.transition=r}}function im(){return Ct().memoizedState}function k1(e,t,n){var r=bn(e);if(n={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null},lm(e))om(t,n);else if(n=Bh(e,t,n,r),n!==null){var i=Ke();It(n,e,r,i),sm(n,t,r)}}function S1(e,t,n){var r=bn(e),i={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null};if(lm(e))om(t,i);else{var l=e.alternate;if(e.lanes===0&&(l===null||l.lanes===0)&&(l=t.lastRenderedReducer,l!==null))try{var o=t.lastRenderedState,s=l(o,n);if(i.hasEagerState=!0,i.eagerState=s,Mt(s,o)){var a=t.interleaved;a===null?(i.next=i,Gu(t)):(i.next=a.next,a.next=i),t.interleaved=i;return}}catch{}finally{}n=Bh(e,t,i,r),n!==null&&(i=Ke(),It(n,e,r,i),sm(n,t,r))}}function lm(e){var t=e.alternate;return e===ve||t!==null&&t===ve}function om(e,t){wi=go=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function sm(e,t,n){if(n&4194240){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,Iu(e,n)}}var yo={readContext:St,useCallback:Fe,useContext:Fe,useEffect:Fe,useImperativeHandle:Fe,useInsertionEffect:Fe,useLayoutEffect:Fe,useMemo:Fe,useReducer:Fe,useRef:Fe,useState:Fe,useDebugValue:Fe,useDeferredValue:Fe,useTransition:Fe,useMutableSource:Fe,useSyncExternalStore:Fe,useId:Fe,unstable_isNewReconciler:!1},C1={readContext:St,useCallback:function(e,t){return zt().memoizedState=[e,t===void 0?null:t],e},useContext:St,useEffect:Ld,useImperativeHandle:function(e,t,n){return n=n!=null?n.concat([e]):null,Ul(4194308,4,Zh.bind(null,t,e),n)},useLayoutEffect:function(e,t){return Ul(4194308,4,e,t)},useInsertionEffect:function(e,t){return Ul(4,2,e,t)},useMemo:function(e,t){var n=zt();return t=t===void 0?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=zt();return t=n!==void 0?n(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=k1.bind(null,ve,e),[r.memoizedState,e]},useRef:function(e){var t=zt();return e={current:e},t.memoizedState=e},useState:Ad,useDebugValue:tc,useDeferredValue:function(e){return zt().memoizedState=e},useTransition:function(){var e=Ad(!1),t=e[0];return e=w1.bind(null,e[1]),zt().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var r=ve,i=zt();if(me){if(n===void 0)throw Error(I(407));n=n()}else{if(n=t(),Ae===null)throw Error(I(349));Xn&30||Wh(r,t,n)}i.memoizedState=n;var l={value:n,getSnapshot:t};return i.queue=l,Ld(Kh.bind(null,r,l,e),[e]),r.flags|=2048,$i(9,Gh.bind(null,r,l,n,t),void 0,null),n},useId:function(){var e=zt(),t=Ae.identifierPrefix;if(me){var n=en,r=Zt;n=(r&~(1<<32-Rt(r)-1)).toString(32)+n,t=":"+t+"R"+n,n=Bi++,0<\/script>",e=e.removeChild(e.firstChild)):typeof r.is=="string"?e=o.createElement(n,{is:r.is}):(e=o.createElement(n),n==="select"&&(o=e,r.multiple?o.multiple=!0:r.size&&(o.size=r.size))):e=o.createElementNS(e,n),e[Ft]=t,e[zi]=r,ym(e,t,!1,!1),t.stateNode=e;e:{switch(o=aa(n,r),n){case"dialog":fe("cancel",e),fe("close",e),i=r;break;case"iframe":case"object":case"embed":fe("load",e),i=r;break;case"video":case"audio":for(i=0;i$r&&(t.flags|=128,r=!0,li(l,!1),t.lanes=4194304)}else{if(!r)if(e=mo(o),e!==null){if(t.flags|=128,r=!0,n=e.updateQueue,n!==null&&(t.updateQueue=n,t.flags|=4),li(l,!0),l.tail===null&&l.tailMode==="hidden"&&!o.alternate&&!me)return Be(t),null}else 2*Se()-l.renderingStartTime>$r&&n!==1073741824&&(t.flags|=128,r=!0,li(l,!1),t.lanes=4194304);l.isBackwards?(o.sibling=t.child,t.child=o):(n=l.last,n!==null?n.sibling=o:t.child=o,l.last=o)}return l.tail!==null?(t=l.tail,l.rendering=t,l.tail=t.sibling,l.renderingStartTime=Se(),t.sibling=null,n=ye.current,ue(ye,r?n&1|2:n&1),t):(Be(t),null);case 22:case 23:return sc(),r=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==r&&(t.flags|=8192),r&&t.mode&1?ut&1073741824&&(Be(t),t.subtreeFlags&6&&(t.flags|=8192)):Be(t),null;case 24:return null;case 25:return null}throw Error(I(156,t.tag))}function R1(e,t){switch(Uu(t),t.tag){case 1:return tt(t.type)&&so(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return Br(),pe(et),pe($e),qu(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 5:return Yu(t),null;case 13:if(pe(ye),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(I(340));Or()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return pe(ye),null;case 4:return Br(),null;case 10:return Wu(t.type._context),null;case 22:case 23:return sc(),null;case 24:return null;default:return null}}var Sl=!1,Ue=!1,I1=typeof WeakSet=="function"?WeakSet:Set,B=null;function Nr(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(null)}catch(r){we(e,t,r)}else n.current=null}function La(e,t,n){try{n()}catch(r){we(e,t,r)}}var Gd=!1;function M1(e,t){if(va=ro,e=Nh(),Fu(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var i=r.anchorOffset,l=r.focusNode;r=r.focusOffset;try{n.nodeType,l.nodeType}catch{n=null;break e}var o=0,s=-1,a=-1,c=0,d=0,f=e,p=null;t:for(;;){for(var h;f!==n||i!==0&&f.nodeType!==3||(s=o+i),f!==l||r!==0&&f.nodeType!==3||(a=o+r),f.nodeType===3&&(o+=f.nodeValue.length),(h=f.firstChild)!==null;)p=f,f=h;for(;;){if(f===e)break t;if(p===n&&++c===i&&(s=o),p===l&&++d===r&&(a=o),(h=f.nextSibling)!==null)break;f=p,p=f.parentNode}f=h}n=s===-1||a===-1?null:{start:s,end:a}}else n=null}n=n||{start:0,end:0}}else n=null;for(xa={focusedElem:e,selectionRange:n},ro=!1,B=t;B!==null;)if(t=B,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,B=e;else for(;B!==null;){t=B;try{var w=t.alternate;if(t.flags&1024)switch(t.tag){case 0:case 11:case 15:break;case 1:if(w!==null){var y=w.memoizedProps,S=w.memoizedState,m=t.stateNode,g=m.getSnapshotBeforeUpdate(t.elementType===t.type?y:Pt(t.type,y),S);m.__reactInternalSnapshotBeforeUpdate=g}break;case 3:var x=t.stateNode.containerInfo;x.nodeType===1?x.textContent="":x.nodeType===9&&x.documentElement&&x.removeChild(x.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(I(163))}}catch(N){we(t,t.return,N)}if(e=t.sibling,e!==null){e.return=t.return,B=e;break}B=t.return}return w=Gd,Gd=!1,w}function ki(e,t,n){var r=t.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var i=r=r.next;do{if((i.tag&e)===e){var l=i.destroy;i.destroy=void 0,l!==void 0&&La(t,n,l)}i=i.next}while(i!==r)}}function Bo(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var n=t=t.next;do{if((n.tag&e)===e){var r=n.create;n.destroy=r()}n=n.next}while(n!==t)}}function Da(e){var t=e.ref;if(t!==null){var n=e.stateNode;switch(e.tag){case 5:e=n;break;default:e=n}typeof t=="function"?t(e):t.current=e}}function wm(e){var t=e.alternate;t!==null&&(e.alternate=null,wm(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[Ft],delete t[zi],delete t[Sa],delete t[m1],delete t[g1])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function km(e){return e.tag===5||e.tag===3||e.tag===4}function Kd(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||km(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function za(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.nodeType===8?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(n.nodeType===8?(t=n.parentNode,t.insertBefore(e,n)):(t=n,t.appendChild(e)),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=oo));else if(r!==4&&(e=e.child,e!==null))for(za(e,t,n),e=e.sibling;e!==null;)za(e,t,n),e=e.sibling}function Oa(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(e=e.child,e!==null))for(Oa(e,t,n),e=e.sibling;e!==null;)Oa(e,t,n),e=e.sibling}var Le=null,_t=!1;function un(e,t,n){for(n=n.child;n!==null;)Sm(e,t,n),n=n.sibling}function Sm(e,t,n){if($t&&typeof $t.onCommitFiberUnmount=="function")try{$t.onCommitFiberUnmount(Io,n)}catch{}switch(n.tag){case 5:Ue||Nr(n,t);case 6:var r=Le,i=_t;Le=null,un(e,t,n),Le=r,_t=i,Le!==null&&(_t?(e=Le,n=n.stateNode,e.nodeType===8?e.parentNode.removeChild(n):e.removeChild(n)):Le.removeChild(n.stateNode));break;case 18:Le!==null&&(_t?(e=Le,n=n.stateNode,e.nodeType===8?ws(e.parentNode,n):e.nodeType===1&&ws(e,n),Ii(e)):ws(Le,n.stateNode));break;case 4:r=Le,i=_t,Le=n.stateNode.containerInfo,_t=!0,un(e,t,n),Le=r,_t=i;break;case 0:case 11:case 14:case 15:if(!Ue&&(r=n.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){i=r=r.next;do{var l=i,o=l.destroy;l=l.tag,o!==void 0&&(l&2||l&4)&&La(n,t,o),i=i.next}while(i!==r)}un(e,t,n);break;case 1:if(!Ue&&(Nr(n,t),r=n.stateNode,typeof r.componentWillUnmount=="function"))try{r.props=n.memoizedProps,r.state=n.memoizedState,r.componentWillUnmount()}catch(s){we(n,t,s)}un(e,t,n);break;case 21:un(e,t,n);break;case 22:n.mode&1?(Ue=(r=Ue)||n.memoizedState!==null,un(e,t,n),Ue=r):un(e,t,n);break;default:un(e,t,n)}}function Qd(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n=e.stateNode;n===null&&(n=e.stateNode=new I1),t.forEach(function(r){var i=$1.bind(null,e,r);n.has(r)||(n.add(r),r.then(i,i))})}}function bt(e,t){var n=t.deletions;if(n!==null)for(var r=0;ri&&(i=o),r&=~l}if(r=i,r=Se()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*L1(r/1960))-r,10e?16:e,vn===null)var r=!1;else{if(e=vn,vn=null,wo=0,ne&6)throw Error(I(331));var i=ne;for(ne|=4,B=e.current;B!==null;){var l=B,o=l.child;if(B.flags&16){var s=l.deletions;if(s!==null){for(var a=0;aSe()-lc?Kn(e,0):ic|=n),nt(e,t)}function Tm(e,t){t===0&&(e.mode&1?(t=pl,pl<<=1,!(pl&130023424)&&(pl=4194304)):t=1);var n=Ke();e=ln(e,t),e!==null&&(Xi(e,t,n),nt(e,n))}function U1(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),Tm(e,n)}function $1(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,i=e.memoizedState;i!==null&&(n=i.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(I(314))}r!==null&&r.delete(t),Tm(e,n)}var Rm;Rm=function(e,t,n){if(e!==null)if(e.memoizedProps!==t.pendingProps||et.current)Ze=!0;else{if(!(e.lanes&n)&&!(t.flags&128))return Ze=!1,_1(e,t,n);Ze=!!(e.flags&131072)}else Ze=!1,me&&t.flags&1048576&&Lh(t,co,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;$l(e,t),e=t.pendingProps;var i=zr(t,$e.current);Tr(t,n),i=Ju(null,t,r,e,i,n);var l=Zu();return t.flags|=1,typeof i=="object"&&i!==null&&typeof i.render=="function"&&i.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,tt(r)?(l=!0,ao(t)):l=!1,t.memoizedState=i.state!==null&&i.state!==void 0?i.state:null,Ku(t),i.updater=Fo,t.stateNode=i,i._reactInternals=t,Pa(t,r,e,n),t=Ra(null,t,r,!0,l,n)):(t.tag=0,me&&l&&Bu(t),He(null,t,i,n),t=t.child),t;case 16:r=t.elementType;e:{switch($l(e,t),e=t.pendingProps,i=r._init,r=i(r._payload),t.type=r,i=t.tag=H1(r),e=Pt(r,e),i){case 0:t=Ta(null,t,r,e,n);break e;case 1:t=Vd(null,t,r,e,n);break e;case 11:t=Ud(null,t,r,e,n);break e;case 14:t=$d(null,t,r,Pt(r.type,e),n);break e}throw Error(I(306,r,""))}return t;case 0:return r=t.type,i=t.pendingProps,i=t.elementType===r?i:Pt(r,i),Ta(e,t,r,i,n);case 1:return r=t.type,i=t.pendingProps,i=t.elementType===r?i:Pt(r,i),Vd(e,t,r,i,n);case 3:e:{if(hm(t),e===null)throw Error(I(387));r=t.pendingProps,l=t.memoizedState,i=l.element,Uh(e,t),ho(t,r,null,n);var o=t.memoizedState;if(r=o.element,l.isDehydrated)if(l={element:r,isDehydrated:!1,cache:o.cache,pendingSuspenseBoundaries:o.pendingSuspenseBoundaries,transitions:o.transitions},t.updateQueue.baseState=l,t.memoizedState=l,t.flags&256){i=Ur(Error(I(423)),t),t=Hd(e,t,r,n,i);break e}else if(r!==i){i=Ur(Error(I(424)),t),t=Hd(e,t,r,n,i);break e}else for(ct=Cn(t.stateNode.containerInfo.firstChild),ft=t,me=!0,Tt=null,n=Fh(t,null,r,n),t.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling;else{if(Or(),r===i){t=on(e,t,n);break e}He(e,t,r,n)}t=t.child}return t;case 5:return $h(t),e===null&&Ea(t),r=t.type,i=t.pendingProps,l=e!==null?e.memoizedProps:null,o=i.children,wa(r,i)?o=null:l!==null&&wa(r,l)&&(t.flags|=32),pm(e,t),He(e,t,o,n),t.child;case 6:return e===null&&Ea(t),null;case 13:return mm(e,t,n);case 4:return Qu(t,t.stateNode.containerInfo),r=t.pendingProps,e===null?t.child=Fr(t,null,r,n):He(e,t,r,n),t.child;case 11:return r=t.type,i=t.pendingProps,i=t.elementType===r?i:Pt(r,i),Ud(e,t,r,i,n);case 7:return He(e,t,t.pendingProps,n),t.child;case 8:return He(e,t,t.pendingProps.children,n),t.child;case 12:return He(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,i=t.pendingProps,l=t.memoizedProps,o=i.value,ue(fo,r._currentValue),r._currentValue=o,l!==null)if(Mt(l.value,o)){if(l.children===i.children&&!et.current){t=on(e,t,n);break e}}else for(l=t.child,l!==null&&(l.return=t);l!==null;){var s=l.dependencies;if(s!==null){o=l.child;for(var a=s.firstContext;a!==null;){if(a.context===r){if(l.tag===1){a=tn(-1,n&-n),a.tag=2;var c=l.updateQueue;if(c!==null){c=c.shared;var d=c.pending;d===null?a.next=a:(a.next=d.next,d.next=a),c.pending=a}}l.lanes|=n,a=l.alternate,a!==null&&(a.lanes|=n),ba(l.return,n,t),s.lanes|=n;break}a=a.next}}else if(l.tag===10)o=l.type===t.type?null:l.child;else if(l.tag===18){if(o=l.return,o===null)throw Error(I(341));o.lanes|=n,s=o.alternate,s!==null&&(s.lanes|=n),ba(o,n,t),o=l.sibling}else o=l.child;if(o!==null)o.return=l;else for(o=l;o!==null;){if(o===t){o=null;break}if(l=o.sibling,l!==null){l.return=o.return,o=l;break}o=o.return}l=o}He(e,t,i.children,n),t=t.child}return t;case 9:return i=t.type,r=t.pendingProps.children,Tr(t,n),i=St(i),r=r(i),t.flags|=1,He(e,t,r,n),t.child;case 14:return r=t.type,i=Pt(r,t.pendingProps),i=Pt(r.type,i),$d(e,t,r,i,n);case 15:return dm(e,t,t.type,t.pendingProps,n);case 17:return r=t.type,i=t.pendingProps,i=t.elementType===r?i:Pt(r,i),$l(e,t),t.tag=1,tt(r)?(e=!0,ao(t)):e=!1,Tr(t,n),am(t,r,i),Pa(t,r,i,n),Ra(null,t,r,!0,e,n);case 19:return gm(e,t,n);case 22:return fm(e,t,n)}throw Error(I(156,t.tag))};function Im(e,t){return lh(e,t)}function V1(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function wt(e,t,n,r){return new V1(e,t,n,r)}function uc(e){return e=e.prototype,!(!e||!e.isReactComponent)}function H1(e){if(typeof e=="function")return uc(e)?1:0;if(e!=null){if(e=e.$$typeof,e===Pu)return 11;if(e===_u)return 14}return 2}function jn(e,t){var n=e.alternate;return n===null?(n=wt(e.tag,t,e.key,e.mode),n.elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=e.flags&14680064,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function Wl(e,t,n,r,i,l){var o=2;if(r=e,typeof e=="function")uc(e)&&(o=1);else if(typeof e=="string")o=5;else e:switch(e){case mr:return Qn(n.children,i,l,t);case ju:o=8,i|=8;break;case Js:return e=wt(12,n,t,i|2),e.elementType=Js,e.lanes=l,e;case Zs:return e=wt(13,n,t,i),e.elementType=Zs,e.lanes=l,e;case ea:return e=wt(19,n,t,i),e.elementType=ea,e.lanes=l,e;case $p:return $o(n,i,l,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case Bp:o=10;break e;case Up:o=9;break e;case Pu:o=11;break e;case _u:o=14;break e;case pn:o=16,r=null;break e}throw Error(I(130,e==null?e:typeof e,""))}return t=wt(o,n,t,i),t.elementType=e,t.type=r,t.lanes=l,t}function Qn(e,t,n,r){return e=wt(7,e,r,t),e.lanes=n,e}function $o(e,t,n,r){return e=wt(22,e,r,t),e.elementType=$p,e.lanes=n,e.stateNode={isHidden:!1},e}function Ps(e,t,n){return e=wt(6,e,null,t),e.lanes=n,e}function _s(e,t,n){return t=wt(4,e.children!==null?e.children:[],e.key,t),t.lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function W1(e,t,n,r,i){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=us(0),this.expirationTimes=us(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=us(0),this.identifierPrefix=r,this.onRecoverableError=i,this.mutableSourceEagerHydrationData=null}function cc(e,t,n,r,i,l,o,s,a){return e=new W1(e,t,n,s,a),t===1?(t=1,l===!0&&(t|=8)):t=0,l=wt(3,null,null,t),e.current=l,l.stateNode=e,l.memoizedState={element:r,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},Ku(l),e}function G1(e,t,n){var r=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(Dm)}catch(e){console.error(e)}}Dm(),Dp.exports=ht;var hc=Dp.exports;const X1=To(hc);var nf=hc;qs.createRoot=nf.createRoot,qs.hydrateRoot=nf.hydrateRoot;/**
- * @remix-run/router v1.23.0
- *
- * Copyright (c) Remix Software Inc.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE.md file in the root directory of this source tree.
- *
- * @license MIT
- */function Hi(){return Hi=Object.assign?Object.assign.bind():function(e){for(var t=1;t"u")throw new Error(t)}function zm(e,t){if(!e){typeof console<"u"&&console.warn(t);try{throw new Error(t)}catch{}}}function Z1(){return Math.random().toString(36).substr(2,8)}function lf(e,t){return{usr:e.state,key:e.key,idx:t}}function Va(e,t,n,r){return n===void 0&&(n=null),Hi({pathname:typeof e=="string"?e:e.pathname,search:"",hash:""},typeof t=="string"?Gr(t):t,{state:n,key:t&&t.key||r||Z1()})}function Co(e){let{pathname:t="/",search:n="",hash:r=""}=e;return n&&n!=="?"&&(t+=n.charAt(0)==="?"?n:"?"+n),r&&r!=="#"&&(t+=r.charAt(0)==="#"?r:"#"+r),t}function Gr(e){let t={};if(e){let n=e.indexOf("#");n>=0&&(t.hash=e.substr(n),e=e.substr(0,n));let r=e.indexOf("?");r>=0&&(t.search=e.substr(r),e=e.substr(0,r)),e&&(t.pathname=e)}return t}function e0(e,t,n,r){r===void 0&&(r={});let{window:i=document.defaultView,v5Compat:l=!1}=r,o=i.history,s=xn.Pop,a=null,c=d();c==null&&(c=0,o.replaceState(Hi({},o.state,{idx:c}),""));function d(){return(o.state||{idx:null}).idx}function f(){s=xn.Pop;let S=d(),m=S==null?null:S-c;c=S,a&&a({action:s,location:y.location,delta:m})}function p(S,m){s=xn.Push;let g=Va(y.location,S,m);n&&n(g,S),c=d()+1;let x=lf(g,c),N=y.createHref(g);try{o.pushState(x,"",N)}catch(b){if(b instanceof DOMException&&b.name==="DataCloneError")throw b;i.location.assign(N)}l&&a&&a({action:s,location:y.location,delta:1})}function h(S,m){s=xn.Replace;let g=Va(y.location,S,m);n&&n(g,S),c=d();let x=lf(g,c),N=y.createHref(g);o.replaceState(x,"",N),l&&a&&a({action:s,location:y.location,delta:0})}function w(S){let m=i.location.origin!=="null"?i.location.origin:i.location.href,g=typeof S=="string"?S:Co(S);return g=g.replace(/ $/,"%20"),je(m,"No window.location.(origin|href) available to create URL for href: "+g),new URL(g,m)}let y={get action(){return s},get location(){return e(i,o)},listen(S){if(a)throw new Error("A history only accepts one active listener");return i.addEventListener(rf,f),a=S,()=>{i.removeEventListener(rf,f),a=null}},createHref(S){return t(i,S)},createURL:w,encodeLocation(S){let m=w(S);return{pathname:m.pathname,search:m.search,hash:m.hash}},push:p,replace:h,go(S){return o.go(S)}};return y}var of;(function(e){e.data="data",e.deferred="deferred",e.redirect="redirect",e.error="error"})(of||(of={}));function t0(e,t,n){return n===void 0&&(n="/"),n0(e,t,n,!1)}function n0(e,t,n,r){let i=typeof t=="string"?Gr(t):t,l=mc(i.pathname||"/",n);if(l==null)return null;let o=Om(e);r0(o);let s=null;for(let a=0;s==null&&a{let a={relativePath:s===void 0?l.path||"":s,caseSensitive:l.caseSensitive===!0,childrenIndex:o,route:l};a.relativePath.startsWith("/")&&(je(a.relativePath.startsWith(r),'Absolute route path "'+a.relativePath+'" nested under path '+('"'+r+'" is not valid. An absolute child route path ')+"must start with the combined path of all its parent routes."),a.relativePath=a.relativePath.slice(r.length));let c=Pn([r,a.relativePath]),d=n.concat(a);l.children&&l.children.length>0&&(je(l.index!==!0,"Index routes must not have child routes. Please remove "+('all child routes from route path "'+c+'".')),Om(l.children,t,d,c)),!(l.path==null&&!l.index)&&t.push({path:c,score:c0(c,l.index),routesMeta:d})};return e.forEach((l,o)=>{var s;if(l.path===""||!((s=l.path)!=null&&s.includes("?")))i(l,o);else for(let a of Fm(l.path))i(l,o,a)}),t}function Fm(e){let t=e.split("/");if(t.length===0)return[];let[n,...r]=t,i=n.endsWith("?"),l=n.replace(/\?$/,"");if(r.length===0)return i?[l,""]:[l];let o=Fm(r.join("/")),s=[];return s.push(...o.map(a=>a===""?l:[l,a].join("/"))),i&&s.push(...o),s.map(a=>e.startsWith("/")&&a===""?"/":a)}function r0(e){e.sort((t,n)=>t.score!==n.score?n.score-t.score:d0(t.routesMeta.map(r=>r.childrenIndex),n.routesMeta.map(r=>r.childrenIndex)))}const i0=/^:[\w-]+$/,l0=3,o0=2,s0=1,a0=10,u0=-2,sf=e=>e==="*";function c0(e,t){let n=e.split("/"),r=n.length;return n.some(sf)&&(r+=u0),t&&(r+=o0),n.filter(i=>!sf(i)).reduce((i,l)=>i+(i0.test(l)?l0:l===""?s0:a0),r)}function d0(e,t){return e.length===t.length&&e.slice(0,-1).every((r,i)=>r===t[i])?e[e.length-1]-t[t.length-1]:0}function f0(e,t,n){n===void 0&&(n=!1);let{routesMeta:r}=e,i={},l="/",o=[];for(let s=0;s{let{paramName:p,isOptional:h}=d;if(p==="*"){let y=s[f]||"";o=l.slice(0,l.length-y.length).replace(/(.)\/+$/,"$1")}const w=s[f];return h&&!w?c[p]=void 0:c[p]=(w||"").replace(/%2F/g,"/"),c},{}),pathname:l,pathnameBase:o,pattern:e}}function p0(e,t,n){t===void 0&&(t=!1),n===void 0&&(n=!0),zm(e==="*"||!e.endsWith("*")||e.endsWith("/*"),'Route path "'+e+'" will be treated as if it were '+('"'+e.replace(/\*$/,"/*")+'" because the `*` character must ')+"always follow a `/` in the pattern. To get rid of this warning, "+('please change the route path to "'+e.replace(/\*$/,"/*")+'".'));let r=[],i="^"+e.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(o,s,a)=>(r.push({paramName:s,isOptional:a!=null}),a?"/?([^\\/]+)?":"/([^\\/]+)"));return e.endsWith("*")?(r.push({paramName:"*"}),i+=e==="*"||e==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):n?i+="\\/*$":e!==""&&e!=="/"&&(i+="(?:(?=\\/|$))"),[new RegExp(i,t?void 0:"i"),r]}function h0(e){try{return e.split("/").map(t=>decodeURIComponent(t).replace(/\//g,"%2F")).join("/")}catch(t){return zm(!1,'The URL path "'+e+'" could not be decoded because it is is a malformed URL segment. This is probably due to a bad percent '+("encoding ("+t+").")),e}}function mc(e,t){if(t==="/")return e;if(!e.toLowerCase().startsWith(t.toLowerCase()))return null;let n=t.endsWith("/")?t.length-1:t.length,r=e.charAt(n);return r&&r!=="/"?null:e.slice(n)||"/"}function m0(e,t){t===void 0&&(t="/");let{pathname:n,search:r="",hash:i=""}=typeof e=="string"?Gr(e):e;return{pathname:n?n.startsWith("/")?n:g0(n,t):t,search:x0(r),hash:w0(i)}}function g0(e,t){let n=t.replace(/\/+$/,"").split("/");return e.split("/").forEach(i=>{i===".."?n.length>1&&n.pop():i!=="."&&n.push(i)}),n.length>1?n.join("/"):"/"}function Ts(e,t,n,r){return"Cannot include a '"+e+"' character in a manually specified "+("`to."+t+"` field ["+JSON.stringify(r)+"]. Please separate it out to the ")+("`to."+n+"` field. Alternatively you may provide the full path as ")+'a string in and the router will parse it for you.'}function y0(e){return e.filter((t,n)=>n===0||t.route.path&&t.route.path.length>0)}function Bm(e,t){let n=y0(e);return t?n.map((r,i)=>i===n.length-1?r.pathname:r.pathnameBase):n.map(r=>r.pathnameBase)}function Um(e,t,n,r){r===void 0&&(r=!1);let i;typeof e=="string"?i=Gr(e):(i=Hi({},e),je(!i.pathname||!i.pathname.includes("?"),Ts("?","pathname","search",i)),je(!i.pathname||!i.pathname.includes("#"),Ts("#","pathname","hash",i)),je(!i.search||!i.search.includes("#"),Ts("#","search","hash",i)));let l=e===""||i.pathname==="",o=l?"/":i.pathname,s;if(o==null)s=n;else{let f=t.length-1;if(!r&&o.startsWith("..")){let p=o.split("/");for(;p[0]==="..";)p.shift(),f-=1;i.pathname=p.join("/")}s=f>=0?t[f]:"/"}let a=m0(i,s),c=o&&o!=="/"&&o.endsWith("/"),d=(l||o===".")&&n.endsWith("/");return!a.pathname.endsWith("/")&&(c||d)&&(a.pathname+="/"),a}const Pn=e=>e.join("/").replace(/\/\/+/g,"/"),v0=e=>e.replace(/\/+$/,"").replace(/^\/*/,"/"),x0=e=>!e||e==="?"?"":e.startsWith("?")?e:"?"+e,w0=e=>!e||e==="#"?"":e.startsWith("#")?e:"#"+e;function k0(e){return e!=null&&typeof e.status=="number"&&typeof e.statusText=="string"&&typeof e.internal=="boolean"&&"data"in e}const $m=["post","put","patch","delete"];new Set($m);const S0=["get",...$m];new Set(S0);/**
- * React Router v6.30.1
- *
- * Copyright (c) Remix Software Inc.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE.md file in the root directory of this source tree.
- *
- * @license MIT
- */function Wi(){return Wi=Object.assign?Object.assign.bind():function(e){for(var t=1;t{s.current=!0}),v.useCallback(function(c,d){if(d===void 0&&(d={}),!s.current)return;if(typeof c=="number"){r.go(c);return}let f=Um(c,JSON.parse(o),l,d.relative==="path");e==null&&t!=="/"&&(f.pathname=f.pathname==="/"?t:Pn([t,f.pathname])),(d.replace?r.replace:r.push)(f,d.state,d)},[t,r,o,l,e])}const j0=v.createContext(null);function P0(e){let t=v.useContext(Ln).outlet;return t&&v.createElement(j0.Provider,{value:e},t)}function Wm(e,t){let{relative:n}=t===void 0?{}:t,{future:r}=v.useContext(ir),{matches:i}=v.useContext(Ln),{pathname:l}=nl(),o=JSON.stringify(Bm(i,r.v7_relativeSplatPath));return v.useMemo(()=>Um(e,JSON.parse(o),l,n==="path"),[e,o,l,n])}function _0(e,t){return T0(e,t)}function T0(e,t,n,r){tl()||je(!1);let{navigator:i}=v.useContext(ir),{matches:l}=v.useContext(Ln),o=l[l.length-1],s=o?o.params:{};o&&o.pathname;let a=o?o.pathnameBase:"/";o&&o.route;let c=nl(),d;if(t){var f;let S=typeof t=="string"?Gr(t):t;a==="/"||(f=S.pathname)!=null&&f.startsWith(a)||je(!1),d=S}else d=c;let p=d.pathname||"/",h=p;if(a!=="/"){let S=a.replace(/^\//,"").split("/");h="/"+p.replace(/^\//,"").split("/").slice(S.length).join("/")}let w=t0(e,{pathname:h}),y=L0(w&&w.map(S=>Object.assign({},S,{params:Object.assign({},s,S.params),pathname:Pn([a,i.encodeLocation?i.encodeLocation(S.pathname).pathname:S.pathname]),pathnameBase:S.pathnameBase==="/"?a:Pn([a,i.encodeLocation?i.encodeLocation(S.pathnameBase).pathname:S.pathnameBase])})),l,n,r);return t&&y?v.createElement(Ko.Provider,{value:{location:Wi({pathname:"/",search:"",hash:"",state:null,key:"default"},d),navigationType:xn.Pop}},y):y}function R0(){let e=F0(),t=k0(e)?e.status+" "+e.statusText:e instanceof Error?e.message:JSON.stringify(e),n=e instanceof Error?e.stack:null,i={padding:"0.5rem",backgroundColor:"rgba(200,200,200, 0.5)"},l=null;return v.createElement(v.Fragment,null,v.createElement("h2",null,"Unexpected Application Error!"),v.createElement("h3",{style:{fontStyle:"italic"}},t),n?v.createElement("pre",{style:i},n):null,l)}const I0=v.createElement(R0,null);class M0 extends v.Component{constructor(t){super(t),this.state={location:t.location,revalidation:t.revalidation,error:t.error}}static getDerivedStateFromError(t){return{error:t}}static getDerivedStateFromProps(t,n){return n.location!==t.location||n.revalidation!=="idle"&&t.revalidation==="idle"?{error:t.error,location:t.location,revalidation:t.revalidation}:{error:t.error!==void 0?t.error:n.error,location:n.location,revalidation:t.revalidation||n.revalidation}}componentDidCatch(t,n){console.error("React Router caught the following error during render",t,n)}render(){return this.state.error!==void 0?v.createElement(Ln.Provider,{value:this.props.routeContext},v.createElement(Vm.Provider,{value:this.state.error,children:this.props.component})):this.props.children}}function A0(e){let{routeContext:t,match:n,children:r}=e,i=v.useContext(gc);return i&&i.static&&i.staticContext&&(n.route.errorElement||n.route.ErrorBoundary)&&(i.staticContext._deepestRenderedBoundaryId=n.route.id),v.createElement(Ln.Provider,{value:t},r)}function L0(e,t,n,r){var i;if(t===void 0&&(t=[]),n===void 0&&(n=null),r===void 0&&(r=null),e==null){var l;if(!n)return null;if(n.errors)e=n.matches;else if((l=r)!=null&&l.v7_partialHydration&&t.length===0&&!n.initialized&&n.matches.length>0)e=n.matches;else return null}let o=e,s=(i=n)==null?void 0:i.errors;if(s!=null){let d=o.findIndex(f=>f.route.id&&(s==null?void 0:s[f.route.id])!==void 0);d>=0||je(!1),o=o.slice(0,Math.min(o.length,d+1))}let a=!1,c=-1;if(n&&r&&r.v7_partialHydration)for(let d=0;d=0?o=o.slice(0,c+1):o=[o[0]];break}}}return o.reduceRight((d,f,p)=>{let h,w=!1,y=null,S=null;n&&(h=s&&f.route.id?s[f.route.id]:void 0,y=f.route.errorElement||I0,a&&(c<0&&p===0?(U0("route-fallback",!1),w=!0,S=null):c===p&&(w=!0,S=f.route.hydrateFallbackElement||null)));let m=t.concat(o.slice(0,p+1)),g=()=>{let x;return h?x=y:w?x=S:f.route.Component?x=v.createElement(f.route.Component,null):f.route.element?x=f.route.element:x=d,v.createElement(A0,{match:f,routeContext:{outlet:d,matches:m,isDataRoute:n!=null},children:x})};return n&&(f.route.ErrorBoundary||f.route.errorElement||p===0)?v.createElement(M0,{location:n.location,revalidation:n.revalidation,component:y,error:h,children:g(),routeContext:{outlet:null,matches:m,isDataRoute:!0}}):g()},null)}var Gm=function(e){return e.UseBlocker="useBlocker",e.UseRevalidator="useRevalidator",e.UseNavigateStable="useNavigate",e}(Gm||{}),No=function(e){return e.UseBlocker="useBlocker",e.UseLoaderData="useLoaderData",e.UseActionData="useActionData",e.UseRouteError="useRouteError",e.UseNavigation="useNavigation",e.UseRouteLoaderData="useRouteLoaderData",e.UseMatches="useMatches",e.UseRevalidator="useRevalidator",e.UseNavigateStable="useNavigate",e.UseRouteId="useRouteId",e}(No||{});function D0(e){let t=v.useContext(gc);return t||je(!1),t}function z0(e){let t=v.useContext(C0);return t||je(!1),t}function O0(e){let t=v.useContext(Ln);return t||je(!1),t}function Km(e){let t=O0(),n=t.matches[t.matches.length-1];return n.route.id||je(!1),n.route.id}function F0(){var e;let t=v.useContext(Vm),n=z0(No.UseRouteError),r=Km(No.UseRouteError);return t!==void 0?t:(e=n.errors)==null?void 0:e[r]}function B0(){let{router:e}=D0(Gm.UseNavigateStable),t=Km(No.UseNavigateStable),n=v.useRef(!1);return Hm(()=>{n.current=!0}),v.useCallback(function(i,l){l===void 0&&(l={}),n.current&&(typeof i=="number"?e.navigate(i):e.navigate(i,Wi({fromRouteId:t},l)))},[e,t])}const uf={};function U0(e,t,n){!t&&!uf[e]&&(uf[e]=!0)}function $0(e,t){e==null||e.v7_startTransition,(e==null?void 0:e.v7_relativeSplatPath)===void 0&&(!t||t.v7_relativeSplatPath),t&&(t.v7_fetcherPersist,t.v7_normalizeFormMethod,t.v7_partialHydration,t.v7_skipActionErrorRevalidation)}function V0(e){return P0(e.context)}function pr(e){je(!1)}function H0(e){let{basename:t="/",children:n=null,location:r,navigationType:i=xn.Pop,navigator:l,static:o=!1,future:s}=e;tl()&&je(!1);let a=t.replace(/^\/*/,"/"),c=v.useMemo(()=>({basename:a,navigator:l,static:o,future:Wi({v7_relativeSplatPath:!1},s)}),[a,s,l,o]);typeof r=="string"&&(r=Gr(r));let{pathname:d="/",search:f="",hash:p="",state:h=null,key:w="default"}=r,y=v.useMemo(()=>{let S=mc(d,a);return S==null?null:{location:{pathname:S,search:f,hash:p,state:h,key:w},navigationType:i}},[a,d,f,p,h,w,i]);return y==null?null:v.createElement(ir.Provider,{value:c},v.createElement(Ko.Provider,{children:n,value:y}))}function W0(e){let{children:t,location:n}=e;return _0(Ha(t),n)}new Promise(()=>{});function Ha(e,t){t===void 0&&(t=[]);let n=[];return v.Children.forEach(e,(r,i)=>{if(!v.isValidElement(r))return;let l=[...t,i];if(r.type===v.Fragment){n.push.apply(n,Ha(r.props.children,l));return}r.type!==pr&&je(!1),!r.props.index||!r.props.children||je(!1);let o={id:r.props.id||l.join("-"),caseSensitive:r.props.caseSensitive,element:r.props.element,Component:r.props.Component,index:r.props.index,path:r.props.path,loader:r.props.loader,action:r.props.action,errorElement:r.props.errorElement,ErrorBoundary:r.props.ErrorBoundary,hasErrorBoundary:r.props.ErrorBoundary!=null||r.props.errorElement!=null,shouldRevalidate:r.props.shouldRevalidate,handle:r.props.handle,lazy:r.props.lazy};r.props.children&&(o.children=Ha(r.props.children,l)),n.push(o)}),n}/**
- * React Router DOM v6.30.1
- *
- * Copyright (c) Remix Software Inc.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE.md file in the root directory of this source tree.
- *
- * @license MIT
- */function Wa(){return Wa=Object.assign?Object.assign.bind():function(e){for(var t=1;t=0)&&(n[i]=e[i]);return n}function K0(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function Q0(e,t){return e.button===0&&(!t||t==="_self")&&!K0(e)}const Y0=["onClick","relative","reloadDocument","replace","state","target","to","preventScrollReset","viewTransition"],q0="6";try{window.__reactRouterVersion=q0}catch{}const X0="startTransition",cf=Cu[X0];function J0(e){let{basename:t,children:n,future:r,window:i}=e,l=v.useRef();l.current==null&&(l.current=J1({window:i,v5Compat:!0}));let o=l.current,[s,a]=v.useState({action:o.action,location:o.location}),{v7_startTransition:c}=r||{},d=v.useCallback(f=>{c&&cf?cf(()=>a(f)):a(f)},[a,c]);return v.useLayoutEffect(()=>o.listen(d),[o,d]),v.useEffect(()=>$0(r),[r]),v.createElement(H0,{basename:t,children:n,location:s.location,navigationType:s.action,navigator:o,future:r})}const Z0=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u",ew=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,Eo=v.forwardRef(function(t,n){let{onClick:r,relative:i,reloadDocument:l,replace:o,state:s,target:a,to:c,preventScrollReset:d,viewTransition:f}=t,p=G0(t,Y0),{basename:h}=v.useContext(ir),w,y=!1;if(typeof c=="string"&&ew.test(c)&&(w=c,Z0))try{let x=new URL(window.location.href),N=c.startsWith("//")?new URL(x.protocol+c):new URL(c),b=mc(N.pathname,h);N.origin===x.origin&&b!=null?c=b+N.search+N.hash:y=!0}catch{}let S=N0(c,{relative:i}),m=tw(c,{replace:o,state:s,target:a,preventScrollReset:d,relative:i,viewTransition:f});function g(x){r&&r(x),x.defaultPrevented||m(x)}return v.createElement("a",Wa({},p,{href:w||S,onClick:y||l?r:g,ref:n,target:a}))});var df;(function(e){e.UseScrollRestoration="useScrollRestoration",e.UseSubmit="useSubmit",e.UseSubmitFetcher="useSubmitFetcher",e.UseFetcher="useFetcher",e.useViewTransitionState="useViewTransitionState"})(df||(df={}));var ff;(function(e){e.UseFetcher="useFetcher",e.UseFetchers="useFetchers",e.UseScrollRestoration="useScrollRestoration"})(ff||(ff={}));function tw(e,t){let{target:n,replace:r,state:i,preventScrollReset:l,relative:o,viewTransition:s}=t===void 0?{}:t,a=E0(),c=nl(),d=Wm(e,{relative:o});return v.useCallback(f=>{if(Q0(f,n)){f.preventDefault();let p=r!==void 0?r:Co(c)===Co(d);a(e,{replace:p,state:i,preventScrollReset:l,relative:o,viewTransition:s})}},[c,a,d,r,i,n,e,l,o,s])}function Qm(e){var t,n,r="";if(typeof e=="string"||typeof e=="number")r+=e;else if(typeof e=="object")if(Array.isArray(e)){var i=e.length;for(t=0;te&&(t=0,r=n,n=new Map)}return{get:function(o){var s=n.get(o);if(s!==void 0)return s;if((s=r.get(o))!==void 0)return i(o,s),s},set:function(o,s){n.has(o)?n.set(o,s):i(o,s)}}}var Jm="!";function uw(e){var t=e.separator||":",n=t.length===1,r=t[0],i=t.length;return function(o){for(var s=[],a=0,c=0,d,f=0;fc?d-c:void 0;return{modifiers:s,hasImportantModifier:w,baseClassName:y,maybePostfixModifierPosition:S}}}function cw(e){if(e.length<=1)return e;var t=[],n=[];return e.forEach(function(r){var i=r[0]==="[";i?(t.push.apply(t,n.sort().concat([r])),n=[]):n.push(r)}),t.push.apply(t,n.sort()),t}function dw(e){return{cache:aw(e.cacheSize),splitModifiers:uw(e),...rw(e)}}var fw=/\s+/;function pw(e,t){var n=t.splitModifiers,r=t.getClassGroupId,i=t.getConflictingClassGroupIds,l=new Set;return e.trim().split(fw).map(function(o){var s=n(o),a=s.modifiers,c=s.hasImportantModifier,d=s.baseClassName,f=s.maybePostfixModifierPosition,p=r(f?d.substring(0,f):d),h=!!f;if(!p){if(!f)return{isTailwindClass:!1,originalClassName:o};if(p=r(d),!p)return{isTailwindClass:!1,originalClassName:o};h=!1}var w=cw(a).join(":"),y=c?w+Jm:w;return{isTailwindClass:!0,modifierId:y,classGroupId:p,originalClassName:o,hasPostfixModifier:h}}).reverse().filter(function(o){if(!o.isTailwindClass)return!0;var s=o.modifierId,a=o.classGroupId,c=o.hasPostfixModifier,d=s+a;return l.has(d)?!1:(l.add(d),i(a,c).forEach(function(f){return l.add(s+f)}),!0)}).reverse().map(function(o){return o.originalClassName}).join(" ")}function hw(){for(var e=arguments.length,t=new Array(e),n=0;ne.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),Rw=e=>e.replace(/^([A-Z])|[\s-_]+(\w)/g,(t,n,r)=>r?r.toUpperCase():n.toLowerCase()),gf=e=>{const t=Rw(e);return t.charAt(0).toUpperCase()+t.slice(1)},tg=(...e)=>e.filter((t,n,r)=>!!t&&t.trim()!==""&&r.indexOf(t)===n).join(" ").trim(),Iw=e=>{for(const t in e)if(t.startsWith("aria-")||t==="role"||t==="title")return!0};/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */var Mw={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Aw=v.forwardRef(({color:e="currentColor",size:t=24,strokeWidth:n=2,absoluteStrokeWidth:r,className:i="",children:l,iconNode:o,...s},a)=>v.createElement("svg",{ref:a,...Mw,width:t,height:t,stroke:e,strokeWidth:r?Number(n)*24/Number(t):n,className:tg("lucide",i),...!l&&!Iw(s)&&{"aria-hidden":"true"},...s},[...o.map(([c,d])=>v.createElement(c,d)),...Array.isArray(l)?l:[l]]));/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const ee=(e,t)=>{const n=v.forwardRef(({className:r,...i},l)=>v.createElement(Aw,{ref:l,iconNode:t,className:tg(`lucide-${Tw(gf(e))}`,`lucide-${e}`,r),...i}));return n.displayName=gf(e),n};/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Lw=[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"m12 5 7 7-7 7",key:"xquz4c"}]],Dw=ee("arrow-right",Lw);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const zw=[["path",{d:"M12 7v14",key:"1akyts"}],["path",{d:"M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z",key:"ruj8y"}]],bo=ee("book-open",zw);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Ow=[["path",{d:"M12 8V4H8",key:"hb8ula"}],["rect",{width:"16",height:"12",x:"4",y:"8",rx:"2",key:"enze0r"}],["path",{d:"M2 14h2",key:"vft8re"}],["path",{d:"M20 14h2",key:"4cs60a"}],["path",{d:"M15 13v2",key:"1xurst"}],["path",{d:"M9 13v2",key:"rq6x2g"}]],ng=ee("bot",Ow);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Fw=[["path",{d:"M12 18V5",key:"adv99a"}],["path",{d:"M15 13a4.17 4.17 0 0 1-3-4 4.17 4.17 0 0 1-3 4",key:"1e3is1"}],["path",{d:"M17.598 6.5A3 3 0 1 0 12 5a3 3 0 1 0-5.598 1.5",key:"1gqd8o"}],["path",{d:"M17.997 5.125a4 4 0 0 1 2.526 5.77",key:"iwvgf7"}],["path",{d:"M18 18a4 4 0 0 0 2-7.464",key:"efp6ie"}],["path",{d:"M19.967 17.483A4 4 0 1 1 12 18a4 4 0 1 1-7.967-.517",key:"1gq6am"}],["path",{d:"M6 18a4 4 0 0 1-2-7.464",key:"k1g0md"}],["path",{d:"M6.003 5.125a4 4 0 0 0-2.526 5.77",key:"q97ue3"}]],Ht=ee("brain",Fw);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Bw=[["path",{d:"M8 2v4",key:"1cmpym"}],["path",{d:"M16 2v4",key:"4m81vk"}],["rect",{width:"18",height:"18",x:"3",y:"4",rx:"2",key:"1hopcy"}],["path",{d:"M3 10h18",key:"8toen8"}]],Uw=ee("calendar",Bw);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const $w=[["path",{d:"m6 9 6 6 6-6",key:"qrunsl"}]],rg=ee("chevron-down",$w);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Vw=[["path",{d:"m18 15-6-6-6 6",key:"153udz"}]],Hw=ee("chevron-up",Vw);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Ww=[["path",{d:"M21.801 10A10 10 0 1 1 17 3.335",key:"yps3ct"}],["path",{d:"m9 11 3 3L22 4",key:"1pflzl"}]],Gw=ee("circle-check-big",Ww);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Kw=[["path",{d:"m16 18 6-6-6-6",key:"eg8j8"}],["path",{d:"m8 6-6 6 6 6",key:"ppft3o"}]],Qw=ee("code",Kw);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Yw=[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]],qw=ee("copy",Yw);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Xw=[["path",{d:"M12 20v2",key:"1lh1kg"}],["path",{d:"M12 2v2",key:"tus03m"}],["path",{d:"M17 20v2",key:"1rnc9c"}],["path",{d:"M17 2v2",key:"11trls"}],["path",{d:"M2 12h2",key:"1t8f8n"}],["path",{d:"M2 17h2",key:"7oei6x"}],["path",{d:"M2 7h2",key:"asdhe0"}],["path",{d:"M20 12h2",key:"1q8mjw"}],["path",{d:"M20 17h2",key:"1fpfkl"}],["path",{d:"M20 7h2",key:"1o8tra"}],["path",{d:"M7 20v2",key:"4gnj0m"}],["path",{d:"M7 2v2",key:"1i4yhu"}],["rect",{x:"4",y:"4",width:"16",height:"16",rx:"2",key:"1vbyd7"}],["rect",{x:"8",y:"8",width:"8",height:"8",rx:"1",key:"z9xiuo"}]],ig=ee("cpu",Xw);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Jw=[["path",{d:"M12 15V3",key:"m9g1x1"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["path",{d:"m7 10 5 5 5-5",key:"brsn70"}]],vc=ee("download",Jw);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Zw=[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"1357e3"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}],["path",{d:"M12 7v5l4 2",key:"1fdv2h"}]],ek=ee("history",Zw);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const tk=[["path",{d:"M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8",key:"5wwlr5"}],["path",{d:"M3 10a2 2 0 0 1 .709-1.528l7-5.999a2 2 0 0 1 2.582 0l7 5.999A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z",key:"1d0kgt"}]],nk=ee("house",tk);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const rk=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M12 16v-4",key:"1dtifu"}],["path",{d:"M12 8h.01",key:"e9boi3"}]],lg=ee("info",rk);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const ik=[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56",key:"13zald"}]],Gi=ee("loader-circle",ik);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const lk=[["path",{d:"M4 12h16",key:"1lakjw"}],["path",{d:"M4 18h16",key:"19g7jn"}],["path",{d:"M4 6h16",key:"1o0s65"}]],ok=ee("menu",lk);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const sk=[["path",{d:"M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z",key:"18887p"}]],Rn=ee("message-square",sk);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const ak=[["path",{d:"M12 22a1 1 0 0 1 0-20 10 9 0 0 1 10 9 5 5 0 0 1-5 5h-2.25a1.75 1.75 0 0 0-1.4 2.8l.3.4a1.75 1.75 0 0 1-1.4 2.8z",key:"e79jfc"}],["circle",{cx:"13.5",cy:"6.5",r:".5",fill:"currentColor",key:"1okk4w"}],["circle",{cx:"17.5",cy:"10.5",r:".5",fill:"currentColor",key:"f64h9f"}],["circle",{cx:"6.5",cy:"12.5",r:".5",fill:"currentColor",key:"qy21gx"}],["circle",{cx:"8.5",cy:"7.5",r:".5",fill:"currentColor",key:"fotxhn"}]],uk=ee("palette",ak);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const ck=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}],["path",{d:"M9 3v18",key:"fh3hqa"}],["path",{d:"m16 15-3-3 3-3",key:"14y99z"}]],dk=ee("panel-left-close",ck);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const fk=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}],["path",{d:"M9 3v18",key:"fh3hqa"}],["path",{d:"m14 9 3 3-3 3",key:"8010ee"}]],pk=ee("panel-left-open",fk);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const hk=[["path",{d:"m16 6-8.414 8.586a2 2 0 0 0 2.829 2.829l8.414-8.586a4 4 0 1 0-5.657-5.657l-8.379 8.551a6 6 0 1 0 8.485 8.485l8.379-8.551",key:"1miecu"}]],mk=ee("paperclip",hk);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const gk=[["path",{d:"M13 21h8",key:"1jsn5i"}],["path",{d:"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",key:"1a8usu"}]],yk=ee("pen-line",gk);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const vk=[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]],yf=ee("plus",vk);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const xk=[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"1357e3"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}]],wk=ee("rotate-ccw",xk);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const kk=[["path",{d:"M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z",key:"1ffxy3"}],["path",{d:"m21.854 2.147-10.94 10.939",key:"12cjpa"}]],Sk=ee("send",kk);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Ck=[["rect",{width:"20",height:"8",x:"2",y:"2",rx:"2",ry:"2",key:"ngkwjq"}],["rect",{width:"20",height:"8",x:"2",y:"14",rx:"2",ry:"2",key:"iecqi9"}],["line",{x1:"6",x2:"6.01",y1:"6",y2:"6",key:"16zg32"}],["line",{x1:"6",x2:"6.01",y1:"18",y2:"18",key:"nzw8ys"}]],Nk=ee("server",Ck);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Ek=[["path",{d:"M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915",key:"1i5ecw"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]],xc=ee("settings",Ek);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const bk=[["path",{d:"M12 2v13",key:"1km8f5"}],["path",{d:"m16 6-4-4-4 4",key:"13yo43"}],["path",{d:"M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8",key:"1b2hhj"}]],jk=ee("share",bk);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Pk=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}]],Qa=ee("shield",Pk);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const _k=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}]],Tk=ee("square",_k);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Rk=[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]],wc=ee("trash-2",Rk);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Ik=[["path",{d:"M12 3v12",key:"1x0j5s"}],["path",{d:"m17 8-5-5-5 5",key:"7q97r8"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}]],Mk=ee("upload",Ik);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Ak=[["path",{d:"M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2",key:"975kel"}],["circle",{cx:"12",cy:"7",r:"4",key:"17ys0d"}]],Lk=ee("user",Ak);/**
- * @license lucide-react v0.542.0 - ISC
- *
- * This source code is licensed under the ISC license.
- * See the LICENSE file in the root directory of this source tree.
- */const Dk=[["path",{d:"M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z",key:"1xq2db"}]],rl=ee("zap",Dk),zk=[{name:"Home",href:"/",icon:nk,description:"Overview and getting started"},{name:"Chat Playground",href:"/playground",icon:Rn,description:"AI chatbot with conversation history"},{name:"Model Catalog",href:"/models",icon:bo,description:"Browse and manage models"},{name:"Assistants",href:"/assistants",icon:ng,description:"Custom AI assistants",badge:"Preview"}],Ok=[{name:"Completions",href:"/completions",icon:rl,description:"Text completion endpoint"},{name:"Fine-tuning",href:"/fine-tuning",icon:Ht,description:"Train custom models"},{name:"Settings",href:"/settings",icon:xc,description:"Application settings"}];function Fk(){const e=nl();return u.jsxs("div",{className:"flex flex-col h-full bg-muted/30 border-r",children:[u.jsx("div",{className:"flex items-center h-16 px-6 border-b",children:u.jsxs("div",{className:"flex items-center gap-2",children:[u.jsx("div",{className:"w-8 h-8 bg-gradient-to-br from-blue-500 to-purple-600 rounded-lg flex items-center justify-center",children:u.jsx(Ht,{className:"w-5 h-5 text-white"})}),u.jsxs("div",{children:[u.jsx("h1",{className:"text-lg font-semibold",children:"Edge LLM"}),u.jsx("p",{className:"text-xs text-muted-foreground",children:"Local AI Platform"})]})]})}),u.jsxs("div",{className:"flex-1 overflow-y-auto py-4",children:[u.jsxs("div",{className:"px-3 mb-4",children:[u.jsx("h2",{className:"mb-2 px-3 text-xs font-semibold text-muted-foreground uppercase tracking-wider",children:"Get started"}),u.jsx("nav",{className:"space-y-1",children:zk.map(t=>{const n=e.pathname===t.href;return u.jsxs(Eo,{to:t.href,className:re("flex items-center gap-3 rounded-lg px-3 py-2 text-sm transition-all hover:bg-accent",n?"bg-accent text-accent-foreground font-medium":"text-muted-foreground hover:text-foreground"),children:[u.jsx(t.icon,{className:"h-4 w-4"}),u.jsx("div",{className:"flex-1",children:u.jsxs("div",{className:"flex items-center gap-2",children:[t.name,t.badge&&u.jsx("span",{className:"px-1.5 py-0.5 text-xs bg-blue-100 text-blue-700 rounded-full",children:t.badge})]})})]},t.name)})})]}),u.jsxs("div",{className:"px-3",children:[u.jsx("h2",{className:"mb-2 px-3 text-xs font-semibold text-muted-foreground uppercase tracking-wider",children:"Tools"}),u.jsx("nav",{className:"space-y-1",children:Ok.map(t=>{const n=e.pathname===t.href;return u.jsxs(Eo,{to:t.href,className:re("flex items-center gap-3 rounded-lg px-3 py-2 text-sm transition-all hover:bg-accent",n?"bg-accent text-accent-foreground font-medium":"text-muted-foreground hover:text-foreground"),children:[u.jsx(t.icon,{className:"h-4 w-4"}),t.name]},t.name)})})]})]}),u.jsx("div",{className:"border-t p-4",children:u.jsxs("div",{className:"text-xs text-muted-foreground",children:[u.jsx("p",{className:"mb-1",children:"Local Model Platform"}),u.jsx("p",{children:"Privacy-focused AI"})]})})]})}function vf(e,t){if(typeof e=="function")return e(t);e!=null&&(e.current=t)}function og(...e){return t=>{let n=!1;const r=e.map(i=>{const l=vf(i,t);return!n&&typeof l=="function"&&(n=!0),l});if(n)return()=>{for(let i=0;i{const{children:l,...o}=r,s=v.Children.toArray(l),a=s.find(Vk);if(a){const c=a.props.children,d=s.map(f=>f===a?v.Children.count(c)>1?v.Children.only(null):v.isValidElement(c)?c.props.children:null:f);return u.jsx(t,{...o,ref:i,children:v.isValidElement(c)?v.cloneElement(c,void 0,d):null})}return u.jsx(t,{...o,ref:i,children:l})});return n.displayName=`${e}.Slot`,n}var Bk=Ki("Slot");function Uk(e){const t=v.forwardRef((n,r)=>{const{children:i,...l}=n;if(v.isValidElement(i)){const o=Wk(i),s=Hk(l,i.props);return i.type!==v.Fragment&&(s.ref=r?og(r,o):o),v.cloneElement(i,s)}return v.Children.count(i)>1?v.Children.only(null):null});return t.displayName=`${e}.SlotClone`,t}var sg=Symbol("radix.slottable");function $k(e){const t=({children:n})=>u.jsx(u.Fragment,{children:n});return t.displayName=`${e}.Slottable`,t.__radixId=sg,t}function Vk(e){return v.isValidElement(e)&&typeof e.type=="function"&&"__radixId"in e.type&&e.type.__radixId===sg}function Hk(e,t){const n={...t};for(const r in t){const i=e[r],l=t[r];/^on[A-Z]/.test(r)?i&&l?n[r]=(...s)=>{const a=l(...s);return i(...s),a}:i&&(n[r]=i):r==="style"?n[r]={...i,...l}:r==="className"&&(n[r]=[i,l].filter(Boolean).join(" "))}return{...e,...n}}function Wk(e){var r,i;let t=(r=Object.getOwnPropertyDescriptor(e.props,"ref"))==null?void 0:r.get,n=t&&"isReactWarning"in t&&t.isReactWarning;return n?e.ref:(t=(i=Object.getOwnPropertyDescriptor(e,"ref"))==null?void 0:i.get,n=t&&"isReactWarning"in t&&t.isReactWarning,n?e.props.ref:e.props.ref||e.ref)}const xf=e=>typeof e=="boolean"?`${e}`:e===0?"0":e,wf=Ym,kc=(e,t)=>n=>{var r;if((t==null?void 0:t.variants)==null)return wf(e,n==null?void 0:n.class,n==null?void 0:n.className);const{variants:i,defaultVariants:l}=t,o=Object.keys(i).map(c=>{const d=n==null?void 0:n[c],f=l==null?void 0:l[c];if(d===null)return null;const p=xf(d)||xf(f);return i[c][p]}),s=n&&Object.entries(n).reduce((c,d)=>{let[f,p]=d;return p===void 0||(c[f]=p),c},{}),a=t==null||(r=t.compoundVariants)===null||r===void 0?void 0:r.reduce((c,d)=>{let{class:f,className:p,...h}=d;return Object.entries(h).every(w=>{let[y,S]=w;return Array.isArray(S)?S.includes({...l,...s}[y]):{...l,...s}[y]===S})?[...c,f,p]:c},[]);return wf(e,o,a,n==null?void 0:n.class,n==null?void 0:n.className)},Sc=kc("inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",{variants:{variant:{default:"bg-primary text-primary-foreground shadow hover:bg-primary/90",destructive:"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",outline:"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",secondary:"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",ghost:"hover:bg-accent hover:text-accent-foreground",link:"text-primary underline-offset-4 hover:underline"},size:{default:"h-9 px-4 py-2",sm:"h-8 rounded-md px-3 text-xs",lg:"h-10 rounded-md px-8",icon:"h-9 w-9"}},defaultVariants:{variant:"default",size:"default"}}),te=v.forwardRef(({className:e,variant:t,size:n,asChild:r=!1,...i},l)=>{const o=r?Bk:"button";return u.jsx(o,{className:re(Sc({variant:t,size:n,className:e})),ref:l,...i})});te.displayName="Button";function Gk(){const[e,t]=v.useState(!0);return u.jsxs("div",{className:"flex h-screen bg-background",children:[u.jsx("div",{className:"lg:hidden",children:u.jsx(te,{variant:"ghost",size:"icon",className:"fixed top-4 left-4 z-50",onClick:()=>t(!e),children:u.jsx(ok,{className:"h-5 w-5"})})}),u.jsx("div",{className:`
- ${e?"translate-x-0":"-translate-x-full"}
- fixed inset-y-0 left-0 z-40 w-64 transition-transform duration-300 ease-in-out
- lg:translate-x-0 lg:static lg:inset-0
- `,children:u.jsx(Fk,{})}),e&&u.jsx("div",{className:"fixed inset-0 z-30 bg-black/50 lg:hidden",onClick:()=>t(!1)}),u.jsx("div",{className:"flex-1 flex flex-col overflow-hidden",children:u.jsx("main",{className:"flex-1 overflow-auto",children:u.jsx(V0,{})})})]})}const Ce=v.forwardRef(({className:e,...t},n)=>u.jsx("div",{ref:n,className:re("rounded-xl border bg-card text-card-foreground shadow",e),...t}));Ce.displayName="Card";const We=v.forwardRef(({className:e,...t},n)=>u.jsx("div",{ref:n,className:re("flex flex-col space-y-1.5 p-6",e),...t}));We.displayName="CardHeader";const Je=v.forwardRef(({className:e,...t},n)=>u.jsx("div",{ref:n,className:re("font-semibold leading-none tracking-tight",e),...t}));Je.displayName="CardTitle";const Kk=v.forwardRef(({className:e,...t},n)=>u.jsx("div",{ref:n,className:re("text-sm text-muted-foreground",e),...t}));Kk.displayName="CardDescription";const Ne=v.forwardRef(({className:e,...t},n)=>u.jsx("div",{ref:n,className:re("p-6 pt-0",e),...t}));Ne.displayName="CardContent";const Qk=v.forwardRef(({className:e,...t},n)=>u.jsx("div",{ref:n,className:re("flex items-center p-6 pt-0",e),...t}));Qk.displayName="CardFooter";const Yk=[{icon:Ht,title:"Local AI Models",description:"Run powerful language models locally on your machine with full privacy control.",color:"text-blue-500"},{icon:Rn,title:"Interactive Chat",description:"Playground interface for testing prompts and exploring model capabilities.",color:"text-green-500"},{icon:Qa,title:"Privacy First",description:"Your data never leaves your machine. Complete privacy and security guaranteed.",color:"text-purple-500"},{icon:rl,title:"High Performance",description:"Optimized for speed with model caching and efficient resource management.",color:"text-yellow-500"}],qk=[{title:"Start Chatting",description:"Jump into the playground and start experimenting",href:"/playground",icon:Rn,primary:!0},{title:"Browse Models",description:"Explore available models and their capabilities",href:"/models",icon:bo,primary:!1},{title:"View Settings",description:"Configure your application preferences",href:"/settings",icon:ig,primary:!1}];function Xk(){return u.jsxs("div",{className:"min-h-screen bg-background",children:[u.jsx("div",{className:"border-b",children:u.jsx("div",{className:"flex h-14 items-center px-6",children:u.jsxs("div",{className:"flex items-center gap-2",children:[u.jsx(Ht,{className:"h-5 w-5"}),u.jsx("h1",{className:"text-lg font-semibold",children:"Home"})]})})}),u.jsx("div",{className:"flex-1 p-6",children:u.jsxs("div",{className:"max-w-6xl mx-auto space-y-8",children:[u.jsxs("div",{className:"text-center space-y-4",children:[u.jsxs("div",{className:"inline-flex items-center gap-2 px-3 py-1 bg-blue-100 text-blue-700 rounded-full text-sm",children:[u.jsx(ig,{className:"h-4 w-4"}),"Local AI Platform"]}),u.jsx("h1",{className:"text-4xl font-bold tracking-tight",children:"Welcome to Edge LLM"}),u.jsx("p",{className:"text-xl text-muted-foreground max-w-2xl mx-auto",children:"A powerful local AI platform for running language models privately on your machine. Experience the future of AI without compromising your privacy."})]}),u.jsx("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-4",children:qk.map(e=>u.jsx(Ce,{className:e.primary?"ring-2 ring-blue-500":"",children:u.jsx(Ne,{className:"p-6",children:u.jsxs(Eo,{to:e.href,className:"block space-y-3 group",children:[u.jsxs("div",{className:"flex items-center justify-between",children:[u.jsx(e.icon,{className:`h-8 w-8 ${e.primary?"text-blue-500":"text-muted-foreground"}`}),u.jsx(Dw,{className:"h-4 w-4 text-muted-foreground group-hover:text-foreground transition-colors"})]}),u.jsxs("div",{children:[u.jsx("h3",{className:"font-semibold text-lg",children:e.title}),u.jsx("p",{className:"text-muted-foreground text-sm",children:e.description})]})]})})},e.href))}),u.jsxs("div",{className:"space-y-6",children:[u.jsxs("div",{className:"text-center",children:[u.jsx("h2",{className:"text-2xl font-bold",children:"Key Features"}),u.jsx("p",{className:"text-muted-foreground mt-2",children:"Everything you need for local AI development and experimentation"})]}),u.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6",children:Yk.map((e,t)=>u.jsx(Ce,{children:u.jsxs(Ne,{className:"p-6 space-y-3",children:[u.jsx(e.icon,{className:`h-8 w-8 ${e.color}`}),u.jsxs("div",{children:[u.jsx("h3",{className:"font-semibold",children:e.title}),u.jsx("p",{className:"text-sm text-muted-foreground",children:e.description})]})]})},t))})]}),u.jsxs(Ce,{children:[u.jsx(We,{children:u.jsxs(Je,{className:"flex items-center gap-2",children:[u.jsx(vc,{className:"h-5 w-5"}),"Getting Started"]})}),u.jsxs(Ne,{className:"space-y-4",children:[u.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-6",children:[u.jsxs("div",{className:"space-y-2",children:[u.jsxs("div",{className:"flex items-center gap-2",children:[u.jsx("div",{className:"w-6 h-6 bg-blue-500 text-white rounded-full flex items-center justify-center text-sm font-medium",children:"1"}),u.jsx("h4",{className:"font-medium",children:"Choose a Model"})]}),u.jsx("p",{className:"text-sm text-muted-foreground pl-8",children:"Browse the model catalog and select a model that fits your needs."})]}),u.jsxs("div",{className:"space-y-2",children:[u.jsxs("div",{className:"flex items-center gap-2",children:[u.jsx("div",{className:"w-6 h-6 bg-blue-500 text-white rounded-full flex items-center justify-center text-sm font-medium",children:"2"}),u.jsx("h4",{className:"font-medium",children:"Load the Model"})]}),u.jsx("p",{className:"text-sm text-muted-foreground pl-8",children:"Click the load button to download and prepare the model for use."})]}),u.jsxs("div",{className:"space-y-2",children:[u.jsxs("div",{className:"flex items-center gap-2",children:[u.jsx("div",{className:"w-6 h-6 bg-blue-500 text-white rounded-full flex items-center justify-center text-sm font-medium",children:"3"}),u.jsx("h4",{className:"font-medium",children:"Start Chatting"})]}),u.jsx("p",{className:"text-sm text-muted-foreground pl-8",children:"Go to the playground and start experimenting with prompts."})]})]}),u.jsx("div",{className:"pt-4 border-t",children:u.jsx(Eo,{to:"/playground",children:u.jsxs(te,{className:"w-full md:w-auto",children:[u.jsx(Rn,{className:"h-4 w-4 mr-2"}),"Open Playground"]})})})]})]}),u.jsxs(Ce,{children:[u.jsx(We,{children:u.jsx(Je,{children:"System Status"})}),u.jsx(Ne,{children:u.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-4",children:[u.jsxs("div",{className:"flex items-center gap-3",children:[u.jsx("div",{className:"w-2 h-2 bg-green-500 rounded-full"}),u.jsxs("div",{children:[u.jsx("p",{className:"text-sm font-medium",children:"Backend"}),u.jsx("p",{className:"text-xs text-muted-foreground",children:"Running"})]})]}),u.jsxs("div",{className:"flex items-center gap-3",children:[u.jsx("div",{className:"w-2 h-2 bg-yellow-500 rounded-full"}),u.jsxs("div",{children:[u.jsx("p",{className:"text-sm font-medium",children:"Models"}),u.jsx("p",{className:"text-xs text-muted-foreground",children:"Ready to load"})]})]}),u.jsxs("div",{className:"flex items-center gap-3",children:[u.jsx("div",{className:"w-2 h-2 bg-blue-500 rounded-full"}),u.jsxs("div",{children:[u.jsx("p",{className:"text-sm font-medium",children:"Platform"}),u.jsx("p",{className:"text-xs text-muted-foreground",children:"Local"})]})]})]})})]})]})})]})}function ag(e,[t,n]){return Math.min(n,Math.max(t,e))}function De(e,t,{checkForDefaultPrevented:n=!0}={}){return function(i){if(e==null||e(i),n===!1||!i.defaultPrevented)return t==null?void 0:t(i)}}function Jk(e,t){const n=v.createContext(t),r=l=>{const{children:o,...s}=l,a=v.useMemo(()=>s,Object.values(s));return u.jsx(n.Provider,{value:a,children:o})};r.displayName=e+"Provider";function i(l){const o=v.useContext(n);if(o)return o;if(t!==void 0)return t;throw new Error(`\`${l}\` must be used within \`${e}\``)}return[r,i]}function Kr(e,t=[]){let n=[];function r(l,o){const s=v.createContext(o),a=n.length;n=[...n,o];const c=f=>{var m;const{scope:p,children:h,...w}=f,y=((m=p==null?void 0:p[e])==null?void 0:m[a])||s,S=v.useMemo(()=>w,Object.values(w));return u.jsx(y.Provider,{value:S,children:h})};c.displayName=l+"Provider";function d(f,p){var y;const h=((y=p==null?void 0:p[e])==null?void 0:y[a])||s,w=v.useContext(h);if(w)return w;if(o!==void 0)return o;throw new Error(`\`${f}\` must be used within \`${l}\``)}return[c,d]}const i=()=>{const l=n.map(o=>v.createContext(o));return function(s){const a=(s==null?void 0:s[e])||l;return v.useMemo(()=>({[`__scope${e}`]:{...s,[e]:a}}),[s,a])}};return i.scopeName=e,[r,Zk(i,...t)]}function Zk(...e){const t=e[0];if(e.length===1)return t;const n=()=>{const r=e.map(i=>({useScope:i(),scopeName:i.scopeName}));return function(l){const o=r.reduce((s,{useScope:a,scopeName:c})=>{const f=a(l)[`__scope${c}`];return{...s,...f}},{});return v.useMemo(()=>({[`__scope${t.scopeName}`]:o}),[o])}};return n.scopeName=t.scopeName,n}var er=globalThis!=null&&globalThis.document?v.useLayoutEffect:()=>{},eS=Cu[" useInsertionEffect ".trim().toString()]||er;function Qo({prop:e,defaultProp:t,onChange:n=()=>{},caller:r}){const[i,l,o]=tS({defaultProp:t,onChange:n}),s=e!==void 0,a=s?e:i;{const d=v.useRef(e!==void 0);v.useEffect(()=>{const f=d.current;f!==s&&console.warn(`${r} is changing from ${f?"controlled":"uncontrolled"} to ${s?"controlled":"uncontrolled"}. Components should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled value for the lifetime of the component.`),d.current=s},[s,r])}const c=v.useCallback(d=>{var f;if(s){const p=nS(d)?d(e):d;p!==e&&((f=o.current)==null||f.call(o,p))}else l(d)},[s,e,l,o]);return[a,c]}function tS({defaultProp:e,onChange:t}){const[n,r]=v.useState(e),i=v.useRef(n),l=v.useRef(t);return eS(()=>{l.current=t},[t]),v.useEffect(()=>{var o;i.current!==n&&((o=l.current)==null||o.call(l,n),i.current=n)},[n,i]),[n,r,l]}function nS(e){return typeof e=="function"}var rS=v.createContext(void 0);function iS(e){const t=v.useContext(rS);return e||t||"ltr"}function ug(e){const t=v.useRef({value:e,previous:e});return v.useMemo(()=>(t.current.value!==e&&(t.current.previous=t.current.value,t.current.value=e),t.current.previous),[e])}function cg(e){const[t,n]=v.useState(void 0);return er(()=>{if(e){n({width:e.offsetWidth,height:e.offsetHeight});const r=new ResizeObserver(i=>{if(!Array.isArray(i)||!i.length)return;const l=i[0];let o,s;if("borderBoxSize"in l){const a=l.borderBoxSize,c=Array.isArray(a)?a[0]:a;o=c.inlineSize,s=c.blockSize}else o=e.offsetWidth,s=e.offsetHeight;n({width:o,height:s})});return r.observe(e,{box:"border-box"}),()=>r.unobserve(e)}else n(void 0)},[e]),t}var lS=["a","button","div","form","h2","h3","img","input","label","li","nav","ol","p","select","span","svg","ul"],_e=lS.reduce((e,t)=>{const n=Ki(`Primitive.${t}`),r=v.forwardRef((i,l)=>{const{asChild:o,...s}=i,a=o?n:t;return typeof window<"u"&&(window[Symbol.for("radix-ui")]=!0),u.jsx(a,{...s,ref:l})});return r.displayName=`Primitive.${t}`,{...e,[t]:r}},{});function oS(e,t){e&&hc.flushSync(()=>e.dispatchEvent(t))}function sS(e){const t=e+"CollectionProvider",[n,r]=Kr(t),[i,l]=n(t,{collectionRef:{current:null},itemMap:new Map}),o=y=>{const{scope:S,children:m}=y,g=qt.useRef(null),x=qt.useRef(new Map).current;return u.jsx(i,{scope:S,itemMap:x,collectionRef:g,children:m})};o.displayName=t;const s=e+"CollectionSlot",a=Ki(s),c=qt.forwardRef((y,S)=>{const{scope:m,children:g}=y,x=l(s,m),N=Pe(S,x.collectionRef);return u.jsx(a,{ref:N,children:g})});c.displayName=s;const d=e+"CollectionItemSlot",f="data-radix-collection-item",p=Ki(d),h=qt.forwardRef((y,S)=>{const{scope:m,children:g,...x}=y,N=qt.useRef(null),b=Pe(S,N),E=l(d,m);return qt.useEffect(()=>(E.itemMap.set(N,{ref:N,...x}),()=>void E.itemMap.delete(N))),u.jsx(p,{[f]:"",ref:b,children:g})});h.displayName=d;function w(y){const S=l(e+"CollectionConsumer",y);return qt.useCallback(()=>{const g=S.collectionRef.current;if(!g)return[];const x=Array.from(g.querySelectorAll(`[${f}]`));return Array.from(S.itemMap.values()).sort((E,j)=>x.indexOf(E.ref.current)-x.indexOf(j.ref.current))},[S.collectionRef,S.itemMap])}return[{Provider:o,Slot:c,ItemSlot:h},w,r]}var dg=["PageUp","PageDown"],fg=["ArrowUp","ArrowDown","ArrowLeft","ArrowRight"],pg={"from-left":["Home","PageDown","ArrowDown","ArrowLeft"],"from-right":["Home","PageDown","ArrowDown","ArrowRight"],"from-bottom":["Home","PageDown","ArrowDown","ArrowLeft"],"from-top":["Home","PageDown","ArrowUp","ArrowLeft"]},Qr="Slider",[Ya,aS,uS]=sS(Qr),[hg,kP]=Kr(Qr,[uS]),[cS,Yo]=hg(Qr),mg=v.forwardRef((e,t)=>{const{name:n,min:r=0,max:i=100,step:l=1,orientation:o="horizontal",disabled:s=!1,minStepsBetweenThumbs:a=0,defaultValue:c=[r],value:d,onValueChange:f=()=>{},onValueCommit:p=()=>{},inverted:h=!1,form:w,...y}=e,S=v.useRef(new Set),m=v.useRef(0),x=o==="horizontal"?dS:fS,[N=[],b]=Qo({prop:d,defaultProp:c,onChange:A=>{var H;(H=[...S.current][m.current])==null||H.focus(),f(A)}}),E=v.useRef(N);function j(A){const T=yS(N,A);L(A,T)}function R(A){L(A,m.current)}function z(){const A=E.current[m.current];N[m.current]!==A&&p(N)}function L(A,T,{commit:H}={commit:!1}){const F=kS(l),$=SS(Math.round((A-r)/l)*l+r,F),Q=ag($,[r,i]);b((Y=[])=>{const _=mS(Y,Q,T);if(wS(_,a*l)){m.current=_.indexOf(Q);const O=String(_)!==String(Y);return O&&H&&p(_),O?_:Y}else return Y})}return u.jsx(cS,{scope:e.__scopeSlider,name:n,disabled:s,min:r,max:i,valueIndexToChangeRef:m,thumbs:S.current,values:N,orientation:o,form:w,children:u.jsx(Ya.Provider,{scope:e.__scopeSlider,children:u.jsx(Ya.Slot,{scope:e.__scopeSlider,children:u.jsx(x,{"aria-disabled":s,"data-disabled":s?"":void 0,...y,ref:t,onPointerDown:De(y.onPointerDown,()=>{s||(E.current=N)}),min:r,max:i,inverted:h,onSlideStart:s?void 0:j,onSlideMove:s?void 0:R,onSlideEnd:s?void 0:z,onHomeKeyDown:()=>!s&&L(r,0,{commit:!0}),onEndKeyDown:()=>!s&&L(i,N.length-1,{commit:!0}),onStepKeyDown:({event:A,direction:T})=>{if(!s){const $=dg.includes(A.key)||A.shiftKey&&fg.includes(A.key)?10:1,Q=m.current,Y=N[Q],_=l*$*T;L(Y+_,Q,{commit:!0})}}})})})})});mg.displayName=Qr;var[gg,yg]=hg(Qr,{startEdge:"left",endEdge:"right",size:"width",direction:1}),dS=v.forwardRef((e,t)=>{const{min:n,max:r,dir:i,inverted:l,onSlideStart:o,onSlideMove:s,onSlideEnd:a,onStepKeyDown:c,...d}=e,[f,p]=v.useState(null),h=Pe(t,x=>p(x)),w=v.useRef(void 0),y=iS(i),S=y==="ltr",m=S&&!l||!S&&l;function g(x){const N=w.current||f.getBoundingClientRect(),b=[0,N.width],j=Cc(b,m?[n,r]:[r,n]);return w.current=N,j(x-N.left)}return u.jsx(gg,{scope:e.__scopeSlider,startEdge:m?"left":"right",endEdge:m?"right":"left",direction:m?1:-1,size:"width",children:u.jsx(vg,{dir:y,"data-orientation":"horizontal",...d,ref:h,style:{...d.style,"--radix-slider-thumb-transform":"translateX(-50%)"},onSlideStart:x=>{const N=g(x.clientX);o==null||o(N)},onSlideMove:x=>{const N=g(x.clientX);s==null||s(N)},onSlideEnd:()=>{w.current=void 0,a==null||a()},onStepKeyDown:x=>{const b=pg[m?"from-left":"from-right"].includes(x.key);c==null||c({event:x,direction:b?-1:1})}})})}),fS=v.forwardRef((e,t)=>{const{min:n,max:r,inverted:i,onSlideStart:l,onSlideMove:o,onSlideEnd:s,onStepKeyDown:a,...c}=e,d=v.useRef(null),f=Pe(t,d),p=v.useRef(void 0),h=!i;function w(y){const S=p.current||d.current.getBoundingClientRect(),m=[0,S.height],x=Cc(m,h?[r,n]:[n,r]);return p.current=S,x(y-S.top)}return u.jsx(gg,{scope:e.__scopeSlider,startEdge:h?"bottom":"top",endEdge:h?"top":"bottom",size:"height",direction:h?1:-1,children:u.jsx(vg,{"data-orientation":"vertical",...c,ref:f,style:{...c.style,"--radix-slider-thumb-transform":"translateY(50%)"},onSlideStart:y=>{const S=w(y.clientY);l==null||l(S)},onSlideMove:y=>{const S=w(y.clientY);o==null||o(S)},onSlideEnd:()=>{p.current=void 0,s==null||s()},onStepKeyDown:y=>{const m=pg[h?"from-bottom":"from-top"].includes(y.key);a==null||a({event:y,direction:m?-1:1})}})})}),vg=v.forwardRef((e,t)=>{const{__scopeSlider:n,onSlideStart:r,onSlideMove:i,onSlideEnd:l,onHomeKeyDown:o,onEndKeyDown:s,onStepKeyDown:a,...c}=e,d=Yo(Qr,n);return u.jsx(_e.span,{...c,ref:t,onKeyDown:De(e.onKeyDown,f=>{f.key==="Home"?(o(f),f.preventDefault()):f.key==="End"?(s(f),f.preventDefault()):dg.concat(fg).includes(f.key)&&(a(f),f.preventDefault())}),onPointerDown:De(e.onPointerDown,f=>{const p=f.target;p.setPointerCapture(f.pointerId),f.preventDefault(),d.thumbs.has(p)?p.focus():r(f)}),onPointerMove:De(e.onPointerMove,f=>{f.target.hasPointerCapture(f.pointerId)&&i(f)}),onPointerUp:De(e.onPointerUp,f=>{const p=f.target;p.hasPointerCapture(f.pointerId)&&(p.releasePointerCapture(f.pointerId),l(f))})})}),xg="SliderTrack",wg=v.forwardRef((e,t)=>{const{__scopeSlider:n,...r}=e,i=Yo(xg,n);return u.jsx(_e.span,{"data-disabled":i.disabled?"":void 0,"data-orientation":i.orientation,...r,ref:t})});wg.displayName=xg;var qa="SliderRange",kg=v.forwardRef((e,t)=>{const{__scopeSlider:n,...r}=e,i=Yo(qa,n),l=yg(qa,n),o=v.useRef(null),s=Pe(t,o),a=i.values.length,c=i.values.map(p=>Ng(p,i.min,i.max)),d=a>1?Math.min(...c):0,f=100-Math.max(...c);return u.jsx(_e.span,{"data-orientation":i.orientation,"data-disabled":i.disabled?"":void 0,...r,ref:s,style:{...e.style,[l.startEdge]:d+"%",[l.endEdge]:f+"%"}})});kg.displayName=qa;var Xa="SliderThumb",Sg=v.forwardRef((e,t)=>{const n=aS(e.__scopeSlider),[r,i]=v.useState(null),l=Pe(t,s=>i(s)),o=v.useMemo(()=>r?n().findIndex(s=>s.ref.current===r):-1,[n,r]);return u.jsx(pS,{...e,ref:l,index:o})}),pS=v.forwardRef((e,t)=>{const{__scopeSlider:n,index:r,name:i,...l}=e,o=Yo(Xa,n),s=yg(Xa,n),[a,c]=v.useState(null),d=Pe(t,g=>c(g)),f=a?o.form||!!a.closest("form"):!0,p=cg(a),h=o.values[r],w=h===void 0?0:Ng(h,o.min,o.max),y=gS(r,o.values.length),S=p==null?void 0:p[s.size],m=S?vS(S,w,s.direction):0;return v.useEffect(()=>{if(a)return o.thumbs.add(a),()=>{o.thumbs.delete(a)}},[a,o.thumbs]),u.jsxs("span",{style:{transform:"var(--radix-slider-thumb-transform)",position:"absolute",[s.startEdge]:`calc(${w}% + ${m}px)`},children:[u.jsx(Ya.ItemSlot,{scope:e.__scopeSlider,children:u.jsx(_e.span,{role:"slider","aria-label":e["aria-label"]||y,"aria-valuemin":o.min,"aria-valuenow":h,"aria-valuemax":o.max,"aria-orientation":o.orientation,"data-orientation":o.orientation,"data-disabled":o.disabled?"":void 0,tabIndex:o.disabled?void 0:0,...l,ref:d,style:h===void 0?{display:"none"}:e.style,onFocus:De(e.onFocus,()=>{o.valueIndexToChangeRef.current=r})})}),f&&u.jsx(Cg,{name:i??(o.name?o.name+(o.values.length>1?"[]":""):void 0),form:o.form,value:h},r)]})});Sg.displayName=Xa;var hS="RadioBubbleInput",Cg=v.forwardRef(({__scopeSlider:e,value:t,...n},r)=>{const i=v.useRef(null),l=Pe(i,r),o=ug(t);return v.useEffect(()=>{const s=i.current;if(!s)return;const a=window.HTMLInputElement.prototype,d=Object.getOwnPropertyDescriptor(a,"value").set;if(o!==t&&d){const f=new Event("input",{bubbles:!0});d.call(s,t),s.dispatchEvent(f)}},[o,t]),u.jsx(_e.input,{style:{display:"none"},...n,ref:l,defaultValue:t})});Cg.displayName=hS;function mS(e=[],t,n){const r=[...e];return r[n]=t,r.sort((i,l)=>i-l)}function Ng(e,t,n){const l=100/(n-t)*(e-t);return ag(l,[0,100])}function gS(e,t){return t>2?`Value ${e+1} of ${t}`:t===2?["Minimum","Maximum"][e]:void 0}function yS(e,t){if(e.length===1)return 0;const n=e.map(i=>Math.abs(i-t)),r=Math.min(...n);return n.indexOf(r)}function vS(e,t,n){const r=e/2,l=Cc([0,50],[0,r]);return(r-l(t)*n)*n}function xS(e){return e.slice(0,-1).map((t,n)=>e[n+1]-t)}function wS(e,t){if(t>0){const n=xS(e);return Math.min(...n)>=t}return!0}function Cc(e,t){return n=>{if(e[0]===e[1]||t[0]===t[1])return t[0];const r=(t[1]-t[0])/(e[1]-e[0]);return t[0]+r*(n-e[0])}}function kS(e){return(String(e).split(".")[1]||"").length}function SS(e,t){const n=Math.pow(10,t);return Math.round(e*n)/n}var Eg=mg,CS=wg,NS=kg,ES=Sg;const Ja=v.forwardRef(({className:e,...t},n)=>u.jsxs(Eg,{ref:n,className:re("relative flex w-full touch-none select-none items-center",e),...t,children:[u.jsx(CS,{className:"relative h-1.5 w-full grow overflow-hidden rounded-full bg-primary/20",children:u.jsx(NS,{className:"absolute h-full bg-primary"})}),u.jsx(ES,{className:"block h-4 w-4 rounded-full border border-primary/50 bg-background shadow transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50"})]}));Ja.displayName=Eg.displayName;var bS="Label",bg=v.forwardRef((e,t)=>u.jsx(_e.label,{...e,ref:t,onMouseDown:n=>{var i;n.target.closest("button, input, select, textarea")||((i=e.onMouseDown)==null||i.call(e,n),!n.defaultPrevented&&n.detail>1&&n.preventDefault())}}));bg.displayName=bS;var jg=bg;const jS=kc("text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"),Te=v.forwardRef(({className:e,...t},n)=>u.jsx(jg,{ref:n,className:re(jS(),e),...t}));Te.displayName=jg.displayName;const PS=kc("inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",{variants:{variant:{default:"border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",secondary:"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",destructive:"border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",outline:"text-foreground"}},defaultVariants:{variant:"default"}});function Ge({className:e,variant:t,...n}){return u.jsx("div",{className:re(PS({variant:t}),e),...n})}var _S=Cu[" useId ".trim().toString()]||(()=>{}),TS=0;function Gl(e){const[t,n]=v.useState(_S());return er(()=>{e||n(r=>r??String(TS++))},[e]),e||(t?`radix-${t}`:"")}function Qi(e){const t=v.useRef(e);return v.useEffect(()=>{t.current=e}),v.useMemo(()=>(...n)=>{var r;return(r=t.current)==null?void 0:r.call(t,...n)},[])}function RS(e,t=globalThis==null?void 0:globalThis.document){const n=Qi(e);v.useEffect(()=>{const r=i=>{i.key==="Escape"&&n(i)};return t.addEventListener("keydown",r,{capture:!0}),()=>t.removeEventListener("keydown",r,{capture:!0})},[n,t])}var IS="DismissableLayer",Za="dismissableLayer.update",MS="dismissableLayer.pointerDownOutside",AS="dismissableLayer.focusOutside",kf,Pg=v.createContext({layers:new Set,layersWithOutsidePointerEventsDisabled:new Set,branches:new Set}),_g=v.forwardRef((e,t)=>{const{disableOutsidePointerEvents:n=!1,onEscapeKeyDown:r,onPointerDownOutside:i,onFocusOutside:l,onInteractOutside:o,onDismiss:s,...a}=e,c=v.useContext(Pg),[d,f]=v.useState(null),p=(d==null?void 0:d.ownerDocument)??(globalThis==null?void 0:globalThis.document),[,h]=v.useState({}),w=Pe(t,j=>f(j)),y=Array.from(c.layers),[S]=[...c.layersWithOutsidePointerEventsDisabled].slice(-1),m=y.indexOf(S),g=d?y.indexOf(d):-1,x=c.layersWithOutsidePointerEventsDisabled.size>0,N=g>=m,b=zS(j=>{const R=j.target,z=[...c.branches].some(L=>L.contains(R));!N||z||(i==null||i(j),o==null||o(j),j.defaultPrevented||s==null||s())},p),E=OS(j=>{const R=j.target;[...c.branches].some(L=>L.contains(R))||(l==null||l(j),o==null||o(j),j.defaultPrevented||s==null||s())},p);return RS(j=>{g===c.layers.size-1&&(r==null||r(j),!j.defaultPrevented&&s&&(j.preventDefault(),s()))},p),v.useEffect(()=>{if(d)return n&&(c.layersWithOutsidePointerEventsDisabled.size===0&&(kf=p.body.style.pointerEvents,p.body.style.pointerEvents="none"),c.layersWithOutsidePointerEventsDisabled.add(d)),c.layers.add(d),Sf(),()=>{n&&c.layersWithOutsidePointerEventsDisabled.size===1&&(p.body.style.pointerEvents=kf)}},[d,p,n,c]),v.useEffect(()=>()=>{d&&(c.layers.delete(d),c.layersWithOutsidePointerEventsDisabled.delete(d),Sf())},[d,c]),v.useEffect(()=>{const j=()=>h({});return document.addEventListener(Za,j),()=>document.removeEventListener(Za,j)},[]),u.jsx(_e.div,{...a,ref:w,style:{pointerEvents:x?N?"auto":"none":void 0,...e.style},onFocusCapture:De(e.onFocusCapture,E.onFocusCapture),onBlurCapture:De(e.onBlurCapture,E.onBlurCapture),onPointerDownCapture:De(e.onPointerDownCapture,b.onPointerDownCapture)})});_g.displayName=IS;var LS="DismissableLayerBranch",DS=v.forwardRef((e,t)=>{const n=v.useContext(Pg),r=v.useRef(null),i=Pe(t,r);return v.useEffect(()=>{const l=r.current;if(l)return n.branches.add(l),()=>{n.branches.delete(l)}},[n.branches]),u.jsx(_e.div,{...e,ref:i})});DS.displayName=LS;function zS(e,t=globalThis==null?void 0:globalThis.document){const n=Qi(e),r=v.useRef(!1),i=v.useRef(()=>{});return v.useEffect(()=>{const l=s=>{if(s.target&&!r.current){let a=function(){Tg(MS,n,c,{discrete:!0})};const c={originalEvent:s};s.pointerType==="touch"?(t.removeEventListener("click",i.current),i.current=a,t.addEventListener("click",i.current,{once:!0})):a()}else t.removeEventListener("click",i.current);r.current=!1},o=window.setTimeout(()=>{t.addEventListener("pointerdown",l)},0);return()=>{window.clearTimeout(o),t.removeEventListener("pointerdown",l),t.removeEventListener("click",i.current)}},[t,n]),{onPointerDownCapture:()=>r.current=!0}}function OS(e,t=globalThis==null?void 0:globalThis.document){const n=Qi(e),r=v.useRef(!1);return v.useEffect(()=>{const i=l=>{l.target&&!r.current&&Tg(AS,n,{originalEvent:l},{discrete:!1})};return t.addEventListener("focusin",i),()=>t.removeEventListener("focusin",i)},[t,n]),{onFocusCapture:()=>r.current=!0,onBlurCapture:()=>r.current=!1}}function Sf(){const e=new CustomEvent(Za);document.dispatchEvent(e)}function Tg(e,t,n,{discrete:r}){const i=n.originalEvent.target,l=new CustomEvent(e,{bubbles:!1,cancelable:!0,detail:n});t&&i.addEventListener(e,t,{once:!0}),r?oS(i,l):i.dispatchEvent(l)}var Rs="focusScope.autoFocusOnMount",Is="focusScope.autoFocusOnUnmount",Cf={bubbles:!1,cancelable:!0},FS="FocusScope",Rg=v.forwardRef((e,t)=>{const{loop:n=!1,trapped:r=!1,onMountAutoFocus:i,onUnmountAutoFocus:l,...o}=e,[s,a]=v.useState(null),c=Qi(i),d=Qi(l),f=v.useRef(null),p=Pe(t,y=>a(y)),h=v.useRef({paused:!1,pause(){this.paused=!0},resume(){this.paused=!1}}).current;v.useEffect(()=>{if(r){let y=function(x){if(h.paused||!s)return;const N=x.target;s.contains(N)?f.current=N:fn(f.current,{select:!0})},S=function(x){if(h.paused||!s)return;const N=x.relatedTarget;N!==null&&(s.contains(N)||fn(f.current,{select:!0}))},m=function(x){if(document.activeElement===document.body)for(const b of x)b.removedNodes.length>0&&fn(s)};document.addEventListener("focusin",y),document.addEventListener("focusout",S);const g=new MutationObserver(m);return s&&g.observe(s,{childList:!0,subtree:!0}),()=>{document.removeEventListener("focusin",y),document.removeEventListener("focusout",S),g.disconnect()}}},[r,s,h.paused]),v.useEffect(()=>{if(s){Ef.add(h);const y=document.activeElement;if(!s.contains(y)){const m=new CustomEvent(Rs,Cf);s.addEventListener(Rs,c),s.dispatchEvent(m),m.defaultPrevented||(BS(WS(Ig(s)),{select:!0}),document.activeElement===y&&fn(s))}return()=>{s.removeEventListener(Rs,c),setTimeout(()=>{const m=new CustomEvent(Is,Cf);s.addEventListener(Is,d),s.dispatchEvent(m),m.defaultPrevented||fn(y??document.body,{select:!0}),s.removeEventListener(Is,d),Ef.remove(h)},0)}}},[s,c,d,h]);const w=v.useCallback(y=>{if(!n&&!r||h.paused)return;const S=y.key==="Tab"&&!y.altKey&&!y.ctrlKey&&!y.metaKey,m=document.activeElement;if(S&&m){const g=y.currentTarget,[x,N]=US(g);x&&N?!y.shiftKey&&m===N?(y.preventDefault(),n&&fn(x,{select:!0})):y.shiftKey&&m===x&&(y.preventDefault(),n&&fn(N,{select:!0})):m===g&&y.preventDefault()}},[n,r,h.paused]);return u.jsx(_e.div,{tabIndex:-1,...o,ref:p,onKeyDown:w})});Rg.displayName=FS;function BS(e,{select:t=!1}={}){const n=document.activeElement;for(const r of e)if(fn(r,{select:t}),document.activeElement!==n)return}function US(e){const t=Ig(e),n=Nf(t,e),r=Nf(t.reverse(),e);return[n,r]}function Ig(e){const t=[],n=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT,{acceptNode:r=>{const i=r.tagName==="INPUT"&&r.type==="hidden";return r.disabled||r.hidden||i?NodeFilter.FILTER_SKIP:r.tabIndex>=0?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}});for(;n.nextNode();)t.push(n.currentNode);return t}function Nf(e,t){for(const n of e)if(!$S(n,{upTo:t}))return n}function $S(e,{upTo:t}){if(getComputedStyle(e).visibility==="hidden")return!0;for(;e;){if(t!==void 0&&e===t)return!1;if(getComputedStyle(e).display==="none")return!0;e=e.parentElement}return!1}function VS(e){return e instanceof HTMLInputElement&&"select"in e}function fn(e,{select:t=!1}={}){if(e&&e.focus){const n=document.activeElement;e.focus({preventScroll:!0}),e!==n&&VS(e)&&t&&e.select()}}var Ef=HS();function HS(){let e=[];return{add(t){const n=e[0];t!==n&&(n==null||n.pause()),e=bf(e,t),e.unshift(t)},remove(t){var n;e=bf(e,t),(n=e[0])==null||n.resume()}}}function bf(e,t){const n=[...e],r=n.indexOf(t);return r!==-1&&n.splice(r,1),n}function WS(e){return e.filter(t=>t.tagName!=="A")}var GS="Portal",Mg=v.forwardRef((e,t)=>{var s;const{container:n,...r}=e,[i,l]=v.useState(!1);er(()=>l(!0),[]);const o=n||i&&((s=globalThis==null?void 0:globalThis.document)==null?void 0:s.body);return o?X1.createPortal(u.jsx(_e.div,{...r,ref:t}),o):null});Mg.displayName=GS;function KS(e,t){return v.useReducer((n,r)=>t[n][r]??n,e)}var il=e=>{const{present:t,children:n}=e,r=QS(t),i=typeof n=="function"?n({present:r.isPresent}):v.Children.only(n),l=Pe(r.ref,YS(i));return typeof n=="function"||r.isPresent?v.cloneElement(i,{ref:l}):null};il.displayName="Presence";function QS(e){const[t,n]=v.useState(),r=v.useRef(null),i=v.useRef(e),l=v.useRef("none"),o=e?"mounted":"unmounted",[s,a]=KS(o,{mounted:{UNMOUNT:"unmounted",ANIMATION_OUT:"unmountSuspended"},unmountSuspended:{MOUNT:"mounted",ANIMATION_END:"unmounted"},unmounted:{MOUNT:"mounted"}});return v.useEffect(()=>{const c=bl(r.current);l.current=s==="mounted"?c:"none"},[s]),er(()=>{const c=r.current,d=i.current;if(d!==e){const p=l.current,h=bl(c);e?a("MOUNT"):h==="none"||(c==null?void 0:c.display)==="none"?a("UNMOUNT"):a(d&&p!==h?"ANIMATION_OUT":"UNMOUNT"),i.current=e}},[e,a]),er(()=>{if(t){let c;const d=t.ownerDocument.defaultView??window,f=h=>{const y=bl(r.current).includes(CSS.escape(h.animationName));if(h.target===t&&y&&(a("ANIMATION_END"),!i.current)){const S=t.style.animationFillMode;t.style.animationFillMode="forwards",c=d.setTimeout(()=>{t.style.animationFillMode==="forwards"&&(t.style.animationFillMode=S)})}},p=h=>{h.target===t&&(l.current=bl(r.current))};return t.addEventListener("animationstart",p),t.addEventListener("animationcancel",f),t.addEventListener("animationend",f),()=>{d.clearTimeout(c),t.removeEventListener("animationstart",p),t.removeEventListener("animationcancel",f),t.removeEventListener("animationend",f)}}else a("ANIMATION_END")},[t,a]),{isPresent:["mounted","unmountSuspended"].includes(s),ref:v.useCallback(c=>{r.current=c?getComputedStyle(c):null,n(c)},[])}}function bl(e){return(e==null?void 0:e.animationName)||"none"}function YS(e){var r,i;let t=(r=Object.getOwnPropertyDescriptor(e.props,"ref"))==null?void 0:r.get,n=t&&"isReactWarning"in t&&t.isReactWarning;return n?e.ref:(t=(i=Object.getOwnPropertyDescriptor(e,"ref"))==null?void 0:i.get,n=t&&"isReactWarning"in t&&t.isReactWarning,n?e.props.ref:e.props.ref||e.ref)}var Ms=0;function qS(){v.useEffect(()=>{const e=document.querySelectorAll("[data-radix-focus-guard]");return document.body.insertAdjacentElement("afterbegin",e[0]??jf()),document.body.insertAdjacentElement("beforeend",e[1]??jf()),Ms++,()=>{Ms===1&&document.querySelectorAll("[data-radix-focus-guard]").forEach(t=>t.remove()),Ms--}},[])}function jf(){const e=document.createElement("span");return e.setAttribute("data-radix-focus-guard",""),e.tabIndex=0,e.style.outline="none",e.style.opacity="0",e.style.position="fixed",e.style.pointerEvents="none",e}var Bt=function(){return Bt=Object.assign||function(t){for(var n,r=1,i=arguments.length;r"u")return pC;var t=hC(e),n=document.documentElement.clientWidth,r=window.innerWidth;return{left:t[0],top:t[1],right:t[2],gap:Math.max(0,r-n+t[2]-t[0])}},gC=zg(),Ir="data-scroll-locked",yC=function(e,t,n,r){var i=e.left,l=e.top,o=e.right,s=e.gap;return n===void 0&&(n="margin"),`
- .`.concat(JS,` {
- overflow: hidden `).concat(r,`;
- padding-right: `).concat(s,"px ").concat(r,`;
- }
- body[`).concat(Ir,`] {
- overflow: hidden `).concat(r,`;
- overscroll-behavior: contain;
- `).concat([t&&"position: relative ".concat(r,";"),n==="margin"&&`
- padding-left: `.concat(i,`px;
- padding-top: `).concat(l,`px;
- padding-right: `).concat(o,`px;
- margin-left:0;
- margin-top:0;
- margin-right: `).concat(s,"px ").concat(r,`;
- `),n==="padding"&&"padding-right: ".concat(s,"px ").concat(r,";")].filter(Boolean).join(""),`
- }
-
- .`).concat(Kl,` {
- right: `).concat(s,"px ").concat(r,`;
- }
-
- .`).concat(Ql,` {
- margin-right: `).concat(s,"px ").concat(r,`;
- }
-
- .`).concat(Kl," .").concat(Kl,` {
- right: 0 `).concat(r,`;
- }
-
- .`).concat(Ql," .").concat(Ql,` {
- margin-right: 0 `).concat(r,`;
- }
-
- body[`).concat(Ir,`] {
- `).concat(ZS,": ").concat(s,`px;
- }
-`)},Tf=function(){var e=parseInt(document.body.getAttribute(Ir)||"0",10);return isFinite(e)?e:0},vC=function(){v.useEffect(function(){return document.body.setAttribute(Ir,(Tf()+1).toString()),function(){var e=Tf()-1;e<=0?document.body.removeAttribute(Ir):document.body.setAttribute(Ir,e.toString())}},[])},xC=function(e){var t=e.noRelative,n=e.noImportant,r=e.gapMode,i=r===void 0?"margin":r;vC();var l=v.useMemo(function(){return mC(i)},[i]);return v.createElement(gC,{styles:yC(l,!t,i,n?"":"!important")})},eu=!1;if(typeof window<"u")try{var jl=Object.defineProperty({},"passive",{get:function(){return eu=!0,!0}});window.addEventListener("test",jl,jl),window.removeEventListener("test",jl,jl)}catch{eu=!1}var ur=eu?{passive:!1}:!1,wC=function(e){return e.tagName==="TEXTAREA"},Og=function(e,t){if(!(e instanceof Element))return!1;var n=window.getComputedStyle(e);return n[t]!=="hidden"&&!(n.overflowY===n.overflowX&&!wC(e)&&n[t]==="visible")},kC=function(e){return Og(e,"overflowY")},SC=function(e){return Og(e,"overflowX")},Rf=function(e,t){var n=t.ownerDocument,r=t;do{typeof ShadowRoot<"u"&&r instanceof ShadowRoot&&(r=r.host);var i=Fg(e,r);if(i){var l=Bg(e,r),o=l[1],s=l[2];if(o>s)return!0}r=r.parentNode}while(r&&r!==n.body);return!1},CC=function(e){var t=e.scrollTop,n=e.scrollHeight,r=e.clientHeight;return[t,n,r]},NC=function(e){var t=e.scrollLeft,n=e.scrollWidth,r=e.clientWidth;return[t,n,r]},Fg=function(e,t){return e==="v"?kC(t):SC(t)},Bg=function(e,t){return e==="v"?CC(t):NC(t)},EC=function(e,t){return e==="h"&&t==="rtl"?-1:1},bC=function(e,t,n,r,i){var l=EC(e,window.getComputedStyle(t).direction),o=l*r,s=n.target,a=t.contains(s),c=!1,d=o>0,f=0,p=0;do{if(!s)break;var h=Bg(e,s),w=h[0],y=h[1],S=h[2],m=y-S-l*w;(w||m)&&Fg(e,s)&&(f+=m,p+=w);var g=s.parentNode;s=g&&g.nodeType===Node.DOCUMENT_FRAGMENT_NODE?g.host:g}while(!a&&s!==document.body||a&&(t.contains(s)||t===s));return(d&&(i&&Math.abs(f)<1||!i&&o>f)||!d&&(i&&Math.abs(p)<1||!i&&-o>p))&&(c=!0),c},Pl=function(e){return"changedTouches"in e?[e.changedTouches[0].clientX,e.changedTouches[0].clientY]:[0,0]},If=function(e){return[e.deltaX,e.deltaY]},Mf=function(e){return e&&"current"in e?e.current:e},jC=function(e,t){return e[0]===t[0]&&e[1]===t[1]},PC=function(e){return`
- .block-interactivity-`.concat(e,` {pointer-events: none;}
- .allow-interactivity-`).concat(e,` {pointer-events: all;}
-`)},_C=0,cr=[];function TC(e){var t=v.useRef([]),n=v.useRef([0,0]),r=v.useRef(),i=v.useState(_C++)[0],l=v.useState(zg)[0],o=v.useRef(e);v.useEffect(function(){o.current=e},[e]),v.useEffect(function(){if(e.inert){document.body.classList.add("block-interactivity-".concat(i));var y=XS([e.lockRef.current],(e.shards||[]).map(Mf),!0).filter(Boolean);return y.forEach(function(S){return S.classList.add("allow-interactivity-".concat(i))}),function(){document.body.classList.remove("block-interactivity-".concat(i)),y.forEach(function(S){return S.classList.remove("allow-interactivity-".concat(i))})}}},[e.inert,e.lockRef.current,e.shards]);var s=v.useCallback(function(y,S){if("touches"in y&&y.touches.length===2||y.type==="wheel"&&y.ctrlKey)return!o.current.allowPinchZoom;var m=Pl(y),g=n.current,x="deltaX"in y?y.deltaX:g[0]-m[0],N="deltaY"in y?y.deltaY:g[1]-m[1],b,E=y.target,j=Math.abs(x)>Math.abs(N)?"h":"v";if("touches"in y&&j==="h"&&E.type==="range")return!1;var R=Rf(j,E);if(!R)return!0;if(R?b=j:(b=j==="v"?"h":"v",R=Rf(j,E)),!R)return!1;if(!r.current&&"changedTouches"in y&&(x||N)&&(r.current=b),!b)return!0;var z=r.current||b;return bC(z,S,y,z==="h"?x:N,!0)},[]),a=v.useCallback(function(y){var S=y;if(!(!cr.length||cr[cr.length-1]!==l)){var m="deltaY"in S?If(S):Pl(S),g=t.current.filter(function(b){return b.name===S.type&&(b.target===S.target||S.target===b.shadowParent)&&jC(b.delta,m)})[0];if(g&&g.should){S.cancelable&&S.preventDefault();return}if(!g){var x=(o.current.shards||[]).map(Mf).filter(Boolean).filter(function(b){return b.contains(S.target)}),N=x.length>0?s(S,x[0]):!o.current.noIsolation;N&&S.cancelable&&S.preventDefault()}}},[]),c=v.useCallback(function(y,S,m,g){var x={name:y,delta:S,target:m,should:g,shadowParent:RC(m)};t.current.push(x),setTimeout(function(){t.current=t.current.filter(function(N){return N!==x})},1)},[]),d=v.useCallback(function(y){n.current=Pl(y),r.current=void 0},[]),f=v.useCallback(function(y){c(y.type,If(y),y.target,s(y,e.lockRef.current))},[]),p=v.useCallback(function(y){c(y.type,Pl(y),y.target,s(y,e.lockRef.current))},[]);v.useEffect(function(){return cr.push(l),e.setCallbacks({onScrollCapture:f,onWheelCapture:f,onTouchMoveCapture:p}),document.addEventListener("wheel",a,ur),document.addEventListener("touchmove",a,ur),document.addEventListener("touchstart",d,ur),function(){cr=cr.filter(function(y){return y!==l}),document.removeEventListener("wheel",a,ur),document.removeEventListener("touchmove",a,ur),document.removeEventListener("touchstart",d,ur)}},[]);var h=e.removeScrollBar,w=e.inert;return v.createElement(v.Fragment,null,w?v.createElement(l,{styles:PC(i)}):null,h?v.createElement(xC,{noRelative:e.noRelative,gapMode:e.gapMode}):null)}function RC(e){for(var t=null;e!==null;)e instanceof ShadowRoot&&(t=e.host,e=e.host),e=e.parentNode;return t}const IC=oC(Dg,TC);var Ug=v.forwardRef(function(e,t){return v.createElement(qo,Bt({},e,{ref:t,sideCar:IC}))});Ug.classNames=qo.classNames;const MC=Ug;var AC=function(e){if(typeof document>"u")return null;var t=Array.isArray(e)?e[0]:e;return t.ownerDocument.body},dr=new WeakMap,_l=new WeakMap,Tl={},zs=0,$g=function(e){return e&&(e.host||$g(e.parentNode))},LC=function(e,t){return t.map(function(n){if(e.contains(n))return n;var r=$g(n);return r&&e.contains(r)?r:(console.error("aria-hidden",n,"in not contained inside",e,". Doing nothing"),null)}).filter(function(n){return!!n})},DC=function(e,t,n,r){var i=LC(t,Array.isArray(e)?e:[e]);Tl[n]||(Tl[n]=new WeakMap);var l=Tl[n],o=[],s=new Set,a=new Set(i),c=function(f){!f||s.has(f)||(s.add(f),c(f.parentNode))};i.forEach(c);var d=function(f){!f||a.has(f)||Array.prototype.forEach.call(f.children,function(p){if(s.has(p))d(p);else try{var h=p.getAttribute(r),w=h!==null&&h!=="false",y=(dr.get(p)||0)+1,S=(l.get(p)||0)+1;dr.set(p,y),l.set(p,S),o.push(p),y===1&&w&&_l.set(p,!0),S===1&&p.setAttribute(n,"true"),w||p.setAttribute(r,"true")}catch(m){console.error("aria-hidden: cannot operate on ",p,m)}})};return d(t),s.clear(),zs++,function(){o.forEach(function(f){var p=dr.get(f)-1,h=l.get(f)-1;dr.set(f,p),l.set(f,h),p||(_l.has(f)||f.removeAttribute(r),_l.delete(f)),h||f.removeAttribute(n)}),zs--,zs||(dr=new WeakMap,dr=new WeakMap,_l=new WeakMap,Tl={})}},zC=function(e,t,n){n===void 0&&(n="data-aria-hidden");var r=Array.from(Array.isArray(e)?e:[e]),i=t||AC(e);return i?(r.push.apply(r,Array.from(i.querySelectorAll("[aria-live], script"))),DC(r,i,n,"aria-hidden")):function(){return null}},Xo="Dialog",[Vg,Hg]=Kr(Xo),[OC,At]=Vg(Xo),Wg=e=>{const{__scopeDialog:t,children:n,open:r,defaultOpen:i,onOpenChange:l,modal:o=!0}=e,s=v.useRef(null),a=v.useRef(null),[c,d]=Qo({prop:r,defaultProp:i??!1,onChange:l,caller:Xo});return u.jsx(OC,{scope:t,triggerRef:s,contentRef:a,contentId:Gl(),titleId:Gl(),descriptionId:Gl(),open:c,onOpenChange:d,onOpenToggle:v.useCallback(()=>d(f=>!f),[d]),modal:o,children:n})};Wg.displayName=Xo;var Gg="DialogTrigger",Kg=v.forwardRef((e,t)=>{const{__scopeDialog:n,...r}=e,i=At(Gg,n),l=Pe(t,i.triggerRef);return u.jsx(_e.button,{type:"button","aria-haspopup":"dialog","aria-expanded":i.open,"aria-controls":i.contentId,"data-state":bc(i.open),...r,ref:l,onClick:De(e.onClick,i.onOpenToggle)})});Kg.displayName=Gg;var Nc="DialogPortal",[FC,Qg]=Vg(Nc,{forceMount:void 0}),Yg=e=>{const{__scopeDialog:t,forceMount:n,children:r,container:i}=e,l=At(Nc,t);return u.jsx(FC,{scope:t,forceMount:n,children:v.Children.map(r,o=>u.jsx(il,{present:n||l.open,children:u.jsx(Mg,{asChild:!0,container:i,children:o})}))})};Yg.displayName=Nc;var jo="DialogOverlay",qg=v.forwardRef((e,t)=>{const n=Qg(jo,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,l=At(jo,e.__scopeDialog);return l.modal?u.jsx(il,{present:r||l.open,children:u.jsx(UC,{...i,ref:t})}):null});qg.displayName=jo;var BC=Ki("DialogOverlay.RemoveScroll"),UC=v.forwardRef((e,t)=>{const{__scopeDialog:n,...r}=e,i=At(jo,n);return u.jsx(MC,{as:BC,allowPinchZoom:!0,shards:[i.contentRef],children:u.jsx(_e.div,{"data-state":bc(i.open),...r,ref:t,style:{pointerEvents:"auto",...r.style}})})}),tr="DialogContent",Xg=v.forwardRef((e,t)=>{const n=Qg(tr,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,l=At(tr,e.__scopeDialog);return u.jsx(il,{present:r||l.open,children:l.modal?u.jsx($C,{...i,ref:t}):u.jsx(VC,{...i,ref:t})})});Xg.displayName=tr;var $C=v.forwardRef((e,t)=>{const n=At(tr,e.__scopeDialog),r=v.useRef(null),i=Pe(t,n.contentRef,r);return v.useEffect(()=>{const l=r.current;if(l)return zC(l)},[]),u.jsx(Jg,{...e,ref:i,trapFocus:n.open,disableOutsidePointerEvents:!0,onCloseAutoFocus:De(e.onCloseAutoFocus,l=>{var o;l.preventDefault(),(o=n.triggerRef.current)==null||o.focus()}),onPointerDownOutside:De(e.onPointerDownOutside,l=>{const o=l.detail.originalEvent,s=o.button===0&&o.ctrlKey===!0;(o.button===2||s)&&l.preventDefault()}),onFocusOutside:De(e.onFocusOutside,l=>l.preventDefault())})}),VC=v.forwardRef((e,t)=>{const n=At(tr,e.__scopeDialog),r=v.useRef(!1),i=v.useRef(!1);return u.jsx(Jg,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:l=>{var o,s;(o=e.onCloseAutoFocus)==null||o.call(e,l),l.defaultPrevented||(r.current||(s=n.triggerRef.current)==null||s.focus(),l.preventDefault()),r.current=!1,i.current=!1},onInteractOutside:l=>{var a,c;(a=e.onInteractOutside)==null||a.call(e,l),l.defaultPrevented||(r.current=!0,l.detail.originalEvent.type==="pointerdown"&&(i.current=!0));const o=l.target;((c=n.triggerRef.current)==null?void 0:c.contains(o))&&l.preventDefault(),l.detail.originalEvent.type==="focusin"&&i.current&&l.preventDefault()}})}),Jg=v.forwardRef((e,t)=>{const{__scopeDialog:n,trapFocus:r,onOpenAutoFocus:i,onCloseAutoFocus:l,...o}=e,s=At(tr,n),a=v.useRef(null),c=Pe(t,a);return qS(),u.jsxs(u.Fragment,{children:[u.jsx(Rg,{asChild:!0,loop:!0,trapped:r,onMountAutoFocus:i,onUnmountAutoFocus:l,children:u.jsx(_g,{role:"dialog",id:s.contentId,"aria-describedby":s.descriptionId,"aria-labelledby":s.titleId,"data-state":bc(s.open),...o,ref:c,onDismiss:()=>s.onOpenChange(!1)})}),u.jsxs(u.Fragment,{children:[u.jsx(WC,{titleId:s.titleId}),u.jsx(KC,{contentRef:a,descriptionId:s.descriptionId})]})]})}),Ec="DialogTitle",Zg=v.forwardRef((e,t)=>{const{__scopeDialog:n,...r}=e,i=At(Ec,n);return u.jsx(_e.h2,{id:i.titleId,...r,ref:t})});Zg.displayName=Ec;var ey="DialogDescription",ty=v.forwardRef((e,t)=>{const{__scopeDialog:n,...r}=e,i=At(ey,n);return u.jsx(_e.p,{id:i.descriptionId,...r,ref:t})});ty.displayName=ey;var ny="DialogClose",ry=v.forwardRef((e,t)=>{const{__scopeDialog:n,...r}=e,i=At(ny,n);return u.jsx(_e.button,{type:"button",...r,ref:t,onClick:De(e.onClick,()=>i.onOpenChange(!1))})});ry.displayName=ny;function bc(e){return e?"open":"closed"}var iy="DialogTitleWarning",[HC,ly]=Jk(iy,{contentName:tr,titleName:Ec,docsSlug:"dialog"}),WC=({titleId:e})=>{const t=ly(iy),n=`\`${t.contentName}\` requires a \`${t.titleName}\` for the component to be accessible for screen reader users.
-
-If you want to hide the \`${t.titleName}\`, you can wrap it with our VisuallyHidden component.
-
-For more information, see https://radix-ui.com/primitives/docs/components/${t.docsSlug}`;return v.useEffect(()=>{e&&(document.getElementById(e)||console.error(n))},[n,e]),null},GC="DialogDescriptionWarning",KC=({contentRef:e,descriptionId:t})=>{const r=`Warning: Missing \`Description\` or \`aria-describedby={undefined}\` for {${ly(GC).contentName}}.`;return v.useEffect(()=>{var l;const i=(l=e.current)==null?void 0:l.getAttribute("aria-describedby");t&&i&&(document.getElementById(t)||console.warn(r))},[r,e,t]),null},QC=Wg,YC=Kg,qC=Yg,XC=qg,JC=Xg,ZC=Zg,eN=ty,oy=ry,sy="AlertDialog",[tN,SP]=Kr(sy,[Hg]),an=Hg(),ay=e=>{const{__scopeAlertDialog:t,...n}=e,r=an(t);return u.jsx(QC,{...r,...n,modal:!0})};ay.displayName=sy;var nN="AlertDialogTrigger",rN=v.forwardRef((e,t)=>{const{__scopeAlertDialog:n,...r}=e,i=an(n);return u.jsx(YC,{...i,...r,ref:t})});rN.displayName=nN;var iN="AlertDialogPortal",uy=e=>{const{__scopeAlertDialog:t,...n}=e,r=an(t);return u.jsx(qC,{...r,...n})};uy.displayName=iN;var lN="AlertDialogOverlay",cy=v.forwardRef((e,t)=>{const{__scopeAlertDialog:n,...r}=e,i=an(n);return u.jsx(XC,{...i,...r,ref:t})});cy.displayName=lN;var Mr="AlertDialogContent",[oN,sN]=tN(Mr),aN=$k("AlertDialogContent"),dy=v.forwardRef((e,t)=>{const{__scopeAlertDialog:n,children:r,...i}=e,l=an(n),o=v.useRef(null),s=Pe(t,o),a=v.useRef(null);return u.jsx(HC,{contentName:Mr,titleName:fy,docsSlug:"alert-dialog",children:u.jsx(oN,{scope:n,cancelRef:a,children:u.jsxs(JC,{role:"alertdialog",...l,...i,ref:s,onOpenAutoFocus:De(i.onOpenAutoFocus,c=>{var d;c.preventDefault(),(d=a.current)==null||d.focus({preventScroll:!0})}),onPointerDownOutside:c=>c.preventDefault(),onInteractOutside:c=>c.preventDefault(),children:[u.jsx(aN,{children:r}),u.jsx(cN,{contentRef:o})]})})})});dy.displayName=Mr;var fy="AlertDialogTitle",py=v.forwardRef((e,t)=>{const{__scopeAlertDialog:n,...r}=e,i=an(n);return u.jsx(ZC,{...i,...r,ref:t})});py.displayName=fy;var hy="AlertDialogDescription",my=v.forwardRef((e,t)=>{const{__scopeAlertDialog:n,...r}=e,i=an(n);return u.jsx(eN,{...i,...r,ref:t})});my.displayName=hy;var uN="AlertDialogAction",gy=v.forwardRef((e,t)=>{const{__scopeAlertDialog:n,...r}=e,i=an(n);return u.jsx(oy,{...i,...r,ref:t})});gy.displayName=uN;var yy="AlertDialogCancel",vy=v.forwardRef((e,t)=>{const{__scopeAlertDialog:n,...r}=e,{cancelRef:i}=sN(yy,n),l=an(n),o=Pe(t,i);return u.jsx(oy,{...l,...r,ref:o})});vy.displayName=yy;var cN=({contentRef:e})=>{const t=`\`${Mr}\` requires a description for the component to be accessible for screen reader users.
-
-You can add a description to the \`${Mr}\` by passing a \`${hy}\` component as a child, which also benefits sighted users by adding visible context to the dialog.
-
-Alternatively, you can use your own component as a description by assigning it an \`id\` and passing the same value to the \`aria-describedby\` prop in \`${Mr}\`. If the description is confusing or duplicative for sighted users, you can use the \`@radix-ui/react-visually-hidden\` primitive as a wrapper around your description component.
-
-For more information, see https://radix-ui.com/primitives/docs/components/alert-dialog`;return v.useEffect(()=>{var r;document.getElementById((r=e.current)==null?void 0:r.getAttribute("aria-describedby"))||console.warn(t)},[t,e]),null},dN=ay,fN=uy,xy=cy,wy=dy,ky=gy,Sy=vy,Cy=py,Ny=my;const Af=dN,pN=fN,Ey=v.forwardRef(({className:e,...t},n)=>u.jsx(xy,{className:re("fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",e),...t,ref:n}));Ey.displayName=xy.displayName;const tu=v.forwardRef(({className:e,...t},n)=>u.jsxs(pN,{children:[u.jsx(Ey,{}),u.jsx(wy,{ref:n,className:re("fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",e),...t})]}));tu.displayName=wy.displayName;const nu=({className:e,...t})=>u.jsx("div",{className:re("flex flex-col space-y-2 text-center sm:text-left",e),...t});nu.displayName="AlertDialogHeader";const ru=({className:e,...t})=>u.jsx("div",{className:re("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",e),...t});ru.displayName="AlertDialogFooter";const iu=v.forwardRef(({className:e,...t},n)=>u.jsx(Cy,{ref:n,className:re("text-lg font-semibold",e),...t}));iu.displayName=Cy.displayName;const lu=v.forwardRef(({className:e,...t},n)=>u.jsx(Ny,{ref:n,className:re("text-sm text-muted-foreground",e),...t}));lu.displayName=Ny.displayName;const ou=v.forwardRef(({className:e,...t},n)=>u.jsx(ky,{ref:n,className:re(Sc(),e),...t}));ou.displayName=ky.displayName;const su=v.forwardRef(({className:e,...t},n)=>u.jsx(Sy,{ref:n,className:re(Sc({variant:"outline"}),"mt-2 sm:mt-0",e),...t}));su.displayName=Sy.displayName;var Jo="Collapsible",[hN,CP]=Kr(Jo),[mN,jc]=hN(Jo),by=v.forwardRef((e,t)=>{const{__scopeCollapsible:n,open:r,defaultOpen:i,disabled:l,onOpenChange:o,...s}=e,[a,c]=Qo({prop:r,defaultProp:i??!1,onChange:o,caller:Jo});return u.jsx(mN,{scope:n,disabled:l,contentId:Gl(),open:a,onOpenToggle:v.useCallback(()=>c(d=>!d),[c]),children:u.jsx(_e.div,{"data-state":_c(a),"data-disabled":l?"":void 0,...s,ref:t})})});by.displayName=Jo;var jy="CollapsibleTrigger",Py=v.forwardRef((e,t)=>{const{__scopeCollapsible:n,...r}=e,i=jc(jy,n);return u.jsx(_e.button,{type:"button","aria-controls":i.contentId,"aria-expanded":i.open||!1,"data-state":_c(i.open),"data-disabled":i.disabled?"":void 0,disabled:i.disabled,...r,ref:t,onClick:De(e.onClick,i.onOpenToggle)})});Py.displayName=jy;var Pc="CollapsibleContent",_y=v.forwardRef((e,t)=>{const{forceMount:n,...r}=e,i=jc(Pc,e.__scopeCollapsible);return u.jsx(il,{present:n||i.open,children:({present:l})=>u.jsx(gN,{...r,ref:t,present:l})})});_y.displayName=Pc;var gN=v.forwardRef((e,t)=>{const{__scopeCollapsible:n,present:r,children:i,...l}=e,o=jc(Pc,n),[s,a]=v.useState(r),c=v.useRef(null),d=Pe(t,c),f=v.useRef(0),p=f.current,h=v.useRef(0),w=h.current,y=o.open||s,S=v.useRef(y),m=v.useRef(void 0);return v.useEffect(()=>{const g=requestAnimationFrame(()=>S.current=!1);return()=>cancelAnimationFrame(g)},[]),er(()=>{const g=c.current;if(g){m.current=m.current||{transitionDuration:g.style.transitionDuration,animationName:g.style.animationName},g.style.transitionDuration="0s",g.style.animationName="none";const x=g.getBoundingClientRect();f.current=x.height,h.current=x.width,S.current||(g.style.transitionDuration=m.current.transitionDuration,g.style.animationName=m.current.animationName),a(r)}},[o.open,r]),u.jsx(_e.div,{"data-state":_c(o.open),"data-disabled":o.disabled?"":void 0,id:o.contentId,hidden:!y,...l,ref:d,style:{"--radix-collapsible-content-height":p?`${p}px`:void 0,"--radix-collapsible-content-width":w?`${w}px`:void 0,...e.style},children:y&&i})});function _c(e){return e?"open":"closed"}var yN=by;const vN=yN,xN=Py,wN=_y;function kN(e,t){const n=t||{};return(e[e.length-1]===""?[...e,""]:e).join((n.padRight?" ":"")+","+(n.padLeft===!1?"":" ")).trim()}const SN=/^[$_\p{ID_Start}][$_\u{200C}\u{200D}\p{ID_Continue}]*$/u,CN=/^[$_\p{ID_Start}][-$_\u{200C}\u{200D}\p{ID_Continue}]*$/u,NN={};function Lf(e,t){return((t||NN).jsx?CN:SN).test(e)}const EN=/[ \t\n\f\r]/g;function bN(e){return typeof e=="object"?e.type==="text"?Df(e.value):!1:Df(e)}function Df(e){return e.replace(EN,"")===""}class ll{constructor(t,n,r){this.normal=n,this.property=t,r&&(this.space=r)}}ll.prototype.normal={};ll.prototype.property={};ll.prototype.space=void 0;function Ty(e,t){const n={},r={};for(const i of e)Object.assign(n,i.property),Object.assign(r,i.normal);return new ll(n,r,t)}function au(e){return e.toLowerCase()}class it{constructor(t,n){this.attribute=n,this.property=t}}it.prototype.attribute="";it.prototype.booleanish=!1;it.prototype.boolean=!1;it.prototype.commaOrSpaceSeparated=!1;it.prototype.commaSeparated=!1;it.prototype.defined=!1;it.prototype.mustUseProperty=!1;it.prototype.number=!1;it.prototype.overloadedBoolean=!1;it.prototype.property="";it.prototype.spaceSeparated=!1;it.prototype.space=void 0;let jN=0;const K=or(),Ee=or(),uu=or(),M=or(),ae=or(),Ar=or(),at=or();function or(){return 2**++jN}const cu=Object.freeze(Object.defineProperty({__proto__:null,boolean:K,booleanish:Ee,commaOrSpaceSeparated:at,commaSeparated:Ar,number:M,overloadedBoolean:uu,spaceSeparated:ae},Symbol.toStringTag,{value:"Module"})),Os=Object.keys(cu);class Tc extends it{constructor(t,n,r,i){let l=-1;if(super(t,n),zf(this,"space",i),typeof r=="number")for(;++l4&&n.slice(0,4)==="data"&&IN.test(t)){if(t.charAt(4)==="-"){const l=t.slice(5).replace(Of,LN);r="data"+l.charAt(0).toUpperCase()+l.slice(1)}else{const l=t.slice(4);if(!Of.test(l)){let o=l.replace(RN,AN);o.charAt(0)!=="-"&&(o="-"+o),t="data"+o}}i=Tc}return new i(r,t)}function AN(e){return"-"+e.toLowerCase()}function LN(e){return e.charAt(1).toUpperCase()}const DN=Ty([Ry,PN,Ay,Ly,Dy],"html"),Rc=Ty([Ry,_N,Ay,Ly,Dy],"svg");function zN(e){return e.join(" ").trim()}var Ic={},Ff=/\/\*[^*]*\*+([^/*][^*]*\*+)*\//g,ON=/\n/g,FN=/^\s*/,BN=/^(\*?[-#/*\\\w]+(\[[0-9a-z_-]+\])?)\s*/,UN=/^:\s*/,$N=/^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*?\)|[^};])+)/,VN=/^[;\s]*/,HN=/^\s+|\s+$/g,WN=`
-`,Bf="/",Uf="*",$n="",GN="comment",KN="declaration",QN=function(e,t){if(typeof e!="string")throw new TypeError("First argument must be a string");if(!e)return[];t=t||{};var n=1,r=1;function i(w){var y=w.match(ON);y&&(n+=y.length);var S=w.lastIndexOf(WN);r=~S?w.length-S:r+w.length}function l(){var w={line:n,column:r};return function(y){return y.position=new o(w),c(),y}}function o(w){this.start=w,this.end={line:n,column:r},this.source=t.source}o.prototype.content=e;function s(w){var y=new Error(t.source+":"+n+":"+r+": "+w);if(y.reason=w,y.filename=t.source,y.line=n,y.column=r,y.source=e,!t.silent)throw y}function a(w){var y=w.exec(e);if(y){var S=y[0];return i(S),e=e.slice(S.length),y}}function c(){a(FN)}function d(w){var y;for(w=w||[];y=f();)y!==!1&&w.push(y);return w}function f(){var w=l();if(!(Bf!=e.charAt(0)||Uf!=e.charAt(1))){for(var y=2;$n!=e.charAt(y)&&(Uf!=e.charAt(y)||Bf!=e.charAt(y+1));)++y;if(y+=2,$n===e.charAt(y-1))return s("End of comment missing");var S=e.slice(2,y-2);return r+=2,i(S),e=e.slice(y),r+=2,w({type:GN,comment:S})}}function p(){var w=l(),y=a(BN);if(y){if(f(),!a(UN))return s("property missing ':'");var S=a($N),m=w({type:KN,property:$f(y[0].replace(Ff,$n)),value:S?$f(S[0].replace(Ff,$n)):$n});return a(VN),m}}function h(){var w=[];d(w);for(var y;y=p();)y!==!1&&(w.push(y),d(w));return w}return c(),h()};function $f(e){return e?e.replace(HN,$n):$n}var YN=Xl&&Xl.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(Ic,"__esModule",{value:!0});Ic.default=XN;var qN=YN(QN);function XN(e,t){var n=null;if(!e||typeof e!="string")return n;var r=(0,qN.default)(e),i=typeof t=="function";return r.forEach(function(l){if(l.type==="declaration"){var o=l.property,s=l.value;i?t(o,s,l):s&&(n=n||{},n[o]=s)}}),n}var Zo={};Object.defineProperty(Zo,"__esModule",{value:!0});Zo.camelCase=void 0;var JN=/^--[a-zA-Z0-9_-]+$/,ZN=/-([a-z])/g,eE=/^[^-]+$/,tE=/^-(webkit|moz|ms|o|khtml)-/,nE=/^-(ms)-/,rE=function(e){return!e||eE.test(e)||JN.test(e)},iE=function(e,t){return t.toUpperCase()},Vf=function(e,t){return"".concat(t,"-")},lE=function(e,t){return t===void 0&&(t={}),rE(e)?e:(e=e.toLowerCase(),t.reactCompat?e=e.replace(nE,Vf):e=e.replace(tE,Vf),e.replace(ZN,iE))};Zo.camelCase=lE;var oE=Xl&&Xl.__importDefault||function(e){return e&&e.__esModule?e:{default:e}},sE=oE(Ic),aE=Zo;function du(e,t){var n={};return!e||typeof e!="string"||(0,sE.default)(e,function(r,i){r&&i&&(n[(0,aE.camelCase)(r,t)]=i)}),n}du.default=du;var uE=du;const cE=To(uE),zy=Oy("end"),Mc=Oy("start");function Oy(e){return t;function t(n){const r=n&&n.position&&n.position[e]||{};if(typeof r.line=="number"&&r.line>0&&typeof r.column=="number"&&r.column>0)return{line:r.line,column:r.column,offset:typeof r.offset=="number"&&r.offset>-1?r.offset:void 0}}}function dE(e){const t=Mc(e),n=zy(e);if(t&&n)return{start:t,end:n}}function Ni(e){return!e||typeof e!="object"?"":"position"in e||"type"in e?Hf(e.position):"start"in e||"end"in e?Hf(e):"line"in e||"column"in e?fu(e):""}function fu(e){return Wf(e&&e.line)+":"+Wf(e&&e.column)}function Hf(e){return fu(e&&e.start)+"-"+fu(e&&e.end)}function Wf(e){return e&&typeof e=="number"?e:1}class Ve extends Error{constructor(t,n,r){super(),typeof n=="string"&&(r=n,n=void 0);let i="",l={},o=!1;if(n&&("line"in n&&"column"in n?l={place:n}:"start"in n&&"end"in n?l={place:n}:"type"in n?l={ancestors:[n],place:n.position}:l={...n}),typeof t=="string"?i=t:!l.cause&&t&&(o=!0,i=t.message,l.cause=t),!l.ruleId&&!l.source&&typeof r=="string"){const a=r.indexOf(":");a===-1?l.ruleId=r:(l.source=r.slice(0,a),l.ruleId=r.slice(a+1))}if(!l.place&&l.ancestors&&l.ancestors){const a=l.ancestors[l.ancestors.length-1];a&&(l.place=a.position)}const s=l.place&&"start"in l.place?l.place.start:l.place;this.ancestors=l.ancestors||void 0,this.cause=l.cause||void 0,this.column=s?s.column:void 0,this.fatal=void 0,this.file="",this.message=i,this.line=s?s.line:void 0,this.name=Ni(l.place)||"1:1",this.place=l.place||void 0,this.reason=this.message,this.ruleId=l.ruleId||void 0,this.source=l.source||void 0,this.stack=o&&l.cause&&typeof l.cause.stack=="string"?l.cause.stack:"",this.actual=void 0,this.expected=void 0,this.note=void 0,this.url=void 0}}Ve.prototype.file="";Ve.prototype.name="";Ve.prototype.reason="";Ve.prototype.message="";Ve.prototype.stack="";Ve.prototype.column=void 0;Ve.prototype.line=void 0;Ve.prototype.ancestors=void 0;Ve.prototype.cause=void 0;Ve.prototype.fatal=void 0;Ve.prototype.place=void 0;Ve.prototype.ruleId=void 0;Ve.prototype.source=void 0;const Ac={}.hasOwnProperty,fE=new Map,pE=/[A-Z]/g,hE=new Set(["table","tbody","thead","tfoot","tr"]),mE=new Set(["td","th"]),Fy="https://github.com/syntax-tree/hast-util-to-jsx-runtime";function gE(e,t){if(!t||t.Fragment===void 0)throw new TypeError("Expected `Fragment` in options");const n=t.filePath||void 0;let r;if(t.development){if(typeof t.jsxDEV!="function")throw new TypeError("Expected `jsxDEV` in options when `development: true`");r=NE(n,t.jsxDEV)}else{if(typeof t.jsx!="function")throw new TypeError("Expected `jsx` in production options");if(typeof t.jsxs!="function")throw new TypeError("Expected `jsxs` in production options");r=CE(n,t.jsx,t.jsxs)}const i={Fragment:t.Fragment,ancestors:[],components:t.components||{},create:r,elementAttributeNameCase:t.elementAttributeNameCase||"react",evaluater:t.createEvaluater?t.createEvaluater():void 0,filePath:n,ignoreInvalidStyle:t.ignoreInvalidStyle||!1,passKeys:t.passKeys!==!1,passNode:t.passNode||!1,schema:t.space==="svg"?Rc:DN,stylePropertyNameCase:t.stylePropertyNameCase||"dom",tableCellAlignToStyle:t.tableCellAlignToStyle!==!1},l=By(i,e,void 0);return l&&typeof l!="string"?l:i.create(e,i.Fragment,{children:l||void 0},void 0)}function By(e,t,n){if(t.type==="element")return yE(e,t,n);if(t.type==="mdxFlowExpression"||t.type==="mdxTextExpression")return vE(e,t);if(t.type==="mdxJsxFlowElement"||t.type==="mdxJsxTextElement")return wE(e,t,n);if(t.type==="mdxjsEsm")return xE(e,t);if(t.type==="root")return kE(e,t,n);if(t.type==="text")return SE(e,t)}function yE(e,t,n){const r=e.schema;let i=r;t.tagName.toLowerCase()==="svg"&&r.space==="html"&&(i=Rc,e.schema=i),e.ancestors.push(t);const l=$y(e,t.tagName,!1),o=EE(e,t);let s=Dc(e,t);return hE.has(t.tagName)&&(s=s.filter(function(a){return typeof a=="string"?!bN(a):!0})),Uy(e,o,l,t),Lc(o,s),e.ancestors.pop(),e.schema=r,e.create(t,l,o,n)}function vE(e,t){if(t.data&&t.data.estree&&e.evaluater){const r=t.data.estree.body[0];return r.type,e.evaluater.evaluateExpression(r.expression)}Yi(e,t.position)}function xE(e,t){if(t.data&&t.data.estree&&e.evaluater)return e.evaluater.evaluateProgram(t.data.estree);Yi(e,t.position)}function wE(e,t,n){const r=e.schema;let i=r;t.name==="svg"&&r.space==="html"&&(i=Rc,e.schema=i),e.ancestors.push(t);const l=t.name===null?e.Fragment:$y(e,t.name,!0),o=bE(e,t),s=Dc(e,t);return Uy(e,o,l,t),Lc(o,s),e.ancestors.pop(),e.schema=r,e.create(t,l,o,n)}function kE(e,t,n){const r={};return Lc(r,Dc(e,t)),e.create(t,e.Fragment,r,n)}function SE(e,t){return t.value}function Uy(e,t,n,r){typeof n!="string"&&n!==e.Fragment&&e.passNode&&(t.node=r)}function Lc(e,t){if(t.length>0){const n=t.length>1?t:t[0];n&&(e.children=n)}}function CE(e,t,n){return r;function r(i,l,o,s){const c=Array.isArray(o.children)?n:t;return s?c(l,o,s):c(l,o)}}function NE(e,t){return n;function n(r,i,l,o){const s=Array.isArray(l.children),a=Mc(r);return t(i,l,o,s,{columnNumber:a?a.column-1:void 0,fileName:e,lineNumber:a?a.line:void 0},void 0)}}function EE(e,t){const n={};let r,i;for(i in t.properties)if(i!=="children"&&Ac.call(t.properties,i)){const l=jE(e,i,t.properties[i]);if(l){const[o,s]=l;e.tableCellAlignToStyle&&o==="align"&&typeof s=="string"&&mE.has(t.tagName)?r=s:n[o]=s}}if(r){const l=n.style||(n.style={});l[e.stylePropertyNameCase==="css"?"text-align":"textAlign"]=r}return n}function bE(e,t){const n={};for(const r of t.attributes)if(r.type==="mdxJsxExpressionAttribute")if(r.data&&r.data.estree&&e.evaluater){const l=r.data.estree.body[0];l.type;const o=l.expression;o.type;const s=o.properties[0];s.type,Object.assign(n,e.evaluater.evaluateExpression(s.argument))}else Yi(e,t.position);else{const i=r.name;let l;if(r.value&&typeof r.value=="object")if(r.value.data&&r.value.data.estree&&e.evaluater){const s=r.value.data.estree.body[0];s.type,l=e.evaluater.evaluateExpression(s.expression)}else Yi(e,t.position);else l=r.value===null?!0:r.value;n[i]=l}return n}function Dc(e,t){const n=[];let r=-1;const i=e.passKeys?new Map:fE;for(;++ri?0:i+t:t=t>i?i:t,n=n>0?n:0,r.length<1e4)o=Array.from(r),o.unshift(t,n),e.splice(...o);else for(n&&e.splice(t,n);l0?(Wt(e,e.length,0,t),e):t}const Qf={}.hasOwnProperty;function LE(e){const t={};let n=-1;for(;++n13&&n<32||n>126&&n<160||n>55295&&n<57344||n>64975&&n<65008||(n&65535)===65535||(n&65535)===65534||n>1114111?"๏ฟฝ":String.fromCodePoint(n)}function Lr(e){return e.replace(/[\t\n\r ]+/g," ").replace(/^ | $/g,"").toLowerCase().toUpperCase()}const Ut=Dn(/[A-Za-z]/),dt=Dn(/[\dA-Za-z]/),OE=Dn(/[#-'*+\--9=?A-Z^-~]/);function pu(e){return e!==null&&(e<32||e===127)}const hu=Dn(/\d/),FE=Dn(/[\dA-Fa-f]/),BE=Dn(/[!-/:-@[-`{-~]/);function V(e){return e!==null&&e<-2}function rt(e){return e!==null&&(e<0||e===32)}function ie(e){return e===-2||e===-1||e===32}const UE=Dn(/\p{P}|\p{S}/u),$E=Dn(/\s/);function Dn(e){return t;function t(n){return n!==null&&n>-1&&e.test(String.fromCharCode(n))}}function qr(e){const t=[];let n=-1,r=0,i=0;for(;++n55295&&l<57344){const s=e.charCodeAt(n+1);l<56320&&s>56319&&s<57344?(o=String.fromCharCode(l,s),i=1):o="๏ฟฝ"}else o=String.fromCharCode(l);o&&(t.push(e.slice(r,n),encodeURIComponent(o)),r=n+i+1,o=""),i&&(n+=i,i=0)}return t.join("")+e.slice(r)}function ce(e,t,n,r){const i=r?r-1:Number.POSITIVE_INFINITY;let l=0;return o;function o(a){return ie(a)?(e.enter(n),s(a)):t(a)}function s(a){return ie(a)&&l++o))return;const j=t.events.length;let R=j,z,L;for(;R--;)if(t.events[R][0]==="exit"&&t.events[R][1].type==="chunkFlow"){if(z){L=t.events[R][1].end;break}z=!0}for(m(r),E=j;Ex;){const b=n[N];t.containerState=b[1],b[0].exit.call(t,e)}n.length=x}function g(){i.write([null]),l=void 0,i=void 0,t.containerState._closeFlow=void 0}}function KE(e,t,n){return ce(e,e.attempt(this.parser.constructs.document,t,n),"linePrefix",this.parser.constructs.disable.null.includes("codeIndented")?void 0:4)}function qf(e){if(e===null||rt(e)||$E(e))return 1;if(UE(e))return 2}function Oc(e,t,n){const r=[];let i=-1;for(;++i1&&e[n][1].end.offset-e[n][1].start.offset>1?2:1;const f={...e[r][1].end},p={...e[n][1].start};Xf(f,-a),Xf(p,a),o={type:a>1?"strongSequence":"emphasisSequence",start:f,end:{...e[r][1].end}},s={type:a>1?"strongSequence":"emphasisSequence",start:{...e[n][1].start},end:p},l={type:a>1?"strongText":"emphasisText",start:{...e[r][1].end},end:{...e[n][1].start}},i={type:a>1?"strong":"emphasis",start:{...o.start},end:{...s.end}},e[r][1].end={...o.start},e[n][1].start={...s.end},c=[],e[r][1].end.offset-e[r][1].start.offset&&(c=xt(c,[["enter",e[r][1],t],["exit",e[r][1],t]])),c=xt(c,[["enter",i,t],["enter",o,t],["exit",o,t],["enter",l,t]]),c=xt(c,Oc(t.parser.constructs.insideSpan.null,e.slice(r+1,n),t)),c=xt(c,[["exit",l,t],["enter",s,t],["exit",s,t],["exit",i,t]]),e[n][1].end.offset-e[n][1].start.offset?(d=2,c=xt(c,[["enter",e[n][1],t],["exit",e[n][1],t]])):d=0,Wt(e,r-1,n-r+3,c),n=r+c.length-d-2;break}}for(n=-1;++n0&&ie(E)?ce(e,g,"linePrefix",l+1)(E):g(E)}function g(E){return E===null||V(E)?e.check(Jf,y,N)(E):(e.enter("codeFlowValue"),x(E))}function x(E){return E===null||V(E)?(e.exit("codeFlowValue"),g(E)):(e.consume(E),x)}function N(E){return e.exit("codeFenced"),t(E)}function b(E,j,R){let z=0;return L;function L($){return E.enter("lineEnding"),E.consume($),E.exit("lineEnding"),A}function A($){return E.enter("codeFencedFence"),ie($)?ce(E,T,"linePrefix",r.parser.constructs.disable.null.includes("codeIndented")?void 0:4)($):T($)}function T($){return $===s?(E.enter("codeFencedFenceSequence"),H($)):R($)}function H($){return $===s?(z++,E.consume($),H):z>=o?(E.exit("codeFencedFenceSequence"),ie($)?ce(E,F,"whitespace")($):F($)):R($)}function F($){return $===null||V($)?(E.exit("codeFencedFence"),j($)):R($)}}}function lb(e,t,n){const r=this;return i;function i(o){return o===null?n(o):(e.enter("lineEnding"),e.consume(o),e.exit("lineEnding"),l)}function l(o){return r.parser.lazy[r.now().line]?n(o):t(o)}}const Bs={name:"codeIndented",tokenize:sb},ob={partial:!0,tokenize:ab};function sb(e,t,n){const r=this;return i;function i(c){return e.enter("codeIndented"),ce(e,l,"linePrefix",4+1)(c)}function l(c){const d=r.events[r.events.length-1];return d&&d[1].type==="linePrefix"&&d[2].sliceSerialize(d[1],!0).length>=4?o(c):n(c)}function o(c){return c===null?a(c):V(c)?e.attempt(ob,o,a)(c):(e.enter("codeFlowValue"),s(c))}function s(c){return c===null||V(c)?(e.exit("codeFlowValue"),o(c)):(e.consume(c),s)}function a(c){return e.exit("codeIndented"),t(c)}}function ab(e,t,n){const r=this;return i;function i(o){return r.parser.lazy[r.now().line]?n(o):V(o)?(e.enter("lineEnding"),e.consume(o),e.exit("lineEnding"),i):ce(e,l,"linePrefix",4+1)(o)}function l(o){const s=r.events[r.events.length-1];return s&&s[1].type==="linePrefix"&&s[2].sliceSerialize(s[1],!0).length>=4?t(o):V(o)?i(o):n(o)}}const ub={name:"codeText",previous:db,resolve:cb,tokenize:fb};function cb(e){let t=e.length-4,n=3,r,i;if((e[n][1].type==="lineEnding"||e[n][1].type==="space")&&(e[t][1].type==="lineEnding"||e[t][1].type==="space")){for(r=n;++r=this.left.length+this.right.length)throw new RangeError("Cannot access index `"+t+"` in a splice buffer of size `"+(this.left.length+this.right.length)+"`");return tthis.left.length?this.right.slice(this.right.length-r+this.left.length,this.right.length-t+this.left.length).reverse():this.left.slice(t).concat(this.right.slice(this.right.length-r+this.left.length).reverse())}splice(t,n,r){const i=n||0;this.setCursor(Math.trunc(t));const l=this.right.splice(this.right.length-i,Number.POSITIVE_INFINITY);return r&&ui(this.left,r),l.reverse()}pop(){return this.setCursor(Number.POSITIVE_INFINITY),this.left.pop()}push(t){this.setCursor(Number.POSITIVE_INFINITY),this.left.push(t)}pushMany(t){this.setCursor(Number.POSITIVE_INFINITY),ui(this.left,t)}unshift(t){this.setCursor(0),this.right.push(t)}unshiftMany(t){this.setCursor(0),ui(this.right,t.reverse())}setCursor(t){if(!(t===this.left.length||t>this.left.length&&this.right.length===0||t<0&&this.left.length===0))if(t=4?t(o):e.interrupt(r.parser.constructs.flow,n,t)(o)}}function Yy(e,t,n,r,i,l,o,s,a){const c=a||Number.POSITIVE_INFINITY;let d=0;return f;function f(m){return m===60?(e.enter(r),e.enter(i),e.enter(l),e.consume(m),e.exit(l),p):m===null||m===32||m===41||pu(m)?n(m):(e.enter(r),e.enter(o),e.enter(s),e.enter("chunkString",{contentType:"string"}),y(m))}function p(m){return m===62?(e.enter(l),e.consume(m),e.exit(l),e.exit(i),e.exit(r),t):(e.enter(s),e.enter("chunkString",{contentType:"string"}),h(m))}function h(m){return m===62?(e.exit("chunkString"),e.exit(s),p(m)):m===null||m===60||V(m)?n(m):(e.consume(m),m===92?w:h)}function w(m){return m===60||m===62||m===92?(e.consume(m),h):h(m)}function y(m){return!d&&(m===null||m===41||rt(m))?(e.exit("chunkString"),e.exit(s),e.exit(o),e.exit(r),t(m)):d999||h===null||h===91||h===93&&!a||h===94&&!s&&"_hiddenFootnoteSupport"in o.parser.constructs?n(h):h===93?(e.exit(l),e.enter(i),e.consume(h),e.exit(i),e.exit(r),t):V(h)?(e.enter("lineEnding"),e.consume(h),e.exit("lineEnding"),d):(e.enter("chunkString",{contentType:"string"}),f(h))}function f(h){return h===null||h===91||h===93||V(h)||s++>999?(e.exit("chunkString"),d(h)):(e.consume(h),a||(a=!ie(h)),h===92?p:f)}function p(h){return h===91||h===92||h===93?(e.consume(h),s++,f):f(h)}}function Xy(e,t,n,r,i,l){let o;return s;function s(p){return p===34||p===39||p===40?(e.enter(r),e.enter(i),e.consume(p),e.exit(i),o=p===40?41:p,a):n(p)}function a(p){return p===o?(e.enter(i),e.consume(p),e.exit(i),e.exit(r),t):(e.enter(l),c(p))}function c(p){return p===o?(e.exit(l),a(o)):p===null?n(p):V(p)?(e.enter("lineEnding"),e.consume(p),e.exit("lineEnding"),ce(e,c,"linePrefix")):(e.enter("chunkString",{contentType:"string"}),d(p))}function d(p){return p===o||p===null||V(p)?(e.exit("chunkString"),c(p)):(e.consume(p),p===92?f:d)}function f(p){return p===o||p===92?(e.consume(p),d):d(p)}}function Ei(e,t){let n;return r;function r(i){return V(i)?(e.enter("lineEnding"),e.consume(i),e.exit("lineEnding"),n=!0,r):ie(i)?ce(e,r,n?"linePrefix":"lineSuffix")(i):t(i)}}const wb={name:"definition",tokenize:Sb},kb={partial:!0,tokenize:Cb};function Sb(e,t,n){const r=this;let i;return l;function l(h){return e.enter("definition"),o(h)}function o(h){return qy.call(r,e,s,n,"definitionLabel","definitionLabelMarker","definitionLabelString")(h)}function s(h){return i=Lr(r.sliceSerialize(r.events[r.events.length-1][1]).slice(1,-1)),h===58?(e.enter("definitionMarker"),e.consume(h),e.exit("definitionMarker"),a):n(h)}function a(h){return rt(h)?Ei(e,c)(h):c(h)}function c(h){return Yy(e,d,n,"definitionDestination","definitionDestinationLiteral","definitionDestinationLiteralMarker","definitionDestinationRaw","definitionDestinationString")(h)}function d(h){return e.attempt(kb,f,f)(h)}function f(h){return ie(h)?ce(e,p,"whitespace")(h):p(h)}function p(h){return h===null||V(h)?(e.exit("definition"),r.parser.defined.push(i),t(h)):n(h)}}function Cb(e,t,n){return r;function r(s){return rt(s)?Ei(e,i)(s):n(s)}function i(s){return Xy(e,l,n,"definitionTitle","definitionTitleMarker","definitionTitleString")(s)}function l(s){return ie(s)?ce(e,o,"whitespace")(s):o(s)}function o(s){return s===null||V(s)?t(s):n(s)}}const Nb={name:"hardBreakEscape",tokenize:Eb};function Eb(e,t,n){return r;function r(l){return e.enter("hardBreakEscape"),e.consume(l),i}function i(l){return V(l)?(e.exit("hardBreakEscape"),t(l)):n(l)}}const bb={name:"headingAtx",resolve:jb,tokenize:Pb};function jb(e,t){let n=e.length-2,r=3,i,l;return e[r][1].type==="whitespace"&&(r+=2),n-2>r&&e[n][1].type==="whitespace"&&(n-=2),e[n][1].type==="atxHeadingSequence"&&(r===n-1||n-4>r&&e[n-2][1].type==="whitespace")&&(n-=r+1===n?2:4),n>r&&(i={type:"atxHeadingText",start:e[r][1].start,end:e[n][1].end},l={type:"chunkText",start:e[r][1].start,end:e[n][1].end,contentType:"text"},Wt(e,r,n-r+1,[["enter",i,t],["enter",l,t],["exit",l,t],["exit",i,t]])),e}function Pb(e,t,n){let r=0;return i;function i(d){return e.enter("atxHeading"),l(d)}function l(d){return e.enter("atxHeadingSequence"),o(d)}function o(d){return d===35&&r++<6?(e.consume(d),o):d===null||rt(d)?(e.exit("atxHeadingSequence"),s(d)):n(d)}function s(d){return d===35?(e.enter("atxHeadingSequence"),a(d)):d===null||V(d)?(e.exit("atxHeading"),t(d)):ie(d)?ce(e,s,"whitespace")(d):(e.enter("atxHeadingText"),c(d))}function a(d){return d===35?(e.consume(d),a):(e.exit("atxHeadingSequence"),s(d))}function c(d){return d===null||d===35||rt(d)?(e.exit("atxHeadingText"),s(d)):(e.consume(d),c)}}const _b=["address","article","aside","base","basefont","blockquote","body","caption","center","col","colgroup","dd","details","dialog","dir","div","dl","dt","fieldset","figcaption","figure","footer","form","frame","frameset","h1","h2","h3","h4","h5","h6","head","header","hr","html","iframe","legend","li","link","main","menu","menuitem","nav","noframes","ol","optgroup","option","p","param","search","section","summary","table","tbody","td","tfoot","th","thead","title","tr","track","ul"],ep=["pre","script","style","textarea"],Tb={concrete:!0,name:"htmlFlow",resolveTo:Mb,tokenize:Ab},Rb={partial:!0,tokenize:Db},Ib={partial:!0,tokenize:Lb};function Mb(e){let t=e.length;for(;t--&&!(e[t][0]==="enter"&&e[t][1].type==="htmlFlow"););return t>1&&e[t-2][1].type==="linePrefix"&&(e[t][1].start=e[t-2][1].start,e[t+1][1].start=e[t-2][1].start,e.splice(t-2,2)),e}function Ab(e,t,n){const r=this;let i,l,o,s,a;return c;function c(C){return d(C)}function d(C){return e.enter("htmlFlow"),e.enter("htmlFlowData"),e.consume(C),f}function f(C){return C===33?(e.consume(C),p):C===47?(e.consume(C),l=!0,y):C===63?(e.consume(C),i=3,r.interrupt?t:k):Ut(C)?(e.consume(C),o=String.fromCharCode(C),S):n(C)}function p(C){return C===45?(e.consume(C),i=2,h):C===91?(e.consume(C),i=5,s=0,w):Ut(C)?(e.consume(C),i=4,r.interrupt?t:k):n(C)}function h(C){return C===45?(e.consume(C),r.interrupt?t:k):n(C)}function w(C){const ke="CDATA[";return C===ke.charCodeAt(s++)?(e.consume(C),s===ke.length?r.interrupt?t:T:w):n(C)}function y(C){return Ut(C)?(e.consume(C),o=String.fromCharCode(C),S):n(C)}function S(C){if(C===null||C===47||C===62||rt(C)){const ke=C===47,lt=o.toLowerCase();return!ke&&!l&&ep.includes(lt)?(i=1,r.interrupt?t(C):T(C)):_b.includes(o.toLowerCase())?(i=6,ke?(e.consume(C),m):r.interrupt?t(C):T(C)):(i=7,r.interrupt&&!r.parser.lazy[r.now().line]?n(C):l?g(C):x(C))}return C===45||dt(C)?(e.consume(C),o+=String.fromCharCode(C),S):n(C)}function m(C){return C===62?(e.consume(C),r.interrupt?t:T):n(C)}function g(C){return ie(C)?(e.consume(C),g):L(C)}function x(C){return C===47?(e.consume(C),L):C===58||C===95||Ut(C)?(e.consume(C),N):ie(C)?(e.consume(C),x):L(C)}function N(C){return C===45||C===46||C===58||C===95||dt(C)?(e.consume(C),N):b(C)}function b(C){return C===61?(e.consume(C),E):ie(C)?(e.consume(C),b):x(C)}function E(C){return C===null||C===60||C===61||C===62||C===96?n(C):C===34||C===39?(e.consume(C),a=C,j):ie(C)?(e.consume(C),E):R(C)}function j(C){return C===a?(e.consume(C),a=null,z):C===null||V(C)?n(C):(e.consume(C),j)}function R(C){return C===null||C===34||C===39||C===47||C===60||C===61||C===62||C===96||rt(C)?b(C):(e.consume(C),R)}function z(C){return C===47||C===62||ie(C)?x(C):n(C)}function L(C){return C===62?(e.consume(C),A):n(C)}function A(C){return C===null||V(C)?T(C):ie(C)?(e.consume(C),A):n(C)}function T(C){return C===45&&i===2?(e.consume(C),Q):C===60&&i===1?(e.consume(C),Y):C===62&&i===4?(e.consume(C),W):C===63&&i===3?(e.consume(C),k):C===93&&i===5?(e.consume(C),O):V(C)&&(i===6||i===7)?(e.exit("htmlFlowData"),e.check(Rb,J,H)(C)):C===null||V(C)?(e.exit("htmlFlowData"),H(C)):(e.consume(C),T)}function H(C){return e.check(Ib,F,J)(C)}function F(C){return e.enter("lineEnding"),e.consume(C),e.exit("lineEnding"),$}function $(C){return C===null||V(C)?H(C):(e.enter("htmlFlowData"),T(C))}function Q(C){return C===45?(e.consume(C),k):T(C)}function Y(C){return C===47?(e.consume(C),o="",_):T(C)}function _(C){if(C===62){const ke=o.toLowerCase();return ep.includes(ke)?(e.consume(C),W):T(C)}return Ut(C)&&o.length<8?(e.consume(C),o+=String.fromCharCode(C),_):T(C)}function O(C){return C===93?(e.consume(C),k):T(C)}function k(C){return C===62?(e.consume(C),W):C===45&&i===2?(e.consume(C),k):T(C)}function W(C){return C===null||V(C)?(e.exit("htmlFlowData"),J(C)):(e.consume(C),W)}function J(C){return e.exit("htmlFlow"),t(C)}}function Lb(e,t,n){const r=this;return i;function i(o){return V(o)?(e.enter("lineEnding"),e.consume(o),e.exit("lineEnding"),l):n(o)}function l(o){return r.parser.lazy[r.now().line]?n(o):t(o)}}function Db(e,t,n){return r;function r(i){return e.enter("lineEnding"),e.consume(i),e.exit("lineEnding"),e.attempt(es,t,n)}}const zb={name:"htmlText",tokenize:Ob};function Ob(e,t,n){const r=this;let i,l,o;return s;function s(k){return e.enter("htmlText"),e.enter("htmlTextData"),e.consume(k),a}function a(k){return k===33?(e.consume(k),c):k===47?(e.consume(k),b):k===63?(e.consume(k),x):Ut(k)?(e.consume(k),R):n(k)}function c(k){return k===45?(e.consume(k),d):k===91?(e.consume(k),l=0,w):Ut(k)?(e.consume(k),g):n(k)}function d(k){return k===45?(e.consume(k),h):n(k)}function f(k){return k===null?n(k):k===45?(e.consume(k),p):V(k)?(o=f,Y(k)):(e.consume(k),f)}function p(k){return k===45?(e.consume(k),h):f(k)}function h(k){return k===62?Q(k):k===45?p(k):f(k)}function w(k){const W="CDATA[";return k===W.charCodeAt(l++)?(e.consume(k),l===W.length?y:w):n(k)}function y(k){return k===null?n(k):k===93?(e.consume(k),S):V(k)?(o=y,Y(k)):(e.consume(k),y)}function S(k){return k===93?(e.consume(k),m):y(k)}function m(k){return k===62?Q(k):k===93?(e.consume(k),m):y(k)}function g(k){return k===null||k===62?Q(k):V(k)?(o=g,Y(k)):(e.consume(k),g)}function x(k){return k===null?n(k):k===63?(e.consume(k),N):V(k)?(o=x,Y(k)):(e.consume(k),x)}function N(k){return k===62?Q(k):x(k)}function b(k){return Ut(k)?(e.consume(k),E):n(k)}function E(k){return k===45||dt(k)?(e.consume(k),E):j(k)}function j(k){return V(k)?(o=j,Y(k)):ie(k)?(e.consume(k),j):Q(k)}function R(k){return k===45||dt(k)?(e.consume(k),R):k===47||k===62||rt(k)?z(k):n(k)}function z(k){return k===47?(e.consume(k),Q):k===58||k===95||Ut(k)?(e.consume(k),L):V(k)?(o=z,Y(k)):ie(k)?(e.consume(k),z):Q(k)}function L(k){return k===45||k===46||k===58||k===95||dt(k)?(e.consume(k),L):A(k)}function A(k){return k===61?(e.consume(k),T):V(k)?(o=A,Y(k)):ie(k)?(e.consume(k),A):z(k)}function T(k){return k===null||k===60||k===61||k===62||k===96?n(k):k===34||k===39?(e.consume(k),i=k,H):V(k)?(o=T,Y(k)):ie(k)?(e.consume(k),T):(e.consume(k),F)}function H(k){return k===i?(e.consume(k),i=void 0,$):k===null?n(k):V(k)?(o=H,Y(k)):(e.consume(k),H)}function F(k){return k===null||k===34||k===39||k===60||k===61||k===96?n(k):k===47||k===62||rt(k)?z(k):(e.consume(k),F)}function $(k){return k===47||k===62||rt(k)?z(k):n(k)}function Q(k){return k===62?(e.consume(k),e.exit("htmlTextData"),e.exit("htmlText"),t):n(k)}function Y(k){return e.exit("htmlTextData"),e.enter("lineEnding"),e.consume(k),e.exit("lineEnding"),_}function _(k){return ie(k)?ce(e,O,"linePrefix",r.parser.constructs.disable.null.includes("codeIndented")?void 0:4)(k):O(k)}function O(k){return e.enter("htmlTextData"),o(k)}}const Fc={name:"labelEnd",resolveAll:$b,resolveTo:Vb,tokenize:Hb},Fb={tokenize:Wb},Bb={tokenize:Gb},Ub={tokenize:Kb};function $b(e){let t=-1;const n=[];for(;++t=3&&(c===null||V(c))?(e.exit("thematicBreak"),t(c)):n(c)}function a(c){return c===i?(e.consume(c),r++,a):(e.exit("thematicBreakSequence"),ie(c)?ce(e,s,"whitespace")(c):s(c))}}const qe={continuation:{tokenize:r2},exit:l2,name:"list",tokenize:n2},e2={partial:!0,tokenize:o2},t2={partial:!0,tokenize:i2};function n2(e,t,n){const r=this,i=r.events[r.events.length-1];let l=i&&i[1].type==="linePrefix"?i[2].sliceSerialize(i[1],!0).length:0,o=0;return s;function s(h){const w=r.containerState.type||(h===42||h===43||h===45?"listUnordered":"listOrdered");if(w==="listUnordered"?!r.containerState.marker||h===r.containerState.marker:hu(h)){if(r.containerState.type||(r.containerState.type=w,e.enter(w,{_container:!0})),w==="listUnordered")return e.enter("listItemPrefix"),h===42||h===45?e.check(Yl,n,c)(h):c(h);if(!r.interrupt||h===49)return e.enter("listItemPrefix"),e.enter("listItemValue"),a(h)}return n(h)}function a(h){return hu(h)&&++o<10?(e.consume(h),a):(!r.interrupt||o<2)&&(r.containerState.marker?h===r.containerState.marker:h===41||h===46)?(e.exit("listItemValue"),c(h)):n(h)}function c(h){return e.enter("listItemMarker"),e.consume(h),e.exit("listItemMarker"),r.containerState.marker=r.containerState.marker||h,e.check(es,r.interrupt?n:d,e.attempt(e2,p,f))}function d(h){return r.containerState.initialBlankLine=!0,l++,p(h)}function f(h){return ie(h)?(e.enter("listItemPrefixWhitespace"),e.consume(h),e.exit("listItemPrefixWhitespace"),p):n(h)}function p(h){return r.containerState.size=l+r.sliceSerialize(e.exit("listItemPrefix"),!0).length,t(h)}}function r2(e,t,n){const r=this;return r.containerState._closeFlow=void 0,e.check(es,i,l);function i(s){return r.containerState.furtherBlankLines=r.containerState.furtherBlankLines||r.containerState.initialBlankLine,ce(e,t,"listItemIndent",r.containerState.size+1)(s)}function l(s){return r.containerState.furtherBlankLines||!ie(s)?(r.containerState.furtherBlankLines=void 0,r.containerState.initialBlankLine=void 0,o(s)):(r.containerState.furtherBlankLines=void 0,r.containerState.initialBlankLine=void 0,e.attempt(t2,t,o)(s))}function o(s){return r.containerState._closeFlow=!0,r.interrupt=void 0,ce(e,e.attempt(qe,t,n),"linePrefix",r.parser.constructs.disable.null.includes("codeIndented")?void 0:4)(s)}}function i2(e,t,n){const r=this;return ce(e,i,"listItemIndent",r.containerState.size+1);function i(l){const o=r.events[r.events.length-1];return o&&o[1].type==="listItemIndent"&&o[2].sliceSerialize(o[1],!0).length===r.containerState.size?t(l):n(l)}}function l2(e){e.exit(this.containerState.type)}function o2(e,t,n){const r=this;return ce(e,i,"listItemPrefixWhitespace",r.parser.constructs.disable.null.includes("codeIndented")?void 0:4+1);function i(l){const o=r.events[r.events.length-1];return!ie(l)&&o&&o[1].type==="listItemPrefixWhitespace"?t(l):n(l)}}const tp={name:"setextUnderline",resolveTo:s2,tokenize:a2};function s2(e,t){let n=e.length,r,i,l;for(;n--;)if(e[n][0]==="enter"){if(e[n][1].type==="content"){r=n;break}e[n][1].type==="paragraph"&&(i=n)}else e[n][1].type==="content"&&e.splice(n,1),!l&&e[n][1].type==="definition"&&(l=n);const o={type:"setextHeading",start:{...e[r][1].start},end:{...e[e.length-1][1].end}};return e[i][1].type="setextHeadingText",l?(e.splice(i,0,["enter",o,t]),e.splice(l+1,0,["exit",e[r][1],t]),e[r][1].end={...e[l][1].end}):e[r][1]=o,e.push(["exit",o,t]),e}function a2(e,t,n){const r=this;let i;return l;function l(c){let d=r.events.length,f;for(;d--;)if(r.events[d][1].type!=="lineEnding"&&r.events[d][1].type!=="linePrefix"&&r.events[d][1].type!=="content"){f=r.events[d][1].type==="paragraph";break}return!r.parser.lazy[r.now().line]&&(r.interrupt||f)?(e.enter("setextHeadingLine"),i=c,o(c)):n(c)}function o(c){return e.enter("setextHeadingLineSequence"),s(c)}function s(c){return c===i?(e.consume(c),s):(e.exit("setextHeadingLineSequence"),ie(c)?ce(e,a,"lineSuffix")(c):a(c))}function a(c){return c===null||V(c)?(e.exit("setextHeadingLine"),t(c)):n(c)}}const u2={tokenize:c2};function c2(e){const t=this,n=e.attempt(es,r,e.attempt(this.parser.constructs.flowInitial,i,ce(e,e.attempt(this.parser.constructs.flow,i,e.attempt(mb,i)),"linePrefix")));return n;function r(l){if(l===null){e.consume(l);return}return e.enter("lineEndingBlank"),e.consume(l),e.exit("lineEndingBlank"),t.currentConstruct=void 0,n}function i(l){if(l===null){e.consume(l);return}return e.enter("lineEnding"),e.consume(l),e.exit("lineEnding"),t.currentConstruct=void 0,n}}const d2={resolveAll:Zy()},f2=Jy("string"),p2=Jy("text");function Jy(e){return{resolveAll:Zy(e==="text"?h2:void 0),tokenize:t};function t(n){const r=this,i=this.parser.constructs[e],l=n.attempt(i,o,s);return o;function o(d){return c(d)?l(d):s(d)}function s(d){if(d===null){n.consume(d);return}return n.enter("data"),n.consume(d),a}function a(d){return c(d)?(n.exit("data"),l(d)):(n.consume(d),a)}function c(d){if(d===null)return!0;const f=i[d];let p=-1;if(f)for(;++p-1){const s=o[0];typeof s=="string"?o[0]=s.slice(r):o.shift()}l>0&&o.push(e[i].slice(0,l))}return o}function j2(e,t){let n=-1;const r=[];let i;for(;++n0){const Et=G.tokenStack[G.tokenStack.length-1];(Et[1]||rp).call(G,void 0,Et[0])}for(D.position={start:dn(P.length>0?P[0][1].start:{line:1,column:1,offset:0}),end:dn(P.length>0?P[P.length-2][1].end:{line:1,column:1,offset:0})},se=-1;++se1?"-"+s:""),dataFootnoteRef:!0,ariaDescribedBy:["footnote-label"]},children:[{type:"text",value:String(o)}]};e.patch(t,a);const c={type:"element",tagName:"sup",properties:{},children:[a]};return e.patch(t,c),e.applyData(t,c)}function H2(e,t){const n={type:"element",tagName:"h"+t.depth,properties:{},children:e.all(t)};return e.patch(t,n),e.applyData(t,n)}function W2(e,t){if(e.options.allowDangerousHtml){const n={type:"raw",value:t.value};return e.patch(t,n),e.applyData(t,n)}}function nv(e,t){const n=t.referenceType;let r="]";if(n==="collapsed"?r+="[]":n==="full"&&(r+="["+(t.label||t.identifier)+"]"),t.type==="imageReference")return[{type:"text",value:"!["+t.alt+r}];const i=e.all(t),l=i[0];l&&l.type==="text"?l.value="["+l.value:i.unshift({type:"text",value:"["});const o=i[i.length-1];return o&&o.type==="text"?o.value+=r:i.push({type:"text",value:r}),i}function G2(e,t){const n=String(t.identifier).toUpperCase(),r=e.definitionById.get(n);if(!r)return nv(e,t);const i={src:qr(r.url||""),alt:t.alt};r.title!==null&&r.title!==void 0&&(i.title=r.title);const l={type:"element",tagName:"img",properties:i,children:[]};return e.patch(t,l),e.applyData(t,l)}function K2(e,t){const n={src:qr(t.url)};t.alt!==null&&t.alt!==void 0&&(n.alt=t.alt),t.title!==null&&t.title!==void 0&&(n.title=t.title);const r={type:"element",tagName:"img",properties:n,children:[]};return e.patch(t,r),e.applyData(t,r)}function Q2(e,t){const n={type:"text",value:t.value.replace(/\r?\n|\r/g," ")};e.patch(t,n);const r={type:"element",tagName:"code",properties:{},children:[n]};return e.patch(t,r),e.applyData(t,r)}function Y2(e,t){const n=String(t.identifier).toUpperCase(),r=e.definitionById.get(n);if(!r)return nv(e,t);const i={href:qr(r.url||"")};r.title!==null&&r.title!==void 0&&(i.title=r.title);const l={type:"element",tagName:"a",properties:i,children:e.all(t)};return e.patch(t,l),e.applyData(t,l)}function q2(e,t){const n={href:qr(t.url)};t.title!==null&&t.title!==void 0&&(n.title=t.title);const r={type:"element",tagName:"a",properties:n,children:e.all(t)};return e.patch(t,r),e.applyData(t,r)}function X2(e,t,n){const r=e.all(t),i=n?J2(n):rv(t),l={},o=[];if(typeof t.checked=="boolean"){const d=r[0];let f;d&&d.type==="element"&&d.tagName==="p"?f=d:(f={type:"element",tagName:"p",properties:{},children:[]},r.unshift(f)),f.children.length>0&&f.children.unshift({type:"text",value:" "}),f.children.unshift({type:"element",tagName:"input",properties:{type:"checkbox",checked:t.checked,disabled:!0},children:[]}),l.className=["task-list-item"]}let s=-1;for(;++s1}function Z2(e,t){const n={},r=e.all(t);let i=-1;for(typeof t.start=="number"&&t.start!==1&&(n.start=t.start);++i0){const o={type:"element",tagName:"tbody",properties:{},children:e.wrap(n,!0)},s=Mc(t.children[1]),a=zy(t.children[t.children.length-1]);s&&a&&(o.position={start:s,end:a}),i.push(o)}const l={type:"element",tagName:"table",properties:{},children:e.wrap(i,!0)};return e.patch(t,l),e.applyData(t,l)}function ij(e,t,n){const r=n?n.children:void 0,l=(r?r.indexOf(t):1)===0?"th":"td",o=n&&n.type==="table"?n.align:void 0,s=o?o.length:t.children.length;let a=-1;const c=[];for(;++a0,!0),r[0]),i=r.index+r[0].length,r=n.exec(t);return l.push(op(t.slice(i),i>0,!1)),l.join("")}function op(e,t,n){let r=0,i=e.length;if(t){let l=e.codePointAt(r);for(;l===ip||l===lp;)r++,l=e.codePointAt(r)}if(n){let l=e.codePointAt(i-1);for(;l===ip||l===lp;)i--,l=e.codePointAt(i-1)}return i>r?e.slice(r,i):""}function sj(e,t){const n={type:"text",value:oj(String(t.value))};return e.patch(t,n),e.applyData(t,n)}function aj(e,t){const n={type:"element",tagName:"hr",properties:{},children:[]};return e.patch(t,n),e.applyData(t,n)}const uj={blockquote:O2,break:F2,code:B2,delete:U2,emphasis:$2,footnoteReference:V2,heading:H2,html:W2,imageReference:G2,image:K2,inlineCode:Q2,linkReference:Y2,link:q2,listItem:X2,list:Z2,paragraph:ej,root:tj,strong:nj,table:rj,tableCell:lj,tableRow:ij,text:sj,thematicBreak:aj,toml:Rl,yaml:Rl,definition:Rl,footnoteDefinition:Rl};function Rl(){}const iv=-1,ts=0,bi=1,Po=2,Bc=3,Uc=4,$c=5,Vc=6,lv=7,ov=8,sp=typeof self=="object"?self:globalThis,cj=(e,t)=>{const n=(i,l)=>(e.set(l,i),i),r=i=>{if(e.has(i))return e.get(i);const[l,o]=t[i];switch(l){case ts:case iv:return n(o,i);case bi:{const s=n([],i);for(const a of o)s.push(r(a));return s}case Po:{const s=n({},i);for(const[a,c]of o)s[r(a)]=r(c);return s}case Bc:return n(new Date(o),i);case Uc:{const{source:s,flags:a}=o;return n(new RegExp(s,a),i)}case $c:{const s=n(new Map,i);for(const[a,c]of o)s.set(r(a),r(c));return s}case Vc:{const s=n(new Set,i);for(const a of o)s.add(r(a));return s}case lv:{const{name:s,message:a}=o;return n(new sp[s](a),i)}case ov:return n(BigInt(o),i);case"BigInt":return n(Object(BigInt(o)),i);case"ArrayBuffer":return n(new Uint8Array(o).buffer,o);case"DataView":{const{buffer:s}=new Uint8Array(o);return n(new DataView(s),o)}}return n(new sp[l](o),i)};return r},ap=e=>cj(new Map,e)(0),fr="",{toString:dj}={},{keys:fj}=Object,ci=e=>{const t=typeof e;if(t!=="object"||!e)return[ts,t];const n=dj.call(e).slice(8,-1);switch(n){case"Array":return[bi,fr];case"Object":return[Po,fr];case"Date":return[Bc,fr];case"RegExp":return[Uc,fr];case"Map":return[$c,fr];case"Set":return[Vc,fr];case"DataView":return[bi,n]}return n.includes("Array")?[bi,n]:n.includes("Error")?[lv,n]:[Po,n]},Il=([e,t])=>e===ts&&(t==="function"||t==="symbol"),pj=(e,t,n,r)=>{const i=(o,s)=>{const a=r.push(o)-1;return n.set(s,a),a},l=o=>{if(n.has(o))return n.get(o);let[s,a]=ci(o);switch(s){case ts:{let d=o;switch(a){case"bigint":s=ov,d=o.toString();break;case"function":case"symbol":if(e)throw new TypeError("unable to serialize "+a);d=null;break;case"undefined":return i([iv],o)}return i([s,d],o)}case bi:{if(a){let p=o;return a==="DataView"?p=new Uint8Array(o.buffer):a==="ArrayBuffer"&&(p=new Uint8Array(o)),i([a,[...p]],o)}const d=[],f=i([s,d],o);for(const p of o)d.push(l(p));return f}case Po:{if(a)switch(a){case"BigInt":return i([a,o.toString()],o);case"Boolean":case"Number":case"String":return i([a,o.valueOf()],o)}if(t&&"toJSON"in o)return l(o.toJSON());const d=[],f=i([s,d],o);for(const p of fj(o))(e||!Il(ci(o[p])))&&d.push([l(p),l(o[p])]);return f}case Bc:return i([s,o.toISOString()],o);case Uc:{const{source:d,flags:f}=o;return i([s,{source:d,flags:f}],o)}case $c:{const d=[],f=i([s,d],o);for(const[p,h]of o)(e||!(Il(ci(p))||Il(ci(h))))&&d.push([l(p),l(h)]);return f}case Vc:{const d=[],f=i([s,d],o);for(const p of o)(e||!Il(ci(p)))&&d.push(l(p));return f}}const{message:c}=o;return i([s,{name:a,message:c}],o)};return l},up=(e,{json:t,lossy:n}={})=>{const r=[];return pj(!(t||n),!!t,new Map,r)(e),r},_o=typeof structuredClone=="function"?(e,t)=>t&&("json"in t||"lossy"in t)?ap(up(e,t)):structuredClone(e):(e,t)=>ap(up(e,t));function hj(e,t){const n=[{type:"text",value:"โฉ"}];return t>1&&n.push({type:"element",tagName:"sup",properties:{},children:[{type:"text",value:String(t)}]}),n}function mj(e,t){return"Back to reference "+(e+1)+(t>1?"-"+t:"")}function gj(e){const t=typeof e.options.clobberPrefix=="string"?e.options.clobberPrefix:"user-content-",n=e.options.footnoteBackContent||hj,r=e.options.footnoteBackLabel||mj,i=e.options.footnoteLabel||"Footnotes",l=e.options.footnoteLabelTagName||"h2",o=e.options.footnoteLabelProperties||{className:["sr-only"]},s=[];let a=-1;for(;++a0&&w.push({type:"text",value:" "});let g=typeof n=="string"?n:n(a,h);typeof g=="string"&&(g={type:"text",value:g}),w.push({type:"element",tagName:"a",properties:{href:"#"+t+"fnref-"+p+(h>1?"-"+h:""),dataFootnoteBackref:"",ariaLabel:typeof r=="string"?r:r(a,h),className:["data-footnote-backref"]},children:Array.isArray(g)?g:[g]})}const S=d[d.length-1];if(S&&S.type==="element"&&S.tagName==="p"){const g=S.children[S.children.length-1];g&&g.type==="text"?g.value+=" ":S.children.push({type:"text",value:" "}),S.children.push(...w)}else d.push(...w);const m={type:"element",tagName:"li",properties:{id:t+"fn-"+p},children:e.wrap(d,!0)};e.patch(c,m),s.push(m)}if(s.length!==0)return{type:"element",tagName:"section",properties:{dataFootnotes:!0,className:["footnotes"]},children:[{type:"element",tagName:l,properties:{..._o(o),id:"footnote-label"},children:[{type:"text",value:i}]},{type:"text",value:`
-`},{type:"element",tagName:"ol",properties:{},children:e.wrap(s,!0)},{type:"text",value:`
-`}]}}const sv=function(e){if(e==null)return wj;if(typeof e=="function")return ns(e);if(typeof e=="object")return Array.isArray(e)?yj(e):vj(e);if(typeof e=="string")return xj(e);throw new Error("Expected function, string, or object as test")};function yj(e){const t=[];let n=-1;for(;++n":""))+")"})}return p;function p(){let h=av,w,y,S;if((!t||l(a,c,d[d.length-1]||void 0))&&(h=Ej(n(a,d)),h[0]===cp))return h;if("children"in a&&a.children){const m=a;if(m.children&&h[0]!==Cj)for(y=(r?m.children.length:-1)+o,S=d.concat(m);y>-1&&y0&&n.push({type:"text",value:`
-`}),n}function dp(e){let t=0,n=e.charCodeAt(t);for(;n===9||n===32;)t++,n=e.charCodeAt(t);return e.slice(t)}function fp(e,t){const n=jj(e,t),r=n.one(e,void 0),i=gj(n),l=Array.isArray(r)?{type:"root",children:r}:r||{type:"root",children:[]};return i&&l.children.push({type:"text",value:`
-`},i),l}function Ij(e,t){return e&&"run"in e?async function(n,r){const i=fp(n,{file:r,...t});await e.run(i,r)}:function(n,r){return fp(n,{file:r,...e||t})}}function pp(e){if(e)throw e}var ql=Object.prototype.hasOwnProperty,cv=Object.prototype.toString,hp=Object.defineProperty,mp=Object.getOwnPropertyDescriptor,gp=function(t){return typeof Array.isArray=="function"?Array.isArray(t):cv.call(t)==="[object Array]"},yp=function(t){if(!t||cv.call(t)!=="[object Object]")return!1;var n=ql.call(t,"constructor"),r=t.constructor&&t.constructor.prototype&&ql.call(t.constructor.prototype,"isPrototypeOf");if(t.constructor&&!n&&!r)return!1;var i;for(i in t);return typeof i>"u"||ql.call(t,i)},vp=function(t,n){hp&&n.name==="__proto__"?hp(t,n.name,{enumerable:!0,configurable:!0,value:n.newValue,writable:!0}):t[n.name]=n.newValue},xp=function(t,n){if(n==="__proto__")if(ql.call(t,n)){if(mp)return mp(t,n).value}else return;return t[n]},Mj=function e(){var t,n,r,i,l,o,s=arguments[0],a=1,c=arguments.length,d=!1;for(typeof s=="boolean"&&(d=s,s=arguments[1]||{},a=2),(s==null||typeof s!="object"&&typeof s!="function")&&(s={});ao.length;let a;s&&o.push(i);try{a=e.apply(this,o)}catch(c){const d=c;if(s&&n)throw d;return i(d)}s||(a&&a.then&&typeof a.then=="function"?a.then(l,i):a instanceof Error?i(a):l(a))}function i(o,...s){n||(n=!0,t(o,...s))}function l(o){i(null,o)}}const Ot={basename:Dj,dirname:zj,extname:Oj,join:Fj,sep:"/"};function Dj(e,t){if(t!==void 0&&typeof t!="string")throw new TypeError('"ext" argument must be a string');ol(e);let n=0,r=-1,i=e.length,l;if(t===void 0||t.length===0||t.length>e.length){for(;i--;)if(e.codePointAt(i)===47){if(l){n=i+1;break}}else r<0&&(l=!0,r=i+1);return r<0?"":e.slice(n,r)}if(t===e)return"";let o=-1,s=t.length-1;for(;i--;)if(e.codePointAt(i)===47){if(l){n=i+1;break}}else o<0&&(l=!0,o=i+1),s>-1&&(e.codePointAt(i)===t.codePointAt(s--)?s<0&&(r=i):(s=-1,r=o));return n===r?r=o:r<0&&(r=e.length),e.slice(n,r)}function zj(e){if(ol(e),e.length===0)return".";let t=-1,n=e.length,r;for(;--n;)if(e.codePointAt(n)===47){if(r){t=n;break}}else r||(r=!0);return t<0?e.codePointAt(0)===47?"/":".":t===1&&e.codePointAt(0)===47?"//":e.slice(0,t)}function Oj(e){ol(e);let t=e.length,n=-1,r=0,i=-1,l=0,o;for(;t--;){const s=e.codePointAt(t);if(s===47){if(o){r=t+1;break}continue}n<0&&(o=!0,n=t+1),s===46?i<0?i=t:l!==1&&(l=1):i>-1&&(l=-1)}return i<0||n<0||l===0||l===1&&i===n-1&&i===r+1?"":e.slice(i,n)}function Fj(...e){let t=-1,n;for(;++t0&&e.codePointAt(e.length-1)===47&&(n+="/"),t?"/"+n:n}function Uj(e,t){let n="",r=0,i=-1,l=0,o=-1,s,a;for(;++o<=e.length;){if(o2){if(a=n.lastIndexOf("/"),a!==n.length-1){a<0?(n="",r=0):(n=n.slice(0,a),r=n.length-1-n.lastIndexOf("/")),i=o,l=0;continue}}else if(n.length>0){n="",r=0,i=o,l=0;continue}}t&&(n=n.length>0?n+"/..":"..",r=2)}else n.length>0?n+="/"+e.slice(i+1,o):n=e.slice(i+1,o),r=o-i-1;i=o,l=0}else s===46&&l>-1?l++:l=-1}return n}function ol(e){if(typeof e!="string")throw new TypeError("Path must be a string. Received "+JSON.stringify(e))}const $j={cwd:Vj};function Vj(){return"/"}function vu(e){return!!(e!==null&&typeof e=="object"&&"href"in e&&e.href&&"protocol"in e&&e.protocol&&e.auth===void 0)}function Hj(e){if(typeof e=="string")e=new URL(e);else if(!vu(e)){const t=new TypeError('The "path" argument must be of type string or an instance of URL. Received `'+e+"`");throw t.code="ERR_INVALID_ARG_TYPE",t}if(e.protocol!=="file:"){const t=new TypeError("The URL must be of scheme file");throw t.code="ERR_INVALID_URL_SCHEME",t}return Wj(e)}function Wj(e){if(e.hostname!==""){const r=new TypeError('File URL host must be "localhost" or empty on darwin');throw r.code="ERR_INVALID_FILE_URL_HOST",r}const t=e.pathname;let n=-1;for(;++n0){let[h,...w]=d;const y=r[p][1];yu(y)&&yu(h)&&(h=$s(!0,y,h)),r[p]=[c,h,...w]}}}}const Yj=new Hc().freeze();function Gs(e,t){if(typeof t!="function")throw new TypeError("Cannot `"+e+"` without `parser`")}function Ks(e,t){if(typeof t!="function")throw new TypeError("Cannot `"+e+"` without `compiler`")}function Qs(e,t){if(t)throw new Error("Cannot call `"+e+"` on a frozen processor.\nCreate a new processor first, by calling it: use `processor()` instead of `processor`.")}function kp(e){if(!yu(e)||typeof e.type!="string")throw new TypeError("Expected node, got `"+e+"`")}function Sp(e,t,n){if(!n)throw new Error("`"+e+"` finished async. Use `"+t+"` instead")}function Ml(e){return qj(e)?e:new dv(e)}function qj(e){return!!(e&&typeof e=="object"&&"message"in e&&"messages"in e)}function Xj(e){return typeof e=="string"||Jj(e)}function Jj(e){return!!(e&&typeof e=="object"&&"byteLength"in e&&"byteOffset"in e)}const Zj="https://github.com/remarkjs/react-markdown/blob/main/changelog.md",Cp=[],Np={allowDangerousHtml:!0},eP=/^(https?|ircs?|mailto|xmpp)$/i,tP=[{from:"astPlugins",id:"remove-buggy-html-in-markdown-parser"},{from:"allowDangerousHtml",id:"remove-buggy-html-in-markdown-parser"},{from:"allowNode",id:"replace-allownode-allowedtypes-and-disallowedtypes",to:"allowElement"},{from:"allowedTypes",id:"replace-allownode-allowedtypes-and-disallowedtypes",to:"allowedElements"},{from:"className",id:"remove-classname"},{from:"disallowedTypes",id:"replace-allownode-allowedtypes-and-disallowedtypes",to:"disallowedElements"},{from:"escapeHtml",id:"remove-buggy-html-in-markdown-parser"},{from:"includeElementIndex",id:"#remove-includeelementindex"},{from:"includeNodeIndex",id:"change-includenodeindex-to-includeelementindex"},{from:"linkTarget",id:"remove-linktarget"},{from:"plugins",id:"change-plugins-to-remarkplugins",to:"remarkPlugins"},{from:"rawSourcePos",id:"#remove-rawsourcepos"},{from:"renderers",id:"change-renderers-to-components",to:"components"},{from:"source",id:"change-source-to-children",to:"children"},{from:"sourcePos",id:"#remove-sourcepos"},{from:"transformImageUri",id:"#add-urltransform",to:"urlTransform"},{from:"transformLinkUri",id:"#add-urltransform",to:"urlTransform"}];function nP(e){const t=rP(e),n=iP(e);return lP(t.runSync(t.parse(n),n),e)}function rP(e){const t=e.rehypePlugins||Cp,n=e.remarkPlugins||Cp,r=e.remarkRehypeOptions?{...e.remarkRehypeOptions,...Np}:Np;return Yj().use(z2).use(n).use(Ij,r).use(t)}function iP(e){const t=e.children||"",n=new dv;return typeof t=="string"&&(n.value=t),n}function lP(e,t){const n=t.allowedElements,r=t.allowElement,i=t.components,l=t.disallowedElements,o=t.skipHtml,s=t.unwrapDisallowed,a=t.urlTransform||oP;for(const d of tP)Object.hasOwn(t,d.from)&&(""+d.from+(d.to?"use `"+d.to+"` instead":"remove it")+Zj+d.id,void 0);return uv(e,c),gE(e,{Fragment:u.Fragment,components:i,ignoreInvalidStyle:!0,jsx:u.jsx,jsxs:u.jsxs,passKeys:!0,passNode:!0});function c(d,f,p){if(d.type==="raw"&&p&&typeof f=="number")return o?p.children.splice(f,1):p.children[f]={type:"text",value:d.value},f;if(d.type==="element"){let h;for(h in Fs)if(Object.hasOwn(Fs,h)&&Object.hasOwn(d.properties,h)){const w=d.properties[h],y=Fs[h];(y===null||y.includes(d.tagName))&&(d.properties[h]=a(String(w||""),h,d))}}if(d.type==="element"){let h=n?!n.includes(d.tagName):l?l.includes(d.tagName):!1;if(!h&&r&&typeof f=="number"&&(h=!r(d,f,p)),h&&p&&typeof f=="number")return s&&d.children?p.children.splice(f,1,...d.children):p.children.splice(f,1),f}}}function oP(e){const t=e.indexOf(":"),n=e.indexOf("?"),r=e.indexOf("#"),i=e.indexOf("/");return t===-1||i!==-1&&t>i||n!==-1&&t>n||r!==-1&&t>r||eP.test(e.slice(0,t))?e:""}function sP({message:e,onCopy:t}){const[n,r]=v.useState(!1),i=e.role==="user",l=e.role==="system",o=()=>{t?t(e.content):navigator.clipboard.writeText(e.content)},s=a=>new Date(a).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"});return l?u.jsx("div",{className:"flex justify-center my-4",children:u.jsxs(Ge,{variant:"outline",className:"text-xs",children:[u.jsx(Rn,{className:"h-3 w-3 mr-1"}),"System prompt set"]})}):u.jsxs("div",{className:re("flex gap-3 mb-4",i?"flex-row-reverse":"flex-row"),children:[u.jsx("div",{className:re("flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center",i?"bg-blue-500 text-white":"bg-muted border"),children:i?u.jsx(Lk,{className:"h-4 w-4"}):e.supports_thinking?u.jsx(Ht,{className:"h-4 w-4"}):u.jsx(ng,{className:"h-4 w-4"})}),u.jsxs("div",{className:re("flex-1 max-w-[80%] space-y-2",i?"items-end":"items-start"),children:[u.jsxs(Ce,{className:re("relative",i?"bg-blue-500 text-white border-blue-500":"bg-muted/50"),children:[u.jsxs(Ne,{className:"p-3",children:[!i&&e.model_used&&u.jsxs("div",{className:"flex items-center gap-2 mb-2 text-xs text-muted-foreground",children:[e.supports_thinking?u.jsx(Ht,{className:"h-3 w-3"}):u.jsx(rl,{className:"h-3 w-3"}),u.jsx("span",{children:e.model_used}),u.jsx(Ge,{variant:"secondary",className:"text-xs",children:e.supports_thinking?"Thinking":"Instruct"})]}),!i&&e.thinking_content&&u.jsxs("div",{className:"mb-3",children:[u.jsxs(te,{variant:"ghost",size:"sm",onClick:()=>r(!n),className:"h-auto p-2 text-xs font-normal",children:[u.jsx(Ht,{className:"h-3 w-3 mr-2"}),"Thinking Process",n?u.jsx(Hw,{className:"h-3 w-3 ml-2"}):u.jsx(rg,{className:"h-3 w-3 ml-2"})]}),n&&u.jsx(Ce,{className:"mt-2 bg-background/50",children:u.jsx(Ne,{className:"p-3",children:u.jsx("pre",{className:"text-xs font-mono whitespace-pre-wrap text-muted-foreground",children:e.thinking_content})})})]}),u.jsx("div",{className:"text-sm",children:i?u.jsx("div",{className:"whitespace-pre-wrap",children:e.content}):u.jsx("div",{className:`prose prose-sm max-w-none dark:prose-invert
- prose-headings:font-semibold prose-headings:text-foreground
- prose-p:text-foreground prose-p:leading-relaxed
- prose-strong:text-foreground prose-strong:font-semibold
- prose-em:text-muted-foreground
- prose-code:bg-muted prose-code:px-1 prose-code:py-0.5 prose-code:rounded prose-code:text-sm
- prose-pre:bg-muted prose-pre:border prose-pre:rounded-md
- prose-ul:text-foreground prose-ol:text-foreground
- prose-li:text-foreground
- prose-blockquote:border-l-muted-foreground prose-blockquote:text-muted-foreground`,children:u.jsx(nP,{components:{h1:({children:a})=>u.jsx("h1",{className:"text-lg font-bold mb-2 text-foreground",children:a}),h2:({children:a})=>u.jsx("h2",{className:"text-base font-semibold mb-2 text-foreground",children:a}),h3:({children:a})=>u.jsx("h3",{className:"text-sm font-semibold mb-1 text-foreground",children:a}),p:({children:a})=>u.jsx("p",{className:"mb-2 last:mb-0 text-foreground leading-relaxed",children:a}),strong:({children:a})=>u.jsx("strong",{className:"font-semibold text-foreground",children:a}),em:({children:a})=>u.jsx("em",{className:"italic text-muted-foreground",children:a}),code:({children:a})=>u.jsx("code",{className:"bg-muted px-1 py-0.5 rounded text-xs font-mono",children:a}),ul:({children:a})=>u.jsx("ul",{className:"mb-2 space-y-1 text-foreground",children:a}),ol:({children:a})=>u.jsx("ol",{className:"mb-2 space-y-1 text-foreground",children:a}),li:({children:a})=>u.jsx("li",{className:"text-foreground",children:a})},children:e.content})})})]}),!i&&u.jsx("div",{className:"absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity",children:u.jsx(te,{variant:"ghost",size:"sm",onClick:o,className:"h-6 w-6 p-0",children:u.jsx(qw,{className:"h-3 w-3"})})})]}),u.jsx("div",{className:re("text-xs text-muted-foreground px-1",i?"text-right":"text-left"),children:s(e.timestamp)})]})]})}const fv=v.forwardRef(({className:e,...t},n)=>u.jsx("textarea",{className:re("flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",e),ref:n,...t}));fv.displayName="Textarea";function aP({value:e,onChange:t,onSubmit:n,onStop:r,isLoading:i=!1,disabled:l=!1,placeholder:o="Type your message...",maxRows:s=6}){const a=v.useRef(null),[c,d]=v.useState(1);v.useEffect(()=>{if(a.current){a.current.style.height="auto";const w=a.current.scrollHeight,S=Math.min(Math.max(Math.ceil(w/24),1),s);d(S)}},[e,s]);const f=w=>{w.key==="Enter"&&!w.shiftKey&&(w.preventDefault(),!i&&e.trim()&&!l&&n())},p=w=>{w.preventDefault(),!i&&e.trim()&&!l&&n()},h=e.trim()&&!l&&!i;return u.jsx("div",{className:"border-t bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60",children:u.jsx("div",{className:"p-4",children:u.jsxs("form",{onSubmit:p,className:"space-y-3",children:[u.jsxs("div",{className:"relative flex items-end gap-2",children:[u.jsxs("div",{className:"flex-1 relative",children:[u.jsx(fv,{ref:a,value:e,onChange:w=>t(w.target.value),onKeyDown:f,placeholder:o,disabled:l,rows:c,className:re("min-h-[40px] max-h-[150px] resize-none pr-12","focus:ring-2 focus:ring-blue-500 focus:border-blue-500","placeholder:text-muted-foreground"),style:{lineHeight:"1.5"}}),u.jsx(te,{type:"button",variant:"ghost",size:"sm",className:"absolute right-2 bottom-2 h-6 w-6 p-0 text-muted-foreground hover:text-foreground",disabled:l,children:u.jsx(mk,{className:"h-4 w-4"})})]}),i?u.jsx(te,{type:"button",variant:"destructive",size:"sm",onClick:r,className:"h-10 w-10 p-0",children:u.jsx(Tk,{className:"h-4 w-4"})}):u.jsx(te,{type:"submit",size:"sm",disabled:!h,className:re("h-10 w-10 p-0 transition-colors",h?"bg-blue-500 hover:bg-blue-600 text-white":"bg-muted text-muted-foreground"),children:u.jsx(Sk,{className:"h-4 w-4"})})]}),u.jsxs("div",{className:"flex items-center justify-between text-xs text-muted-foreground",children:[u.jsx("span",{children:"Press Enter to send, Shift+Enter for new line"}),u.jsxs("span",{children:[e.length," characters"]})]})]})})})}function uP({messages:e,input:t,onInputChange:n,onSubmit:r,onStop:i,isLoading:l=!1,disabled:o=!1,className:s,placeholder:a="Ask me anything..."}){const c=v.useRef(null),d=v.useRef(null);v.useEffect(()=>{c.current&&c.current.scrollIntoView({behavior:"smooth"})},[e,l]);const f=p=>{navigator.clipboard.writeText(p)};return u.jsxs("div",{className:re("flex flex-col h-full",s),children:[u.jsxs("div",{ref:d,className:"flex-1 overflow-y-auto p-4 space-y-4",children:[e.length===0?u.jsx("div",{className:"flex-1 flex items-center justify-center text-center",children:u.jsx("div",{className:"max-w-md space-y-4",children:u.jsxs("div",{className:"text-muted-foreground",children:[u.jsx("h3",{className:"text-lg font-medium",children:"Start a conversation"}),u.jsx("p",{className:"text-sm",children:"Ask me anything! I can help with coding, writing, analysis, and more."})]})})}):u.jsxs(u.Fragment,{children:[e.map(p=>u.jsx("div",{className:"group",children:u.jsx(sP,{message:p,onCopy:f})},p.id)),l&&u.jsxs("div",{className:"flex gap-3 mb-4",children:[u.jsx("div",{className:"flex-shrink-0 w-8 h-8 rounded-full bg-muted border flex items-center justify-center",children:u.jsx(Gi,{className:"h-4 w-4 animate-spin"})}),u.jsx("div",{className:"flex-1 max-w-[80%]",children:u.jsx("div",{className:"bg-muted/50 rounded-lg p-3",children:u.jsxs("div",{className:"flex items-center gap-2 text-sm text-muted-foreground",children:[u.jsx(Gi,{className:"h-4 w-4 animate-spin"}),u.jsx("span",{children:"Thinking..."})]})})})]})]}),u.jsx("div",{ref:c})]}),u.jsx(aP,{value:t,onChange:n,onSubmit:r,onStop:i,isLoading:l,disabled:o,placeholder:a})]})}function cP({sessions:e,currentSessionId:t,onSelectSession:n,onNewSession:r,onDeleteSession:i,onRenameSession:l}){const[o,s]=v.useState(null),[a,c]=v.useState(""),d=y=>{s(y.id),c(y.title)},f=()=>{o&&a.trim()&&l&&l(o,a.trim()),s(null),c("")},p=()=>{s(null),c("")},h=y=>{const S=new Date(y),g=new Date().getTime()-S.getTime(),x=Math.floor(g/(1e3*60*60*24));return x===0?"Today":x===1?"Yesterday":x<7?`${x} days ago`:S.toLocaleDateString()},w=e.reduce((y,S)=>{const m=h(S.updated_at);return y[m]||(y[m]=[]),y[m].push(S),y},{});return u.jsxs("div",{className:"h-full flex flex-col",children:[u.jsxs("div",{className:"p-4 border-b",children:[u.jsxs("div",{className:"flex items-center justify-between mb-3",children:[u.jsx("h2",{className:"font-semibold text-sm",children:"Chat Sessions"}),u.jsx(te,{onClick:r,size:"sm",className:"h-8 w-8 p-0",children:u.jsx(yf,{className:"h-4 w-4"})})]}),u.jsxs(te,{onClick:r,variant:"outline",className:"w-full justify-start",size:"sm",children:[u.jsx(yf,{className:"h-4 w-4 mr-2"}),"New Chat"]})]}),u.jsx("div",{className:"flex-1 overflow-y-auto p-2 space-y-4",children:Object.keys(w).length===0?u.jsxs("div",{className:"flex flex-col items-center justify-center h-32 text-center",children:[u.jsx(Rn,{className:"h-8 w-8 text-muted-foreground mb-2"}),u.jsx("p",{className:"text-sm text-muted-foreground",children:"No chat sessions yet"}),u.jsx("p",{className:"text-xs text-muted-foreground",children:"Start a new conversation"})]}):Object.entries(w).map(([y,S])=>u.jsxs("div",{className:"space-y-2",children:[u.jsxs("div",{className:"flex items-center gap-2 px-2",children:[u.jsx(Uw,{className:"h-3 w-3 text-muted-foreground"}),u.jsx("span",{className:"text-xs font-medium text-muted-foreground uppercase tracking-wider",children:y})]}),u.jsx("div",{className:"space-y-1",children:S.map(m=>{var g;return u.jsx(Ce,{className:re("cursor-pointer transition-colors hover:bg-accent/50 group",t===m.id&&"bg-accent border-primary"),onClick:()=>n(m.id),children:u.jsx(Ne,{className:"p-3",children:o===m.id?u.jsxs("div",{className:"space-y-2",children:[u.jsx("input",{type:"text",value:a,onChange:x=>c(x.target.value),className:"w-full text-sm bg-transparent border border-input rounded px-2 py-1",onKeyDown:x=>{x.key==="Enter"&&f(),x.key==="Escape"&&p()},autoFocus:!0}),u.jsxs("div",{className:"flex gap-1",children:[u.jsx(te,{size:"sm",onClick:f,className:"h-6 px-2 text-xs",children:"Save"}),u.jsx(te,{size:"sm",variant:"outline",onClick:p,className:"h-6 px-2 text-xs",children:"Cancel"})]})]}):u.jsxs("div",{className:"space-y-2",children:[u.jsxs("div",{className:"flex items-start justify-between",children:[u.jsx("h3",{className:"text-sm font-medium line-clamp-2 flex-1 mr-2",children:m.title}),u.jsxs("div",{className:"opacity-0 group-hover:opacity-100 transition-opacity flex gap-1",children:[l&&u.jsx(te,{variant:"ghost",size:"sm",onClick:x=>{x.stopPropagation(),d(m)},className:"h-6 w-6 p-0",children:u.jsx(yk,{className:"h-3 w-3"})}),u.jsx(te,{variant:"ghost",size:"sm",onClick:x=>{x.stopPropagation(),i(m.id)},className:"h-6 w-6 p-0 text-destructive hover:text-destructive",children:u.jsx(wc,{className:"h-3 w-3"})})]})]}),u.jsxs("div",{className:"flex items-center justify-between text-xs text-muted-foreground",children:[u.jsxs("span",{children:[m.messages.length," messages"]}),m.model_name&&u.jsx(Ge,{variant:"outline",className:"text-xs",children:(g=m.model_name.split("/").pop())==null?void 0:g.split("-")[0]})]})]})})},m.id)})})]},y))})]})}const Ys="edge-llm-chat-store",he={load(){try{const e=localStorage.getItem(Ys);return e?JSON.parse(e):{sessions:[],current_session_id:null}}catch(e){return console.error("Failed to load chat store:",e),{sessions:[],current_session_id:null}}},save(e){try{localStorage.setItem(Ys,JSON.stringify(e))}catch(t){console.error("Failed to save chat store:",t)}},createSession(e,t,n){const r=Date.now(),i={id:`session_${r}_${Math.random().toString(36).substr(2,9)}`,title:e||`New Chat ${new Date(r).toLocaleDateString()}`,messages:[],created_at:r,updated_at:r,model_name:t,system_prompt:n},l=this.load();return l.sessions.unshift(i),this.save(l),i},addMessageToSession(e,t){const n=this.load(),r=n.sessions.find(i=>i.id===e);if(r){const i={...t,id:`msg_${Date.now()}_${Math.random().toString(36).substr(2,9)}`,timestamp:Date.now()};r.messages.push(i),r.updated_at=Date.now(),r.messages.length===1&&t.role==="user"&&(r.title=t.content.slice(0,50)+(t.content.length>50?"...":"")),this.save(n)}},getSession(e){return this.load().sessions.find(n=>n.id===e)||null},updateSession(e,t){const n=this.load(),r=n.sessions.findIndex(i=>i.id===e);r!==-1&&(n.sessions[r]={...n.sessions[r],...t,updated_at:Date.now()},this.save(n))},deleteSession(e){const t=this.load();t.sessions=t.sessions.filter(n=>n.id!==e),t.current_session_id===e&&(t.current_session_id=t.sessions.length>0?t.sessions[0].id:null),this.save(t)},setCurrentSession(e){const t=this.load();t.current_session_id=e,this.save(t)},getCurrentSession(){const e=this.load();return e.current_session_id?this.getSession(e.current_session_id):null},getAllSessions(){return this.load().sessions.sort((t,n)=>n.updated_at-t.updated_at)},clear(){localStorage.removeItem(Ys)}};function dP(e={}){const{api_endpoint:t="http://localhost:8000/generate",defaultModel:n="Qwen/Qwen3-4B-Instruct-2507",defaultSystemPrompt:r=""}=e,[i,l]=v.useState([]),[o,s]=v.useState(null),[a,c]=v.useState(""),[d,f]=v.useState({isLoading:!1,error:null}),[p,h]=v.useState(n),[w,y]=v.useState(r),[S,m]=v.useState(.7),[g,x]=v.useState(1024),N=i.find(F=>F.id===o)||null,b=(N==null?void 0:N.messages)||[];v.useEffect(()=>{const F=he.getAllSessions();l(F);const $=he.getCurrentSession();$?s($.id):F.length>0&&(s(F[0].id),he.setCurrentSession(F[0].id))},[]);const E=v.useCallback(()=>{const F=he.createSession(void 0,p,w);return l(he.getAllSessions()),s(F.id),he.setCurrentSession(F.id),F.id},[p,w]),j=v.useCallback(F=>{s(F),he.setCurrentSession(F)},[]),R=v.useCallback(F=>{he.deleteSession(F);const $=he.getAllSessions();l($),o===F&&($.length>0?(s($[0].id),he.setCurrentSession($[0].id)):s(null))},[o]),z=v.useCallback((F,$)=>{he.updateSession(F,{title:$}),l(he.getAllSessions())},[]),L=v.useCallback(F=>{o&&(he.addMessageToSession(o,F),l(he.getAllSessions()))},[o]),A=v.useCallback(async()=>{if(!a.trim()||d.isLoading)return;let F=o;F||(F=E());const $=a.trim();c(""),f({isLoading:!0,error:null}),F&&(he.addMessageToSession(F,{role:"user",content:$}),l(he.getAllSessions()));const Q=he.getSession(F),Y=(Q==null?void 0:Q.messages)&&Q.messages.length>1;w&&!Y&&F&&(he.addMessageToSession(F,{role:"system",content:w}),l(he.getAllSessions()));try{const _=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({prompt:$,system_prompt:w||null,model_name:p,temperature:S,max_new_tokens:g})});if(!_.ok){const k=await _.json();throw new Error(k.detail||`HTTP error! status: ${_.status}`)}const O=await _.json();F&&(he.addMessageToSession(F,{role:"assistant",content:O.content,thinking_content:O.thinking_content,model_used:O.model_used,supports_thinking:O.supports_thinking}),l(he.getAllSessions())),f({isLoading:!1,error:null})}catch(_){const O=_ instanceof Error?_.message:"An error occurred";f({isLoading:!1,error:O}),F&&(he.addMessageToSession(F,{role:"assistant",content:`Sorry, I encountered an error: ${O}`}),l(he.getAllSessions()))}},[a,d.isLoading,o,E,L,w,b.length,t,p,S,g]),T=v.useCallback(()=>{f({isLoading:!1,error:null})},[]),H=v.useCallback(()=>{he.clear(),l([]),s(null)},[]);return{sessions:i,currentSession:N,currentSessionId:o,createNewSession:E,selectSession:j,deleteSession:R,renameSession:z,clearAllSessions:H,messages:b,input:a,setInput:c,sendMessage:A,stopGeneration:T,isLoading:d.isLoading,error:d.error,selectedModel:p,setSelectedModel:h,systemPrompt:w,setSystemPrompt:y,temperature:S,setTemperature:m,maxTokens:g,setMaxTokens:x}}function fP(){var ot,Nt,Gt,Kt,zn,sl;const{sessions:e,currentSession:t,currentSessionId:n,createNewSession:r,selectSession:i,deleteSession:l,renameSession:o,messages:s,input:a,setInput:c,sendMessage:d,stopGeneration:f,isLoading:p,selectedModel:h,setSelectedModel:w,systemPrompt:y,setSystemPrompt:S,temperature:m,setTemperature:g,maxTokens:x,setMaxTokens:N}=dP(),[b,E]=v.useState(!1),[j,R]=v.useState(!1),[z,L]=v.useState([]),[A,T]=v.useState(null),[H,F]=v.useState(!1),[$,Q]=v.useState(!1),[Y,_]=v.useState({action:"load",model:null}),O=[{name:"Default Assistant",prompt:"You are a helpful, harmless, and honest AI assistant. Provide clear, accurate, and well-structured responses."},{name:"Code Expert",prompt:"You are an expert software developer. Provide clean, efficient code with clear explanations. Always follow best practices and include comments where helpful."},{name:"Technical Writer",prompt:"You are a technical writer. Create clear, comprehensive documentation and explanations. Use proper formatting and structure your responses logically."},{name:"Creative Writer",prompt:"You are a creative writer. Use vivid language, engaging storytelling, and imaginative descriptions. Be expressive and artistic in your responses."},{name:"Research Assistant",prompt:"You are a research assistant. Provide detailed, well-researched responses with clear reasoning. Cite sources when relevant and present information objectively."},{name:"Teacher",prompt:"You are an experienced teacher. Explain concepts clearly, use examples, and break down complex topics into understandable parts. Be encouraging and patient."}],k=[{title:"Marketing Slogan",description:"Create a catchy marketing slogan for a new eco-friendly product.",prompt:"Create a catchy marketing slogan for a new eco-friendly water bottle that keeps drinks cold for 24 hours. The target audience is environmentally conscious millennials and Gen Z consumers."},{title:"Creative Storytelling",description:"Write a short story about a time traveler.",prompt:"Write a 300-word short story about a time traveler who accidentally changes a major historical event while trying to observe ancient Rome."},{title:"Technical Explanation",description:"Explain a complex technical concept simply.",prompt:"Explain how blockchain technology works in simple terms that a 12-year-old could understand, using analogies and examples."},{title:"Code Generation",description:"Generate code with explanations.",prompt:"Write a Python function that takes a list of numbers and returns the second largest number. Include error handling and detailed comments explaining each step."}];v.useEffect(()=>{W()},[]),v.useEffect(()=>{if(h&&!z.find(U=>U.model_name===h&&U.is_loaded)){const U=z.find(ge=>ge.is_loaded);U&&w(U.model_name)}},[z,h,w]);const W=async()=>{try{const U=await fetch("http://localhost:8000/models");if(U.ok){const ge=await U.json();if(L(ge.models),ge.current_model&&h!==ge.current_model)w(ge.current_model);else if(!h){const gt=ge.models.find(On=>On.is_loaded);gt&&w(gt.model_name)}}}catch(U){console.error("Failed to fetch models:",U)}},J=U=>{_({action:"load",model:U}),F(!0)},C=U=>{_({action:"unload",model:U}),Q(!0)},ke=async()=>{const U=Y.model;if(U){T(U.model_name),F(!1);try{const ge=await fetch("http://localhost:8000/load-model",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model_name:U.model_name})});if(ge.ok)await W(),w(U.model_name);else{const gt=await ge.json();console.error(`Failed to load model: ${gt.detail||"Unknown error"}`)}}catch(ge){console.error(`Failed to load model: ${ge instanceof Error?ge.message:"Unknown error"}`)}finally{T(null)}}},lt=async()=>{const U=Y.model;if(U){Q(!1);try{const ge=await fetch("http://localhost:8000/unload-model",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model_name:U.model_name})});if(ge.ok){if(await W(),h===U.model_name){const gt=z.find(On=>On.is_loaded&&On.model_name!==U.model_name);gt&&w(gt.model_name)}}else{const gt=await ge.json();console.error(`Failed to unload model: ${gt.detail||"Unknown error"}`)}}catch(ge){console.error(`Failed to unload model: ${ge instanceof Error?ge.message:"Unknown error"}`)}}},oe=U=>{c(U)};return u.jsxs("div",{className:"min-h-screen bg-background flex",children:[u.jsx("div",{className:`
- ${b?"translate-x-0":"-translate-x-full"}
- fixed inset-y-0 left-0 z-50 w-80 bg-background border-r transition-transform duration-300 ease-in-out
- lg:translate-x-0 lg:static lg:inset-0
- `,children:u.jsx(cP,{sessions:e,currentSessionId:n,onSelectSession:i,onNewSession:r,onDeleteSession:l,onRenameSession:o})}),b&&u.jsx("div",{className:"fixed inset-0 z-40 bg-black/50 lg:hidden",onClick:()=>E(!1)}),u.jsxs("div",{className:"flex-1 flex flex-col overflow-hidden",children:[u.jsx("div",{className:"border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60",children:u.jsxs("div",{className:"flex h-14 items-center px-6",children:[u.jsxs("div",{className:"flex items-center gap-2",children:[u.jsx(te,{variant:"ghost",size:"sm",onClick:()=>E(!b),className:"lg:hidden",children:b?u.jsx(dk,{className:"h-4 w-4"}):u.jsx(pk,{className:"h-4 w-4"})}),u.jsx(Rn,{className:"h-5 w-5"}),u.jsx("h1",{className:"text-lg font-semibold",children:"Chat Playground"}),t&&u.jsxs(Ge,{variant:"outline",className:"text-xs",children:[t.title.slice(0,20),"..."]})]}),u.jsxs("div",{className:"ml-auto flex items-center gap-2 overflow-x-auto",children:[u.jsxs(te,{variant:"outline",size:"sm",onClick:()=>E(!b),className:"hidden lg:flex flex-shrink-0",children:[u.jsx(ek,{className:"h-4 w-4 mr-2"}),u.jsx("span",{className:"hidden sm:inline",children:"Sessions"})]}),u.jsxs(te,{variant:"outline",size:"sm",className:"flex-shrink-0",children:[u.jsx(Qw,{className:"h-4 w-4 mr-2"}),u.jsx("span",{className:"hidden sm:inline",children:"View code"}),u.jsx("span",{className:"sm:hidden",children:"Code"})]}),u.jsxs(te,{variant:"outline",size:"sm",className:"flex-shrink-0",children:[u.jsx(Mk,{className:"h-4 w-4 mr-2"}),u.jsx("span",{className:"hidden sm:inline",children:"Import"})]}),u.jsxs(te,{variant:"outline",size:"sm",className:"flex-shrink-0",children:[u.jsx(jk,{className:"h-4 w-4 mr-2"}),u.jsx("span",{className:"hidden sm:inline",children:"Export"})]})]})]})}),u.jsxs("div",{className:"flex-1 flex overflow-hidden",children:[u.jsxs("div",{className:"flex-1 flex flex-col",children:[s.length===0&&u.jsx("div",{className:"p-6 border-b",children:u.jsxs(Ce,{children:[u.jsx(We,{children:u.jsx(Je,{className:"text-base",children:"Start with a sample prompt"})}),u.jsx(Ne,{children:u.jsx("div",{className:"grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 gap-4",children:k.map((U,ge)=>u.jsx(te,{variant:"outline",className:"h-auto p-4 text-left justify-start min-w-0",onClick:()=>oe(U.prompt),disabled:p,children:u.jsxs("div",{className:"min-w-0",children:[u.jsx("div",{className:"font-medium text-sm mb-1 truncate",children:U.title}),u.jsx("div",{className:"text-xs text-muted-foreground line-clamp-2",children:U.description})]})},ge))})})]})}),u.jsx(uP,{messages:s,input:a,onInputChange:c,onSubmit:d,onStop:f,isLoading:p,disabled:!h||!((ot=z.find(U=>U.model_name===h))!=null&&ot.is_loaded),placeholder:!h||!((Nt=z.find(U=>U.model_name===h))!=null&&Nt.is_loaded)?"Please load a model first...":"Ask me anything...",className:"flex-1"})]}),u.jsx("div",{className:"w-80 border-l bg-muted/30 overflow-y-auto",children:u.jsxs("div",{className:"p-4 space-y-6",children:[u.jsxs("div",{className:"flex items-center gap-2",children:[u.jsx(xc,{className:"h-4 w-4"}),u.jsx("h2",{className:"font-semibold text-sm",children:"Configuration"})]}),u.jsxs(Ce,{children:[u.jsx(We,{children:u.jsx(Je,{className:"text-sm",children:"Model Management"})}),u.jsx(Ne,{className:"space-y-3",children:z.map(U=>u.jsx("div",{className:"border rounded-lg p-3 overflow-hidden",children:u.jsxs("div",{className:"space-y-3",children:[u.jsxs("div",{className:"flex items-start gap-2",children:[U.supports_thinking?u.jsx(Ht,{className:"h-4 w-4 flex-shrink-0"}):u.jsx(rl,{className:"h-4 w-4 flex-shrink-0"}),u.jsxs("div",{className:"flex-1 min-w-0",children:[u.jsxs("div",{className:"flex items-center gap-2 mb-1 flex-wrap",children:[u.jsx("span",{className:"font-medium text-sm truncate",children:U.name}),U.model_name===h&&u.jsx(Ge,{variant:"default",className:"text-xs flex-shrink-0",children:"Active"}),U.is_loaded&&U.model_name!==h&&u.jsx(Ge,{variant:"secondary",className:"text-xs flex-shrink-0",children:"Loaded"})]}),u.jsxs("p",{className:"text-xs text-muted-foreground break-words",children:[U.description," โข ",U.size_gb]})]})]}),U.is_loaded&&u.jsxs("div",{className:"flex items-center gap-2",children:[u.jsx("input",{type:"radio",name:"selectedModel",value:U.model_name,checked:h===U.model_name,onChange:()=>w(U.model_name),className:"h-3 w-3 flex-shrink-0"}),u.jsx(Te,{className:"text-xs",children:"Use for generation"})]}),u.jsx("div",{className:"flex justify-end",children:U.is_loaded?u.jsxs(te,{variant:"outline",size:"sm",onClick:()=>C(U),disabled:p,className:"h-8 px-3 text-xs flex-shrink-0",children:[u.jsx(wc,{className:"h-3 w-3 mr-2"}),"Unload"]}):u.jsx(te,{variant:"outline",size:"sm",onClick:()=>J(U),disabled:p||A===U.model_name,className:"h-8 px-3 text-xs flex-shrink-0 min-w-[80px]",children:A===U.model_name?u.jsxs(u.Fragment,{children:[u.jsx(Gi,{className:"h-3 w-3 mr-2 animate-spin"}),"Loading..."]}):u.jsxs(u.Fragment,{children:[u.jsx(vc,{className:"h-3 w-3 mr-2"}),"Load"]})})})]})},U.model_name))})]}),u.jsxs(Ce,{children:[u.jsx(We,{children:u.jsx(Je,{className:"text-sm",children:"Parameters"})}),u.jsxs(Ne,{className:"space-y-4",children:[u.jsxs("div",{children:[u.jsxs(Te,{className:"text-xs font-medium",children:["Temperature: ",m.toFixed(2)]}),u.jsx(Ja,{value:[m],onValueChange:U=>g(U[0]),min:0,max:2,step:.01,className:"mt-2",disabled:p}),u.jsx("p",{className:"text-xs text-muted-foreground mt-1",children:"Lower = more focused, Higher = more creative"})]}),u.jsxs("div",{children:[u.jsxs(Te,{className:"text-xs font-medium",children:["Max Tokens: ",x]}),u.jsx(Ja,{value:[x],onValueChange:U=>N(U[0]),min:100,max:4096,step:100,className:"mt-2",disabled:p})]})]})]}),u.jsx(Ce,{children:u.jsxs(vN,{open:j,onOpenChange:R,children:[u.jsx(We,{children:u.jsx(xN,{asChild:!0,children:u.jsxs(te,{variant:"ghost",className:"w-full justify-between p-0",disabled:p,children:[u.jsxs("div",{className:"flex items-center gap-2",children:[u.jsx(Rn,{className:"h-4 w-4"}),u.jsx("span",{className:"text-sm font-medium",children:"System Prompt"}),y&&u.jsx(Ge,{variant:"secondary",className:"text-xs",children:"Custom"})]}),u.jsx(rg,{className:`h-4 w-4 transition-transform ${j?"transform rotate-180":""}`})]})})}),u.jsx(wN,{children:u.jsxs(Ne,{className:"space-y-3",children:[u.jsxs("div",{children:[u.jsx(Te,{className:"text-xs font-medium text-muted-foreground",children:"Quick Presets"}),u.jsx("div",{className:"grid grid-cols-1 gap-1 mt-1",children:O.map(U=>u.jsx(te,{variant:"outline",size:"sm",className:"h-auto p-2 text-xs justify-start",onClick:()=>S(U.prompt),disabled:p,children:U.name},U.name))})]}),u.jsxs("div",{children:[u.jsxs("div",{className:"flex items-center justify-between mb-2",children:[u.jsx(Te,{htmlFor:"system-prompt",className:"text-xs font-medium",children:"Custom System Prompt"}),y&&u.jsxs(te,{variant:"ghost",size:"sm",onClick:()=>S(""),className:"h-6 px-2 text-xs",disabled:p,children:[u.jsx(wk,{className:"h-3 w-3 mr-1"}),"Clear"]})]}),u.jsx("textarea",{id:"system-prompt",value:y,onChange:U=>S(U.target.value),placeholder:"Enter custom system prompt to define how the model should behave...",className:"w-full min-h-[80px] text-xs p-2 border rounded-md bg-background",disabled:p}),u.jsx("p",{className:"text-xs text-muted-foreground mt-1",children:"System prompts define the model's role and behavior."})]})]})})]})})]})})]})]}),u.jsx(Af,{open:H,onOpenChange:F,children:u.jsxs(tu,{children:[u.jsxs(nu,{children:[u.jsx(iu,{children:"Load Model"}),u.jsxs(lu,{children:["Do you want to load ",u.jsx("strong",{children:(Gt=Y.model)==null?void 0:Gt.name}),"?",u.jsx("br",{}),u.jsx("br",{}),u.jsx("strong",{children:"Size:"})," ",(Kt=Y.model)==null?void 0:Kt.size_gb,u.jsx("br",{}),u.jsx("strong",{children:"Note:"})," This will download the model if it's not already cached locally. This may take several minutes and use significant bandwidth and storage."]})]}),u.jsxs(ru,{children:[u.jsx(su,{children:"Cancel"}),u.jsx(ou,{onClick:ke,children:"Load Model"})]})]})}),u.jsx(Af,{open:$,onOpenChange:Q,children:u.jsxs(tu,{children:[u.jsxs(nu,{children:[u.jsx(iu,{children:"Unload Model"}),u.jsxs(lu,{children:["Are you sure you want to unload ",u.jsx("strong",{children:(zn=Y.model)==null?void 0:zn.name}),"?",u.jsx("br",{}),u.jsx("br",{}),"This will free up memory but you'll need to reload it to use it again.",((sl=Y.model)==null?void 0:sl.model_name)===h&&u.jsxs(u.Fragment,{children:[u.jsx("br",{}),u.jsx("br",{}),u.jsx("strong",{children:"Warning:"})," This is the currently active model."]})]})]}),u.jsxs(ru,{children:[u.jsx(su,{children:"Cancel"}),u.jsx(ou,{onClick:lt,children:"Unload Model"})]})]})})]})}function pP(){const[e,t]=v.useState([]),[n,r]=v.useState(!0),[i,l]=v.useState(null);v.useEffect(()=>{o()},[]);const o=async()=>{try{const c=await fetch("http://localhost:8000/models");if(c.ok){const d=await c.json();t(d.models)}}catch(c){console.error("Failed to fetch models:",c)}finally{r(!1)}},s=async c=>{l(c);try{(await fetch("http://localhost:8000/load-model",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model_name:c})})).ok&&await o()}catch(d){console.error("Failed to load model:",d)}finally{l(null)}},a=async c=>{try{(await fetch("http://localhost:8000/unload-model",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model_name:c})})).ok&&await o()}catch(d){console.error("Failed to unload model:",d)}};return n?u.jsxs("div",{className:"min-h-screen bg-background",children:[u.jsx("div",{className:"border-b",children:u.jsx("div",{className:"flex h-14 items-center px-6",children:u.jsxs("div",{className:"flex items-center gap-2",children:[u.jsx(bo,{className:"h-5 w-5"}),u.jsx("h1",{className:"text-lg font-semibold",children:"Model Catalog"})]})})}),u.jsx("div",{className:"flex items-center justify-center h-64",children:u.jsx(Gi,{className:"h-8 w-8 animate-spin"})})]}):u.jsxs("div",{className:"min-h-screen bg-background",children:[u.jsx("div",{className:"border-b",children:u.jsxs("div",{className:"flex h-14 items-center px-6",children:[u.jsxs("div",{className:"flex items-center gap-2",children:[u.jsx(bo,{className:"h-5 w-5"}),u.jsx("h1",{className:"text-lg font-semibold",children:"Model Catalog"})]}),u.jsx("div",{className:"ml-auto",children:u.jsx(te,{variant:"outline",size:"sm",onClick:o,children:"Refresh"})})]})}),u.jsx("div",{className:"flex-1 p-6",children:u.jsxs("div",{className:"max-w-4xl mx-auto space-y-6",children:[u.jsx(Ce,{className:"bg-blue-50 border-blue-200",children:u.jsx(Ne,{className:"pt-6",children:u.jsxs("div",{className:"flex items-start gap-3",children:[u.jsx(lg,{className:"h-5 w-5 text-blue-600 mt-0.5"}),u.jsxs("div",{children:[u.jsx("h3",{className:"font-medium text-blue-900",children:"Model Management"}),u.jsx("p",{className:"text-sm text-blue-700 mt-1",children:"Load models to use them in the playground. Models are cached locally for faster access. Each model requires significant storage space and initial download time."})]})]})})}),u.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-6",children:e.map(c=>u.jsxs(Ce,{className:"relative",children:[u.jsx(We,{children:u.jsx("div",{className:"flex items-start justify-between",children:u.jsxs("div",{className:"flex items-center gap-3",children:[c.supports_thinking?u.jsx(Ht,{className:"h-6 w-6 text-blue-500"}):u.jsx(rl,{className:"h-6 w-6 text-green-500"}),u.jsxs("div",{children:[u.jsx(Je,{className:"text-lg",children:c.name}),u.jsxs("div",{className:"flex items-center gap-2 mt-1",children:[u.jsx(Ge,{variant:c.supports_thinking?"default":"secondary",children:c.supports_thinking?"Thinking Model":"Instruction Model"}),c.is_loaded&&u.jsxs(Ge,{variant:"outline",className:"text-green-600 border-green-600",children:[u.jsx(Gw,{className:"h-3 w-3 mr-1"}),"Loaded"]})]})]})]})})}),u.jsxs(Ne,{className:"space-y-4",children:[u.jsxs("div",{children:[u.jsx("p",{className:"text-sm text-muted-foreground mb-2",children:c.description}),u.jsxs("div",{className:"flex items-center gap-4 text-xs text-muted-foreground",children:[u.jsxs("span",{children:["Size: ",c.size_gb]}),u.jsx("span",{children:"Format: Safetensors"})]})]}),u.jsxs("div",{className:"space-y-2",children:[u.jsx("h4",{className:"text-sm font-medium",children:"Capabilities"}),u.jsxs("div",{className:"flex flex-wrap gap-2",children:[u.jsx(Ge,{variant:"outline",className:"text-xs",children:"Text Generation"}),u.jsx(Ge,{variant:"outline",className:"text-xs",children:"Conversation"}),u.jsx(Ge,{variant:"outline",className:"text-xs",children:"Code"}),c.supports_thinking&&u.jsx(Ge,{variant:"outline",className:"text-xs",children:"Reasoning"})]})]}),u.jsx("div",{className:"pt-2 border-t",children:c.is_loaded?u.jsxs("div",{className:"flex gap-2",children:[u.jsxs(te,{variant:"outline",size:"sm",onClick:()=>a(c.model_name),className:"flex-1",children:[u.jsx(wc,{className:"h-4 w-4 mr-2"}),"Unload"]}),u.jsx(te,{size:"sm",className:"flex-1",asChild:!0,children:u.jsx("a",{href:"/playground",children:"Use in Playground"})})]}):u.jsx(te,{onClick:()=>s(c.model_name),disabled:i===c.model_name,className:"w-full",size:"sm",children:i===c.model_name?u.jsxs(u.Fragment,{children:[u.jsx(Gi,{className:"h-4 w-4 mr-2 animate-spin"}),"Loading..."]}):u.jsxs(u.Fragment,{children:[u.jsx(vc,{className:"h-4 w-4 mr-2"}),"Load Model"]})})})]})]},c.model_name))}),u.jsxs(Ce,{children:[u.jsx(We,{children:u.jsx(Je,{children:"Model Statistics"})}),u.jsx(Ne,{children:u.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-4",children:[u.jsxs("div",{className:"text-center",children:[u.jsx("div",{className:"text-2xl font-bold text-blue-600",children:e.length}),u.jsx("div",{className:"text-sm text-muted-foreground",children:"Available Models"})]}),u.jsxs("div",{className:"text-center",children:[u.jsx("div",{className:"text-2xl font-bold text-green-600",children:e.filter(c=>c.is_loaded).length}),u.jsx("div",{className:"text-sm text-muted-foreground",children:"Loaded Models"})]}),u.jsxs("div",{className:"text-center",children:[u.jsx("div",{className:"text-2xl font-bold text-purple-600",children:e.filter(c=>c.supports_thinking).length}),u.jsx("div",{className:"text-sm text-muted-foreground",children:"Thinking Models"})]})]})})]})]})})]})}var rs="Switch",[hP,NP]=Kr(rs),[mP,gP]=hP(rs),pv=v.forwardRef((e,t)=>{const{__scopeSwitch:n,name:r,checked:i,defaultChecked:l,required:o,disabled:s,value:a="on",onCheckedChange:c,form:d,...f}=e,[p,h]=v.useState(null),w=Pe(t,x=>h(x)),y=v.useRef(!1),S=p?d||!!p.closest("form"):!0,[m,g]=Qo({prop:i,defaultProp:l??!1,onChange:c,caller:rs});return u.jsxs(mP,{scope:n,checked:m,disabled:s,children:[u.jsx(_e.button,{type:"button",role:"switch","aria-checked":m,"aria-required":o,"data-state":yv(m),"data-disabled":s?"":void 0,disabled:s,value:a,...f,ref:w,onClick:De(e.onClick,x=>{g(N=>!N),S&&(y.current=x.isPropagationStopped(),y.current||x.stopPropagation())})}),S&&u.jsx(gv,{control:p,bubbles:!y.current,name:r,value:a,checked:m,required:o,disabled:s,form:d,style:{transform:"translateX(-100%)"}})]})});pv.displayName=rs;var hv="SwitchThumb",mv=v.forwardRef((e,t)=>{const{__scopeSwitch:n,...r}=e,i=gP(hv,n);return u.jsx(_e.span,{"data-state":yv(i.checked),"data-disabled":i.disabled?"":void 0,...r,ref:t})});mv.displayName=hv;var yP="SwitchBubbleInput",gv=v.forwardRef(({__scopeSwitch:e,control:t,checked:n,bubbles:r=!0,...i},l)=>{const o=v.useRef(null),s=Pe(o,l),a=ug(n),c=cg(t);return v.useEffect(()=>{const d=o.current;if(!d)return;const f=window.HTMLInputElement.prototype,h=Object.getOwnPropertyDescriptor(f,"checked").set;if(a!==n&&h){const w=new Event("click",{bubbles:r});h.call(d,n),d.dispatchEvent(w)}},[a,n,r]),u.jsx("input",{type:"checkbox","aria-hidden":!0,defaultChecked:n,...i,tabIndex:-1,ref:s,style:{...i.style,...c,position:"absolute",pointerEvents:"none",opacity:0,margin:0}})});gv.displayName=yP;function yv(e){return e?"checked":"unchecked"}var vv=pv,vP=mv;const Dt=v.forwardRef(({className:e,...t},n)=>u.jsx(vv,{className:re("peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",e),...t,ref:n,children:u.jsx(vP,{className:re("pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0")})}));Dt.displayName=vv.displayName;function xP(){return u.jsxs("div",{className:"min-h-screen bg-background",children:[u.jsx("div",{className:"border-b",children:u.jsx("div",{className:"flex h-14 items-center px-6",children:u.jsxs("div",{className:"flex items-center gap-2",children:[u.jsx(xc,{className:"h-5 w-5"}),u.jsx("h1",{className:"text-lg font-semibold",children:"Settings"})]})})}),u.jsx("div",{className:"flex-1 p-6",children:u.jsxs("div",{className:"max-w-4xl mx-auto space-y-6",children:[u.jsxs(Ce,{children:[u.jsx(We,{children:u.jsxs(Je,{className:"flex items-center gap-2",children:[u.jsx(Nk,{className:"h-5 w-5"}),"Server Configuration"]})}),u.jsxs(Ne,{className:"space-y-4",children:[u.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:[u.jsxs("div",{children:[u.jsx(Te,{className:"text-sm font-medium",children:"Backend URL"}),u.jsx("div",{className:"mt-1 p-2 bg-muted rounded-md text-sm font-mono",children:"http://localhost:8000"})]}),u.jsxs("div",{children:[u.jsx(Te,{className:"text-sm font-medium",children:"Frontend URL"}),u.jsx("div",{className:"mt-1 p-2 bg-muted rounded-md text-sm font-mono",children:"http://localhost:5173"})]})]}),u.jsx("div",{className:"pt-2 border-t",children:u.jsxs("div",{className:"flex items-center justify-between",children:[u.jsxs("div",{children:[u.jsx(Te,{className:"text-sm font-medium",children:"Auto-connect on startup"}),u.jsx("p",{className:"text-xs text-muted-foreground",children:"Automatically connect to backend when app starts"})]}),u.jsx(Dt,{defaultChecked:!0})]})})]})]}),u.jsxs(Ce,{children:[u.jsx(We,{children:u.jsxs(Je,{className:"flex items-center gap-2",children:[u.jsx(Ht,{className:"h-5 w-5"}),"Model Preferences"]})}),u.jsxs(Ne,{className:"space-y-4",children:[u.jsxs("div",{className:"flex items-center justify-between",children:[u.jsxs("div",{children:[u.jsx(Te,{className:"text-sm font-medium",children:"Auto-load last used model"}),u.jsx("p",{className:"text-xs text-muted-foreground",children:"Automatically load the last used model on startup"})]}),u.jsx(Dt,{})]}),u.jsxs("div",{className:"flex items-center justify-between",children:[u.jsxs("div",{children:[u.jsx(Te,{className:"text-sm font-medium",children:"Show model thinking process"}),u.jsx("p",{className:"text-xs text-muted-foreground",children:"Display thinking content for supported models"})]}),u.jsx(Dt,{defaultChecked:!0})]}),u.jsxs("div",{className:"flex items-center justify-between",children:[u.jsxs("div",{children:[u.jsx(Te,{className:"text-sm font-medium",children:"Confirm model loading"}),u.jsx("p",{className:"text-xs text-muted-foreground",children:"Show confirmation dialog before downloading models"})]}),u.jsx(Dt,{defaultChecked:!0})]})]})]}),u.jsxs(Ce,{children:[u.jsx(We,{children:u.jsxs(Je,{className:"flex items-center gap-2",children:[u.jsx(uk,{className:"h-5 w-5"}),"Interface"]})}),u.jsxs(Ne,{className:"space-y-4",children:[u.jsxs("div",{className:"flex items-center justify-between",children:[u.jsxs("div",{children:[u.jsx(Te,{className:"text-sm font-medium",children:"Dark mode"}),u.jsx("p",{className:"text-xs text-muted-foreground",children:"Use dark theme for the interface"})]}),u.jsx(Dt,{})]}),u.jsxs("div",{className:"flex items-center justify-between",children:[u.jsxs("div",{children:[u.jsx(Te,{className:"text-sm font-medium",children:"Compact mode"}),u.jsx("p",{className:"text-xs text-muted-foreground",children:"Use smaller spacing for more content"})]}),u.jsx(Dt,{})]}),u.jsxs("div",{className:"flex items-center justify-between",children:[u.jsxs("div",{children:[u.jsx(Te,{className:"text-sm font-medium",children:"Show sample prompts"}),u.jsx("p",{className:"text-xs text-muted-foreground",children:"Display quick start prompts in playground"})]}),u.jsx(Dt,{defaultChecked:!0})]})]})]}),u.jsxs(Ce,{children:[u.jsx(We,{children:u.jsxs(Je,{className:"flex items-center gap-2",children:[u.jsx(Qa,{className:"h-5 w-5"}),"Privacy & Data"]})}),u.jsxs(Ne,{className:"space-y-4",children:[u.jsxs("div",{className:"p-3 bg-green-50 border border-green-200 rounded-md",children:[u.jsxs("div",{className:"flex items-center gap-2 mb-2",children:[u.jsx(Qa,{className:"h-4 w-4 text-green-600"}),u.jsx("span",{className:"text-sm font-medium text-green-800",children:"Local Processing"})]}),u.jsx("p",{className:"text-xs text-green-700",children:"All AI processing happens locally on your machine. No data is sent to external servers."})]}),u.jsxs("div",{className:"flex items-center justify-between",children:[u.jsxs("div",{children:[u.jsx(Te,{className:"text-sm font-medium",children:"Save conversation history"}),u.jsx("p",{className:"text-xs text-muted-foreground",children:"Store conversations locally for reference"})]}),u.jsx(Dt,{})]}),u.jsxs("div",{className:"flex items-center justify-between",children:[u.jsxs("div",{children:[u.jsx(Te,{className:"text-sm font-medium",children:"Save system prompts"}),u.jsx("p",{className:"text-xs text-muted-foreground",children:"Remember custom system prompts"})]}),u.jsx(Dt,{defaultChecked:!0})]})]})]}),u.jsxs(Ce,{children:[u.jsx(We,{children:u.jsxs(Je,{className:"flex items-center gap-2",children:[u.jsx(lg,{className:"h-5 w-5"}),"System Information"]})}),u.jsxs(Ne,{className:"space-y-4",children:[u.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:[u.jsxs("div",{children:[u.jsx(Te,{className:"text-sm font-medium",children:"Platform"}),u.jsxs("div",{className:"mt-1 flex items-center gap-2",children:[u.jsx(Ge,{variant:"outline",children:"Local"}),u.jsx("span",{className:"text-sm text-muted-foreground",children:"Privacy-focused AI"})]})]}),u.jsxs("div",{children:[u.jsx(Te,{className:"text-sm font-medium",children:"Version"}),u.jsx("div",{className:"mt-1",children:u.jsx(Ge,{variant:"outline",children:"v1.0.0"})})]})]}),u.jsx("div",{className:"pt-2 border-t",children:u.jsxs("div",{className:"flex items-center justify-between",children:[u.jsxs("div",{children:[u.jsx("p",{className:"text-sm font-medium",children:"Storage Location"}),u.jsx("p",{className:"text-xs text-muted-foreground font-mono",children:"~/.edge-llm/models/"})]}),u.jsx(te,{variant:"outline",size:"sm",children:"Open Folder"})]})})]})]}),u.jsxs(Ce,{children:[u.jsx(We,{children:u.jsx(Je,{children:"Actions"})}),u.jsx(Ne,{children:u.jsxs("div",{className:"flex gap-2",children:[u.jsx(te,{variant:"outline",children:"Export Settings"}),u.jsx(te,{variant:"outline",children:"Import Settings"}),u.jsx(te,{variant:"outline",children:"Reset to Defaults"})]})})]})]})})]})}function wP(){return u.jsx(J0,{children:u.jsx(W0,{children:u.jsxs(pr,{path:"/",element:u.jsx(Gk,{}),children:[u.jsx(pr,{index:!0,element:u.jsx(Xk,{})}),u.jsx(pr,{path:"playground",element:u.jsx(fP,{})}),u.jsx(pr,{path:"models",element:u.jsx(pP,{})}),u.jsx(pr,{path:"settings",element:u.jsx(xP,{})})]})})})}qs.createRoot(document.getElementById("root")).render(u.jsx(qt.StrictMode,{children:u.jsx(wP,{})}));
diff --git a/static/assets/index-5d859784.css b/static/assets/index-5d859784.css
deleted file mode 100644
index c12f6c5f13390dbe39f43187a906c94a4a81926b..0000000000000000000000000000000000000000
--- a/static/assets/index-5d859784.css
+++ /dev/null
@@ -1 +0,0 @@
-*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}body{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity, 1))}:root{--background: 0 0% 100%;--foreground: 0 0% 3.9%;--card: 0 0% 100%;--card-foreground: 0 0% 3.9%;--popover: 0 0% 100%;--popover-foreground: 0 0% 3.9%;--primary: 0 0% 9%;--primary-foreground: 0 0% 98%;--secondary: 0 0% 96.1%;--secondary-foreground: 0 0% 9%;--muted: 0 0% 96.1%;--muted-foreground: 0 0% 45.1%;--accent: 0 0% 96.1%;--accent-foreground: 0 0% 9%;--destructive: 0 84.2% 60.2%;--destructive-foreground: 0 0% 98%;--border: 0 0% 89.8%;--input: 0 0% 89.8%;--ring: 0 0% 3.9%;--chart-1: 12 76% 61%;--chart-2: 173 58% 39%;--chart-3: 197 37% 24%;--chart-4: 43 74% 66%;--chart-5: 27 87% 67%;--radius: .5rem }.dark{--background: 0 0% 3.9%;--foreground: 0 0% 98%;--card: 0 0% 3.9%;--card-foreground: 0 0% 98%;--popover: 0 0% 3.9%;--popover-foreground: 0 0% 98%;--primary: 0 0% 98%;--primary-foreground: 0 0% 9%;--secondary: 0 0% 14.9%;--secondary-foreground: 0 0% 98%;--muted: 0 0% 14.9%;--muted-foreground: 0 0% 63.9%;--accent: 0 0% 14.9%;--accent-foreground: 0 0% 98%;--destructive: 0 62.8% 30.6%;--destructive-foreground: 0 0% 98%;--border: 0 0% 14.9%;--input: 0 0% 14.9%;--ring: 0 0% 83.1%;--chart-1: 220 70% 50%;--chart-2: 160 60% 45%;--chart-3: 30 80% 55%;--chart-4: 280 65% 60%;--chart-5: 340 75% 55% }*{border-color:hsl(var(--border))}body{background-color:hsl(var(--background));color:hsl(var(--foreground))}.prose{color:var(--tw-prose-body);max-width:65ch}.prose :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-lead);font-size:1.25em;line-height:1.6;margin-top:1.2em;margin-bottom:1.2em}.prose :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-links);text-decoration:underline;font-weight:500}.prose :where(strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-bold);font-weight:600}.prose :where(a strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.prose :where(ol[type=A]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=A s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=I]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type=I s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type="1"]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal}.prose :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:disc;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{font-weight:400;color:var(--tw-prose-counters)}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-bullets)}.prose :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.25em}.prose :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--tw-prose-hr);border-top-width:1px;margin-top:3em;margin-bottom:3em}.prose :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-style:italic;color:var(--tw-prose-quotes);border-inline-start-width:.25rem;border-inline-start-color:var(--tw-prose-quote-borders);quotes:"โ""โ""โ""โ";margin-top:1.6em;margin-bottom:1.6em;padding-inline-start:1em}.prose :where(blockquote p:first-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:open-quote}.prose :where(blockquote p:last-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:close-quote}.prose :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:800;font-size:2.25em;margin-top:0;margin-bottom:.8888889em;line-height:1.1111111}.prose :where(h1 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:900;color:inherit}.prose :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:700;font-size:1.5em;margin-top:2em;margin-bottom:1em;line-height:1.3333333}.prose :where(h2 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:800;color:inherit}.prose :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;font-size:1.25em;margin-top:1.6em;margin-bottom:.6em;line-height:1.6}.prose :where(h3 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.5em;margin-bottom:.5em;line-height:1.5}.prose :where(h4 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){display:block;margin-top:2em;margin-bottom:2em}.prose :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-family:inherit;color:var(--tw-prose-kbd);box-shadow:0 0 0 1px rgb(var(--tw-prose-kbd-shadows) / 10%),0 3px 0 rgb(var(--tw-prose-kbd-shadows) / 10%);font-size:.875em;border-radius:.3125rem;padding-top:.1875em;padding-inline-end:.375em;padding-bottom:.1875em;padding-inline-start:.375em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-code);font-weight:600;font-size:.875em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:"`"}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:"`"}.prose :where(a code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h1 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.875em}.prose :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.9em}.prose :where(h4 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-pre-code);background-color:var(--tw-prose-pre-bg);overflow-x:auto;font-weight:400;font-size:.875em;line-height:1.7142857;margin-top:1.7142857em;margin-bottom:1.7142857em;border-radius:.375rem;padding-top:.8571429em;padding-inline-end:1.1428571em;padding-bottom:.8571429em;padding-inline-start:1.1428571em}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)){background-color:transparent;border-width:0;border-radius:0;padding:0;font-weight:inherit;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:none}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:none}.prose :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){width:100%;table-layout:auto;margin-top:2em;margin-bottom:2em;font-size:.875em;line-height:1.7142857}.prose :where(thead):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-th-borders)}.prose :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;vertical-align:bottom;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.prose :where(tbody tr):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-td-borders)}.prose :where(tbody tr:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:0}.prose :where(tbody td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:baseline}.prose :where(tfoot):not(:where([class~=not-prose],[class~=not-prose] *)){border-top-width:1px;border-top-color:var(--tw-prose-th-borders)}.prose :where(tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:top}.prose :where(th,td):not(:where([class~=not-prose],[class~=not-prose] *)){text-align:start}.prose :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-captions);font-size:.875em;line-height:1.4285714;margin-top:.8571429em}.prose{--tw-prose-body: #374151;--tw-prose-headings: #111827;--tw-prose-lead: #4b5563;--tw-prose-links: #111827;--tw-prose-bold: #111827;--tw-prose-counters: #6b7280;--tw-prose-bullets: #d1d5db;--tw-prose-hr: #e5e7eb;--tw-prose-quotes: #111827;--tw-prose-quote-borders: #e5e7eb;--tw-prose-captions: #6b7280;--tw-prose-kbd: #111827;--tw-prose-kbd-shadows: 17 24 39;--tw-prose-code: #111827;--tw-prose-pre-code: #e5e7eb;--tw-prose-pre-bg: #1f2937;--tw-prose-th-borders: #d1d5db;--tw-prose-td-borders: #e5e7eb;--tw-prose-invert-body: #d1d5db;--tw-prose-invert-headings: #fff;--tw-prose-invert-lead: #9ca3af;--tw-prose-invert-links: #fff;--tw-prose-invert-bold: #fff;--tw-prose-invert-counters: #9ca3af;--tw-prose-invert-bullets: #4b5563;--tw-prose-invert-hr: #374151;--tw-prose-invert-quotes: #f3f4f6;--tw-prose-invert-quote-borders: #374151;--tw-prose-invert-captions: #9ca3af;--tw-prose-invert-kbd: #fff;--tw-prose-invert-kbd-shadows: 255 255 255;--tw-prose-invert-code: #fff;--tw-prose-invert-pre-code: #d1d5db;--tw-prose-invert-pre-bg: rgb(0 0 0 / 50%);--tw-prose-invert-th-borders: #4b5563;--tw-prose-invert-td-borders: #374151;font-size:1rem;line-height:1.75}.prose :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;margin-bottom:.5em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(.prose>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(.prose>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(.prose>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;padding-inline-start:1.625em}.prose :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.5714286em;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.prose :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(.prose>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(.prose>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.prose-sm{font-size:.875rem;line-height:1.7142857}.prose-sm :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em}.prose-sm :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.2857143em;line-height:1.5555556;margin-top:.8888889em;margin-bottom:.8888889em}.prose-sm :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em;margin-bottom:1.3333333em;padding-inline-start:1.1111111em}.prose-sm :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:2.1428571em;margin-top:0;margin-bottom:.8em;line-height:1.2}.prose-sm :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.4285714em;margin-top:1.6em;margin-bottom:.8em;line-height:1.4}.prose-sm :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.2857143em;margin-top:1.5555556em;margin-bottom:.4444444em;line-height:1.5555556}.prose-sm :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.4285714em;margin-bottom:.5714286em;line-height:1.4285714}.prose-sm :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose-sm :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;border-radius:.3125rem;padding-top:.1428571em;padding-inline-end:.3571429em;padding-bottom:.1428571em;padding-inline-start:.3571429em}.prose-sm :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em}.prose-sm :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.9em}.prose-sm :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8888889em}.prose-sm :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;line-height:1.6666667;margin-top:1.6666667em;margin-bottom:1.6666667em;border-radius:.25rem;padding-top:.6666667em;padding-inline-end:1em;padding-bottom:.6666667em;padding-inline-start:1em}.prose-sm :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em;padding-inline-start:1.5714286em}.prose-sm :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em;padding-inline-start:1.5714286em}.prose-sm :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.2857143em;margin-bottom:.2857143em}.prose-sm :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.4285714em}.prose-sm :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.4285714em}.prose-sm :where(.prose-sm>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5714286em;margin-bottom:.5714286em}.prose-sm :where(.prose-sm>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(.prose-sm>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.1428571em}.prose-sm :where(.prose-sm>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(.prose-sm>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.1428571em}.prose-sm :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5714286em;margin-bottom:.5714286em}.prose-sm :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em}.prose-sm :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.2857143em;padding-inline-start:1.5714286em}.prose-sm :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2.8571429em;margin-bottom:2.8571429em}.prose-sm :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;line-height:1.5}.prose-sm :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:1em;padding-bottom:.6666667em;padding-inline-start:1em}.prose-sm :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose-sm :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose-sm :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.6666667em;padding-inline-end:1em;padding-bottom:.6666667em;padding-inline-start:1em}.prose-sm :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose-sm :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose-sm :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose-sm :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;line-height:1.3333333;margin-top:.6666667em}.prose-sm :where(.prose-sm>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(.prose-sm>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.pointer-events-none{pointer-events:none}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.inset-y-0{top:0;bottom:0}.bottom-2{bottom:.5rem}.left-0{left:0}.left-4{left:1rem}.left-\[50\%\]{left:50%}.right-2{right:.5rem}.top-2{top:.5rem}.top-4{top:1rem}.top-\[50\%\]{top:50%}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.-mx-1{margin-left:-.25rem;margin-right:-.25rem}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-4{margin-top:1rem;margin-bottom:1rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.ml-2{margin-left:.5rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.block{display:block}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-14{height:3.5rem}.h-16{height:4rem}.h-2{height:.5rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-32{height:8rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-64{height:16rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-\[var\(--radix-select-trigger-height\)\]{height:var(--radix-select-trigger-height)}.h-auto{height:auto}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-\[--radix-select-content-available-height\]{max-height:var(--radix-select-content-available-height)}.max-h-\[150px\]{max-height:150px}.min-h-\[40px\]{min-height:40px}.min-h-\[60px\]{min-height:60px}.min-h-\[80px\]{min-height:80px}.min-h-screen{min-height:100vh}.w-10{width:2.5rem}.w-2{width:.5rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-64{width:16rem}.w-8{width:2rem}.w-80{width:20rem}.w-9{width:2.25rem}.w-full{width:100%}.min-w-0{min-width:0px}.min-w-\[80px\]{min-width:80px}.min-w-\[8rem\]{min-width:8rem}.min-w-\[var\(--radix-select-trigger-width\)\]{min-width:var(--radix-select-trigger-width)}.max-w-2xl{max-width:42rem}.max-w-4xl{max-width:56rem}.max-w-6xl{max-width:72rem}.max-w-\[80\%\]{max-width:80%}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.max-w-none{max-width:none}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.grow{flex-grow:1}.origin-\[--radix-select-content-transform-origin\]{transform-origin:var(--radix-select-content-transform-origin)}.-translate-x-full{--tw-translate-x: -100%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-\[-50\%\]{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-\[-50\%\]{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-180{--tw-rotate: 180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize-none{resize:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-row-reverse{flex-direction:row-reverse}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.375rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(2rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-x-hidden{overflow-x:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-blue-200{--tw-border-opacity: 1;border-color:rgb(191 219 254 / var(--tw-border-opacity, 1))}.border-blue-500{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.border-green-200{--tw-border-opacity: 1;border-color:rgb(187 247 208 / var(--tw-border-opacity, 1))}.border-green-600{--tw-border-opacity: 1;border-color:rgb(22 163 74 / var(--tw-border-opacity, 1))}.border-input{border-color:hsl(var(--input))}.border-primary{border-color:hsl(var(--primary))}.border-primary\/50{border-color:hsl(var(--primary) / .5)}.border-transparent{border-color:transparent}.bg-accent{background-color:hsl(var(--accent))}.bg-background{background-color:hsl(var(--background))}.bg-background\/50{background-color:hsl(var(--background) / .5)}.bg-background\/95{background-color:hsl(var(--background) / .95)}.bg-black\/50{background-color:#00000080}.bg-black\/80{background-color:#000c}.bg-blue-100{--tw-bg-opacity: 1;background-color:rgb(219 234 254 / var(--tw-bg-opacity, 1))}.bg-blue-50{--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity, 1))}.bg-blue-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.bg-card{background-color:hsl(var(--card))}.bg-destructive{background-color:hsl(var(--destructive))}.bg-green-50{--tw-bg-opacity: 1;background-color:rgb(240 253 244 / var(--tw-bg-opacity, 1))}.bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-muted{background-color:hsl(var(--muted))}.bg-muted\/30{background-color:hsl(var(--muted) / .3)}.bg-muted\/50{background-color:hsl(var(--muted) / .5)}.bg-popover{background-color:hsl(var(--popover))}.bg-primary{background-color:hsl(var(--primary))}.bg-primary\/20{background-color:hsl(var(--primary) / .2)}.bg-secondary{background-color:hsl(var(--secondary))}.bg-transparent{background-color:transparent}.bg-yellow-500{--tw-bg-opacity: 1;background-color:rgb(234 179 8 / var(--tw-bg-opacity, 1))}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.from-blue-500{--tw-gradient-from: #3b82f6 var(--tw-gradient-from-position);--tw-gradient-to: rgb(59 130 246 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.to-purple-600{--tw-gradient-to: #9333ea var(--tw-gradient-to-position)}.p-0{padding:0}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-4{padding-top:1rem;padding-bottom:1rem}.pl-2{padding-left:.5rem}.pl-8{padding-left:2rem}.pr-12{padding-right:3rem}.pr-8{padding-right:2rem}.pt-0{padding-top:0}.pt-2{padding-top:.5rem}.pt-4{padding-top:1rem}.pt-6{padding-top:1.5rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.italic{font-style:italic}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.text-accent-foreground{color:hsl(var(--accent-foreground))}.text-blue-500{--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity, 1))}.text-blue-600{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.text-blue-700{--tw-text-opacity: 1;color:rgb(29 78 216 / var(--tw-text-opacity, 1))}.text-blue-900{--tw-text-opacity: 1;color:rgb(30 58 138 / var(--tw-text-opacity, 1))}.text-card-foreground{color:hsl(var(--card-foreground))}.text-destructive{color:hsl(var(--destructive))}.text-destructive-foreground{color:hsl(var(--destructive-foreground))}.text-foreground{color:hsl(var(--foreground))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-green-600{--tw-text-opacity: 1;color:rgb(22 163 74 / var(--tw-text-opacity, 1))}.text-green-700{--tw-text-opacity: 1;color:rgb(21 128 61 / var(--tw-text-opacity, 1))}.text-green-800{--tw-text-opacity: 1;color:rgb(22 101 52 / var(--tw-text-opacity, 1))}.text-muted-foreground{color:hsl(var(--muted-foreground))}.text-popover-foreground{color:hsl(var(--popover-foreground))}.text-primary{color:hsl(var(--primary))}.text-primary-foreground{color:hsl(var(--primary-foreground))}.text-purple-500{--tw-text-opacity: 1;color:rgb(168 85 247 / var(--tw-text-opacity, 1))}.text-purple-600{--tw-text-opacity: 1;color:rgb(147 51 234 / var(--tw-text-opacity, 1))}.text-secondary-foreground{color:hsl(var(--secondary-foreground))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-yellow-500{--tw-text-opacity: 1;color:rgb(234 179 8 / var(--tw-text-opacity, 1))}.underline-offset-4{text-underline-offset:4px}.opacity-0{opacity:0}.opacity-50{opacity:.5}.shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.ring-0{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-2{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-blue-500{--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.ring-offset-background{--tw-ring-offset-color: hsl(var(--background))}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur{--tw-backdrop-blur: blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-filter{-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}@keyframes enter{0%{opacity:var(--tw-enter-opacity, 1);transform:translate3d(var(--tw-enter-translate-x, 0),var(--tw-enter-translate-y, 0),0) scale3d(var(--tw-enter-scale, 1),var(--tw-enter-scale, 1),var(--tw-enter-scale, 1)) rotate(var(--tw-enter-rotate, 0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity, 1);transform:translate3d(var(--tw-exit-translate-x, 0),var(--tw-exit-translate-y, 0),0) scale3d(var(--tw-exit-scale, 1),var(--tw-exit-scale, 1),var(--tw-exit-scale, 1)) rotate(var(--tw-exit-rotate, 0))}}.duration-200{animation-duration:.2s}.duration-300{animation-duration:.3s}.ease-in-out{animation-timing-function:cubic-bezier(.4,0,.2,1)}.running{animation-play-state:running}.line-clamp-2{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.dark\:prose-invert:is(.dark *){--tw-prose-body: var(--tw-prose-invert-body);--tw-prose-headings: var(--tw-prose-invert-headings);--tw-prose-lead: var(--tw-prose-invert-lead);--tw-prose-links: var(--tw-prose-invert-links);--tw-prose-bold: var(--tw-prose-invert-bold);--tw-prose-counters: var(--tw-prose-invert-counters);--tw-prose-bullets: var(--tw-prose-invert-bullets);--tw-prose-hr: var(--tw-prose-invert-hr);--tw-prose-quotes: var(--tw-prose-invert-quotes);--tw-prose-quote-borders: var(--tw-prose-invert-quote-borders);--tw-prose-captions: var(--tw-prose-invert-captions);--tw-prose-kbd: var(--tw-prose-invert-kbd);--tw-prose-kbd-shadows: var(--tw-prose-invert-kbd-shadows);--tw-prose-code: var(--tw-prose-invert-code);--tw-prose-pre-code: var(--tw-prose-invert-pre-code);--tw-prose-pre-bg: var(--tw-prose-invert-pre-bg);--tw-prose-th-borders: var(--tw-prose-invert-th-borders);--tw-prose-td-borders: var(--tw-prose-invert-td-borders)}.placeholder\:text-muted-foreground::-moz-placeholder{color:hsl(var(--muted-foreground))}.placeholder\:text-muted-foreground::placeholder{color:hsl(var(--muted-foreground))}.last\:mb-0:last-child{margin-bottom:0}.hover\:bg-accent:hover{background-color:hsl(var(--accent))}.hover\:bg-accent\/50:hover{background-color:hsl(var(--accent) / .5)}.hover\:bg-blue-600:hover{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.hover\:bg-destructive\/80:hover{background-color:hsl(var(--destructive) / .8)}.hover\:bg-destructive\/90:hover{background-color:hsl(var(--destructive) / .9)}.hover\:bg-primary\/80:hover{background-color:hsl(var(--primary) / .8)}.hover\:bg-primary\/90:hover{background-color:hsl(var(--primary) / .9)}.hover\:bg-secondary\/80:hover{background-color:hsl(var(--secondary) / .8)}.hover\:text-accent-foreground:hover{color:hsl(var(--accent-foreground))}.hover\:text-destructive:hover{color:hsl(var(--destructive))}.hover\:text-foreground:hover{color:hsl(var(--foreground))}.hover\:underline:hover{text-decoration-line:underline}.focus\:border-blue-500:focus{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.focus\:bg-accent:focus{background-color:hsl(var(--accent))}.focus\:text-accent-foreground:focus{color:hsl(var(--accent-foreground))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-1:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-blue-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.focus\:ring-ring:focus{--tw-ring-color: hsl(var(--ring))}.focus\:ring-offset-2:focus{--tw-ring-offset-width: 2px}.focus-visible\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\:ring-1:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color: hsl(var(--ring))}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width: 2px}.focus-visible\:ring-offset-background:focus-visible{--tw-ring-offset-color: hsl(var(--background))}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:text-foreground{color:hsl(var(--foreground))}.group:hover .group-hover\:opacity-100{opacity:1}.peer:disabled~.peer-disabled\:cursor-not-allowed{cursor:not-allowed}.peer:disabled~.peer-disabled\:opacity-70{opacity:.7}.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[side\=bottom\]\:translate-y-1[data-side=bottom]{--tw-translate-y: .25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=left\]\:-translate-x-1[data-side=left]{--tw-translate-x: -.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=right\]\:translate-x-1[data-side=right]{--tw-translate-x: .25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=top\]\:-translate-y-1[data-side=top]{--tw-translate-y: -.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[state\=checked\]\:translate-x-4[data-state=checked]{--tw-translate-x: 1rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[state\=unchecked\]\:translate-x-0[data-state=unchecked]{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[state\=checked\]\:bg-primary[data-state=checked]{background-color:hsl(var(--primary))}.data-\[state\=unchecked\]\:bg-input[data-state=unchecked]{background-color:hsl(var(--input))}.data-\[placeholder\]\:text-muted-foreground[data-placeholder]{color:hsl(var(--muted-foreground))}.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}.data-\[state\=open\]\:animate-in[data-state=open]{animation-name:enter;animation-duration:.15s;--tw-enter-opacity: initial;--tw-enter-scale: initial;--tw-enter-rotate: initial;--tw-enter-translate-x: initial;--tw-enter-translate-y: initial}.data-\[state\=closed\]\:animate-out[data-state=closed]{animation-name:exit;animation-duration:.15s;--tw-exit-opacity: initial;--tw-exit-scale: initial;--tw-exit-rotate: initial;--tw-exit-translate-x: initial;--tw-exit-translate-y: initial}.data-\[state\=closed\]\:fade-out-0[data-state=closed]{--tw-exit-opacity: 0}.data-\[state\=open\]\:fade-in-0[data-state=open]{--tw-enter-opacity: 0}.data-\[state\=closed\]\:zoom-out-95[data-state=closed]{--tw-exit-scale: .95}.data-\[state\=open\]\:zoom-in-95[data-state=open]{--tw-enter-scale: .95}.data-\[side\=bottom\]\:slide-in-from-top-2[data-side=bottom]{--tw-enter-translate-y: -.5rem}.data-\[side\=left\]\:slide-in-from-right-2[data-side=left]{--tw-enter-translate-x: .5rem}.data-\[side\=right\]\:slide-in-from-left-2[data-side=right]{--tw-enter-translate-x: -.5rem}.data-\[side\=top\]\:slide-in-from-bottom-2[data-side=top]{--tw-enter-translate-y: .5rem}.data-\[state\=closed\]\:slide-out-to-left-1\/2[data-state=closed]{--tw-exit-translate-x: -50%}.data-\[state\=closed\]\:slide-out-to-top-\[48\%\][data-state=closed]{--tw-exit-translate-y: -48%}.data-\[state\=open\]\:slide-in-from-left-1\/2[data-state=open]{--tw-enter-translate-x: -50%}.data-\[state\=open\]\:slide-in-from-top-\[48\%\][data-state=open]{--tw-enter-translate-y: -48%}.prose-headings\:font-semibold :is(:where(h1,h2,h3,h4,h5,h6,th):not(:where([class~=not-prose],[class~=not-prose] *))){font-weight:600}.prose-headings\:text-foreground :is(:where(h1,h2,h3,h4,h5,h6,th):not(:where([class~=not-prose],[class~=not-prose] *))){color:hsl(var(--foreground))}.prose-p\:leading-relaxed :is(:where(p):not(:where([class~=not-prose],[class~=not-prose] *))){line-height:1.625}.prose-p\:text-foreground :is(:where(p):not(:where([class~=not-prose],[class~=not-prose] *))){color:hsl(var(--foreground))}.prose-blockquote\:border-l-muted-foreground :is(:where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *))){border-left-color:hsl(var(--muted-foreground))}.prose-blockquote\:text-muted-foreground :is(:where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *))){color:hsl(var(--muted-foreground))}.prose-strong\:font-semibold :is(:where(strong):not(:where([class~=not-prose],[class~=not-prose] *))){font-weight:600}.prose-strong\:text-foreground :is(:where(strong):not(:where([class~=not-prose],[class~=not-prose] *))){color:hsl(var(--foreground))}.prose-em\:text-muted-foreground :is(:where(em):not(:where([class~=not-prose],[class~=not-prose] *))){color:hsl(var(--muted-foreground))}.prose-code\:rounded :is(:where(code):not(:where([class~=not-prose],[class~=not-prose] *))){border-radius:.25rem}.prose-code\:bg-muted :is(:where(code):not(:where([class~=not-prose],[class~=not-prose] *))){background-color:hsl(var(--muted))}.prose-code\:px-1 :is(:where(code):not(:where([class~=not-prose],[class~=not-prose] *))){padding-left:.25rem;padding-right:.25rem}.prose-code\:py-0\.5 :is(:where(code):not(:where([class~=not-prose],[class~=not-prose] *))){padding-top:.125rem;padding-bottom:.125rem}.prose-code\:text-sm :is(:where(code):not(:where([class~=not-prose],[class~=not-prose] *))){font-size:.875rem;line-height:1.25rem}.prose-pre\:rounded-md :is(:where(pre):not(:where([class~=not-prose],[class~=not-prose] *))){border-radius:calc(var(--radius) - 2px)}.prose-pre\:border :is(:where(pre):not(:where([class~=not-prose],[class~=not-prose] *))){border-width:1px}.prose-pre\:bg-muted :is(:where(pre):not(:where([class~=not-prose],[class~=not-prose] *))){background-color:hsl(var(--muted))}.prose-ol\:text-foreground :is(:where(ol):not(:where([class~=not-prose],[class~=not-prose] *))){color:hsl(var(--foreground))}.prose-ul\:text-foreground :is(:where(ul):not(:where([class~=not-prose],[class~=not-prose] *))){color:hsl(var(--foreground))}.prose-li\:text-foreground :is(:where(li):not(:where([class~=not-prose],[class~=not-prose] *))){color:hsl(var(--foreground))}@supports (backdrop-filter: var(--tw)){.supports-\[backdrop-filter\]\:bg-background\/60{background-color:hsl(var(--background) / .6)}}@media (min-width: 640px){.sm\:mt-0{margin-top:0}.sm\:inline{display:inline}.sm\:hidden{display:none}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:justify-end{justify-content:flex-end}.sm\:space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.sm\:rounded-lg{border-radius:var(--radius)}.sm\:text-left{text-align:left}}@media (min-width: 768px){.md\:w-auto{width:auto}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width: 1024px){.lg\:static{position:static}.lg\:inset-0{top:0;right:0;bottom:0;left:0}.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media (min-width: 1280px){.xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}.\[\&\>span\]\:line-clamp-1>span{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1}.\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.\[\&_svg\]\:size-4 svg{width:1rem;height:1rem}.\[\&_svg\]\:shrink-0 svg{flex-shrink:0}
diff --git a/static/index.html b/static/index.html
index 52d02e18322b5cb155df781fa9420938119ea080..6394fe8fe3fc4878cf0ecd59b970d7a2757002e0 100644
--- a/static/index.html
+++ b/static/index.html
@@ -5,8 +5,8 @@
Edge LLM Platform
-
-
+
+