diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..5e3fedd --- /dev/null +++ b/.dockerignore @@ -0,0 +1,64 @@ +# Git files +.git +.gitignore +.gitattributes + +# Python cache +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +*.egg-info/ +dist/ +build/ +*.egg + +# Virtual environments +.venv/ +venv/ +ENV/ +env/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS files +.DS_Store +Thumbs.db + +# Workspace and cache (will be mounted as volumes) +workspace/ +cache/ + +# Documentation and examples +README.md +LICENSE +asset/ +examples/ +evaluation/ + +# Docker files themselves +Dockerfile +docker-compose.yaml +.dockerignore + +# Logs +*.log +logs/ + +# Test files +tests/ +*.test +.pytest_cache/ +.coverage +htmlcov/ + +# Temporary files +tmp/ +temp/ +*.tmp diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..d97248c --- /dev/null +++ b/.env.example @@ -0,0 +1,61 @@ +# InfantAgent Environment Configuration +# Copy this file to .env and fill in your values + +# Required: Anthropic API Key +# Get your key from: https://console.anthropic.com/ +ANTHROPIC_API_KEY=your_anthropic_api_key_here + +# Optional: Hugging Face Token (for downloading UI-TARS model) +# Get your token from: https://huggingface.co/settings/tokens +# Required if the model is gated or for faster downloads +HF_API_KEY=your_huggingface_token_here + +# Optional: Hugging Face cache directory +# HF_HOME=~/.cache/huggingface + +# Optional: GPU Configuration +# Specify which CUDA devices to use (comma-separated list) +# Default GPU allocation: +# - GPU 0: Computer container (display rendering) +# - GPU 2,3: vLLM server (UI-TARS model with tensor parallelism) +# - InfantAgent server: Runs on CPU (no GPU needed) +# Note: Adjust based on your available GPUs +# CUDA_VISIBLE_DEVICES=0,2,3 + +# Optional: Computer Container Configuration +# NVIDIA driver type: Tesla, GeForce, or other +# NVIDIA_DRIVER=Tesla +# GPU device(s) visible to the computer container +# NVIDIA_VISIBLE_DEVICES=0 +# Specific GPU device ID(s) for docker deploy (e.g., '0' or '0,1') +# NVIDIA_DEVICE_ID=0 +# Render type: gpu or software +# RENDER_TYPE=gpu +# User account to create in the container: infant or root +# CREATE_USER_ACCOUNT=infant +# Workspace mount path on host (default: ./workspace) +# WORKSPACE_MOUNT_PATH=./workspace +# Cache directory on host (default: ./cache) +# CACHE_DIR=./cache + +# Optional: Other API Keys (if using different models) +# OPENAI_API_KEY=your_openai_key_here +# GOOGLE_API_KEY=your_google_key_here + +# Optional: Custom Model Configuration +# Uncomment and modify if you want to use different models +# MODEL=claude-sonnet-4-6 +# MODEL_OSS=ByteDance-Seed/UI-TARS-1.5-7B + +# Optional: Agent Configuration +# MAX_ITERATIONS=100 +# MAX_BUDGET_PER_TASK=20 +# DEBUG=false + +# Optional: Network Configuration +# AGENT_PORT=8000 +# GUI_PORT=4443 # Guacamole web interface port (maps to container's 8080) +# SSH_PORT=58673 # SSH port for computer container (maps to container's 22) + +# Optional: Timezone +# TZ=America/New_York diff --git a/DOCKER_SETUP.md b/DOCKER_SETUP.md new file mode 100644 index 0000000..b16b457 --- /dev/null +++ b/DOCKER_SETUP.md @@ -0,0 +1,444 @@ +# Docker Setup Guide for InfantAgent + +This guide explains how to run InfantAgent using Docker and Docker Compose. + +## Prerequisites + +- Docker Engine 20.10+ with Docker Compose V2 +- NVIDIA Docker runtime (nvidia-docker2) - only required for GPU-accelerated components +- NVIDIA GPU with CUDA support (minimum 3 GPUs recommended for full setup) + - 2 GPUs for vLLM server (UI-TARS model with tensor parallelism) + - 1 GPU for computer container (display rendering) + - InfantAgent server runs on CPU (no GPU needed) + - Can work with fewer GPUs by adjusting configuration +- At least 32GB RAM (64GB recommended) +- 100GB free disk space (for model downloads and workspace) + +## Quick Start + +### 1. Clone the Repository + +```bash +git clone https://github.com/bin123apple/InfantAgent.git +cd InfantAgent +``` + +### 2. Set Environment Variables + +Create a `.env` file in the project root: + +```bash +# Copy the example file +cp .env.example .env + +# Edit with your values +nano .env +``` + +Required variables: +```bash +# Required: Your Anthropic API key +ANTHROPIC_API_KEY=your_api_key_here + +# Optional but recommended: Hugging Face token for UI-TARS model +HUGGING_FACE_HUB_TOKEN=your_huggingface_token_here + +# Optional: Specify which GPUs to use (default: 0,1,2,3) +CUDA_VISIBLE_DEVICES=0,1,2,3 +``` + +### 3. Build and Start Services + +```bash +# Build both containers +docker-compose build + +# Start all services +docker-compose up -d + +# View logs +docker-compose logs -f +``` + +### 4. Access the Services + +- **InfantAgent API**: http://localhost:8000 +- **vLLM Server API**: http://localhost:8001/v1 (OpenAI-compatible API) +- **Guacamole Desktop**: http://localhost:4443/guacamole/#/client/GNOME + - Username: `web` + - Password: `web` + - Desktop credentials: `infant` / `123` + +## Architecture + +The Docker Compose setup includes three main services: + +### 1. vllm-server (OSS Model Server) +- Hosts the UI-TARS-1.5-7B model using vLLM +- OpenAI-compatible API endpoint +- Tensor parallelism across 2 GPUs for faster inference +- Exposed on port 8001 +- Automatic model download from Hugging Face + +### 2. infant-agent (Main Server) +- Runs the FastAPI backend server +- Handles agent logic and orchestration +- Connects to vLLM server for OSS model inference +- Runs on CPU (no GPU required) +- Exposed on port 8000 + +### 3. computer-container (Desktop Environment) +- Ubuntu 22.04 with GNOME desktop +- Accessible via Guacamole web interface +- SSH access on port 22222 +- Shared workspace with agent server + +## Configuration + +### GPU Configuration + +By default, the setup uses 3 GPUs: +- **GPU 2,3**: vLLM server (UI-TARS-1.5-7B model with tensor parallelism) +- **GPU 0**: Computer container (display rendering) +- **InfantAgent server**: Runs on CPU (no GPU needed) + +**For systems with fewer GPUs:** + +If you only have 2 GPUs, modify `docker-compose.yaml`: + +```yaml +# vLLM server - use single GPU +vllm-server: + environment: + - CUDA_VISIBLE_DEVICES=1 + command: > + --model ByteDance-Seed/UI-TARS-1.5-7B + --tensor-parallel-size 1 # Changed from 2 + deploy: + resources: + reservations: + devices: + - driver: nvidia + device_ids: ['1'] # Single GPU + +# Computer - use first GPU +computer-container: + deploy: + resources: + reservations: + devices: + - driver: nvidia + device_ids: ['0'] + +# InfantAgent - no GPU needed (runs on CPU) +``` + +If you only have 1 GPU (vLLM only, no desktop GUI): +```yaml +# vLLM server - use the only GPU +vllm-server: + environment: + - CUDA_VISIBLE_DEVICES=0 + deploy: + resources: + reservations: + devices: + - driver: nvidia + device_ids: ['0'] + +# Computer container - disable or run without GPU +computer-container: + # Comment out the deploy.resources section to run without GPU + # Or don't start it: docker-compose up -d vllm-server infant-agent +``` + +### Workspace Volumes + +The `./workspace` directory is shared between: +- Host machine: `./workspace` +- Agent server: `/app/workspace` +- Computer container: `/workspace` + +Files created in any location are accessible from all others. + +### Config Updates + +To update the configuration without rebuilding: + +1. Edit `config.toml` +2. Restart the agent service: + ```bash + docker-compose restart infant-agent + ``` + +## Common Commands + +```bash +# Start services +docker-compose up -d + +# Stop services +docker-compose down + +# View logs +docker-compose logs -f infant-agent +docker-compose logs -f vllm-server +docker-compose logs -f computer-container + +# Restart a specific service +docker-compose restart infant-agent + +# Check vLLM server status and model loading progress +docker-compose logs -f vllm-server | grep -i "model\|loading\|ready" + +# Rebuild after code changes +docker-compose build --no-cache infant-agent +docker-compose up -d + +# Access agent container shell +docker exec -it infant-agent-server bash + +# Access computer container shell +docker exec -it infant-computer bash + +# Monitor resource usage +docker stats +``` + +## Troubleshooting + +### GPU Not Detected + +Verify NVIDIA Docker runtime is installed: + +```bash +docker run --rm --gpus all nvidia/cuda:12.1.0-base-ubuntu22.04 nvidia-smi +``` + +If this fails, install nvidia-docker2: + +```bash +# Ubuntu/Debian +distribution=$(. /etc/os-release;echo $ID$VERSION_ID) +curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add - +curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list +sudo apt-get update && sudo apt-get install -y nvidia-docker2 +sudo systemctl restart docker +``` + +### Container Won't Start + +Check logs for errors: + +```bash +docker-compose logs computer-container +``` + +Common issues: +- Port conflicts: Change ports in `docker-compose.yaml` +- Insufficient memory: Increase Docker memory limit +- Missing GPU: Check `nvidia-smi` output + +### Connection Refused to Guacamole + +The computer container takes 1-2 minutes to fully initialize: + +1. Check container status: `docker-compose ps` +2. Wait for health check to pass +3. Try accessing: http://localhost:4443/guacamole/ + +### vLLM Server Issues + +**Model fails to download:** +- Check Hugging Face token is set: `echo $HUGGING_FACE_HUB_TOKEN` +- Verify internet connection +- Check disk space: `df -h` +- View download progress: `docker-compose logs -f vllm-server` + +**Out of memory errors:** +- Reduce `--gpu-memory-utilization` from 0.9 to 0.7 +- Use single GPU instead of tensor parallelism +- Reduce `--max-model-len` from 8192 to 4096 + +**vLLM server not responding:** +```bash +# Check if model is loaded +curl http://localhost:8001/v1/models + +# Test inference +curl http://localhost:8001/v1/completions \ + -H "Content-Type: application/json" \ + -d '{ + "model": "ByteDance-Seed/UI-TARS-1.5-7B", + "prompt": "Hello, how are you?", + "max_tokens": 50 + }' +``` + +The vLLM server takes 2-5 minutes to download and load the model on first startup. + +### Build Failures + +Clear Docker cache and rebuild: + +```bash +docker-compose down -v +docker system prune -a +docker-compose build --no-cache +docker-compose up -d +``` + +## Production Deployment + +For production use, consider: + +1. **Use docker-compose.prod.yaml** with: + - Resource limits + - Logging configuration + - Secrets management + - Network security + +2. **Enable HTTPS** for Guacamole: + - Use a reverse proxy (nginx/traefik) + - Configure SSL certificates + +3. **Persistent Storage**: + - Use named volumes instead of bind mounts + - Regular backup of workspace data + +4. **Monitoring**: + - Add Prometheus exporters + - Configure health check endpoints + - Set up alerting + +## Advanced Configuration + +### Custom Build Args + +Build with custom Python version: + +```bash +docker-compose build --build-arg PYTHON_VERSION=3.11 infant-agent +``` + +### Multi-GPU Setup + +For systems with 4+ GPUs, distribute workload: + +```yaml +infant-agent: + environment: + - CUDA_VISIBLE_DEVICES=0,1,2,3 + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: 4 + capabilities: [gpu] +``` + +### Using Different OSS Models + +To use a different model with vLLM, modify the `docker-compose.yaml`: + +```yaml +vllm-server: + command: > + --model meta-llama/Llama-3-8B # Change model here + --host 0.0.0.0 + --port 8000 + --tensor-parallel-size 1 # Adjust based on model size + --max-model-len 4096 + --gpu-memory-utilization 0.9 + --trust-remote-code +``` + +Popular models: +- `meta-llama/Llama-3-8B` +- `mistralai/Mistral-7B-v0.1` +- `Qwen/Qwen2-7B` +- `ByteDance-Seed/UI-TARS-1.5-7B` (default, optimized for UI tasks) + +### vLLM Performance Tuning + +**For faster inference:** +```yaml +command: > + --model ByteDance-Seed/UI-TARS-1.5-7B + --enable-prefix-caching # Cache common prefixes + --enable-chunked-prefill # Process long prompts efficiently + --max-num-seqs 16 # Increase batch size +``` + +**For lower memory usage:** +```yaml +command: > + --model ByteDance-Seed/UI-TARS-1.5-7B + --gpu-memory-utilization 0.7 # Reduce from 0.9 + --max-model-len 4096 # Reduce from 8192 + --quantization awq # Use quantization (if model supports) +``` + +### Development Mode + +Mount source code for live reloading: + +```yaml +infant-agent: + volumes: + - ./infant:/app/infant + - ./backend.py:/app/backend.py + command: uvicorn backend:app --reload --host 0.0.0.0 --port 8000 +``` + +### Running Without vLLM (API-only mode) + +If you want to use only commercial APIs (no OSS models): + +```bash +# Start without vLLM server +docker-compose up -d computer-container infant-agent + +# Or comment out vllm-server in docker-compose.yaml +``` + +Update `config.toml`: +```toml +use_oss_llm = false +``` + +## Maintenance + +### Update to Latest Version + +```bash +cd InfantAgent +git pull origin main +docker-compose build --no-cache +docker-compose up -d +``` + +### Clean Up Resources + +```bash +# Remove stopped containers +docker-compose down + +# Remove all data (including volumes) +docker-compose down -v + +# Clean up unused Docker resources +docker system prune -a --volumes +``` + +## Support + +For issues and questions: +- GitHub Issues: https://github.com/bin123apple/InfantAgent/issues +- Discord: https://discord.gg/urxApEGcwV +- Documentation: https://github.com/bin123apple/InfantAgent + +## License + +This project is licensed under the MIT License. See LICENSE file for details. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e32a82d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,58 @@ +# Dockerfile for InfantAgent Server +# Build from project root: docker build -t infant-agent:latest -f Dockerfile . +FROM python:3.11-slim-bookworm + +# Prevent interactive prompts during installation +ENV DEBIAN_FRONTEND=noninteractive +ENV PYTHONUNBUFFERED=1 + +# Install system dependencies including OpenCV requirements +RUN apt-get update && apt-get install -y --no-install-recommends \ + curl \ + ca-certificates \ + build-essential \ + git \ + libgl1 \ + libglib2.0-0 \ + libsm6 \ + libxext6 \ + libxrender-dev \ + openssh-client \ + libgomp1 \ + && rm -rf /var/lib/apt/lists/* + +# Download and install uv +ADD https://astral.sh/uv/install.sh /uv-installer.sh +RUN sh /uv-installer.sh && rm /uv-installer.sh +ENV PATH="/root/.local/bin/:$PATH" + +# Create working directory +WORKDIR /app + +# Copy dependency files first for better layer caching +COPY pyproject.toml ./ + +# Create virtual environment and install dependencies +RUN uv venv && \ + uv pip install -e . + +# Copy application code +COPY infant ./infant +COPY config.toml ./ +COPY backend.py ./ +COPY frontend ./frontend + +# Activate virtual environment +ENV PATH="/app/.venv/bin:$PATH" +ENV VIRTUAL_ENV="/app/.venv" + +# Create necessary directories +RUN mkdir -p /tmp/cache /tmp/file_store /app/workspace && \ + chmod 777 /tmp/cache /tmp/file_store /app/workspace + +# Expose the backend port +EXPOSE 8008 + +# Run the web server +CMD ["python", "backend.py"] + \ No newline at end of file diff --git a/Dockerfile_vllm b/Dockerfile_vllm new file mode 100644 index 0000000..fa74679 --- /dev/null +++ b/Dockerfile_vllm @@ -0,0 +1,11 @@ +FROM nvidia/cuda:12.2.2-devel-ubuntu22.04 + +RUN apt-get update && apt-get install -y \ + curl \ + python3 python3-pip \ + && rm -rf /var/lib/apt/lists/* + +RUN pip3 install vllm + + +CMD ["vllm", "serve", "ByteDance-Seed/UI-TARS-1.5-7B", "--host", "0.0.0.0", "--port", "8005", "--max-model-len", "8192", "--gpu-memory-utilization", "0.8", "--dtype", "float16", "--api-key", "infant"] diff --git a/README.md b/README.md index 1afc00e..5fedd24 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,134 @@ Weโ€™ve switched from NoMachine to the open-source Guacamole for desktop sharing Now, it is only tested on `linux` server with `Nvidia Tesla GPU (A100, H200 ...)`. The GPU is for open-spurce model inference. There may be some bugs for Mac/Windows. -## Setup +## Setup + +### Option 1: Docker Compose Setup (Recommended) + +This is the recommended approach for running InfantAgent as a complete containerized system with all services. + +1. **Prerequisites** + - Docker and Docker Compose installed + - NVIDIA GPU with drivers installed (for vLLM inference) + - NVIDIA Container Toolkit installed + +2. **Configure Environment Variables** + + Create a `.env` file in the project root: + ```bash + cd InfantAgent + cp .env.example .env + ``` + + Edit `.env` and add your API keys: + ```bash + # Required: Claude API key for the agent + ANTHROPIC_API_KEY=your_anthropic_api_key_here + + # Optional: Hugging Face token for downloading models + HF_API_KEY=your_huggingface_token_here + + # Optional: Custom port configuration + SSH_PORT=63710 + GUI_PORT=4443 + ``` + +3. **Build and Start All Services** + ```bash + # Build all containers (first time only) + docker compose build + + # Start all services in background + docker compose up -d + + # View logs + docker compose logs -f + + # View agent logs specifically + docker compose logs -f infant-agent-cli + ``` + +4. **Access the Services** + - **Guacamole Web Desktop**: http://localhost:4443/guacamole/ + - Login: `web` / `web` + - Connection: Click "GNOME Desktop (RDP)" + - **SSH to Computer Container**: `ssh infant@localhost -p 63710` (password: `123`) + - **RDP Direct Access**: `localhost:3389` (username: `infant`, password: `123`) + - **vLLM Server**: http://localhost:8005 (for OSS model inference) + +5. **Managing Containers** + ```bash + # Stop all services + docker compose down + + # Restart specific service + docker compose restart infant-agent-cli + + # Rebuild after code changes + docker compose down + docker compose build infant-agent + docker compose up -d + + # View running containers + docker compose ps + + # Execute commands in agent container + docker exec -it infant-agent-cli bash + ``` + +6. **Interacting with the Agent** + + The agent is ready to receive prompts once you see the log: + ``` + INFO: Current working directory: /workspace + Input your request or use type exit to refresh the agent: + ``` + + **Recommended Method - Direct Attach:** + ```bash + # Attach to the running container + docker attach infant-agent-cli + + # Type your prompt and press Enter + # Example: "Create a Python script to analyze data.csv" + + # To detach without stopping, press: Ctrl+P then Ctrl+Q + ``` + + **Alternative Methods:** + ```bash + # Send prompt via pipe + echo "Your task here" | docker attach --no-stdin --sig-proxy=false infant-agent-cli + + # View logs in real-time + docker logs -f infant-agent-cli + + # Using provided scripts (may have limitations) + python3 send_prompt.py "Create a Python script" + ./agent_cli.sh logs -f + ``` + + **๐Ÿ“– For detailed usage instructions, see [USAGE.md](USAGE.md)** + +7. **Troubleshooting** + ```bash + # Check service health + docker compose ps + + # View detailed logs + docker compose logs infant-computer + docker compose logs vllm-server + + # Restart unhealthy services + docker compose restart + + # Clean up and rebuild + docker compose down -v # Remove volumes + docker compose build --no-cache + docker compose up -d + ``` + +### Option 2: Manual Setup (Advanced) 1. Setup environment ``` diff --git a/VLLM_SETUP.md b/VLLM_SETUP.md new file mode 100644 index 0000000..4d98865 --- /dev/null +++ b/VLLM_SETUP.md @@ -0,0 +1,411 @@ +# vLLM Server Setup Guide + +This guide focuses on the vLLM server component for hosting the UI-TARS-1.5-7B model. + +## Quick Start + +The vLLM server is automatically started when you run `docker-compose up -d`. It serves the UI-TARS-1.5-7B model via an OpenAI-compatible API. + +## Configuration + +### Default Settings + +The vLLM server is configured in [docker-compose.yaml](docker-compose.yaml): + +```yaml +vllm-server: + image: vllm/vllm-openai:latest + ports: + - "8001:8000" + environment: + - CUDA_VISIBLE_DEVICES=2,3 + command: > + --model ByteDance-Seed/UI-TARS-1.5-7B + --tensor-parallel-size 2 + --max-model-len 8192 + --gpu-memory-utilization 0.9 +``` + +### GPU Requirements + +- **Minimum**: 1x GPU with 16GB VRAM +- **Recommended**: 2x GPUs with 24GB VRAM each (for tensor parallelism) +- **Model Size**: ~14GB (7B parameters) + +### Environment Variables + +Set in your `.env` file: + +```bash +# Optional but recommended for faster downloads +HUGGING_FACE_HUB_TOKEN=your_token_here + +# GPU allocation (adjust based on your system) +CUDA_VISIBLE_DEVICES=0,1,2,3 +``` + +## Accessing the API + +### Health Check + +```bash +curl http://localhost:8001/health +``` + +### List Models + +```bash +curl http://localhost:8001/v1/models +``` + +### Test Inference + +```bash +curl http://localhost:8001/v1/completions \ + -H "Content-Type: application/json" \ + -d '{ + "model": "ByteDance-Seed/UI-TARS-1.5-7B", + "prompt": "Click on the login button", + "max_tokens": 100, + "temperature": 0.7 + }' +``` + +### Chat Completions + +```bash +curl http://localhost:8001/v1/chat/completions \ + -H "Content-Type: application/json" \ + -d '{ + "model": "ByteDance-Seed/UI-TARS-1.5-7B", + "messages": [ + {"role": "user", "content": "What should I click to login?"} + ], + "max_tokens": 100 + }' +``` + +## Integration with InfantAgent + +The InfantAgent server automatically connects to vLLM via the environment variable: + +```yaml +infant-agent: + environment: + - VLLM_BASE_URL=http://vllm-server:8000/v1 +``` + +In your `config.toml`, set: + +```toml +use_oss_llm = true +base_url_oss = "http://vllm-server:8000/v1" +model_oss = "ByteDance-Seed/UI-TARS-1.5-7B" +``` + +## Performance Tuning + +### For 2 GPUs (Recommended) + +```yaml +vllm-server: + environment: + - CUDA_VISIBLE_DEVICES=2,3 + command: > + --model ByteDance-Seed/UI-TARS-1.5-7B + --tensor-parallel-size 2 + --max-model-len 8192 + --gpu-memory-utilization 0.9 + --disable-custom-all-reduce +``` + +### For 1 GPU (Minimum) + +```yaml +vllm-server: + environment: + - CUDA_VISIBLE_DEVICES=1 + command: > + --model ByteDance-Seed/UI-TARS-1.5-7B + --tensor-parallel-size 1 + --max-model-len 4096 + --gpu-memory-utilization 0.85 +``` + +### High Throughput Mode + +```yaml +command: > + --model ByteDance-Seed/UI-TARS-1.5-7B + --tensor-parallel-size 2 + --max-model-len 8192 + --max-num-seqs 32 + --enable-prefix-caching + --enable-chunked-prefill +``` + +### Low Memory Mode + +```yaml +command: > + --model ByteDance-Seed/UI-TARS-1.5-7B + --tensor-parallel-size 1 + --max-model-len 2048 + --gpu-memory-utilization 0.7 + --max-num-seqs 8 +``` + +## Using Alternative Models + +### Llama 3 8B + +```yaml +vllm-server: + command: > + --model meta-llama/Llama-3-8B + --tensor-parallel-size 1 + --max-model-len 8192 +``` + +### Mistral 7B + +```yaml +vllm-server: + command: > + --model mistralai/Mistral-7B-v0.1 + --tensor-parallel-size 1 + --max-model-len 8192 +``` + +### Qwen 2 7B + +```yaml +vllm-server: + command: > + --model Qwen/Qwen2-7B + --tensor-parallel-size 1 + --max-model-len 8192 + --trust-remote-code +``` + +## Monitoring + +### View Logs + +```bash +# All logs +docker-compose logs -f vllm-server + +# Model loading progress +docker-compose logs -f vllm-server | grep -i "loading\|model\|ready" + +# Errors only +docker-compose logs vllm-server | grep -i "error\|fail" +``` + +### Check GPU Usage + +```bash +# From host +nvidia-smi + +# From container +docker exec vllm-server nvidia-smi +``` + +### Monitor Performance + +```bash +# Container stats +docker stats vllm-server + +# Request latency +time curl http://localhost:8001/v1/completions \ + -H "Content-Type: application/json" \ + -d '{"model": "ByteDance-Seed/UI-TARS-1.5-7B", "prompt": "test", "max_tokens": 10}' +``` + +## Troubleshooting + +### Model Download Fails + +**Issue**: Cannot download model from Hugging Face + +**Solutions**: +1. Set Hugging Face token: + ```bash + export HUGGING_FACE_HUB_TOKEN=your_token_here + docker-compose up -d vllm-server + ``` + +2. Pre-download model: + ```bash + # On host machine + huggingface-cli download ByteDance-Seed/UI-TARS-1.5-7B + + # Mount in docker-compose.yaml + volumes: + - ~/.cache/huggingface:/root/.cache/huggingface + ``` + +### Out of Memory (OOM) + +**Issue**: CUDA out of memory errors + +**Solutions**: +1. Reduce memory utilization: + ```yaml + --gpu-memory-utilization 0.7 # Down from 0.9 + ``` + +2. Reduce context length: + ```yaml + --max-model-len 4096 # Down from 8192 + ``` + +3. Use single GPU: + ```yaml + --tensor-parallel-size 1 # Down from 2 + ``` + +4. Reduce batch size: + ```yaml + --max-num-seqs 4 # Smaller batch + ``` + +### Slow Inference + +**Issue**: Requests taking too long + +**Solutions**: +1. Enable caching: + ```yaml + --enable-prefix-caching + ``` + +2. Increase batch size: + ```yaml + --max-num-seqs 16 + ``` + +3. Use tensor parallelism (if you have 2+ GPUs): + ```yaml + --tensor-parallel-size 2 + ``` + +### Server Not Responding + +**Issue**: Cannot connect to http://localhost:8001 + +**Check**: +```bash +# Container status +docker-compose ps vllm-server + +# Port binding +docker port vllm-server + +# Logs +docker-compose logs vllm-server + +# Health +curl http://localhost:8001/health +``` + +**Wait Time**: First startup takes 2-5 minutes to download and load the model. + +### Model Not Found + +**Issue**: "Model not found" errors + +**Solution**: +Ensure the model name matches exactly: +```yaml +# Correct +--model ByteDance-Seed/UI-TARS-1.5-7B + +# Wrong +--model UI-TARS-1.5-7B +--model ByteDance/UI-TARS-1.5-7B +``` + +## Advanced Configuration + +### Custom Sampling Parameters + +```yaml +command: > + --model ByteDance-Seed/UI-TARS-1.5-7B + --tensor-parallel-size 2 + --max-model-len 8192 + --temperature 0.8 + --top-p 0.95 + --top-k 50 +``` + +### Quantization (Lower Memory) + +```yaml +command: > + --model ByteDance-Seed/UI-TARS-1.5-7B + --quantization awq + --tensor-parallel-size 1 +``` + +Note: Model must support quantization format. + +### Distributed Inference (4+ GPUs) + +```yaml +vllm-server: + environment: + - CUDA_VISIBLE_DEVICES=0,1,2,3 + command: > + --model ByteDance-Seed/UI-TARS-1.5-7B + --tensor-parallel-size 4 + --pipeline-parallel-size 1 +``` + +## API Documentation + +The vLLM server implements the OpenAI API specification: + +- **Completions**: `POST /v1/completions` +- **Chat Completions**: `POST /v1/chat/completions` +- **Models**: `GET /v1/models` +- **Health**: `GET /health` + +Full API docs: https://docs.vllm.ai/en/latest/serving/openai_compatible_server.html + +## Performance Benchmarks + +On 2x A100 40GB GPUs: +- **Cold Start**: 2-3 minutes (model download + loading) +- **Warm Start**: 30-60 seconds (model loading only) +- **Inference**: 20-50 tokens/second (depends on batch size) +- **Latency**: 100-300ms for first token + +## Best Practices + +1. **Pre-download models** before production deployment +2. **Use tensor parallelism** for models > 13B parameters +3. **Enable prefix caching** for repeated prompts +4. **Monitor GPU memory** and adjust utilization +5. **Set appropriate max-model-len** based on use case +6. **Use health checks** to ensure server readiness + +## Resources + +- vLLM Documentation: https://docs.vllm.ai/ +- UI-TARS Model: https://huggingface.co/ByteDance-Seed/UI-TARS-1.5-7B +- OpenAI API Spec: https://platform.openai.com/docs/api-reference +- InfantAgent Issues: https://github.com/bin123apple/InfantAgent/issues + +## Support + +For vLLM-specific issues: +- vLLM GitHub: https://github.com/vllm-project/vllm +- vLLM Discord: https://discord.gg/vllm + +For InfantAgent integration: +- GitHub Issues: https://github.com/bin123apple/InfantAgent/issues diff --git a/agent_cli.sh b/agent_cli.sh new file mode 100755 index 0000000..ddaad7b --- /dev/null +++ b/agent_cli.sh @@ -0,0 +1,127 @@ +#!/bin/bash +# Simple CLI wrapper to interact with InfantAgent via Docker + +CONTAINER_NAME="infant-agent-cli" + +# 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 check if container is running +check_container() { + if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then + echo -e "${RED}โŒ Error: Container '${CONTAINER_NAME}' is not running.${NC}" + echo -e "${YELLOW}Please start the containers with: docker compose up -d${NC}" + exit 1 + fi +} + +# Function to send prompt via stdin +send_prompt() { + local prompt="$1" + echo -e "${BLUE}๐Ÿ“ค Sending prompt to agent:${NC} $prompt" + echo -e "${YELLOW}โณ Agent is processing...${NC}" + echo "" + + # Send the prompt to the container's stdin + echo "$prompt" | docker attach --no-stdin "$CONTAINER_NAME" 2>/dev/null || \ + echo "$prompt" | docker exec -i "$CONTAINER_NAME" /bin/bash -c "cat" || \ + echo -e "${RED}Failed to send prompt. Try: docker exec -it $CONTAINER_NAME python -m infant${NC}" +} + +# Function to view logs +view_logs() { + echo -e "${BLUE}๐Ÿ“‹ Viewing agent logs...${NC}" + if [ "$1" = "-f" ]; then + docker logs -f "$CONTAINER_NAME" + else + docker logs --tail 100 "$CONTAINER_NAME" + fi +} + +# Function to show status +show_status() { + echo -e "${BLUE}๐Ÿ“Š Container Status:${NC}" + docker ps --filter "name=$CONTAINER_NAME" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" +} + +# Function to enter interactive mode +interactive_mode() { + echo -e "${GREEN}๐Ÿค– Entering interactive mode with agent...${NC}" + docker exec -it "$CONTAINER_NAME" /bin/bash +} + +# Function to show help +show_help() { + cat << EOF +${GREEN}InfantAgent CLI - Docker Container Interface${NC} + +Usage: $0 [COMMAND] [OPTIONS] + +${YELLOW}Commands:${NC} + send "prompt" Send a prompt to the agent + logs View agent logs (last 100 lines) + logs -f Follow agent logs in real-time + status Show container status + shell Open interactive shell in container + restart Restart the agent container + help Show this help message + +${YELLOW}Examples:${NC} + $0 send "Create a Python script to analyze data.csv" + $0 logs -f + $0 status + $0 shell + +${YELLOW}Direct interaction:${NC} + For direct interaction with the agent, use: + docker exec -it ${CONTAINER_NAME} /bin/bash + # Then inside container: + # The agent is already running with input prompt + +${YELLOW}View logs while agent is running:${NC} + docker logs -f ${CONTAINER_NAME} +EOF +} + +# Main script logic +case "$1" in + send) + check_container + if [ -z "$2" ]; then + echo -e "${RED}โŒ Error: Please provide a prompt${NC}" + echo "Usage: $0 send \"your prompt here\"" + exit 1 + fi + send_prompt "$2" + ;; + logs) + check_container + view_logs "$2" + ;; + status) + check_container + show_status + ;; + shell) + check_container + interactive_mode + ;; + restart) + echo -e "${YELLOW}โณ Restarting agent container...${NC}" + docker restart "$CONTAINER_NAME" + echo -e "${GREEN}โœ… Container restarted${NC}" + ;; + help|--help|-h|"") + show_help + ;; + *) + echo -e "${RED}โŒ Unknown command: $1${NC}" + echo "" + show_help + exit 1 + ;; +esac diff --git a/backend.py b/backend.py index b4e5d37..d1a0fa3 100644 --- a/backend.py +++ b/backend.py @@ -28,7 +28,7 @@ app = FastAPI() upstream_http = httpx.AsyncClient(verify=False, follow_redirects=True) -upstream_aiohttp = ClientSession(connector=TCPConnector(ssl=False)) +upstream_aiohttp = None # Enable CORS app.add_middleware( @@ -41,6 +41,8 @@ @app.on_event("startup") async def startup_event(): + global upstream_aiohttp + upstream_aiohttp = ClientSession(connector=TCPConnector(ssl=False)) logger.info("[WS] connecting to {url}") @@ -73,14 +75,30 @@ async def shutdown_event(): logger.error(f"Error during cleanup: {str(e)}") # Redirect to frontend -@app.get("/") -async def root(): +@app.get("/", name="root_frontend") +async def root_frontend(): return RedirectResponse(url="/frontend/index.html") # Static files app.mount("/frontend", StaticFiles(directory="frontend"), name="frontend") # Status API +@app.get("/api/guacamole-token") +async def guacamole_token(): + """Get a fresh Guacamole auth token for the iframe.""" + try: + resp = await upstream_http.post( + "http://infant-computer:8080/guacamole/api/tokens", + data={"username": "web", "password": "web"}, + headers={"Content-Type": "application/x-www-form-urlencoded"}, + ) + if resp.status_code == 200: + data = resp.json() + return {"success": True, "token": data["authToken"]} + return {"success": False, "error": f"Guacamole returned {resp.status_code}"} + except Exception as e: + return {"success": False, "error": str(e)} + @app.get("/api/status") async def status(): if agent: @@ -88,7 +106,7 @@ async def status(): "success": True, "status": "ready", "currentTask": "none", - "model": agent._planning_llm.model, + "model": agent.planning_llm.model_name, "sessionActive": True, } return {"success": True, "status": "ready", "currentTask": "none", "model": "demo", "sessionActive": False} @@ -100,8 +118,12 @@ async def chat(data: dict): if not user_message: return {"success": False, "error": "No message provided"} if agent and run_single_step: - response = await run_single_step(agent, user_message) - return {"success": True, "response": response, "status": "completed"} + try: + response = await run_single_step(agent, user_message) + return {"success": True, "response": response, "status": "completed"} + except Exception as e: + logger.error(f"Error in chat: {e}") + return {"success": False, "error": str(e)} await asyncio.sleep(1) return {"success": True, "response": f"Demo mode: Received '{user_message}'", "status": "completed"} @@ -121,16 +143,26 @@ async def reset(): @app.post("/api/settings") async def settings(data: dict): global config, agent, computer - config.model = data.get('model') - config.api_key = data.get('apiKey') - config.temperature = float(data.get('temperature')) - config.max_tokens = int(data.get('maxTokens')) - print(agent) + # Load base config from config.toml first + try: + user_config = config._load() + config.__dict__.update(user_config) + except FileNotFoundError: + logger.warning("config.toml not found, using defaults") + # Apply frontend settings + if data.get('model'): + config.model = data.get('model') + if data.get('apiKey'): + config.api_key = data.get('apiKey') + if data.get('temperature') is not None: + config.temperature = float(data.get('temperature')) + if data.get('maxTokens') is not None: + config.max_output_tokens = int(data.get('maxTokens')) + # Finalize config (resolves paths, SSH settings, etc.) + config.finalize_config() if agent: - # ๅฎž้™…ๅฎž็Žฐไธญๅบ”่ฏฅๆ›ดๆ–ฐ agent ้…็ฝฎ await agent.update_agent_config(config) return {"success": True, "message": "Agent updated", "appliedSettings": data} - await asyncio.sleep(0.5) return {"success": True, "message": "Agent initialized", "appliedSettings": data} @app.get("/api/initialize") @@ -433,26 +465,8 @@ def get_forward_params(request: Request): return params # โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€” -# root: capture sid & redirect +# root: handled by root_frontend above # โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€” -@app.get("/") -async def root(request: Request): - logger.debug(f"[ROOT] incoming path={request.url.path} query={request.url.query!r}") - qs = request.url.query - target = "/gui/" + (f"?{qs}" if qs else "") - logger.debug(f"[ROOT] redirecting to {target}") - resp = RedirectResponse(target) - if "sid" in request.query_params: - sid_val = request.query_params["sid"] - logger.debug(f"[ROOT] setting cookie sid={sid_val!r}") - resp.set_cookie( - key="sid", - value=sid_val, - httponly=True, - secure=False, # allow HTTP - ) - logger.debug("[ROOT] response prepared") - return resp # โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€” # SSE proxy @@ -462,7 +476,7 @@ async def sse(request: Request): params = get_forward_params(request) logger.debug(f"[SSE] path={request.url.path} params={params} cookies={dict(request.cookies)}") upstream = await upstream_aiohttp.get( - f"https://localhost:4443{request.url.path}", + f"https://infant-computer:8080{request.url.path}", params=params, ssl=False ) @@ -481,7 +495,7 @@ async def gen(): async def ws_proxy(ws: WebSocket): await ws.accept() sid = ws.query_params.get("sid") or ws.cookies.get("sid") - url = f"wss://localhost:4443{ws.url.path}" + (f"?sid={sid}" if sid else "") + url = f"wss://infant-computer:8080{ws.url.path}" + (f"?sid={sid}" if sid else "") logger.debug(f"[WS] connecting to {url}") upstream = await upstream_aiohttp.ws_connect(url, ssl=False) logger.debug("[WS upstream] connected") @@ -515,7 +529,7 @@ async def to_client(): @app.api_route("/gui/{full_path:path}", methods=["GET","POST","HEAD","OPTIONS"]) async def gui_proxy(request: Request, full_path: str): params = get_forward_params(request) - upstream_url = f"https://localhost:4443/gui/{full_path}" + upstream_url = f"https://infant-computer:8080/gui/{full_path}" logger.debug(f"[HTTP] {request.method} {upstream_url} params={params} headers={dict(request.headers)}") resp_up = await upstream_http.request( @@ -555,12 +569,160 @@ async def gui_proxy(request: Request, full_path: str): media_type=resp_up.headers.get("content-type"), ) +# โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€” +# Guacamole HTTP proxy +# โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€” +@app.api_route("/guacamole/{full_path:path}", methods=["GET","POST","PUT","DELETE","HEAD","OPTIONS"]) +async def guacamole_proxy(request: Request, full_path: str): + params = get_forward_params(request) + upstream_url = f"http://infant-computer:8080/guacamole/{full_path}" + logger.debug(f"[guacamole] {request.method} {upstream_url} params={params}") + + body = await request.body() + fwd_headers = { + k: v for k, v in request.headers.items() + if k.lower() not in ("host", "content-length", "transfer-encoding") + } + resp_up = await upstream_http.request( + method=request.method, + url=upstream_url, + params=params, + content=body, + headers=fwd_headers, + ) + logger.debug(f"[guacamole upstream] status={resp_up.status_code}") + + headers = { + k: v for k, v in resp_up.headers.multi_items() + if k.lower() not in ("x-frame-options", "content-security-policy", "set-cookie") + } + + # Rewrite Set-Cookie: drop the upstream Domain (so the cookie attaches to our + # proxy host), but preserve SameSite/Secure/HttpOnly/Path as set by Guacamole. + cookies = resp_up.headers.get_list("set-cookie") + rewritten = [] + for raw in cookies: + c = SimpleCookie() + try: + c.load(raw) + except Exception: + rewritten.append(raw) + continue + for morsel in c.values(): + parts = [f"{morsel.key}={morsel.value}"] + parts.append(f"Path={morsel['path'] or '/'}") + if morsel["expires"]: + parts.append(f"Expires={morsel['expires']}") + if morsel["max-age"]: + parts.append(f"Max-Age={morsel['max-age']}") + if morsel["samesite"]: + parts.append(f"SameSite={morsel['samesite']}") + if morsel["secure"]: + parts.append("Secure") + if morsel["httponly"]: + parts.append("HttpOnly") + rewritten.append("; ".join(parts)) + if rewritten: + # Starlette's Response needs a MutableHeaders to send multiple Set-Cookie lines. + resp = Response( + content=resp_up.content, + status_code=resp_up.status_code, + headers=headers, + media_type=resp_up.headers.get("content-type"), + ) + for ck in rewritten: + resp.raw_headers.append((b"set-cookie", ck.encode("latin-1"))) + return resp + + return Response( + content=resp_up.content, + status_code=resp_up.status_code, + headers=headers, + media_type=resp_up.headers.get("content-type"), + ) + +# โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€” +# Guacamole WebSocket proxy +# โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€” +@app.websocket("/guacamole/websocket-tunnel") +async def guacamole_ws_proxy(ws: WebSocket): + # Guacamole's guacamole-common-js WebSocketTunnel connects with subprotocol + # "guacamole". The server MUST echo it back or the browser fails the handshake. + requested_protocols = ws.headers.get("sec-websocket-protocol", "") + client_protocols = [p.strip() for p in requested_protocols.split(",") if p.strip()] + selected = "guacamole" if "guacamole" in client_protocols else (client_protocols[0] if client_protocols else None) + + params = dict(ws.query_params) + qs = "&".join(f"{k}={v}" for k, v in params.items()) + url = f"ws://infant-computer:8080/guacamole/websocket-tunnel" + (f"?{qs}" if qs else "") + logger.debug(f"[guacamole WS] connecting to {url} protocols={client_protocols}") + + # Forward the browser's Cookie header so upstream session state stays consistent. + upstream_headers = {} + cookie = ws.headers.get("cookie") + if cookie: + upstream_headers["Cookie"] = cookie + + upstream = await upstream_aiohttp.ws_connect( + url, + ssl=False, + protocols=client_protocols or (), + headers=upstream_headers or None, + ) + logger.debug(f"[guacamole WS] connected upstream protocol={upstream.protocol}") + + # Prefer the subprotocol the upstream actually selected; fall back to our guess. + accept_protocol = upstream.protocol or selected + await ws.accept(subprotocol=accept_protocol) + + closed = asyncio.Event() + + async def up_to_client(): + try: + async for msg in upstream: + if closed.is_set(): + break + if msg.type == WSMsgType.TEXT: + await ws.send_text(msg.data) + elif msg.type == WSMsgType.BINARY: + await ws.send_bytes(msg.data) + elif msg.type in (WSMsgType.CLOSE, WSMsgType.CLOSING, WSMsgType.CLOSED): + break + except Exception: + pass + finally: + closed.set() + + async def client_to_up(): + try: + while not closed.is_set(): + m = await ws.receive() + if m["type"] == "websocket.receive": + if "text" in m: + await upstream.send_str(m["text"]) + else: + await upstream.send_bytes(m["bytes"]) + else: + break + except (WebSocketDisconnect, Exception): + pass + finally: + closed.set() + + try: + await asyncio.gather(up_to_client(), client_to_up()) + except Exception: + pass + finally: + if not upstream.closed: + await upstream.close() + # โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€” # proxy for /nxplayer/* (Web Player assets) # โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€” @app.api_route("/nxplayer/{full_path:path}", methods=["GET","HEAD","OPTIONS"]) async def nxplayer_proxy(request: Request, full_path: str): - upstream_url = f"https://localhost:4443/nxplayer/{full_path}" + upstream_url = f"https://infant-computer:8080/nxplayer/{full_path}" logger.debug(f"[nxplayer] {request.method} {upstream_url}") resp_up = await upstream_http.request( method=request.method, @@ -582,5 +744,6 @@ async def nxplayer_proxy(request: Request, full_path: str): ) if __name__ == "__main__": import uvicorn - print("Starting server on http://localhost:8001") - uvicorn.run("backend:app", host="0.0.0.0", port=8001, reload=True) + port = int(os.getenv("BACKEND_PORT", "8008")) + print(f"Starting server on http://localhost:{port}") + uvicorn.run("backend:app", host="0.0.0.0", port=port, reload=True) diff --git a/config.toml b/config.toml index dc62505..48801f4 100644 --- a/config.toml +++ b/config.toml @@ -1,31 +1,31 @@ # Optional, if not provided, the Global LLM will be used +# api_key is inherited from ANTHROPIC_API_KEY env var โ€” do NOT set empty api_key here [planning_llm] -model = "claude-sonnet-4-20250514" -api_key = "" # YOUR API KEY +model = "claude-sonnet-4-6" # Optional, if not provided, the Global LLM will be used [classification_llm] -model = "claude-sonnet-4-20250514" -api_key = "" # YOUR API KEY +model = "claude-sonnet-4-6" # Optional, if not provided, the Global LLM will be used [execution_llm] -model = "claude-sonnet-4-20250514" -api_key = "" # YOUR API KEY +model = "claude-sonnet-4-6" -# Optional, Visual Grounding model, if not provided, the default VG model will be used +# Visual Grounding model served by the vLLM container (OpenAI-compatible API). +# The "openai/" prefix tells litellm to use the OpenAI provider with our custom base_url. [vg_llm] -model_oss = "ByteDance-Seed/UI-TARS-1.5-7B" +model_oss = "openai/ByteDance-Seed/UI-TARS-1.5-7B" +base_url_oss = "http://vllm-server:8005/v1" +api_key_oss = "infant" # Optional, File Editing model, if not provided, the default model will be used [fe_llm] -model = "claude-sonnet-4-20250514" +model = "claude-sonnet-4-6" # Optional, Toolmaker model, if not provided, the default model will be used [tm_llm] -model = "claude-sonnet-4-20250514" +model = "claude-sonnet-4-6" # Audio parsing model, if not provided, the default model will be used [ap_llm] model = "gpt-4o-audio-preview" -api_key = "" # YOUR API KEY \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..76938b7 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,163 @@ +version: '3.8' + +services: + # vLLM server for OSS model inference + + vllm-server: + build: + context: . + dockerfile: Dockerfile_vllm + container_name: vllm-server + restart: unless-stopped + ports: + - "8005:8005" + environment: + - HUGGING_FACE_HUB_TOKEN=${HF_API_KEY:-} + volumes: + # Cache Hugging Face models + - ${HF_HOME:-huggingface-cache}:/root/.cache/huggingface + networks: + - infant-network + deploy: + resources: + reservations: + devices: + - driver: nvidia + device_ids: ['4'] + capabilities: [gpu, utility, compute, graphics] + + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost:8005/health || exit 1"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 90s + + # InfantAgent main CLI application + infant-agent: + build: + context: . + dockerfile: Dockerfile + image: infant-agent:latest + container_name: infant-agent-cli + restart: unless-stopped + stdin_open: true # Enable interactive mode (docker run -i) + tty: true # Allocate a pseudo-TTY (docker run -t) + ports: + - "${BACKEND_PORT:-8008}:8008" + environment: + - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} + - PYTHONUNBUFFERED=1 + - PYTHONIOENCODING=utf-8 + # vLLM server configuration + - base_url_oss=http://vllm-server:8005/v1 + - api_key_oss=infant + - SSH_HOSTNAME=${SSH_HOSTNAME:-computer-container} + - SSH_PORT=${SSH_PORT:-63710} + - SSH_USERNAME=${SSH_USERNAME:-infant} + - SSH_PASSWORD=${SSH_PASSWORD:-123} + volumes: + # Mount workspace for persistent data + - ./workspace:/app/workspace + # Mount cache directory + - ./cache:/tmp/cache + # Mount config for easy updates + - ./config.toml:/app/config.toml:ro + # Mount Docker socket for container management + - /var/run/docker.sock:/var/run/docker.sock + networks: + - infant-network + depends_on: + - infant-computer + - vllm-server + + # Computer container with desktop environment + infant-computer: + build: + context: . + dockerfile: infant/computer/Dockerfile + image: ubuntu-gnome-guacamole:22.04 + container_name: infant-computer + restart: unless-stopped + ports: + # SSH port - maps container port 22 to host (computer.py line 1126) + - "${SSH_PORT:-63710}:22" + # Guacamole web interface - maps container port 8080 to host (computer.py line 1125) + - "${GUI_PORT:-4443}:8080" + # RDP port for remote desktop - maps container port 3389 (computer.py line 1127) + - "3389:3389" + environment: + # Display configuration (computer.py line 1135) + - DISPLAY=:0 + # User account creation - "infant" if run_as_infant, "root" otherwise (computer.py line 1131) + - CreateUserAccount=${CREATE_USER_ACCOUNT:-infant} + # Render type for graphics (computer.py line 1132) + - RenderType=${RENDER_TYPE:-gpu} + # NVIDIA driver type: Tesla, GeForce, etc. (computer.py line 1133) + - NvidiaDriver=${NVIDIA_DRIVER:-Tesla} + # GPU device visibility (computer.py line 1134) + - NVIDIA_VISIBLE_DEVICES=${NVIDIA_VISIBLE_DEVICES:-0} + # Timezone + - TZ=America/New_York + volumes: + # Shared workspace between agent and computer (computer.py line 1169) + - ${WORKSPACE_MOUNT_PATH:-./workspace}:/workspace + # Cache directory for infant user (computer.py line 1170) + - ${CACHE_DIR:-./cache}:/home/infant/.cache + networks: + - infant-network + # Shared memory size (computer.py line 1114) + shm_size: '2gb' + stdin_open: true + tty: true + # Privileged mode required for systemd and desktop (computer.py line 1111) + privileged: true + # User namespace mode (computer.py line 1112) + userns_mode: host + # IPC mode (computer.py line 1113) + ipc: host + # Capabilities required for systemd (computer.py line 1115) + cap_add: + - SYS_ADMIN + - SYS_BOOT + # TTY device access (computer.py line 1116) + devices: + - /dev/tty0 + # deploy: + # resources: + # reservations: + # devices: + # - driver: nvidia + # device_ids: ['${NVIDIA_DEVICE_ID:-0}'] + # capabilities: [gpu, utility, compute, graphics] + # systemd init command (computer.py line 1137) + command: ["/sbin/init", "-D", "-o", "PermitRootLogin=yes"] + healthcheck: + # Check Guacamole is responding on port 8080 (internal port) + test: ["CMD", "curl", "-f", "http://localhost:8080/guacamole/"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 120s + +networks: + infant-network: + driver: bridge + +volumes: + workspace: + driver: local + cache: + driver: local + huggingface-cache: + driver: local + + + +# docker run -it --gpus all \ +# -v HF_HOME:/root/.cache/huggingface \ +# -p 8005:8005 \ +# vllm/vllm-openai:latest \ +# --model ByteDance-Seed/UI-TARS-1.5-7B \ +# --dtype bfloat16 \ +# --gpu-memory-utilization 0.9 diff --git a/evaluation/gaia/run_inference.py b/evaluation/gaia/run_inference.py index aef3677..e2421f0 100644 --- a/evaluation/gaia/run_inference.py +++ b/evaluation/gaia/run_inference.py @@ -8,7 +8,7 @@ import concurrent.futures from infant.config import config, ComputerParams from infant.agent.agent import Agent -from infant.computer.computer import Computer +from infant.computer.computer import create_computer_from_params from infant.llm.llm_api_base import LLM_API_BASED from infant.llm.llm_oss_base import LLM_OSS_BASED from infant.agent.memory.memory import Userrequest, Finish, IPythonRun @@ -63,7 +63,7 @@ async def initialize_docker_agent(instance: dict, config=config)-> Agent: sid = str(uuid.uuid4()) try: - computer = Computer(computer_parameter, sid = sid) + computer = create_computer_from_params(computer_parameter, sid = sid) except: logger.error({traceback.format_exc()}) diff --git a/evaluation/swe_bench/run_inference.py b/evaluation/swe_bench/run_inference.py index 608b961..3aed5a1 100644 --- a/evaluation/swe_bench/run_inference.py +++ b/evaluation/swe_bench/run_inference.py @@ -14,7 +14,7 @@ from infant.config import config, ComputerParams from datasets import load_dataset from infant.agent.agent import Agent -from infant.computer.computer import Computer +from infant.computer.computer import create_computer_from_params from infant.llm.llm_api_base import LLM_API_BASED from infant.llm.llm_oss_base import LLM_OSS_BASED from infant.agent.memory.restore_memory import truncate_output @@ -153,7 +153,7 @@ async def initialize_docker_agent(instance: dict, config=config)-> Agent: sid = str(uuid.uuid4()) try: - computer = Computer(computer_parameter, sid = sid) + computer = create_computer_from_params(computer_parameter, sid = sid) except: logger.error({traceback.format_exc()}) diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..7059a96 --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,12 @@ +# React + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project. diff --git a/frontend/css/styles.css b/frontend/css/styles.css index 6d568bf..9b6dc2b 100644 --- a/frontend/css/styles.css +++ b/frontend/css/styles.css @@ -1,564 +1,920 @@ -/* Global Styles */ -:root { - --primary-color: #4a6fa5; - --secondary-color: #6c757d; - --accent-color: #28a745; - --background-color: #f8f9fa; - --text-color: #333; - --border-color: #dee2e6; - --message-bg-user: #e9f5ff; - --message-bg-system: #f0f0f0; - --shadow: 0 4px 6px rgba(0, 0, 0, 0.1); -} - -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -body { - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; - line-height: 1.6; - color: var(--text-color); - background-color: var(--background-color); -} - -.container { - max-width: 1200px; - margin: 0 auto; - padding: 20px; - display: flex; - flex-direction: column; - min-height: 100vh; -} - -/* Header Styles */ -header { - background-color: var(--primary-color); - color: white; - padding: 15px 20px; - border-radius: 8px 8px 0 0; - box-shadow: var(--shadow); -} - -header h1 { - font-size: 1.8rem; - display: flex; - align-items: center; - gap: 10px; -} - -/* Main Content Styles */ -main { - display: grid; - grid-template-columns: 1fr 300px; - gap: 20px; - flex: 1; - margin: 20px 0; -} - -/* Chat Container Styles */ -.chat-container { - background-color: white; - border-radius: 8px; - box-shadow: var(--shadow); - display: flex; - flex-direction: column; - height: 70vh; -} +/* ========================================================================== + InfantAgent Dashboard โ€” Modern Design System + Inspired by shadcn/ui (neutral palette, clean borders, subtle shadows) + ========================================================================== */ +/* --- Design Tokens --- */ +:root { + --background: #fafafa; + --foreground: #09090b; + --card: #ffffff; + --card-foreground: #09090b; + --muted: #f4f4f5; + --muted-foreground: #71717a; + --border: #e4e4e7; + --input: #e4e4e7; + --primary: #18181b; + --primary-foreground: #fafafa; + --secondary: #f4f4f5; + --secondary-foreground: #18181b; + --accent: #f4f4f5; + --accent-foreground: #18181b; + --destructive: #ef4444; + --success: #22c55e; + --warning: #eab308; + --ring: #a1a1aa; + --radius: 0.5rem; + --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", + Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; + --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, + "Liberation Mono", monospace; +} + +/* --- Reset --- */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body, +html { + height: 100%; + font-family: var(--font-sans); + font-size: 14px; + line-height: 1.5; + color: var(--foreground); + background-color: var(--background); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +/* ========================================================================== + Layout + ========================================================================== */ +.dashboard { + display: flex; + height: 100vh; + overflow: hidden; +} + +/* ========================================================================== + Chat Sidebar (left panel) + ========================================================================== */ +.chat-area { + flex: 0 0 25%; + display: flex; + flex-direction: column; + min-width: 300px; + max-width: 520px; + background: var(--card); + border-right: 1px solid var(--border); +} + +/* --- Header --- */ +.chat-area header { + display: flex; + justify-content: space-between; + align-items: center; + height: 56px; + padding: 0 16px; + border-bottom: 1px solid var(--border); + flex-shrink: 0; +} + +.chat-header-left { + display: flex; + align-items: center; + gap: 10px; +} + +.chat-header-left img { + width: 32px; + height: 32px; + border-radius: 8px; + object-fit: cover; +} + +.chat-area header h1 { + margin: 0; + font-size: 0.875rem; + font-weight: 600; + color: var(--foreground); + letter-spacing: -0.01em; +} + +.setting-btn { + display: inline-flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border-radius: 6px; + background: transparent; + border: none; + color: var(--muted-foreground); + cursor: pointer; + font-size: 0.875rem; + transition: background-color 0.15s, color 0.15s; +} + +.setting-btn:hover { + background: var(--accent); + color: var(--accent-foreground); +} + +/* --- Chat Messages --- */ .chat-messages { - display: flex; - flex-direction: column; - padding: 0; /* ๅŽปๆމๅคšไฝ™ๅ†…่พน่ท */ - margin: 5px 0; - } + flex: 1; + overflow-y: auto; + padding: 16px; + display: flex; + flex-direction: column; + gap: 10px; + border: none; + border-radius: 0; + margin: 0; +} .chat-messages .message { - display: flex; /* ๅ˜ๆˆ flex ๅฎนๅ™จ */ - width: 100%; - box-sizing: border-box; - margin-bottom: 8px; - } -/* ็„ถๅŽ่ฎฉๅ†…ๅฎนๅŒบๅŸŸ่ท‘ๆปกๅ‰ฉไฝ™็ฉบ้—ด */ -.chat-messages .message .message-content { - flex: 1 1 auto; /* ๆ‹‰ไผธๅกซๆปก็ˆถๅฎนๅ™จ */ - max-width: 100%; - box-sizing: border-box; - } - -.message.user { - align-items: flex-end; + display: flex; + width: 100%; + margin-bottom: 0; } -/* ็ฐ่‰ฒๅ†…ๅฎนๅ—้“บๆปก parent */ -.chat-messages .message-content { - display: block; - width: 100%; - box-sizing: border-box; /* padding ไธไผšๆ’‘็ ดๅฎฝๅบฆ */ - background-color: #f0f0f0; /* ็ฐ่‰ฒ่ƒŒๆ™ฏ */ - padding: 8px 12px; /* ๆ นๆฎๅ–œๅฅฝ่ฐƒๆ•ดๅ†…่พน่ท */ - border-radius: 4px; - word-break: break-word; /* ๅ†…ๅฎนๅคช้•ฟๅฏไปฅๆข่กŒ */ - } +.chat-messages .message .message-content { + flex: 1; + max-width: 100%; + border-radius: 12px; + padding: 10px 14px; + font-size: 0.8125rem; + line-height: 1.65; + word-break: break-word; + overflow-wrap: break-word; +} .message.system .message-content { - background-color: var(--message-bg-system); - border-radius: 12px 12px 12px 0; + background: var(--muted); + color: var(--foreground); + border-radius: 12px 12px 12px 4px; } .message.user .message-content { - background-color: var(--message-bg-user); - border-radius: 12px 12px 0 12px; - color: #333; + background: var(--primary); + color: var(--primary-foreground); + border-radius: 12px 12px 4px 12px; } .message-content p { - margin-bottom: 8px; + margin-bottom: 6px; } .message-content p:last-child { - margin-bottom: 0; + margin-bottom: 0; } -.user-input { - display: flex; - padding: 15px; - border-top: 1px solid var(--border-color); - background-color: #f8f9fa; - border-radius: 0 0 8px 8px; +.message-content pre { + background: #1e1e2e; + color: #cdd6f4; + padding: 10px 12px; + border-radius: 6px; + font-family: var(--font-mono); + font-size: 0.75rem; + overflow-x: auto; + margin: 6px 0; + line-height: 1.5; } -.user-input textarea { - flex: 1; - padding: 12px 15px; - border: 1px solid var(--border-color); - border-radius: 20px; - resize: none; - height: 50px; - font-family: inherit; - font-size: 0.95rem; -} - -.user-input button { - background-color: var(--primary-color); - color: white; - border: none; - border-radius: 50%; - width: 50px; - height: 50px; - margin-left: 10px; - cursor: pointer; - transition: background-color 0.2s; - display: flex; - align-items: center; - justify-content: center; +.message-content code { + font-family: var(--font-mono); + font-size: 0.8em; + background: rgba(0, 0, 0, 0.06); + padding: 1px 5px; + border-radius: 4px; } -.user-input button:hover { - background-color: #3a5a8a; +.message-content pre code { + background: none; + padding: 0; } -#resetButton { - background-color: var(--secondary-color); +.message-content ol, +.message-content ul { + padding-left: 1.5em; + margin: 4px 0; } -#resetButton:hover { - background-color: #5a6268; +.message-content li { + margin-bottom: 2px; + list-style-position: outside; } -/* Status Panel Styles */ +/* --- Status Panel --- */ .status-panel { - background-color: white; - border-radius: 8px; - box-shadow: var(--shadow); - overflow: hidden; -} - -.status-header { - background-color: var(--primary-color); - color: white; - padding: 15px; -} - -.status-header h3 { - font-size: 1.2rem; - font-weight: 500; -} - -.status-content { - padding: 15px; + padding: 8px 16px; + border-top: 1px solid var(--border); + background: var(--muted); + flex-shrink: 0; } .status-item { - margin-bottom: 15px; - padding-bottom: 15px; - border-bottom: 1px solid var(--border-color); + display: flex; + gap: 6px; + align-items: center; + margin-bottom: 2px; + font-size: 0.6875rem; + line-height: 1.6; } .status-item:last-child { - margin-bottom: 0; - padding-bottom: 0; - border-bottom: none; + margin-bottom: 0; + padding-bottom: 0; + border-bottom: none; } .status-label { - font-weight: 600; - display: block; - margin-bottom: 5px; - color: var(--secondary-color); + font-weight: 500; + color: var(--muted-foreground); + display: inline; } .status-value { - font-size: 0.95rem; + font-size: 0.6875rem; + font-weight: 500; } -/* Footer Styles */ -footer { - text-align: center; - padding: 15px 0; - color: var(--secondary-color); - border-top: 1px solid var(--border-color); - margin-top: auto; +/* --- User Input --- */ +.user-input { + display: flex; + align-items: flex-end; + gap: 6px; + padding: 12px 16px; + border-top: 1px solid var(--border); + background: var(--card); + flex-shrink: 0; } -footer a { - color: var(--primary-color); - text-decoration: none; +.user-input textarea { + flex: 1; + resize: none; + padding: 8px 12px; + border: 1px solid var(--input); + border-radius: 12px; + font-family: var(--font-sans); + font-size: 0.8125rem; + line-height: 1.5; + color: var(--foreground); + background: var(--muted); + min-height: 38px; + max-height: 120px; + outline: none; + transition: border-color 0.15s, box-shadow 0.15s; } -footer a:hover { - text-decoration: underline; +.user-input textarea:focus { + border-color: var(--ring); + box-shadow: 0 0 0 2px rgba(161, 161, 170, 0.15); } -/* Modal Styles */ -.modal { - display: none; - position: fixed; - z-index: 1000; - left: 0; - top: 0; - width: 100%; - height: 100%; - background-color: rgba(0, 0, 0, 0.5); - align-items: center; - justify-content: center; +.user-input textarea::placeholder { + color: var(--muted-foreground); } -.modal-content { - background-color: white; - padding: 25px; - border-radius: 8px; - width: 90%; - max-width: 500px; - box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); - position: relative; +.user-input button { + display: inline-flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border-radius: 6px; + border: 1px solid var(--border); + background: var(--card); + color: var(--muted-foreground); + cursor: pointer; + transition: all 0.15s; + flex-shrink: 0; + font-size: 0.8125rem; } -.close-button { - position: absolute; - top: 15px; - right: 20px; - font-size: 24px; - cursor: pointer; - color: var(--secondary-color); +.user-input button:hover { + background: var(--accent); + color: var(--accent-foreground); } -.modal h2 { - margin-bottom: 20px; - color: var(--primary-color); +#sendButton { + background: var(--primary); + color: var(--primary-foreground); + border-color: var(--primary); } -.form-group { - margin-bottom: 20px; +#sendButton:hover { + opacity: 0.9; } -.form-group label { - display: block; - margin-bottom: 8px; - font-weight: 500; +#resetButton { + background: var(--card); } -.form-group input, -.form-group select { - width: 100%; - padding: 10px; - border: 1px solid var(--border-color); - border-radius: 4px; - font-size: 0.95rem; +.hidden-input { + display: none; } -.form-group input[type="range"] { - width: calc(100% - 40px); - vertical-align: middle; +/* ========================================================================== + Resizer + ========================================================================== */ +.resizer { + width: 1px; + background-color: var(--border); + cursor: col-resize; + position: relative; + transition: background-color 0.15s; + flex-shrink: 0; } -#temperatureValue { - display: inline-block; - width: 30px; - text-align: right; - margin-left: 5px; +/* Wider invisible grab target */ +.resizer::before { + content: ""; + position: absolute; + top: 0; + bottom: 0; + left: -4px; + right: -4px; + z-index: 10; } -.submit-button { - background-color: var(--accent-color); - color: white; - border: none; - padding: 10px 20px; - border-radius: 4px; - cursor: pointer; - font-size: 1rem; - margin-top: 10px; +.resizer:hover { + background-color: var(--ring); } -.submit-button:hover { - background-color: #218838; +.resizer:active { + background-color: var(--primary); } -/* Responsive Styles */ -@media (max-width: 768px) { - main { - grid-template-columns: 1fr; - } +/* Small drag indicator in the center */ +.resizer::after { + content: ""; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 3px; + height: 28px; + background: var(--border); + border-radius: 2px; + opacity: 0; + transition: opacity 0.15s; +} - .chat-container { - height: 60vh; - } +.resizer:hover::after { + opacity: 1; } -/* ๅœจไฝ ็š„style.cssไธญๆทปๅŠ ไปฅไธ‹ๆ ทๅผ */ +/* ========================================================================== + Right Dashboard + ========================================================================== */ +.dashboard-right { + flex: 1; + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: 1fr 1fr; + gap: 8px; + padding: 8px; + position: relative; + min-width: 400px; + height: 100vh; + box-sizing: border-box; + background: var(--background); + pointer-events: auto; +} + +/* ========================================================================== + Panels + ========================================================================== */ +.panel { + border: 1px solid var(--border); + border-radius: var(--radius); + display: flex; + flex-direction: column; + overflow: hidden; + position: relative; + z-index: 1; + min-height: 0; + min-width: 0; + width: 100%; + height: 100%; + box-sizing: border-box; + background: var(--card); +} -/* Task items styling */ +.panel.fullspan { + grid-column: 1 / -1; + grid-row: 1 / -1; + z-index: 10; +} + +.panel.hidden { + display: none; +} + +/* --- Panel Header --- */ +.panel-header { + display: flex; + align-items: center; + gap: 6px; + padding: 0 12px; + height: 36px; + background: var(--card); + border-bottom: 1px solid var(--border); + font-size: 0.75rem; + font-weight: 500; + color: var(--foreground); + position: relative; + z-index: 100; + flex-shrink: 0; +} + +.panel-header i:first-child { + color: var(--muted-foreground); + font-size: 0.6875rem; +} + +.panel.fullspan .panel-header { + height: 44px; + font-size: 0.875rem; + padding: 0 16px; +} + +/* --- Panel Content --- */ +.panel-content { + flex: 1; + overflow: hidden; + padding: 0; + min-height: 0; + display: flex; + flex-direction: column; + word-wrap: break-word; + word-break: break-all; + overflow-wrap: break-word; + hyphens: auto; + max-width: 100%; +} + +/* --- Fullscreen Button --- */ +.fs-btn { + position: absolute; + top: 50%; + right: 8px; + transform: translateY(-50%); + width: 26px; + height: 26px; + display: inline-flex; + align-items: center; + justify-content: center; + background: transparent; + border: none; + border-radius: 6px; + font-size: 0.6875rem; + color: var(--muted-foreground); + cursor: pointer; + z-index: 101; + pointer-events: auto; + transition: background-color 0.15s, color 0.15s; + line-height: 1; +} + +.fs-btn:hover { + background: var(--accent); + color: var(--accent-foreground); +} + +.fs-btn:focus-visible { + outline: 2px solid var(--ring); + outline-offset: 2px; +} + +.panel.fullspan .fs-btn { + right: 12px; +} + +/* ========================================================================== + Terminal & Notebook + ========================================================================== */ +.terminal-output { + background: #0c0c0c; + color: #4ade80; + font-family: var(--font-mono); + font-size: 0.75rem; + padding: 12px; + flex: 1; + overflow-x: auto; + overflow-y: auto; + white-space: pre-wrap; + word-wrap: break-word; + word-break: normal; + overflow-wrap: break-word; + margin: 0; + min-height: 0; + max-width: 100%; + line-height: 1.6; +} + +.notebook-output { + background: var(--muted); + font-family: var(--font-mono); + font-size: 0.75rem; + padding: 12px; + flex: 1; + overflow-x: auto; + overflow-y: auto; + white-space: pre-wrap; + word-wrap: break-word; + word-break: normal; + overflow-wrap: break-word; + margin: 0; + min-height: 0; + max-width: 100%; + line-height: 1.6; +} + +/* --- Desktop iframe --- */ +.iframe-container { + flex: 1; + min-height: 0; + overflow: hidden; +} + +#desktopFrame { + width: 100%; + height: 100%; + border: none; +} + +/* ========================================================================== + Subtask List + ========================================================================== */ +#subtaskList { + flex: 1; + min-height: 0; + overflow-y: auto; + overflow-x: hidden; + margin: 0; + padding: 8px; + list-style: none; + max-width: 100%; +} + +#subtaskList li { + word-wrap: break-word; + overflow-wrap: break-word; + hyphens: auto; + white-space: normal; + max-width: 100%; +} + +/* --- Task Items --- */ .task-item { - display: flex; - justify-content: space-between; - align-items: center; - padding: 8px 12px; - margin: 4px 0; - background: #f8f9fa; - border-radius: 6px; - border-left: 3px solid #007bff; - transition: all 0.2s ease; + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 12px; + margin-bottom: 4px; + background: var(--muted); + border-radius: 6px; + border-left: 3px solid var(--ring); + transition: background-color 0.15s; } .task-item:hover { - background: #e9ecef; - transform: translateX(2px); + background: var(--accent); } .task-item.completed { - border-left-color: #28a745; - background: #d4edda; - opacity: 0.8; - color: #6c757d; + border-left-color: var(--success); + background: #f0fdf4; + opacity: 0.75; } .task-item.running { - border-left-color: #ffc107; - background: #fff3cd; + border-left-color: var(--warning); + background: #fefce8; } .task-content { - flex: 1; + flex: 1; } .task-name { - display: block; - font-weight: 500; - color: #333; - margin-bottom: 2px; + display: block; + font-weight: 500; + font-size: 0.8125rem; + color: var(--foreground); + margin-bottom: 2px; } .task-description { - font-size: 0.85em; - color: #666; - margin-top: 4px; + font-size: 0.75rem; + color: var(--muted-foreground); + margin-top: 2px; } .task-status { - display: inline-block; - padding: 2px 6px; - border-radius: 12px; - font-size: 0.75em; - font-weight: 500; - text-transform: uppercase; - margin-left: 8px; + display: inline-flex; + align-items: center; + padding: 1px 8px; + border-radius: 9999px; + font-size: 0.625rem; + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.025em; + margin-left: 8px; } .status-pending { - background: #6c757d; - color: white; + background: var(--secondary); + color: var(--secondary-foreground); } .status-running { - background: #ffc107; - color: #212529; + background: #fef9c3; + color: #854d0e; } .status-completed { - background: #28a745; - color: white; + background: #dcfce7; + color: #166534; } .task-actions { - display: flex; - gap: 4px; - opacity: 0; - transition: opacity 0.2s ease; + display: flex; + gap: 4px; + opacity: 0; + transition: opacity 0.15s; } .task-item:hover .task-actions { - opacity: 1; + opacity: 1; } .task-btn { - width: 24px; - height: 24px; - border: none; - border-radius: 50%; - cursor: pointer; - font-size: 12px; - font-weight: bold; - transition: all 0.2s ease; + width: 22px; + height: 22px; + border: none; + border-radius: 6px; + cursor: pointer; + font-size: 0.6875rem; + font-weight: 600; + display: inline-flex; + align-items: center; + justify-content: center; + transition: opacity 0.15s; } .complete-btn { - background: #28a745; - color: white; + background: var(--success); + color: white; } .complete-btn:hover { - background: #218838; - transform: scale(1.1); + opacity: 0.85; } .delete-btn { - background: #dc3545; - color: white; + background: var(--destructive); + color: white; } .delete-btn:hover { - background: #c82333; - transform: scale(1.1); + opacity: 0.85; +} + +/* --- Empty state for subtasks --- */ +#subtaskList li[style*="italic"] { + text-align: center; + padding: 24px 16px; + border: 1px dashed var(--border); + border-radius: var(--radius); + margin: 8px; + font-size: 0.8125rem; + color: var(--muted-foreground); +} + +/* ========================================================================== + Settings Modal + ========================================================================== */ +.modal { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + backdrop-filter: blur(4px); + -webkit-backdrop-filter: blur(4px); + display: none; + justify-content: center; + align-items: center; + z-index: 1000; } -/* Notification styling */ +.modal-content { + background: var(--card); + padding: 24px; + border-radius: 12px; + width: 90%; + max-width: 420px; + border: 1px solid var(--border); + box-shadow: 0 16px 48px rgba(0, 0, 0, 0.12); + position: relative; +} + +.modal-content h2 { + font-size: 1.125rem; + font-weight: 600; + color: var(--foreground); + margin-bottom: 20px; + letter-spacing: -0.01em; +} + +.close-btn { + position: absolute; + top: 16px; + right: 16px; + width: 28px; + height: 28px; + display: inline-flex; + align-items: center; + justify-content: center; + background: transparent; + border: none; + border-radius: 6px; + color: var(--muted-foreground); + cursor: pointer; + transition: background-color 0.15s, color 0.15s; + font-size: 0.875rem; +} + +.close-btn:hover { + background: var(--accent); + color: var(--accent-foreground); +} + +.form-group { + margin-bottom: 16px; +} + +.form-group label { + display: block; + margin-bottom: 6px; + font-weight: 500; + font-size: 0.8125rem; + color: var(--foreground); +} + +.form-group input, +.form-group select { + width: 100%; + padding: 8px 12px; + border: 1px solid var(--input); + border-radius: 6px; + font-size: 0.8125rem; + font-family: var(--font-sans); + color: var(--foreground); + background: var(--card); + outline: none; + transition: border-color 0.15s, box-shadow 0.15s; +} + +.form-group input:focus, +.form-group select:focus { + border-color: var(--ring); + box-shadow: 0 0 0 2px rgba(161, 161, 170, 0.15); +} + +.form-group input[type="range"] { + width: calc(100% - 44px); + vertical-align: middle; + padding: 0; + accent-color: var(--primary); +} + +#temperatureValue { + display: inline-block; + width: 30px; + text-align: right; + margin-left: 8px; + font-size: 0.8125rem; + font-weight: 500; + font-family: var(--font-mono); + color: var(--muted-foreground); +} + +.submit-button { + width: 100%; + padding: 8px 16px; + background: var(--primary); + color: var(--primary-foreground); + border: none; + border-radius: 6px; + font-size: 0.8125rem; + font-weight: 500; + font-family: var(--font-sans); + cursor: pointer; + transition: opacity 0.15s; + margin-top: 8px; +} + +.submit-button:hover { + opacity: 0.9; +} + +/* ========================================================================== + Notifications + ========================================================================== */ .notification { - position: fixed; - top: 20px; - right: 20px; - padding: 12px 20px; - border-radius: 6px; - color: white; - font-weight: 500; - z-index: 1000; - transform: translateX(100%); - animation: slideIn 0.3s ease forwards; + position: fixed; + top: 16px; + right: 16px; + padding: 10px 16px; + border-radius: var(--radius); + font-size: 0.8125rem; + font-weight: 500; + z-index: 1001; + border: 1px solid; + transform: translateX(calc(100% + 20px)); + animation: slideIn 0.2s ease forwards; } .notification-success { - background: #28a745; + background: #f0fdf4; + border-color: #bbf7d0; + color: #166534; } .notification-error { - background: #dc3545; + background: #fef2f2; + border-color: #fecaca; + color: #991b1b; } .notification-info { - background: #17a2b8; + background: #eff6ff; + border-color: #bfdbfe; + color: #1e40af; } .notification.fade-out { - animation: slideOut 0.3s ease forwards; + animation: slideOut 0.2s ease forwards; } @keyframes slideIn { - to { - transform: translateX(0); - } + to { + transform: translateX(0); + } } @keyframes slideOut { - to { - transform: translateX(100%); - } + to { + transform: translateX(calc(100% + 20px)); + } } -/* Empty state */ -#subtaskList li[style*="italic"] { - text-align: center; - padding: 20px; - border: 2px dashed #ddd; - border-radius: 6px; - margin: 10px 0; +/* ========================================================================== + Scrollbars + ========================================================================== */ +::-webkit-scrollbar { + width: 6px; + height: 6px; } -#terminalOutput { - white-space: pre-wrap; - } +::-webkit-scrollbar-track { + background: transparent; +} -/* ่ฎฉ fullspan ๆ—ถๆจช่ทจๆ‰€ๆœ‰ๆ ผๅญ๏ผŒๅนถ็ฝฎ้กถ */ -.panel.fullspan { - grid-column: 1 / -1; - grid-row: 1 / -1; - z-index: 10; - } - - /* ้š่—้ž็›ฎๆ ‡ panel */ - .panel.hidden { - display: none; - } +::-webkit-scrollbar-thumb { + background: var(--border); + border-radius: 3px; +} -/* ไฟ่ฏ dashboard-right ๅŒบๅŸŸๆœฌ่บซๅฏไปฅๆŽฅๆ”ถไบ‹ไปถ */ -.dashboard-right { - position: relative; - pointer-events: auto; - } - -/* 1. Panel ไธ่ฃๅ‰ชๅ…ถๅญๅ…ƒ็ด ๏ผˆๅŒ…ๆ‹ฌ header ๆบขๅ‡บ๏ผ‰ */ -.panel { - position: relative; - overflow: visible !important; - z-index: 1; - } - - /* 2. Header ไฟ่ฏๅœจๆœ€ไธŠๅฑ‚ๅนถไฝฟ็”จ Flex ๅธƒๅฑ€ */ - .panel-header { - position: relative; /* ๅ‚็…ง็ณป๏ผŒไพ›็ปๅฏนๅฎšไฝ็š„ๆŒ‰้’ฎไฝฟ็”จ */ - display: flex; - align-items: center; - justify-content: space-between; - padding: 4px 8px; - background: #fafafa; - z-index: 100; /* ๅŽ‹่ฟ‡ iframe ็ญ‰ๅ†…ๅฎน */ +::-webkit-scrollbar-thumb:hover { + background: var(--ring); +} + +/* ========================================================================== + Responsive + ========================================================================== */ +@media (max-width: 900px) { + .chat-area { + min-width: 240px; + flex-basis: 30%; } - - /* ---------------------------- - ๅ…จๅฑๆŒ‰้’ฎๆ ทๅผ - ---------------------------- */ - - /* 3. ๆŒ‰้’ฎ็ปๅฏนๅฎšไฝๅˆฐ header ๅณไธŠ่ง’ */ - .fs-btn { - position: absolute; - top: 4px; - right: 4px; - width: 24px; - height: 24px; - line-height: 24px; - text-align: center; - - background: transparent; - border: none; - font-size: 1rem; - cursor: pointer; - - z-index: 101; /* ้ซ˜ไบŽ header ๆœฌ่บซ */ - pointer-events: auto; + + .dashboard-right { + min-width: 300px; } - - /* 4. Hover ๅ้ฆˆ */ - .fs-btn:hover { - background: rgba(0, 0, 0, 0.05); - border-radius: 4px; - } \ No newline at end of file +} + +#terminalOutput { + white-space: pre-wrap; +} diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js new file mode 100644 index 0000000..ec2b712 --- /dev/null +++ b/frontend/eslint.config.js @@ -0,0 +1,33 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' + +export default [ + { ignores: ['dist'] }, + { + files: ['**/*.{js,jsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + parserOptions: { + ecmaVersion: 'latest', + ecmaFeatures: { jsx: true }, + sourceType: 'module', + }, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...js.configs.recommended.rules, + ...reactHooks.configs.recommended.rules, + 'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }], + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, + }, +] diff --git a/frontend/index.html b/frontend/index.html index d04d1c3..fb06c09 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -3,328 +3,38 @@ - Infant AI Assistant Dashboard + InfantAgent - -
-
-
- Logo -

Infant AI Assistant

+
+
+ Logo +

InfantAgent

-
-

Welcome to Infant AI Assistant! How can I help you today?

+

Welcome to InfantAgent! How can I help you today?

-
+
- Agent Status: + Status: Ready
- Current Task: + Task: None
@@ -334,24 +44,24 @@

Infant AI Assistant

- + - + - +
- +
- +
- + Subtasks
@@ -359,10 +69,10 @@

Infant AI Assistant

    - +
    - + System Desktop
    @@ -370,16 +80,16 @@

    Infant AI Assistant

    - +
    - + Terminal
    @@ -387,10 +97,10 @@

    Infant AI Assistant

    - +
    - + Jupyter Code
    @@ -408,24 +118,24 @@

    Infant AI Assistant

    Settings

    - +
    -
    - - +
    + +
    - + 0.9
    - +
    @@ -437,47 +147,39 @@

    Settings

    - + - - \ No newline at end of file + diff --git a/frontend/js/script.js b/frontend/js/script.js index a79978d..24e4524 100644 --- a/frontend/js/script.js +++ b/frontend/js/script.js @@ -517,12 +517,32 @@ document.addEventListener('DOMContentLoaded', () => { uploadFolderBtn.addEventListener('click', () => fileUploadFolder.click()); fileUploadFolder.addEventListener('change', handleFileUpload); + // Initialize Guacamole desktop iframe with auth token + loadGuacamoleDesktop(); + // ๅˆๅง‹ๅŒ–่ฟžๆŽฅ console.log('[Debug] connectToBackend() ๅผ€ๅง‹ๆ‰ง่กŒ'); connectToBackend(); }); -// ๅœจไฝ ็š„script.jsไธญๆทปๅŠ ไปฅไธ‹ไปฃ็  +// Load Guacamole desktop with auto-login +async function loadGuacamoleDesktop() { + const frame = document.getElementById('desktopFrame'); + try { + const res = await fetch('/api/guacamole-token'); + const data = await res.json(); + if (data.success) { + // Connection ID: base64("GNOME Desktop (RDP)\0c\0default") + frame.src = `/guacamole/#/client/R05PTUUgRGVza3RvcCAoUkRQKQBjAGRlZmF1bHQ?token=${data.token}`; + } else { + console.error('Failed to get Guacamole token:', data.error); + frame.src = '/guacamole/'; + } + } catch (e) { + console.error('Failed to load Guacamole desktop:', e); + frame.src = '/guacamole/'; + } +} // ๅ…จๅฑ€ๅ˜้‡ๅญ˜ๅ‚จEventSource let tasksEventSource = null; diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..4797836 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,3699 @@ +{ + "name": "frontend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "0.0.0", + "dependencies": { + "@fontsource/roboto": "^5.2.5", + "@mui/icons-material": "^7.1.0", + "@mui/material": "^7.1.0", + "@mui/styled-engine-sc": "^7.1.0", + "@tailwindcss/vite": "^4.1.7", + "lucide-react": "^0.511.0", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "styled-components": "^6.1.18", + "tailwindcss": "^4.1.7" + }, + "devDependencies": { + "@eslint/js": "^9.25.0", + "@types/react": "^19.1.2", + "@types/react-dom": "^19.1.2", + "@vitejs/plugin-react": "^4.4.1", + "eslint": "^9.25.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^16.0.0", + "vite": "^6.3.5" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.2.tgz", + "integrity": "sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.1.tgz", + "integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.1", + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helpers": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/template": "^7.27.1", + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz", + "integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.27.1", + "@babel/types": "^7.27.1", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.1.tgz", + "integrity": "sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz", + "integrity": "sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz", + "integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.27.1" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz", + "integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz", + "integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz", + "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", + "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", + "dependencies": { + "@emotion/memoize": "^0.8.1" + } + }, + "node_modules/@emotion/is-prop-valid/node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==" + }, + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==" + }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==" + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", + "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", + "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", + "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", + "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", + "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", + "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", + "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", + "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", + "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", + "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", + "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", + "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", + "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", + "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", + "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", + "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz", + "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", + "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", + "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", + "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", + "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", + "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", + "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", + "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", + "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", + "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", + "dev": true, + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.2.tgz", + "integrity": "sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", + "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz", + "integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz", + "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==", + "dev": true, + "dependencies": { + "@eslint/core": "^0.14.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@fontsource/roboto": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/@fontsource/roboto/-/roboto-5.2.5.tgz", + "integrity": "sha512-70r2UZ0raqLn5W+sPeKhqlf8wGvUXFWlofaDlcbt/S3d06+17gXKr3VNqDODB0I1ASme3dGT5OJj9NABt7OTZQ==", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mui/core-downloads-tracker": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.1.0.tgz", + "integrity": "sha512-E0OqhZv548Qdc0PwWhLVA2zmjJZSTvaL4ZhoswmI8NJEC1tpW2js6LLP827jrW9MEiXYdz3QS6+hask83w74yQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/icons-material": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-7.1.0.tgz", + "integrity": "sha512-1mUPMAZ+Qk3jfgL5ftRR06ATH/Esi0izHl1z56H+df6cwIlCWG66RXciUqeJCttbOXOQ5y2DCjLZI/4t3Yg3LA==", + "dependencies": { + "@babel/runtime": "^7.27.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@mui/material": "^7.1.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.1.0.tgz", + "integrity": "sha512-ahUJdrhEv+mCp4XHW+tHIEYzZMSRLg8z4AjUOsj44QpD1ZaMxQoVOG2xiHvLFdcsIPbgSRx1bg1eQSheHBgvtg==", + "dependencies": { + "@babel/runtime": "^7.27.1", + "@mui/core-downloads-tracker": "^7.1.0", + "@mui/system": "^7.1.0", + "@mui/types": "^7.4.2", + "@mui/utils": "^7.1.0", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.12", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1", + "react-is": "^19.1.0", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material-pigment-css": "^7.1.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@mui/material-pigment-css": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/private-theming": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.1.0.tgz", + "integrity": "sha512-4Kck4jxhqF6YxNwJdSae1WgDfXVg0lIH6JVJ7gtuFfuKcQCgomJxPvUEOySTFRPz1IZzwz5OAcToskRdffElDA==", + "dependencies": { + "@babel/runtime": "^7.27.1", + "@mui/utils": "^7.1.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.1.0.tgz", + "integrity": "sha512-m0mJ0c6iRC+f9hMeRe0W7zZX1wme3oUX0+XTVHjPG7DJz6OdQ6K/ggEOq7ZdwilcpdsDUwwMfOmvO71qDkYd2w==", + "dependencies": { + "@babel/runtime": "^7.27.1", + "@emotion/cache": "^11.13.5", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine-sc": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@mui/styled-engine-sc/-/styled-engine-sc-7.1.0.tgz", + "integrity": "sha512-6ZIB0uwLN1MtJAmAZgtwDJMMNhSpHQL6K+hWaikPnPjyTe8qyS14fhvoelD16qOp/c2abE2ZSqMQKlDiyApDig==", + "dependencies": { + "@babel/runtime": "^7.27.1", + "@types/hoist-non-react-statics": "^3.3.6", + "csstype": "^3.1.3", + "hoist-non-react-statics": "^3.3.2", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "styled-components": "^6.0.0" + } + }, + "node_modules/@mui/system": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.1.0.tgz", + "integrity": "sha512-iedAWgRJMCxeMHvkEhsDlbvkK+qKf9me6ofsf7twk/jfT4P1ImVf7Rwb5VubEA0sikrVL+1SkoZM41M4+LNAVA==", + "dependencies": { + "@babel/runtime": "^7.27.1", + "@mui/private-theming": "^7.1.0", + "@mui/styled-engine": "^7.1.0", + "@mui/types": "^7.4.2", + "@mui/utils": "^7.1.0", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.2.tgz", + "integrity": "sha512-edRc5JcLPsrlNFYyTPxds+d5oUovuUxnnDtpJUbP6WMeV4+6eaX/mqai1ZIWT62lCOe0nlrON0s9HDiv5en5bA==", + "dependencies": { + "@babel/runtime": "^7.27.1" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.1.0.tgz", + "integrity": "sha512-/OM3S8kSHHmWNOP+NH9xEtpYSG10upXeQ0wLZnfDgmgadTAk5F4MQfFLyZ5FCRJENB3eRzltMmaNl6UtDnPovw==", + "dependencies": { + "@babel/runtime": "^7.27.1", + "@mui/types": "^7.4.2", + "@types/prop-types": "^15.7.14", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.1.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.9", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.9.tgz", + "integrity": "sha512-e9MeMtVWo186sgvFFJOPGy7/d2j2mZhLJIdVW0C/xDluuOvymEATqz6zKsP0ZmXGzQtqlyjz5sC1sYQUoJG98w==", + "dev": true + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.1.tgz", + "integrity": "sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.41.1.tgz", + "integrity": "sha512-DXdQe1BJ6TK47ukAoZLehRHhfKnKg9BjnQYUu9gzhI8Mwa1d2fzxA1aw2JixHVl403bwp1+/o/NhhHtxWJBgEA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.1.tgz", + "integrity": "sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.41.1.tgz", + "integrity": "sha512-egpJACny8QOdHNNMZKf8xY0Is6gIMz+tuqXlusxquWu3F833DcMwmGM7WlvCO9sB3OsPjdC4U0wHw5FabzCGZg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.41.1.tgz", + "integrity": "sha512-DBVMZH5vbjgRk3r0OzgjS38z+atlupJ7xfKIDJdZZL6sM6wjfDNo64aowcLPKIx7LMQi8vybB56uh1Ftck/Atg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.41.1.tgz", + "integrity": "sha512-3FkydeohozEskBxNWEIbPfOE0aqQgB6ttTkJ159uWOFn42VLyfAiyD9UK5mhu+ItWzft60DycIN1Xdgiy8o/SA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.41.1.tgz", + "integrity": "sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.41.1.tgz", + "integrity": "sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.41.1.tgz", + "integrity": "sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.41.1.tgz", + "integrity": "sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.41.1.tgz", + "integrity": "sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.41.1.tgz", + "integrity": "sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.41.1.tgz", + "integrity": "sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.41.1.tgz", + "integrity": "sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.41.1.tgz", + "integrity": "sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.1.tgz", + "integrity": "sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.41.1.tgz", + "integrity": "sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.41.1.tgz", + "integrity": "sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.41.1.tgz", + "integrity": "sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.1.tgz", + "integrity": "sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.7.tgz", + "integrity": "sha512-9rsOpdY9idRI2NH6CL4wORFY0+Q6fnx9XP9Ju+iq/0wJwGD5IByIgFmwVbyy4ymuyprj8Qh4ErxMKTUL4uNh3g==", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "enhanced-resolve": "^5.18.1", + "jiti": "^2.4.2", + "lightningcss": "1.30.1", + "magic-string": "^0.30.17", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.7" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.7.tgz", + "integrity": "sha512-5SF95Ctm9DFiUyjUPnDGkoKItPX/k+xifcQhcqX5RA85m50jw1pT/KzjdvlqxRja45Y52nR4MR9fD1JYd7f8NQ==", + "hasInstallScript": true, + "dependencies": { + "detect-libc": "^2.0.4", + "tar": "^7.4.3" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.7", + "@tailwindcss/oxide-darwin-arm64": "4.1.7", + "@tailwindcss/oxide-darwin-x64": "4.1.7", + "@tailwindcss/oxide-freebsd-x64": "4.1.7", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.7", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.7", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.7", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.7", + "@tailwindcss/oxide-linux-x64-musl": "4.1.7", + "@tailwindcss/oxide-wasm32-wasi": "4.1.7", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.7", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.7" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.7.tgz", + "integrity": "sha512-IWA410JZ8fF7kACus6BrUwY2Z1t1hm0+ZWNEzykKmMNM09wQooOcN/VXr0p/WJdtHZ90PvJf2AIBS/Ceqx1emg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.7.tgz", + "integrity": "sha512-81jUw9To7fimGGkuJ2W5h3/oGonTOZKZ8C2ghm/TTxbwvfSiFSDPd6/A/KE2N7Jp4mv3Ps9OFqg2fEKgZFfsvg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.7.tgz", + "integrity": "sha512-q77rWjEyGHV4PdDBtrzO0tgBBPlQWKY7wZK0cUok/HaGgbNKecegNxCGikuPJn5wFAlIywC3v+WMBt0PEBtwGw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.7.tgz", + "integrity": "sha512-RfmdbbK6G6ptgF4qqbzoxmH+PKfP4KSVs7SRlTwcbRgBwezJkAO3Qta/7gDy10Q2DcUVkKxFLXUQO6J3CRvBGw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.7.tgz", + "integrity": "sha512-OZqsGvpwOa13lVd1z6JVwQXadEobmesxQ4AxhrwRiPuE04quvZHWn/LnihMg7/XkN+dTioXp/VMu/p6A5eZP3g==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.7.tgz", + "integrity": "sha512-voMvBTnJSfKecJxGkoeAyW/2XRToLZ227LxswLAwKY7YslG/Xkw9/tJNH+3IVh5bdYzYE7DfiaPbRkSHFxY1xA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.7.tgz", + "integrity": "sha512-PjGuNNmJeKHnP58M7XyjJyla8LPo+RmwHQpBI+W/OxqrwojyuCQ+GUtygu7jUqTEexejZHr/z3nBc/gTiXBj4A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.7.tgz", + "integrity": "sha512-HMs+Va+ZR3gC3mLZE00gXxtBo3JoSQxtu9lobbZd+DmfkIxR54NO7Z+UQNPsa0P/ITn1TevtFxXTpsRU7qEvWg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.7.tgz", + "integrity": "sha512-MHZ6jyNlutdHH8rd+YTdr3QbXrHXqwIhHw9e7yXEBcQdluGwhpQY2Eku8UZK6ReLaWtQ4gijIv5QoM5eE+qlsA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.7.tgz", + "integrity": "sha512-ANaSKt74ZRzE2TvJmUcbFQ8zS201cIPxUDm5qez5rLEwWkie2SkGtA4P+GPTj+u8N6JbPrC8MtY8RmJA35Oo+A==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@emnapi/wasi-threads": "^1.0.2", + "@napi-rs/wasm-runtime": "^0.2.9", + "@tybys/wasm-util": "^0.9.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.7.tgz", + "integrity": "sha512-HUiSiXQ9gLJBAPCMVRk2RT1ZrBjto7WvqsPBwUrNK2BcdSxMnk19h4pjZjI7zgPhDxlAbJSumTC4ljeA9y0tEw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.7.tgz", + "integrity": "sha512-rYHGmvoHiLJ8hWucSfSOEmdCBIGZIq7SpkPRSqLsH2Ab2YUNgKeAPT1Fi2cx3+hnYOrAb0jp9cRyode3bBW4mQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.7.tgz", + "integrity": "sha512-tYa2fO3zDe41I7WqijyVbRd8oWT0aEID1Eokz5hMT6wShLIHj3yvwj9XbfuloHP9glZ6H+aG2AN/+ZrxJ1Y5RQ==", + "dependencies": { + "@tailwindcss/node": "4.1.7", + "@tailwindcss/oxide": "4.1.7", + "tailwindcss": "4.1.7" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", + "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==" + }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.6.tgz", + "integrity": "sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw==", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/prop-types": { + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==" + }, + "node_modules/@types/react": { + "version": "19.1.5", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.5.tgz", + "integrity": "sha512-piErsCVVbpMMT2r7wbawdZsq4xMvIAhQuac2gedQHysu1TZYEigE6pnFfgZT+/jQnrRuF5r+SHzuehFjfRjr4g==", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.1.5", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.5.tgz", + "integrity": "sha512-CMCjrWucUBZvohgZxkjd6S9h0nZxXjzus6yDfUb+xLxYM7VvjKNH1tQrE9GWLql1XoOP4/Ds3bwFqShHUYraGg==", + "dev": true, + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "peerDependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/stylis": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz", + "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==" + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.5.0.tgz", + "integrity": "sha512-JuLWaEqypaJmOJPLWwO335Ig6jSgC1FTONCWAxnqcQthLTK/Yc9aH6hr9z/87xciejbQcnP3GnA1FWUSWeXaeg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.26.10", + "@babel/plugin-transform-react-jsx-self": "^7.25.9", + "@babel/plugin-transform-react-jsx-source": "^7.25.9", + "@rolldown/pluginutils": "1.0.0-beta.9", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" + } + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.24.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz", + "integrity": "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001716", + "electron-to-chromium": "^1.5.149", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001718", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz", + "integrity": "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "engines": { + "node": ">=18" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.157", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.157.tgz", + "integrity": "sha512-/0ybgsQd1muo8QlnuTpKwtl0oX5YMlUGbm8xyqgDU00motRkKFFbUJySAQBWcY79rVqNLWIWa87BGVGClwAB2w==", + "dev": true + }, + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/esbuild": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", + "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.4", + "@esbuild/android-arm": "0.25.4", + "@esbuild/android-arm64": "0.25.4", + "@esbuild/android-x64": "0.25.4", + "@esbuild/darwin-arm64": "0.25.4", + "@esbuild/darwin-x64": "0.25.4", + "@esbuild/freebsd-arm64": "0.25.4", + "@esbuild/freebsd-x64": "0.25.4", + "@esbuild/linux-arm": "0.25.4", + "@esbuild/linux-arm64": "0.25.4", + "@esbuild/linux-ia32": "0.25.4", + "@esbuild/linux-loong64": "0.25.4", + "@esbuild/linux-mips64el": "0.25.4", + "@esbuild/linux-ppc64": "0.25.4", + "@esbuild/linux-riscv64": "0.25.4", + "@esbuild/linux-s390x": "0.25.4", + "@esbuild/linux-x64": "0.25.4", + "@esbuild/netbsd-arm64": "0.25.4", + "@esbuild/netbsd-x64": "0.25.4", + "@esbuild/openbsd-arm64": "0.25.4", + "@esbuild/openbsd-x64": "0.25.4", + "@esbuild/sunos-x64": "0.25.4", + "@esbuild/win32-arm64": "0.25.4", + "@esbuild/win32-ia32": "0.25.4", + "@esbuild/win32-x64": "0.25.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz", + "integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.20.0", + "@eslint/config-helpers": "^0.2.1", + "@eslint/core": "^0.14.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.27.0", + "@eslint/plugin-kit": "^0.3.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.3.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.20", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.20.tgz", + "integrity": "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==", + "dev": true, + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", + "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.2.0.tgz", + "integrity": "sha512-O+7l9tPdHCU320IigZZPj5zmRCFG9xHmx9cU8FqU2Rp+JN714seHV+2S9+JslCpY4gJwU2vOGox0wzgae/MCEg==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.30.1", + "lightningcss-darwin-x64": "1.30.1", + "lightningcss-freebsd-x64": "1.30.1", + "lightningcss-linux-arm-gnueabihf": "1.30.1", + "lightningcss-linux-arm64-gnu": "1.30.1", + "lightningcss-linux-arm64-musl": "1.30.1", + "lightningcss-linux-x64-gnu": "1.30.1", + "lightningcss-linux-x64-musl": "1.30.1", + "lightningcss-win32-arm64-msvc": "1.30.1", + "lightningcss-win32-x64-msvc": "1.30.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "0.511.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.511.0.tgz", + "integrity": "sha512-VK5a2ydJ7xm8GvBeKLS9mu1pVK6ucef9780JVUjw6bAjJL/QXnd4Y0p7SPeOUMC27YhzNCZvm5d/QX0Tp3rc0w==", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "dependencies": { + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.0" + } + }, + "node_modules/react-is": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz", + "integrity": "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==" + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/rollup": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.1.tgz", + "integrity": "sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw==", + "dependencies": { + "@types/estree": "1.0.7" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.41.1", + "@rollup/rollup-android-arm64": "4.41.1", + "@rollup/rollup-darwin-arm64": "4.41.1", + "@rollup/rollup-darwin-x64": "4.41.1", + "@rollup/rollup-freebsd-arm64": "4.41.1", + "@rollup/rollup-freebsd-x64": "4.41.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.41.1", + "@rollup/rollup-linux-arm-musleabihf": "4.41.1", + "@rollup/rollup-linux-arm64-gnu": "4.41.1", + "@rollup/rollup-linux-arm64-musl": "4.41.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.41.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.41.1", + "@rollup/rollup-linux-riscv64-gnu": "4.41.1", + "@rollup/rollup-linux-riscv64-musl": "4.41.1", + "@rollup/rollup-linux-s390x-gnu": "4.41.1", + "@rollup/rollup-linux-x64-gnu": "4.41.1", + "@rollup/rollup-linux-x64-musl": "4.41.1", + "@rollup/rollup-win32-arm64-msvc": "4.41.1", + "@rollup/rollup-win32-ia32-msvc": "4.41.1", + "@rollup/rollup-win32-x64-msvc": "4.41.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/styled-components": { + "version": "6.1.18", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.18.tgz", + "integrity": "sha512-Mvf3gJFzZCkhjY2Y/Fx9z1m3dxbza0uI9H1CbNZm/jSHCojzJhQ0R7bByrlFJINnMzz/gPulpoFFGymNwrsMcw==", + "dependencies": { + "@emotion/is-prop-valid": "1.2.2", + "@emotion/unitless": "0.8.1", + "@types/stylis": "4.2.5", + "css-to-react-native": "3.2.0", + "csstype": "3.1.3", + "postcss": "8.4.49", + "shallowequal": "1.1.0", + "stylis": "4.3.2", + "tslib": "2.6.2" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0" + } + }, + "node_modules/styled-components/node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + }, + "node_modules/styled-components/node_modules/stylis": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", + "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==" + }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tailwindcss": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.7.tgz", + "integrity": "sha512-kr1o/ErIdNhTz8uzAYL7TpaUuzKIE6QPQ4qmSdxnoX/lo+5wmUHQA6h3L5yIqEImSRnAAURDirLu/BgiXGPAhg==" + }, + "node_modules/tapable": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", + "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..b008c4b --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,35 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@fontsource/roboto": "^5.2.5", + "@mui/icons-material": "^7.1.0", + "@mui/material": "^7.1.0", + "@mui/styled-engine-sc": "^7.1.0", + "@tailwindcss/vite": "^4.1.7", + "lucide-react": "^0.511.0", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "styled-components": "^6.1.18", + "tailwindcss": "^4.1.7" + }, + "devDependencies": { + "@eslint/js": "^9.25.0", + "@types/react": "^19.1.2", + "@types/react-dom": "^19.1.2", + "@vitejs/plugin-react": "^4.4.1", + "eslint": "^9.25.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^16.0.0", + "vite": "^6.3.5" + } +} diff --git a/frontend/public/vite.svg b/frontend/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/frontend/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/App.css b/frontend/src/App.css new file mode 100644 index 0000000..f1d8c73 --- /dev/null +++ b/frontend/src/App.css @@ -0,0 +1 @@ +@import "tailwindcss"; diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx new file mode 100644 index 0000000..4e2844c --- /dev/null +++ b/frontend/src/App.jsx @@ -0,0 +1,20 @@ +// src/App.jsx +import { useState } from 'react'; +import './App.css'; +import Chat from './components/Chat'; +import InfantBackendConnector from './services/BackendService'; + +function App() { + const backendConnector = new InfantBackendConnector({ apiUrl: '/api' }); + + + return ( +
    + + + +
    + ); +} + +export default App; \ No newline at end of file diff --git a/frontend/src/assets/react.svg b/frontend/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/frontend/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/components/Chat.jsx b/frontend/src/components/Chat.jsx new file mode 100644 index 0000000..1e2dd78 --- /dev/null +++ b/frontend/src/components/Chat.jsx @@ -0,0 +1,308 @@ +import React, { useState, useRef, useEffect } from 'react'; +import { Send, Bot, User, Paperclip, X, FileText, Image, File } from 'lucide-react'; + +const Chat = ({ backendConnector }) => { + const [messages, setMessages] = useState([ + { id: 1, text: "Hello! I'm here to help. What would you like to chat about?", sender: 'bot', timestamp: new Date() } + ]); + const [input, setInput] = useState(''); + const [isStreaming, setIsStreaming] = useState(false); + const [streamingMessage, setStreamingMessage] = useState(''); + const [attachedFiles, setAttachedFiles] = useState([]); + const messagesEndRef = useRef(null); + const inputRef = useRef(null); + const fileInputRef = useRef(null); + + const scrollToBottom = () => { + messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); + }; + + useEffect(() => { + scrollToBottom(); + }, [messages, streamingMessage]); + + const getFileIcon = (fileType) => { + if (fileType.startsWith('image/')) return Image; + if (fileType.includes('text') || fileType.includes('document')) return FileText; + return File; + }; + + const formatFileSize = (bytes) => { + if (bytes === 0) return '0 Bytes'; + const k = 1024; + const sizes = ['Bytes', 'KB', 'MB', 'GB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; + }; + + const handleFileSelect = (event) => { + const files = Array.from(event.target.files); + const newFiles = files.map(file => ({ + id: Date.now() + Math.random(), + file: file, + name: file.name, + size: file.size, + type: file.type + })); + + setAttachedFiles(prev => [...prev, ...newFiles]); + // Reset file input + if (fileInputRef.current) { + fileInputRef.current.value = ''; + } + }; + + const removeFile = (fileId) => { + setAttachedFiles(prev => prev.filter(f => f.id !== fileId)); + }; + + const handleFileUpload = () => { + fileInputRef.current?.click(); + }; + + const simulateStreamingResponse = async (userMessage, files) => { + // Simulate different responses based on user input and files + let responses = [ + "That's an interesting question! Let me think about this carefully. Streaming responses are great for creating more engaging user experiences because they provide immediate feedback and make the interaction feel more natural and conversational.", + "I understand what you're asking about. Real-time streaming in chat applications works by sending data in chunks rather than waiting for the complete response. This creates a more dynamic and responsive feel for users.", + "Great point! When implementing streaming functionality, you'll want to consider factors like error handling, connection stability, and user experience. The key is to balance responsiveness with reliability.", + "Thanks for that question! Streaming chat interfaces have become increasingly popular because they reduce perceived latency and keep users engaged throughout longer responses. The technical implementation can vary depending on your backend architecture." + ]; + + if (files && files.length > 0) { + const fileTypes = files.map(f => f.type); + if (fileTypes.some(type => type.startsWith('image/'))) { + responses = [ + "I can see you've uploaded some images! While I can't actually process images in this demo, in a real implementation I would analyze the visual content and provide relevant insights or descriptions.", + "Thanks for sharing those images! In a production chat system, I would be able to examine the visual content, identify objects, read text, and answer questions about what I see in the images.", + "Great! You've attached some image files. Image analysis capabilities would allow me to describe scenes, identify objects, read text from images, and help with visual questions." + ]; + } else if (fileTypes.some(type => type.includes('text') || type.includes('document'))) { + responses = [ + "I notice you've uploaded some documents! In a real implementation, I would read through the content and help you analyze, summarize, or answer questions about the text.", + "Thanks for the document upload! With proper file processing, I could extract the text content, provide summaries, answer questions about the material, or help with analysis.", + "Excellent! You've shared some text documents. Document processing would allow me to read the content and assist with comprehension, analysis, or specific questions about the material." + ]; + } else { + responses = [ + "I see you've uploaded some files! While this is a demo interface, a production system would process these files appropriately based on their type and provide relevant assistance.", + "Thanks for the file upload! Different file types would be handled accordingly - images analyzed, documents read, data files processed, etc.", + "Great! You've attached some files. In a real implementation, I would process these based on their format and help you work with the content." + ]; + } + } + + const response = responses[Math.floor(Math.random() * responses.length)]; + const words = response.split(' '); + + setIsStreaming(true); + setStreamingMessage(''); + + for (let i = 0; i < words.length; i++) { + await new Promise(resolve => setTimeout(resolve, 50 + Math.random() * 100)); + setStreamingMessage(prev => prev + (i === 0 ? words[i] : ' ' + words[i])); + } + + // Complete the streaming and add to messages + const newMessage = { + id: Date.now(), + text: response, + sender: 'bot', + timestamp: new Date() + }; + + setMessages(prev => [...prev, newMessage]); + setStreamingMessage(''); + setIsStreaming(false); + }; + + const handleSend = async () => { + if ((!input.trim() && attachedFiles.length === 0) || isStreaming) return; + + const userMessage = { + id: Date.now(), + text: input.trim(), + sender: 'user', + timestamp: new Date(), + files: attachedFiles.length > 0 ? [...attachedFiles] : undefined + }; + + setMessages(prev => [...prev, userMessage]); + setInput(''); + setAttachedFiles([]); + + // Start streaming response + await simulateStreamingResponse(userMessage.text, userMessage.files); + }; + + const handleKeyPress = (e) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + handleSend(); + } + }; + + const formatTime = (timestamp) => { + return timestamp.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); + }; + + return ( +
    + {/* Header */} +
    +

    + + Streaming Chat + Chat +

    +
    + + {/* Messages Container */} +
    + {messages.map((message) => ( +
    +
    +
    + {message.sender === 'user' ? ( + + ) : ( + + )} +
    +
    +

    {message.text}

    + + {/* File attachments */} + {message.files && message.files.length > 0 && ( +
    + {message.files.map((file) => { + const IconComponent = getFileIcon(file.type); + return ( +
    + + {file.name} + {formatFileSize(file.size)} +
    + ); + })} +
    + )} + +

    + {formatTime(message.timestamp)} +

    +
    +
    +
    + ))} + + {/* Streaming Message */} + {isStreaming && ( +
    +
    +
    + +
    +
    +

    + {streamingMessage} + +

    +
    +
    +
    + )} + +
    +
    + + {/* Input Area */} +
    + {/* File Preview */} + {attachedFiles.length > 0 && ( +
    +
    + {attachedFiles.map((file) => { + const IconComponent = getFileIcon(file.type); + return ( +
    + + {file.name} + {formatFileSize(file.size)} + +
    + ); + })} +
    +
    + )} + +
    +
    + +