diff --git a/PLAN.md b/PLAN.md new file mode 100644 index 000000000..aa1623801 --- /dev/null +++ b/PLAN.md @@ -0,0 +1,186 @@ +# Notifications Feature Implementation Plan + +## Overview +Add push notifications for print events (start, complete, fail) with support for multiple notification providers. + +## Supported Providers (Initial Release) +1. **CallMeBot/WhatsApp** - Free, uses HTTP API with phone number + API key +2. **ntfy** - Self-hosted or ntfy.sh, simple HTTP POST +3. **Pushover** - Commercial ($5 one-time), HTTP API with user key + app token +4. **Telegram** - Free bot API, requires bot token + chat ID +5. **Email (SMTP)** - Universal fallback + +## Database Design + +### New Table: `notification_providers` +```sql +CREATE TABLE notification_providers ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL, -- User-defined name ("My WhatsApp") + provider_type TEXT NOT NULL, -- "callmebot", "ntfy", "pushover", "telegram", "email" + enabled BOOLEAN DEFAULT true, + + -- Provider-specific config (JSON or individual fields) + config TEXT NOT NULL, -- JSON: {"phone": "+1234", "apikey": "xxx"} + + -- Event triggers (which events send notifications) + on_print_start BOOLEAN DEFAULT false, + on_print_complete BOOLEAN DEFAULT true, + on_print_failed BOOLEAN DEFAULT true, + + -- Optional: Link to specific printer (NULL = all printers) + printer_id INTEGER REFERENCES printers(id) ON DELETE SET NULL, + + -- Timestamps + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP +); +``` + +### Config JSON Structure by Provider +```python +# CallMeBot/WhatsApp +{"phone": "+1234567890", "apikey": "123456"} + +# ntfy +{"server": "https://ntfy.sh", "topic": "my-printer", "auth_token": "optional"} + +# Pushover +{"user_key": "xxx", "app_token": "yyy", "priority": 0} + +# Telegram +{"bot_token": "123:ABC", "chat_id": "12345678"} + +# Email (SMTP) +{"smtp_server": "smtp.gmail.com", "smtp_port": 587, "username": "x", "password": "y", "from_email": "x@gmail.com", "to_email": "dest@example.com"} +``` + +## Backend Implementation + +### 1. Model: `backend/app/models/notification.py` +- SQLAlchemy model for `notification_providers` table +- Relationship to Printer (optional, nullable) + +### 2. Schema: `backend/app/schemas/notification.py` +- `NotificationProviderBase` - Common fields +- `NotificationProviderCreate` - For creating new providers +- `NotificationProviderUpdate` - For partial updates +- `NotificationProviderResponse` - API response with id/timestamps +- `NotificationTestRequest` - For testing notifications + +### 3. Service: `backend/app/services/notification_service.py` +Core notification dispatcher with provider implementations: + +```python +class NotificationService: + async def send_notification(self, provider: NotificationProvider, event: str, data: dict) -> bool + async def on_print_start(self, printer_id: int, data: dict, db: AsyncSession) + async def on_print_complete(self, printer_id: int, status: str, data: dict, db: AsyncSession) + + # Provider-specific methods + async def _send_callmebot(self, config: dict, message: str) -> bool + async def _send_ntfy(self, config: dict, title: str, message: str) -> bool + async def _send_pushover(self, config: dict, title: str, message: str) -> bool + async def _send_telegram(self, config: dict, message: str) -> bool + async def _send_email(self, config: dict, subject: str, body: str) -> bool +``` + +### 4. Routes: `backend/app/api/routes/notifications.py` +``` +GET /notifications/ - List all providers +POST /notifications/ - Create provider +GET /notifications/{id} - Get provider details +PATCH /notifications/{id} - Update provider +DELETE /notifications/{id} - Delete provider +POST /notifications/{id}/test - Send test notification +POST /notifications/test-config - Test config before saving +``` + +### 5. Integration in `main.py` +Add calls to notification service in existing event handlers: +- `on_print_start()` - After smart_plug_manager call (line ~244) +- `on_print_complete()` - After archive update (line ~580) + +## Frontend Implementation + +### 1. API Client: `frontend/src/api/client.ts` +Add types and API methods for notification providers. + +### 2. Components +- `NotificationProviderCard.tsx` - Display single provider with enable/disable toggle +- `AddNotificationModal.tsx` - Modal for adding/editing providers with provider-specific forms + +### 3. Settings Page Integration +Add "Notifications" section in SettingsPage.tsx (similar to Smart Plugs section): +- List of configured providers +- Add button +- Per-provider enable/disable +- Test button +- Event toggles (start/complete/failed) + +## Message Templates + +### Print Started +``` +🖨️ Print Started +{printer_name}: {filename} +Estimated time: {est_time} +``` + +### Print Completed +``` +✅ Print Completed +{printer_name}: {filename} +Time: {actual_time} +Filament: {filament_used}g +``` + +### Print Failed +``` +❌ Print Failed +{printer_name}: {filename} +Status: {failure_reason} +Progress: {progress}% +``` + +## Implementation Order + +### Phase 1: Backend Core +1. Create notification model with migrations +2. Create notification schema +3. Create notification service with all 5 providers +4. Create notification routes (CRUD + test) +5. Register routes in main.py +6. Integrate into print event handlers + +### Phase 2: Frontend +7. Add API types and methods +8. Create NotificationProviderCard component +9. Create AddNotificationModal component +10. Add Notifications section to SettingsPage + +### Phase 3: Testing & Polish +11. Test each provider +12. Add error handling and logging +13. Handle network failures gracefully (don't block print events) + +## Technical Notes + +### Async HTTP Requests +Use `httpx` (already available) for async HTTP calls to notification APIs. + +### Error Handling +- Notifications should NEVER block print events +- Log failures but continue processing +- Store last_error and last_success timestamps for UI feedback + +### Security +- Store credentials in database (SQLite file already contains access codes) +- Consider encryption for sensitive fields in future + +### Rate Limiting +- Debounce rapid events (don't spam on quick start/stop cycles) +- Consider per-provider rate limits + +## Questions for User +None - proceeding with the 5 providers as discussed. diff --git a/README.md b/README.md index fe4d4e41e..f648b6345 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,15 @@ v∆v - Search by profile name or filament - Dual-nozzle support for H2 series (auto-detected from MQTT) - Left/Right extruder column layout for dual-nozzle printers +- **Push Notifications** - Get notified about print events via multiple channels: + - WhatsApp (via CallMeBot) + - ntfy (self-hosted or ntfy.sh) + - Pushover + - Telegram + - Email (SMTP with TLS/SSL/plain options) + - Configurable event triggers (start, complete, failed, stopped, progress milestones) + - Quiet hours to suppress notifications during sleep + - Per-printer filtering - **Cloud Profiles Sync** - Access your Bambu Cloud slicer presets - **File Manager** - Browse and manage files on your printer's SD card - **Re-print** - Send archived prints back to any connected printer @@ -528,6 +537,91 @@ Each plug card shows: - On/Off buttons for manual control - Expandable settings panel +### Push Notifications + +Bambusy can send push notifications when print events occur. Notifications are useful for monitoring prints remotely without checking the app constantly. + +#### Supported Providers + +| Provider | Description | Setup Required | +|----------|-------------|----------------| +| **WhatsApp** | Via [CallMeBot](https://www.callmebot.com/blog/free-api-whatsapp-messages/) | Free API key from CallMeBot | +| **ntfy** | Self-hosted or [ntfy.sh](https://ntfy.sh) | Just a topic name (no account needed for public server) | +| **Pushover** | [Pushover](https://pushover.net/) push notifications | Pushover account + app token | +| **Telegram** | Via Telegram Bot | Bot token from @BotFather | +| **Email** | SMTP email | SMTP server credentials | + +#### Adding a Notification Provider + +1. Go to **Settings** > **Notifications** +2. Click **Add Provider** +3. Select a provider type and enter the required configuration +4. Click **Send Test** to verify the configuration works +5. Configure which events should trigger notifications +6. Click **Add** + +#### Event Triggers + +Configure which events send notifications: + +| Event | Description | +|-------|-------------| +| **Print Started** | When a print job begins | +| **Print Completed** | When a print finishes successfully | +| **Print Failed** | When a print fails or errors out | +| **Print Stopped** | When you manually stop/cancel a print | +| **Progress Milestones** | At 25%, 50%, and 75% progress | +| **Printer Offline** | When a printer disconnects | +| **Printer Error** | When HMS errors are detected | +| **Low Filament** | When filament is running low | + +#### Quiet Hours + +Enable quiet hours to suppress notifications during sleep or work hours: + +1. Enable **Quiet Hours** toggle +2. Set start time (e.g., 22:00) +3. Set end time (e.g., 07:00) + +Notifications during quiet hours are silently skipped. + +#### Per-Printer Filtering + +By default, notifications are sent for all printers. To limit notifications to a specific printer: + +1. Open the notification provider settings +2. Select a printer from the **Printer** dropdown +3. Only events from that printer will trigger notifications + +#### Provider Setup Guides + +**WhatsApp (CallMeBot):** +1. Add CallMeBot to your contacts: +34 644 51 95 23 +2. Send "I allow callmebot to send me messages" via WhatsApp +3. You'll receive an API key +4. Enter your phone number (with country code) and API key in Bambusy + +**ntfy:** +1. Choose a unique topic name (e.g., `my-printer-alerts-xyz123`) +2. Subscribe to it on your phone using the ntfy app or web interface +3. Enter the topic name in Bambusy (server defaults to ntfy.sh) + +**Pushover:** +1. Create an account at [pushover.net](https://pushover.net/) +2. Create an application to get an API token +3. Enter your user key and app token in Bambusy + +**Telegram:** +1. Message @BotFather on Telegram to create a bot +2. Get your chat ID by messaging @userinfobot +3. Enter the bot token and chat ID in Bambusy + +**Email:** +1. Configure your SMTP server settings +2. For Gmail, use an App Password (not your regular password) +3. Choose security mode: STARTTLS (port 587), SSL (port 465), or None (port 25) +4. Enable/disable authentication as needed + ## Tech Stack - **Backend**: Python / FastAPI @@ -699,8 +793,8 @@ To fix the printer's clock: - [x] Print scheduling and queuing - [x] Automatic finish photo capture - [x] K-Profiles management (pressure advance) +- [x] Push notifications (WhatsApp, ntfy, Pushover, Telegram, Email) - [ ] Maintenance tracker -- [ ] Notifications (email, push) - [ ] Mobile-optimized UI ## License diff --git a/backend/app/api/routes/notifications.py b/backend/app/api/routes/notifications.py new file mode 100644 index 000000000..bb141ce77 --- /dev/null +++ b/backend/app/api/routes/notifications.py @@ -0,0 +1,223 @@ +"""API routes for notification providers.""" + +import json +import logging + +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession + +from backend.app.core.database import get_db +from backend.app.models.notification import NotificationProvider +from backend.app.schemas.notification import ( + NotificationProviderCreate, + NotificationProviderResponse, + NotificationProviderUpdate, + NotificationTestRequest, + NotificationTestResponse, +) +from backend.app.services.notification_service import notification_service + +logger = logging.getLogger(__name__) + +router = APIRouter(prefix="/notifications", tags=["notifications"]) + + +def _provider_to_dict(provider: NotificationProvider) -> dict: + """Convert a NotificationProvider model to a response dictionary.""" + return { + "id": provider.id, + "name": provider.name, + "provider_type": provider.provider_type, + "enabled": provider.enabled, + "config": json.loads(provider.config) if isinstance(provider.config, str) else provider.config, + # Print lifecycle events + "on_print_start": provider.on_print_start, + "on_print_complete": provider.on_print_complete, + "on_print_failed": provider.on_print_failed, + "on_print_progress": provider.on_print_progress, + # Printer status events + "on_printer_offline": provider.on_printer_offline, + "on_printer_error": provider.on_printer_error, + "on_filament_low": provider.on_filament_low, + # Quiet hours + "quiet_hours_enabled": provider.quiet_hours_enabled, + "quiet_hours_start": provider.quiet_hours_start, + "quiet_hours_end": provider.quiet_hours_end, + # Printer filter + "printer_id": provider.printer_id, + # Status tracking + "last_success": provider.last_success, + "last_error": provider.last_error, + "last_error_at": provider.last_error_at, + # Timestamps + "created_at": provider.created_at, + "updated_at": provider.updated_at, + } + + +@router.get("/", response_model=list[NotificationProviderResponse]) +async def list_notification_providers(db: AsyncSession = Depends(get_db)): + """List all notification providers.""" + result = await db.execute( + select(NotificationProvider).order_by(NotificationProvider.created_at.desc()) + ) + providers = result.scalars().all() + + return [_provider_to_dict(provider) for provider in providers] + + +@router.post("/", response_model=NotificationProviderResponse) +async def create_notification_provider( + provider_data: NotificationProviderCreate, + db: AsyncSession = Depends(get_db), +): + """Create a new notification provider.""" + provider = NotificationProvider( + name=provider_data.name, + provider_type=provider_data.provider_type.value, + enabled=provider_data.enabled, + config=json.dumps(provider_data.config), + # Print lifecycle events + on_print_start=provider_data.on_print_start, + on_print_complete=provider_data.on_print_complete, + on_print_failed=provider_data.on_print_failed, + on_print_progress=provider_data.on_print_progress, + # Printer status events + on_printer_offline=provider_data.on_printer_offline, + on_printer_error=provider_data.on_printer_error, + on_filament_low=provider_data.on_filament_low, + # Quiet hours + quiet_hours_enabled=provider_data.quiet_hours_enabled, + quiet_hours_start=provider_data.quiet_hours_start, + quiet_hours_end=provider_data.quiet_hours_end, + # Printer filter + printer_id=provider_data.printer_id, + ) + + db.add(provider) + await db.commit() + await db.refresh(provider) + + logger.info(f"Created notification provider: {provider.name} ({provider.provider_type})") + + return _provider_to_dict(provider) + + +@router.get("/{provider_id}", response_model=NotificationProviderResponse) +async def get_notification_provider( + provider_id: int, + db: AsyncSession = Depends(get_db), +): + """Get a specific notification provider.""" + result = await db.execute( + select(NotificationProvider).where(NotificationProvider.id == provider_id) + ) + provider = result.scalar_one_or_none() + + if not provider: + raise HTTPException(status_code=404, detail="Notification provider not found") + + return _provider_to_dict(provider) + + +@router.patch("/{provider_id}", response_model=NotificationProviderResponse) +async def update_notification_provider( + provider_id: int, + update_data: NotificationProviderUpdate, + db: AsyncSession = Depends(get_db), +): + """Update a notification provider.""" + result = await db.execute( + select(NotificationProvider).where(NotificationProvider.id == provider_id) + ) + provider = result.scalar_one_or_none() + + if not provider: + raise HTTPException(status_code=404, detail="Notification provider not found") + + # Update only provided fields + update_dict = update_data.model_dump(exclude_unset=True) + + for key, value in update_dict.items(): + if key == "config" and value is not None: + setattr(provider, key, json.dumps(value)) + elif key == "provider_type" and value is not None: + setattr(provider, key, value.value) + else: + setattr(provider, key, value) + + await db.commit() + await db.refresh(provider) + + logger.info(f"Updated notification provider: {provider.name}") + + return _provider_to_dict(provider) + + +@router.delete("/{provider_id}") +async def delete_notification_provider( + provider_id: int, + db: AsyncSession = Depends(get_db), +): + """Delete a notification provider.""" + result = await db.execute( + select(NotificationProvider).where(NotificationProvider.id == provider_id) + ) + provider = result.scalar_one_or_none() + + if not provider: + raise HTTPException(status_code=404, detail="Notification provider not found") + + name = provider.name + await db.delete(provider) + await db.commit() + + logger.info(f"Deleted notification provider: {name}") + + return {"message": f"Notification provider '{name}' deleted"} + + +@router.post("/{provider_id}/test", response_model=NotificationTestResponse) +async def test_notification_provider( + provider_id: int, + db: AsyncSession = Depends(get_db), +): + """Send a test notification using an existing provider.""" + result = await db.execute( + select(NotificationProvider).where(NotificationProvider.id == provider_id) + ) + provider = result.scalar_one_or_none() + + if not provider: + raise HTTPException(status_code=404, detail="Notification provider not found") + + config = json.loads(provider.config) if isinstance(provider.config, str) else provider.config + success, message = await notification_service.send_test_notification( + provider.provider_type, config + ) + + # Update provider status + if success: + from datetime import datetime + provider.last_success = datetime.utcnow() + else: + from datetime import datetime + provider.last_error = message + provider.last_error_at = datetime.utcnow() + + await db.commit() + + return NotificationTestResponse(success=success, message=message) + + +@router.post("/test-config", response_model=NotificationTestResponse) +async def test_notification_config( + test_request: NotificationTestRequest, +): + """Test notification configuration before saving.""" + success, message = await notification_service.send_test_notification( + test_request.provider_type.value, test_request.config + ) + + return NotificationTestResponse(success=success, message=message) diff --git a/backend/app/core/database.py b/backend/app/core/database.py index a51b9806f..40e9d139c 100644 --- a/backend/app/core/database.py +++ b/backend/app/core/database.py @@ -34,7 +34,7 @@ async def get_db() -> AsyncSession: async def init_db(): # Import models to register them with SQLAlchemy - from backend.app.models import printer, archive, filament, settings, smart_plug, print_queue # noqa: F401 + from backend.app.models import printer, archive, filament, settings, smart_plug, print_queue, notification # noqa: F401 async with engine.begin() as conn: await conn.run_sync(Base.metadata.create_all) @@ -73,3 +73,12 @@ async def run_migrations(conn): except Exception: # Column already exists pass + + # Migration: Add on_print_stopped column to notification_providers + try: + await conn.execute(text( + "ALTER TABLE notification_providers ADD COLUMN on_print_stopped BOOLEAN DEFAULT 1" + )) + except Exception: + # Column already exists + pass diff --git a/backend/app/main.py b/backend/app/main.py index dbc34bdd3..2e4cced2a 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -54,8 +54,9 @@ from fastapi.responses import FileResponse from backend.app.core.database import init_db, async_session from sqlalchemy import select, or_ from backend.app.core.websocket import ws_manager -from backend.app.api.routes import printers, archives, websocket, filaments, cloud, smart_plugs, print_queue, kprofiles +from backend.app.api.routes import printers, archives, websocket, filaments, cloud, smart_plugs, print_queue, kprofiles, notifications from backend.app.api.routes import settings as settings_routes +from backend.app.services.notification_service import notification_service from backend.app.services.printer_manager import ( printer_manager, printer_state_to_dict, @@ -431,6 +432,20 @@ async def on_print_start(printer_id: int, data: dict): import logging logging.getLogger(__name__).warning(f"Smart plug on_print_start failed: {e}") + # Send print start notifications + try: + async with async_session() as db: + from backend.app.models.printer import Printer + result = await db.execute( + select(Printer).where(Printer.id == printer_id) + ) + printer = result.scalar_one_or_none() + printer_name = printer.name if printer else f"Printer {printer_id}" + await notification_service.on_print_start(printer_id, printer_name, data, db) + except Exception as e: + import logging + logging.getLogger(__name__).warning(f"Notification on_print_start failed: {e}") + async def on_print_complete(printer_id: int, data: dict): """Handle print completion - update the archive status.""" @@ -668,6 +683,25 @@ async def on_print_complete(printer_id: int, data: dict): import logging logging.getLogger(__name__).warning(f"Smart plug on_print_complete failed: {e}") + # Send print complete notifications + try: + async with async_session() as db: + from backend.app.models.printer import Printer + result = await db.execute( + select(Printer).where(Printer.id == printer_id) + ) + printer = result.scalar_one_or_none() + printer_name = printer.name if printer else f"Printer {printer_id}" + status = data.get("status", "completed") + + # on_print_complete handles all status types: completed, failed, aborted, stopped + await notification_service.on_print_complete( + printer_id, printer_name, status, data, db + ) + except Exception as e: + import logging + logging.getLogger(__name__).warning(f"Notification on_print_complete failed: {e}") + # Update queue item if this was a scheduled print try: async with async_session() as db: @@ -762,6 +796,7 @@ app.include_router(cloud.router, prefix=app_settings.api_prefix) app.include_router(smart_plugs.router, prefix=app_settings.api_prefix) app.include_router(print_queue.router, prefix=app_settings.api_prefix) app.include_router(kprofiles.router, prefix=app_settings.api_prefix) +app.include_router(notifications.router, prefix=app_settings.api_prefix) app.include_router(websocket.router, prefix=app_settings.api_prefix) diff --git a/backend/app/models/notification.py b/backend/app/models/notification.py new file mode 100644 index 000000000..80723d388 --- /dev/null +++ b/backend/app/models/notification.py @@ -0,0 +1,54 @@ +"""Notification provider model for push notifications.""" + +from datetime import datetime + +from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String, Text, Time +from sqlalchemy.orm import relationship + +from backend.app.core.database import Base + + +class NotificationProvider(Base): + """Model for notification providers (WhatsApp, ntfy, Pushover, etc.).""" + + __tablename__ = "notification_providers" + + id = Column(Integer, primary_key=True, index=True) + name = Column(String(100), nullable=False) # User-defined name + provider_type = Column(String(50), nullable=False) # callmebot, ntfy, pushover, telegram, email + enabled = Column(Boolean, default=True) + + # Provider-specific configuration stored as JSON string + config = Column(Text, nullable=False) + + # Event triggers - print lifecycle + on_print_start = Column(Boolean, default=False) + on_print_complete = Column(Boolean, default=True) + on_print_failed = Column(Boolean, default=True) + on_print_stopped = Column(Boolean, default=True) # User cancelled/stopped print + on_print_progress = Column(Boolean, default=False) # 25%, 50%, 75% milestones + + # Event triggers - printer status + on_printer_offline = Column(Boolean, default=False) + on_printer_error = Column(Boolean, default=False) # AMS issues, etc. + on_filament_low = Column(Boolean, default=False) + + # Quiet hours (do not disturb) + quiet_hours_enabled = Column(Boolean, default=False) + quiet_hours_start = Column(String(5), nullable=True) # HH:MM format, e.g., "22:00" + quiet_hours_end = Column(String(5), nullable=True) # HH:MM format, e.g., "07:00" + + # Optional: Link to specific printer (NULL = all printers) + printer_id = Column(Integer, ForeignKey("printers.id", ondelete="SET NULL"), nullable=True) + + # Status tracking + last_success = Column(DateTime, nullable=True) + last_error = Column(Text, nullable=True) + last_error_at = Column(DateTime, nullable=True) + + # Timestamps + created_at = Column(DateTime, default=datetime.utcnow) + updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) + + # Relationships + printer = relationship("Printer", back_populates="notification_providers") diff --git a/backend/app/models/printer.py b/backend/app/models/printer.py index ef8a0e127..f6359c8b2 100644 --- a/backend/app/models/printer.py +++ b/backend/app/models/printer.py @@ -31,7 +31,11 @@ class Printer(Base): smart_plug: Mapped["SmartPlug | None"] = relationship( back_populates="printer", uselist=False ) + notification_providers: Mapped[list["NotificationProvider"]] = relationship( + back_populates="printer" + ) from backend.app.models.archive import PrintArchive # noqa: E402 from backend.app.models.smart_plug import SmartPlug # noqa: E402 +from backend.app.models.notification import NotificationProvider # noqa: E402 diff --git a/backend/app/schemas/notification.py b/backend/app/schemas/notification.py new file mode 100644 index 000000000..c0f812343 --- /dev/null +++ b/backend/app/schemas/notification.py @@ -0,0 +1,168 @@ +"""Pydantic schemas for notification providers.""" + +from datetime import datetime +from enum import Enum +from typing import Any + +from pydantic import BaseModel, Field, field_validator + + +class ProviderType(str, Enum): + """Supported notification provider types.""" + + CALLMEBOT = "callmebot" + NTFY = "ntfy" + PUSHOVER = "pushover" + TELEGRAM = "telegram" + EMAIL = "email" + + +class NotificationProviderBase(BaseModel): + """Base schema for notification providers.""" + + name: str = Field(..., min_length=1, max_length=100, description="User-defined name") + provider_type: ProviderType = Field(..., description="Type of notification provider") + enabled: bool = Field(default=True, description="Whether notifications are enabled") + config: dict[str, Any] = Field(..., description="Provider-specific configuration") + + # Event triggers - print lifecycle + on_print_start: bool = Field(default=False, description="Notify on print start") + on_print_complete: bool = Field(default=True, description="Notify on print complete") + on_print_failed: bool = Field(default=True, description="Notify on print failed") + on_print_stopped: bool = Field(default=True, description="Notify when print is stopped/cancelled") + on_print_progress: bool = Field(default=False, description="Notify at 25%, 50%, 75% progress") + + # Event triggers - printer status + on_printer_offline: bool = Field(default=False, description="Notify when printer goes offline") + on_printer_error: bool = Field(default=False, description="Notify on printer errors (AMS, etc.)") + on_filament_low: bool = Field(default=False, description="Notify when filament is running low") + + # Quiet hours + quiet_hours_enabled: bool = Field(default=False, description="Enable quiet hours") + quiet_hours_start: str | None = Field(default=None, description="Start time in HH:MM format") + quiet_hours_end: str | None = Field(default=None, description="End time in HH:MM format") + + # Printer filter + printer_id: int | None = Field(default=None, description="Specific printer ID or null for all") + + @field_validator("quiet_hours_start", "quiet_hours_end") + @classmethod + def validate_time_format(cls, v: str | None) -> str | None: + if v is None: + return v + try: + parts = v.split(":") + if len(parts) != 2: + raise ValueError("Invalid time format") + hour, minute = int(parts[0]), int(parts[1]) + if not (0 <= hour <= 23 and 0 <= minute <= 59): + raise ValueError("Invalid time range") + return f"{hour:02d}:{minute:02d}" + except (ValueError, TypeError): + raise ValueError("Time must be in HH:MM format (e.g., 22:00)") + + +class NotificationProviderCreate(NotificationProviderBase): + """Schema for creating a notification provider.""" + + pass + + +class NotificationProviderUpdate(BaseModel): + """Schema for updating a notification provider (all fields optional).""" + + name: str | None = Field(default=None, min_length=1, max_length=100) + provider_type: ProviderType | None = None + enabled: bool | None = None + config: dict[str, Any] | None = None + + # Event triggers - print lifecycle + on_print_start: bool | None = None + on_print_complete: bool | None = None + on_print_failed: bool | None = None + on_print_stopped: bool | None = None + on_print_progress: bool | None = None + + # Event triggers - printer status + on_printer_offline: bool | None = None + on_printer_error: bool | None = None + on_filament_low: bool | None = None + + # Quiet hours + quiet_hours_enabled: bool | None = None + quiet_hours_start: str | None = None + quiet_hours_end: str | None = None + + # Printer filter + printer_id: int | None = None + + +class NotificationProviderResponse(NotificationProviderBase): + """Schema for notification provider API responses.""" + + id: int + last_success: datetime | None = None + last_error: str | None = None + last_error_at: datetime | None = None + created_at: datetime + updated_at: datetime + + class Config: + from_attributes = True + + +class NotificationTestRequest(BaseModel): + """Schema for testing notification configuration.""" + + provider_type: ProviderType + config: dict[str, Any] + + +class NotificationTestResponse(BaseModel): + """Schema for test notification response.""" + + success: bool + message: str + + +# Provider-specific config schemas for documentation/validation reference +class CallMeBotConfig(BaseModel): + """CallMeBot/WhatsApp configuration.""" + + phone: str = Field(..., description="Phone number with country code (e.g., +1234567890)") + apikey: str = Field(..., description="API key from CallMeBot") + + +class NtfyConfig(BaseModel): + """ntfy configuration.""" + + server: str = Field(default="https://ntfy.sh", description="ntfy server URL") + topic: str = Field(..., description="Topic name to publish to") + auth_token: str | None = Field(default=None, description="Optional authentication token") + + +class PushoverConfig(BaseModel): + """Pushover configuration.""" + + user_key: str = Field(..., description="Your Pushover user key") + app_token: str = Field(..., description="Your Pushover application token") + priority: int = Field(default=0, ge=-2, le=2, description="Message priority (-2 to 2)") + + +class TelegramConfig(BaseModel): + """Telegram bot configuration.""" + + bot_token: str = Field(..., description="Bot token from @BotFather") + chat_id: str = Field(..., description="Chat ID to send messages to") + + +class EmailConfig(BaseModel): + """Email/SMTP configuration.""" + + smtp_server: str = Field(..., description="SMTP server hostname") + smtp_port: int = Field(default=587, description="SMTP port (587 for TLS, 465 for SSL)") + username: str = Field(..., description="SMTP username/email") + password: str = Field(..., description="SMTP password or app password") + from_email: str = Field(..., description="From email address") + to_email: str = Field(..., description="Recipient email address") + use_tls: bool = Field(default=True, description="Use TLS encryption") diff --git a/backend/app/services/notification_service.py b/backend/app/services/notification_service.py new file mode 100644 index 000000000..b82e23939 --- /dev/null +++ b/backend/app/services/notification_service.py @@ -0,0 +1,534 @@ +"""Notification service for sending push notifications via various providers.""" + +import json +import logging +import smtplib +from datetime import datetime +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText +from typing import Any +from urllib.parse import quote + +import httpx +from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession + +from backend.app.models.notification import NotificationProvider + +logger = logging.getLogger(__name__) + + +class NotificationService: + """Service for sending notifications through various providers.""" + + def __init__(self): + self._http_client: httpx.AsyncClient | None = None + + async def _get_client(self) -> httpx.AsyncClient: + """Get or create HTTP client.""" + if self._http_client is None or self._http_client.is_closed: + self._http_client = httpx.AsyncClient(timeout=30.0) + return self._http_client + + async def close(self): + """Close HTTP client.""" + if self._http_client and not self._http_client.is_closed: + await self._http_client.aclose() + + def _is_in_quiet_hours(self, provider: NotificationProvider) -> bool: + """Check if current time is within provider's quiet hours.""" + if not provider.quiet_hours_enabled: + return False + + if not provider.quiet_hours_start or not provider.quiet_hours_end: + return False + + try: + now = datetime.now() + current_time = now.hour * 60 + now.minute + + start_parts = provider.quiet_hours_start.split(":") + end_parts = provider.quiet_hours_end.split(":") + + start_minutes = int(start_parts[0]) * 60 + int(start_parts[1]) + end_minutes = int(end_parts[0]) * 60 + int(end_parts[1]) + + # Handle overnight quiet hours (e.g., 22:00 to 07:00) + if start_minutes > end_minutes: + # Quiet hours span midnight + return current_time >= start_minutes or current_time < end_minutes + else: + # Same day quiet hours + return start_minutes <= current_time < end_minutes + except (ValueError, TypeError, AttributeError): + logger.warning(f"Invalid quiet hours format for provider {provider.name}") + return False + + def _format_duration(self, seconds: int | None) -> str: + """Format duration in seconds to human-readable string.""" + if seconds is None: + return "Unknown" + hours = seconds // 3600 + minutes = (seconds % 3600) // 60 + if hours > 0: + return f"{hours}h {minutes}m" + return f"{minutes}m" + + def _build_print_start_message(self, printer_name: str, data: dict) -> tuple[str, str]: + """Build notification message for print start event.""" + filename = data.get("filename", "Unknown") + # Clean up filename + if filename.endswith(".gcode.3mf"): + filename = filename[:-10] + elif filename.endswith(".3mf"): + filename = filename[:-4] + + title = "Print Started" + + estimated_time = data.get("raw_data", {}).get("print", {}).get("mc_remaining_time") + time_str = self._format_duration(estimated_time * 60 if estimated_time else None) + + message = f"{printer_name}: {filename}\nEstimated: {time_str}" + return title, message + + def _build_print_complete_message( + self, printer_name: str, status: str, data: dict, archive_data: dict | None = None + ) -> tuple[str, str]: + """Build notification message for print complete event.""" + filename = data.get("filename", "Unknown") + if filename.endswith(".gcode.3mf"): + filename = filename[:-10] + elif filename.endswith(".3mf"): + filename = filename[:-4] + + if status == "completed": + title = "Print Completed" + elif status == "failed": + title = "Print Failed" + elif status in ("aborted", "stopped", "cancelled"): + title = "Print Stopped" + else: + title = "Print Ended" + + lines = [f"{printer_name}: {filename}"] + + if archive_data: + # Add print time if available + if archive_data.get("print_time_seconds"): + lines.append(f"Time: {self._format_duration(archive_data['print_time_seconds'])}") + # Add filament used if available + if archive_data.get("actual_filament_grams"): + lines.append(f"Filament: {archive_data['actual_filament_grams']:.1f}g") + # Add failure reason if failed + if status == "failed" and archive_data.get("failure_reason"): + lines.append(f"Reason: {archive_data['failure_reason']}") + + message = "\n".join(lines) + return title, message + + def _build_progress_message( + self, printer_name: str, filename: str, progress: int + ) -> tuple[str, str]: + """Build notification message for print progress milestone.""" + if filename.endswith(".gcode.3mf"): + filename = filename[:-10] + elif filename.endswith(".3mf"): + filename = filename[:-4] + + title = f"Print {progress}% Complete" + message = f"{printer_name}: {filename}" + return title, message + + def _build_printer_offline_message(self, printer_name: str) -> tuple[str, str]: + """Build notification message for printer offline event.""" + title = "Printer Offline" + message = f"{printer_name} has disconnected" + return title, message + + def _build_printer_error_message( + self, printer_name: str, error_type: str, error_detail: str | None = None + ) -> tuple[str, str]: + """Build notification message for printer error event.""" + title = f"Printer Error: {error_type}" + message = f"{printer_name}" + if error_detail: + message += f"\n{error_detail}" + return title, message + + def _build_filament_low_message( + self, printer_name: str, slot: int, remaining_percent: int + ) -> tuple[str, str]: + """Build notification message for low filament event.""" + title = "Filament Low" + message = f"{printer_name}: Slot {slot} at {remaining_percent}%" + return title, message + + async def send_test_notification( + self, provider_type: str, config: dict[str, Any] + ) -> tuple[bool, str]: + """Send a test notification to verify configuration.""" + title = "BambuTrack Test" + message = "This is a test notification from BambuTrack. If you see this, notifications are working correctly!" + + try: + if provider_type == "callmebot": + return await self._send_callmebot(config, f"{title}\n{message}") + elif provider_type == "ntfy": + return await self._send_ntfy(config, title, message) + elif provider_type == "pushover": + return await self._send_pushover(config, title, message) + elif provider_type == "telegram": + return await self._send_telegram(config, f"*{title}*\n{message}") + elif provider_type == "email": + return await self._send_email(config, title, message) + else: + return False, f"Unknown provider type: {provider_type}" + except Exception as e: + logger.exception(f"Error sending test notification via {provider_type}") + return False, str(e) + + async def _send_callmebot(self, config: dict, message: str) -> tuple[bool, str]: + """Send notification via CallMeBot (WhatsApp).""" + phone = config.get("phone", "").strip() + apikey = config.get("apikey", "").strip() + + if not phone or not apikey: + return False, "Phone number and API key are required" + + # URL encode the message + encoded_message = quote(message) + url = f"https://api.callmebot.com/whatsapp.php?phone={phone}&text={encoded_message}&apikey={apikey}" + + client = await self._get_client() + response = await client.get(url) + + if response.status_code == 200: + return True, "Message sent successfully" + else: + return False, f"HTTP {response.status_code}: {response.text[:200]}" + + async def _send_ntfy(self, config: dict, title: str, message: str) -> tuple[bool, str]: + """Send notification via ntfy.""" + server = config.get("server", "https://ntfy.sh").rstrip("/") + topic = config.get("topic", "").strip() + auth_token = config.get("auth_token", "").strip() + + if not topic: + return False, "Topic is required" + + url = f"{server}/{topic}" + headers = {"Title": title} + + if auth_token: + headers["Authorization"] = f"Bearer {auth_token}" + + client = await self._get_client() + response = await client.post(url, content=message, headers=headers) + + if response.status_code in (200, 204): + return True, "Message sent successfully" + else: + return False, f"HTTP {response.status_code}: {response.text[:200]}" + + async def _send_pushover(self, config: dict, title: str, message: str) -> tuple[bool, str]: + """Send notification via Pushover.""" + user_key = config.get("user_key", "").strip() + app_token = config.get("app_token", "").strip() + priority = config.get("priority", 0) + + if not user_key or not app_token: + return False, "User key and app token are required" + + url = "https://api.pushover.net/1/messages.json" + data = { + "token": app_token, + "user": user_key, + "title": title, + "message": message, + "priority": priority, + } + + client = await self._get_client() + response = await client.post(url, data=data) + + if response.status_code == 200: + return True, "Message sent successfully" + else: + try: + error_data = response.json() + errors = error_data.get("errors", []) + return False, f"Pushover error: {', '.join(errors)}" + except Exception: + return False, f"HTTP {response.status_code}: {response.text[:200]}" + + async def _send_telegram(self, config: dict, message: str) -> tuple[bool, str]: + """Send notification via Telegram bot.""" + bot_token = config.get("bot_token", "").strip() + chat_id = config.get("chat_id", "").strip() + + if not bot_token or not chat_id: + return False, "Bot token and chat ID are required" + + url = f"https://api.telegram.org/bot{bot_token}/sendMessage" + data = { + "chat_id": chat_id, + "text": message, + "parse_mode": "Markdown", + } + + client = await self._get_client() + response = await client.post(url, json=data) + + if response.status_code == 200: + result = response.json() + if result.get("ok"): + return True, "Message sent successfully" + else: + return False, f"Telegram error: {result.get('description', 'Unknown error')}" + else: + return False, f"HTTP {response.status_code}: {response.text[:200]}" + + async def _send_email(self, config: dict, subject: str, body: str) -> tuple[bool, str]: + """Send notification via email (SMTP).""" + smtp_server = config.get("smtp_server", "").strip() + smtp_port = int(config.get("smtp_port", 587)) + username = config.get("username", "").strip() + password = config.get("password", "").strip() + from_email = config.get("from_email", "").strip() + to_email = config.get("to_email", "").strip() + # Security: "starttls" (port 587), "ssl" (port 465), "none" (port 25) + security = config.get("security", "starttls") + # Authentication: "true" or "false" + auth_enabled = config.get("auth_enabled", "true").lower() == "true" + + if not all([smtp_server, from_email, to_email]): + return False, "SMTP server, from email, and to email are required" + + if auth_enabled and not all([username, password]): + return False, "Username and password are required when authentication is enabled" + + try: + msg = MIMEMultipart() + msg["From"] = from_email + msg["To"] = to_email + msg["Subject"] = f"[BambuTrack] {subject}" + msg.attach(MIMEText(body, "plain")) + + if security == "ssl": + # Direct SSL connection (typically port 465) + server = smtplib.SMTP_SSL(smtp_server, smtp_port) + elif security == "starttls": + # STARTTLS upgrade (typically port 587) + server = smtplib.SMTP(smtp_server, smtp_port) + server.starttls() + else: + # No encryption (typically port 25) - use with caution + server = smtplib.SMTP(smtp_server, smtp_port) + + if auth_enabled: + server.login(username, password) + + server.sendmail(from_email, to_email, msg.as_string()) + server.quit() + + return True, "Email sent successfully" + except smtplib.SMTPAuthenticationError: + return False, "SMTP authentication failed - check username/password" + except smtplib.SMTPException as e: + return False, f"SMTP error: {str(e)}" + except Exception as e: + return False, f"Email error: {str(e)}" + + async def _send_to_provider( + self, provider: NotificationProvider, title: str, message: str + ) -> tuple[bool, str]: + """Send notification to a specific provider.""" + # Check quiet hours + if self._is_in_quiet_hours(provider): + logger.info(f"Skipping notification to {provider.name} - quiet hours active") + return True, "Skipped - quiet hours" + + config = json.loads(provider.config) if isinstance(provider.config, str) else provider.config + + try: + if provider.provider_type == "callmebot": + return await self._send_callmebot(config, f"{title}\n{message}") + elif provider.provider_type == "ntfy": + return await self._send_ntfy(config, title, message) + elif provider.provider_type == "pushover": + return await self._send_pushover(config, title, message) + elif provider.provider_type == "telegram": + return await self._send_telegram(config, f"*{title}*\n{message}") + elif provider.provider_type == "email": + return await self._send_email(config, title, message) + else: + return False, f"Unknown provider type: {provider.provider_type}" + except Exception as e: + logger.exception(f"Error sending notification via {provider.provider_type}") + return False, str(e) + + async def _update_provider_status( + self, db: AsyncSession, provider_id: int, success: bool, error: str | None = None + ): + """Update provider status after sending notification.""" + result = await db.execute( + select(NotificationProvider).where(NotificationProvider.id == provider_id) + ) + provider = result.scalar_one_or_none() + if provider: + if success: + provider.last_success = datetime.utcnow() + else: + provider.last_error = error + provider.last_error_at = datetime.utcnow() + await db.commit() + + async def _get_providers_for_event( + self, + db: AsyncSession, + event_field: str, + printer_id: int | None = None, + ) -> list[NotificationProvider]: + """Get all enabled providers that want a specific event type.""" + # Build the query dynamically based on event field + query = select(NotificationProvider).where( + NotificationProvider.enabled == True, + getattr(NotificationProvider, event_field) == True, + ) + + if printer_id is not None: + query = query.where( + (NotificationProvider.printer_id == None) | (NotificationProvider.printer_id == printer_id) + ) + + result = await db.execute(query) + return list(result.scalars().all()) + + async def _send_to_providers( + self, + providers: list[NotificationProvider], + title: str, + message: str, + db: AsyncSession, + ): + """Send notification to multiple providers.""" + for provider in providers: + try: + success, error = await self._send_to_provider(provider, title, message) + await self._update_provider_status(db, provider.id, success, error if not success else None) + if success: + logger.info(f"Sent notification via {provider.name}") + else: + logger.warning(f"Failed to send notification via {provider.name}: {error}") + except Exception as e: + logger.exception(f"Error sending notification via {provider.name}") + await self._update_provider_status(db, provider.id, False, str(e)) + + async def on_print_start( + self, printer_id: int, printer_name: str, data: dict, db: AsyncSession + ): + """Handle print start event - send notifications to relevant providers.""" + logger.info(f"on_print_start called for printer {printer_id} ({printer_name})") + providers = await self._get_providers_for_event(db, "on_print_start", printer_id) + if not providers: + logger.info(f"No notification providers configured for print_start event on printer {printer_id}") + return + + logger.info(f"Found {len(providers)} providers for print_start: {[p.name for p in providers]}") + title, message = self._build_print_start_message(printer_name, data) + await self._send_to_providers(providers, title, message, db) + + async def on_print_complete( + self, + printer_id: int, + printer_name: str, + status: str, + data: dict, + db: AsyncSession, + archive_data: dict | None = None, + ): + """Handle print complete event - send notifications to relevant providers.""" + logger.info(f"on_print_complete called for printer {printer_id} ({printer_name}), status={status}") + # Determine which event type this is + if status == "completed": + event_field = "on_print_complete" + elif status in ("failed",): + event_field = "on_print_failed" + elif status in ("aborted", "stopped", "cancelled"): + event_field = "on_print_stopped" + else: + # Unknown status, default to on_print_complete + logger.warning(f"Unknown print status '{status}', defaulting to on_print_complete") + event_field = "on_print_complete" + + providers = await self._get_providers_for_event(db, event_field, printer_id) + if not providers: + logger.info(f"No notification providers configured for {event_field} event on printer {printer_id}") + return + + logger.info(f"Found {len(providers)} providers for {event_field}: {[p.name for p in providers]}") + title, message = self._build_print_complete_message(printer_name, status, data, archive_data) + await self._send_to_providers(providers, title, message, db) + + async def on_print_progress( + self, + printer_id: int, + printer_name: str, + filename: str, + progress: int, + db: AsyncSession, + ): + """Handle print progress milestone (25%, 50%, 75%).""" + providers = await self._get_providers_for_event(db, "on_print_progress", printer_id) + if not providers: + return + + title, message = self._build_progress_message(printer_name, filename, progress) + await self._send_to_providers(providers, title, message, db) + + async def on_printer_offline( + self, printer_id: int, printer_name: str, db: AsyncSession + ): + """Handle printer offline event.""" + providers = await self._get_providers_for_event(db, "on_printer_offline", printer_id) + if not providers: + return + + title, message = self._build_printer_offline_message(printer_name) + await self._send_to_providers(providers, title, message, db) + + async def on_printer_error( + self, + printer_id: int, + printer_name: str, + error_type: str, + db: AsyncSession, + error_detail: str | None = None, + ): + """Handle printer error event (AMS issues, etc.).""" + providers = await self._get_providers_for_event(db, "on_printer_error", printer_id) + if not providers: + return + + title, message = self._build_printer_error_message(printer_name, error_type, error_detail) + await self._send_to_providers(providers, title, message, db) + + async def on_filament_low( + self, + printer_id: int, + printer_name: str, + slot: int, + remaining_percent: int, + db: AsyncSession, + ): + """Handle low filament event.""" + providers = await self._get_providers_for_event(db, "on_filament_low", printer_id) + if not providers: + return + + title, message = self._build_filament_low_message(printer_name, slot, remaining_percent) + await self._send_to_providers(providers, title, message, db) + + +# Global instance +notification_service = NotificationService() diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index 1f6848fe8..c9f716d4f 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -355,6 +355,129 @@ export interface KProfilesResponse { nozzle_diameter: string; } +// Notification Provider types +export type ProviderType = 'callmebot' | 'ntfy' | 'pushover' | 'telegram' | 'email'; + +export interface NotificationProvider { + id: number; + name: string; + provider_type: ProviderType; + enabled: boolean; + config: Record; + // Print lifecycle events + on_print_start: boolean; + on_print_complete: boolean; + on_print_failed: boolean; + on_print_stopped: boolean; + on_print_progress: boolean; + // Printer status events + on_printer_offline: boolean; + on_printer_error: boolean; + on_filament_low: boolean; + // Quiet hours + quiet_hours_enabled: boolean; + quiet_hours_start: string | null; + quiet_hours_end: string | null; + // Printer filter + printer_id: number | null; + // Status tracking + last_success: string | null; + last_error: string | null; + last_error_at: string | null; + // Timestamps + created_at: string; + updated_at: string; +} + +export interface NotificationProviderCreate { + name: string; + provider_type: ProviderType; + enabled?: boolean; + config: Record; + // Print lifecycle events + on_print_start?: boolean; + on_print_complete?: boolean; + on_print_failed?: boolean; + on_print_stopped?: boolean; + on_print_progress?: boolean; + // Printer status events + on_printer_offline?: boolean; + on_printer_error?: boolean; + on_filament_low?: boolean; + // Quiet hours + quiet_hours_enabled?: boolean; + quiet_hours_start?: string | null; + quiet_hours_end?: string | null; + // Printer filter + printer_id?: number | null; +} + +export interface NotificationProviderUpdate { + name?: string; + provider_type?: ProviderType; + enabled?: boolean; + config?: Record; + // Print lifecycle events + on_print_start?: boolean; + on_print_complete?: boolean; + on_print_failed?: boolean; + on_print_stopped?: boolean; + on_print_progress?: boolean; + // Printer status events + on_printer_offline?: boolean; + on_printer_error?: boolean; + on_filament_low?: boolean; + // Quiet hours + quiet_hours_enabled?: boolean; + quiet_hours_start?: string | null; + quiet_hours_end?: string | null; + // Printer filter + printer_id?: number | null; +} + +export interface NotificationTestRequest { + provider_type: ProviderType; + config: Record; +} + +export interface NotificationTestResponse { + success: boolean; + message: string; +} + +// Provider-specific config types for reference +export interface CallMeBotConfig { + phone: string; + apikey: string; +} + +export interface NtfyConfig { + server?: string; + topic: string; + auth_token?: string | null; +} + +export interface PushoverConfig { + user_key: string; + app_token: string; + priority?: number; +} + +export interface TelegramConfig { + bot_token: string; + chat_id: string; +} + +export interface EmailConfig { + smtp_server: string; + smtp_port?: number; + username: string; + password: string; + from_email: string; + to_email: string; + use_tls?: boolean; +} + // API functions export const api = { // Printers @@ -700,4 +823,27 @@ export const api = { method: 'DELETE', body: JSON.stringify(profile), }), + + // Notification Providers + getNotificationProviders: () => request('/notifications/'), + getNotificationProvider: (id: number) => request(`/notifications/${id}`), + createNotificationProvider: (data: NotificationProviderCreate) => + request('/notifications/', { + method: 'POST', + body: JSON.stringify(data), + }), + updateNotificationProvider: (id: number, data: NotificationProviderUpdate) => + request(`/notifications/${id}`, { + method: 'PATCH', + body: JSON.stringify(data), + }), + deleteNotificationProvider: (id: number) => + request<{ message: string }>(`/notifications/${id}`, { method: 'DELETE' }), + testNotificationProvider: (id: number) => + request(`/notifications/${id}/test`, { method: 'POST' }), + testNotificationConfig: (data: NotificationTestRequest) => + request('/notifications/test-config', { + method: 'POST', + body: JSON.stringify(data), + }), }; diff --git a/frontend/src/components/AddNotificationModal.tsx b/frontend/src/components/AddNotificationModal.tsx new file mode 100644 index 000000000..28e825ef9 --- /dev/null +++ b/frontend/src/components/AddNotificationModal.tsx @@ -0,0 +1,414 @@ +import { useState, useEffect } from 'react'; +import { useMutation, useQueryClient, useQuery } from '@tanstack/react-query'; +import { X, Save, Loader2, Send, CheckCircle, XCircle } from 'lucide-react'; +import { api } from '../api/client'; +import type { NotificationProvider, NotificationProviderCreate, NotificationProviderUpdate, ProviderType } from '../api/client'; +import { Button } from './Button'; + +interface AddNotificationModalProps { + provider?: NotificationProvider | null; + onClose: () => void; +} + +const PROVIDER_OPTIONS: { value: ProviderType; label: string; description: string }[] = [ + { value: 'callmebot', label: 'CallMeBot/WhatsApp', description: 'Free WhatsApp notifications via CallMeBot' }, + { value: 'ntfy', label: 'ntfy', description: 'Free, self-hostable push notifications' }, + { value: 'pushover', label: 'Pushover', description: 'Simple, reliable push notifications' }, + { value: 'telegram', label: 'Telegram', description: 'Notifications via Telegram bot' }, + { value: 'email', label: 'Email', description: 'SMTP email notifications' }, +]; + +export function AddNotificationModal({ provider, onClose }: AddNotificationModalProps) { + const queryClient = useQueryClient(); + const isEditing = !!provider; + + const [name, setName] = useState(provider?.name || ''); + const [providerType, setProviderType] = useState(provider?.provider_type || 'ntfy'); + const [printerId, setPrinterId] = useState(provider?.printer_id || null); + const [quietHoursEnabled, setQuietHoursEnabled] = useState(provider?.quiet_hours_enabled || false); + const [quietHoursStart, setQuietHoursStart] = useState(provider?.quiet_hours_start || '22:00'); + const [quietHoursEnd, setQuietHoursEnd] = useState(provider?.quiet_hours_end || '07:00'); + + // Provider-specific config + const [config, setConfig] = useState>( + provider?.config ? Object.fromEntries(Object.entries(provider.config).map(([k, v]) => [k, String(v)])) : {} + ); + + const [testResult, setTestResult] = useState<{ success: boolean; message: string } | null>(null); + const [error, setError] = useState(null); + + // Fetch printers for linking + const { data: printers } = useQuery({ + queryKey: ['printers'], + queryFn: api.getPrinters, + }); + + // Close on Escape key + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === 'Escape') onClose(); + }; + window.addEventListener('keydown', handleKeyDown); + return () => window.removeEventListener('keydown', handleKeyDown); + }, [onClose]); + + // Test configuration mutation + const testMutation = useMutation({ + mutationFn: () => api.testNotificationConfig({ provider_type: providerType, config }), + onSuccess: (result) => { + setTestResult(result); + setError(null); + }, + onError: (err: Error) => { + setTestResult({ success: false, message: err.message }); + }, + }); + + // Create mutation + const createMutation = useMutation({ + mutationFn: (data: NotificationProviderCreate) => api.createNotificationProvider(data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['notification-providers'] }); + onClose(); + }, + onError: (err: Error) => { + setError(err.message); + }, + }); + + // Update mutation + const updateMutation = useMutation({ + mutationFn: (data: NotificationProviderUpdate) => api.updateNotificationProvider(provider!.id, data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['notification-providers'] }); + onClose(); + }, + onError: (err: Error) => { + setError(err.message); + }, + }); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + setError(null); + + if (!name.trim()) { + setError('Name is required'); + return; + } + + // Validate provider-specific config + const requiredFields = getRequiredFields(providerType); + for (const field of requiredFields) { + if (!config[field.key]?.trim()) { + setError(`${field.label} is required`); + return; + } + } + + const data = { + name: name.trim(), + provider_type: providerType, + config, + printer_id: printerId, + quiet_hours_enabled: quietHoursEnabled, + quiet_hours_start: quietHoursEnabled ? quietHoursStart : null, + quiet_hours_end: quietHoursEnabled ? quietHoursEnd : null, + }; + + if (isEditing) { + updateMutation.mutate(data); + } else { + createMutation.mutate(data); + } + }; + + const isPending = createMutation.isPending || updateMutation.isPending; + + // Get config fields for each provider type + const getConfigFields = (type: ProviderType) => { + switch (type) { + case 'callmebot': + return [ + { key: 'phone', label: 'Phone Number', placeholder: '+1234567890', type: 'text', required: true }, + { key: 'apikey', label: 'API Key', placeholder: 'Your CallMeBot API key', type: 'text', required: true }, + ]; + case 'ntfy': + return [ + { key: 'server', label: 'Server URL', placeholder: 'https://ntfy.sh', type: 'text', required: false }, + { key: 'topic', label: 'Topic', placeholder: 'my-bambutrack', type: 'text', required: true }, + { key: 'auth_token', label: 'Auth Token', placeholder: 'Optional authentication', type: 'password', required: false }, + ]; + case 'pushover': + return [ + { key: 'user_key', label: 'User Key', placeholder: 'Your Pushover user key', type: 'text', required: true }, + { key: 'app_token', label: 'App Token', placeholder: 'Your Pushover app token', type: 'text', required: true }, + { key: 'priority', label: 'Priority', placeholder: '0 (normal)', type: 'number', required: false }, + ]; + case 'telegram': + return [ + { key: 'bot_token', label: 'Bot Token', placeholder: 'Bot token from @BotFather', type: 'password', required: true }, + { key: 'chat_id', label: 'Chat ID', placeholder: 'Your chat or group ID', type: 'text', required: true }, + ]; + case 'email': + return [ + { key: 'smtp_server', label: 'SMTP Server', placeholder: 'smtp.gmail.com', type: 'text', required: true }, + { key: 'smtp_port', label: 'SMTP Port', placeholder: '587', type: 'number', required: false }, + { key: 'security', label: 'Security', type: 'select', required: false, options: [ + { value: 'starttls', label: 'STARTTLS (Port 587)' }, + { value: 'ssl', label: 'SSL/TLS (Port 465)' }, + { value: 'none', label: 'None (Port 25)' }, + ]}, + { key: 'auth_enabled', label: 'Authentication', type: 'select', required: false, options: [ + { value: 'true', label: 'Enabled' }, + { value: 'false', label: 'Disabled' }, + ]}, + { key: 'username', label: 'Username', placeholder: 'your@email.com', type: 'text', required: false }, + { key: 'password', label: 'Password', placeholder: 'App password', type: 'password', required: false }, + { key: 'from_email', label: 'From Email', placeholder: 'your@email.com', type: 'text', required: true }, + { key: 'to_email', label: 'To Email', placeholder: 'recipient@email.com', type: 'text', required: true }, + ]; + default: + return []; + } + }; + + const getRequiredFields = (type: ProviderType) => { + return getConfigFields(type).filter(f => f.required); + }; + + const configFields = getConfigFields(providerType); + + return ( +
+
e.stopPropagation()} + > + {/* Header */} +
+

+ {isEditing ? 'Edit Notification Provider' : 'Add Notification Provider'} +

+ +
+ + {/* Form */} +
+ {error && ( +
+ {error} +
+ )} + + {/* Name */} +
+ + setName(e.target.value)} + placeholder="My Notifications" + className="w-full px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none" + /> +
+ + {/* Provider Type */} +
+ + +

+ {PROVIDER_OPTIONS.find(o => o.value === providerType)?.description} +

+
+ + {/* Provider-specific configuration */} +
+

Configuration

+ {configFields.map((field) => ( +
+ + {field.type === 'select' && field.options ? ( + + ) : ( + { + setConfig({ ...config, [field.key]: e.target.value }); + setTestResult(null); + }} + placeholder={field.placeholder} + className="w-full px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none" + /> + )} +
+ ))} +
+ + {/* Test Button */} +
+ +
+ + {/* Test Result */} + {testResult && ( +
+ {testResult.success ? ( + <> + + {testResult.message} + + ) : ( + <> + + {testResult.message} + + )} +
+ )} + + {/* Link to Printer */} +
+ + +

+ Only send notifications for events from this printer +

+
+ + {/* Quiet Hours */} +
+
+ + +
+ {quietHoursEnabled && ( +
+
+ + setQuietHoursStart(e.target.value)} + className="w-full px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none" + /> +
+
+ + setQuietHoursEnd(e.target.value)} + className="w-full px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none" + /> +
+
+ )} +
+ + {/* Actions */} +
+ + +
+
+
+
+ ); +} diff --git a/frontend/src/components/NotificationProviderCard.tsx b/frontend/src/components/NotificationProviderCard.tsx new file mode 100644 index 000000000..0e39efb60 --- /dev/null +++ b/frontend/src/components/NotificationProviderCard.tsx @@ -0,0 +1,408 @@ +import { useState } from 'react'; +import { useMutation, useQueryClient, useQuery } from '@tanstack/react-query'; +import { Bell, Trash2, Settings2, Edit2, Send, Loader2, CheckCircle, XCircle, Moon, Clock, ChevronDown, ChevronUp } from 'lucide-react'; +import { api } from '../api/client'; +import type { NotificationProvider, NotificationProviderUpdate } from '../api/client'; +import { Card, CardContent } from './Card'; +import { Button } from './Button'; +import { ConfirmModal } from './ConfirmModal'; + +interface NotificationProviderCardProps { + provider: NotificationProvider; + onEdit: (provider: NotificationProvider) => void; +} + +const PROVIDER_LABELS: Record = { + callmebot: 'CallMeBot/WhatsApp', + ntfy: 'ntfy', + pushover: 'Pushover', + telegram: 'Telegram', + email: 'Email', +}; + +export function NotificationProviderCard({ provider, onEdit }: NotificationProviderCardProps) { + const queryClient = useQueryClient(); + const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); + const [isExpanded, setIsExpanded] = useState(false); + const [testResult, setTestResult] = useState<{ success: boolean; message: string } | null>(null); + + // Fetch printers for linking + const { data: printers } = useQuery({ + queryKey: ['printers'], + queryFn: api.getPrinters, + }); + + const linkedPrinter = printers?.find(p => p.id === provider.printer_id); + + // Update mutation + const updateMutation = useMutation({ + mutationFn: (data: NotificationProviderUpdate) => api.updateNotificationProvider(provider.id, data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['notification-providers'] }); + }, + }); + + // Delete mutation + const deleteMutation = useMutation({ + mutationFn: () => api.deleteNotificationProvider(provider.id), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['notification-providers'] }); + }, + }); + + // Test mutation + const testMutation = useMutation({ + mutationFn: () => api.testNotificationProvider(provider.id), + onSuccess: (result) => { + setTestResult(result); + queryClient.invalidateQueries({ queryKey: ['notification-providers'] }); + }, + onError: (err: Error) => { + setTestResult({ success: false, message: err.message }); + }, + }); + + // Format time for display + const formatTime = (time: string | null) => { + if (!time) return ''; + return time; + }; + + return ( + <> + + + {/* Header Row */} +
+
+
+ +
+
+

{provider.name}

+

{PROVIDER_LABELS[provider.provider_type] || provider.provider_type}

+
+
+ + {/* Status indicator */} +
+ {provider.last_success && ( + Last sent: {new Date(provider.last_success).toLocaleDateString()} + )} + {provider.last_error && ( + Error + )} +
+
+ + {/* Linked Printer */} + {linkedPrinter && ( +
+ Printer: + {linkedPrinter.name} +
+ )} + {!linkedPrinter && !provider.printer_id && ( +
+ All printers +
+ )} + + {/* Event summary - show all event tags */} +
+ {provider.on_print_start && ( + Start + )} + {provider.on_print_complete && ( + Complete + )} + {provider.on_print_failed && ( + Failed + )} + {provider.on_print_stopped && ( + Stopped + )} + {provider.on_print_progress && ( + Progress + )} + {provider.on_printer_offline && ( + Offline + )} + {provider.on_printer_error && ( + Error + )} + {provider.on_filament_low && ( + Low Filament + )} + {provider.quiet_hours_enabled && ( + + + Quiet + + )} +
+ + {/* Test Button */} +
+ +
+ + {/* Test Result */} + {testResult && ( +
+ {testResult.success ? ( + + ) : ( + + )} + {testResult.message} +
+ )} + + {/* Toggle Settings Panel */} + + + {/* Expanded Settings */} + {isExpanded && ( +
+ {/* Enabled Toggle */} +
+
+

Enabled

+

Send notifications from this provider

+
+ +
+ + {/* Print Lifecycle Events */} +
+

Print Events

+ +
+

Print Started

+ +
+ +
+

Print Completed

+ +
+ +
+

Print Failed

+ +
+ +
+

Print Stopped

+ +
+ +
+
+

Progress Milestones

+

Notify at 25%, 50%, 75%

+
+ +
+
+ + {/* Printer Status Events */} +
+

Printer Status

+ +
+

Printer Offline

+ +
+ +
+

Printer Error

+ +
+ +
+

Low Filament

+ +
+
+ + {/* Quiet Hours */} +
+
+
+ +

Quiet Hours

+
+ +
+ + {provider.quiet_hours_enabled && ( +
+

No notifications during these hours

+
+ + + {formatTime(provider.quiet_hours_start) || '22:00'} - {formatTime(provider.quiet_hours_end) || '07:00'} + +
+

Edit provider to change quiet hours

+
+ )} +
+ + {/* Action Buttons */} +
+ + +
+
+ )} +
+
+ + {/* Delete Confirmation */} + {showDeleteConfirm && ( + { + deleteMutation.mutate(); + setShowDeleteConfirm(false); + }} + onCancel={() => setShowDeleteConfirm(false)} + /> + )} + + ); +} diff --git a/frontend/src/pages/SettingsPage.tsx b/frontend/src/pages/SettingsPage.tsx index 8d5d0a035..f50814ccb 100644 --- a/frontend/src/pages/SettingsPage.tsx +++ b/frontend/src/pages/SettingsPage.tsx @@ -1,11 +1,13 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; -import { Save, Loader2, Check, Plus, Plug, AlertTriangle, RotateCcw } from 'lucide-react'; +import { Save, Loader2, Check, Plus, Plug, AlertTriangle, RotateCcw, Bell } from 'lucide-react'; import { api } from '../api/client'; -import type { AppSettings, SmartPlug } from '../api/client'; +import type { AppSettings, SmartPlug, NotificationProvider } from '../api/client'; import { Card, CardContent, CardHeader } from '../components/Card'; import { Button } from '../components/Button'; import { SmartPlugCard } from '../components/SmartPlugCard'; import { AddSmartPlugModal } from '../components/AddSmartPlugModal'; +import { NotificationProviderCard } from '../components/NotificationProviderCard'; +import { AddNotificationModal } from '../components/AddNotificationModal'; import { defaultNavItems, getDefaultView, setDefaultView } from '../components/Layout'; import { useState, useEffect } from 'react'; @@ -16,6 +18,8 @@ export function SettingsPage() { const [showSaved, setShowSaved] = useState(false); const [showPlugModal, setShowPlugModal] = useState(false); const [editingPlug, setEditingPlug] = useState(null); + const [showNotificationModal, setShowNotificationModal] = useState(false); + const [editingProvider, setEditingProvider] = useState(null); const [defaultView, setDefaultViewState] = useState(getDefaultView()); const handleDefaultViewChange = (path: string) => { @@ -38,6 +42,11 @@ export function SettingsPage() { queryFn: api.getSmartPlugs, }); + const { data: notificationProviders, isLoading: providersLoading } = useQuery({ + queryKey: ['notification-providers'], + queryFn: api.getNotificationProviders, + }); + const { data: ffmpegStatus } = useQuery({ queryKey: ['ffmpeg-status'], queryFn: api.checkFfmpeg, @@ -335,7 +344,7 @@ export function SettingsPage() { - {/* Right Column - Smart Plugs */} + {/* Middle Column - Smart Plugs */}
@@ -387,6 +396,59 @@ export function SettingsPage() {
+ + {/* Right Column - Notifications */} +
+ + +
+
+ +

Notifications

+
+ +
+
+ +

+ Get notified about print events via WhatsApp, Telegram, Email, and more. +

+ {providersLoading ? ( +
+ +
+ ) : notificationProviders && notificationProviders.length > 0 ? ( +
+ {notificationProviders.map((provider) => ( + { + setEditingProvider(p); + setShowNotificationModal(true); + }} + /> + ))} +
+ ) : ( +
+ +

No notification providers configured

+

Add a provider to get started

+
+ )} +
+
+
{/* Smart Plug Modal */} @@ -399,6 +461,17 @@ export function SettingsPage() { }} /> )} + + {/* Notification Modal */} + {showNotificationModal && ( + { + setShowNotificationModal(false); + setEditingProvider(null); + }} + /> + )} ); } diff --git a/static/assets/index-C2qF9pCa.js b/static/assets/index-0UQnyYo8.js similarity index 57% rename from static/assets/index-C2qF9pCa.js rename to static/assets/index-0UQnyYo8.js index 6f51a3d23..e355e5863 100644 --- a/static/assets/index-C2qF9pCa.js +++ b/static/assets/index-0UQnyYo8.js @@ -1,18 +1,18 @@ -function vee(t,e){for(var n=0;ni[r]})}}}return Object.freeze(Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}))}(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const r of document.querySelectorAll('link[rel="modulepreload"]'))i(r);new MutationObserver(r=>{for(const a of r)if(a.type==="childList")for(const s of a.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&i(s)}).observe(document,{childList:!0,subtree:!0});function n(r){const a={};return r.integrity&&(a.integrity=r.integrity),r.referrerPolicy&&(a.referrerPolicy=r.referrerPolicy),r.crossOrigin==="use-credentials"?a.credentials="include":r.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function i(r){if(r.ep)return;r.ep=!0;const a=n(r);fetch(r.href,a)}})();var ey=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function $s(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var jE={exports:{}},dg={};var QU;function xee(){if(QU)return dg;QU=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.fragment");function n(i,r,a){var s=null;if(a!==void 0&&(s=""+a),r.key!==void 0&&(s=""+r.key),"key"in r){a={};for(var o in r)o!=="key"&&(a[o]=r[o])}else a=r;return r=a.ref,{$$typeof:t,type:i,key:s,ref:r!==void 0?r:null,props:a}}return dg.Fragment=e,dg.jsx=n,dg.jsxs=n,dg}var JU;function yee(){return JU||(JU=1,jE.exports=xee()),jE.exports}var _=yee(),BE={exports:{}},$t={};var e4;function bee(){if(e4)return $t;e4=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.portal"),n=Symbol.for("react.fragment"),i=Symbol.for("react.strict_mode"),r=Symbol.for("react.profiler"),a=Symbol.for("react.consumer"),s=Symbol.for("react.context"),o=Symbol.for("react.forward_ref"),l=Symbol.for("react.suspense"),c=Symbol.for("react.memo"),d=Symbol.for("react.lazy"),f=Symbol.for("react.activity"),p=Symbol.iterator;function m(j){return j===null||typeof j!="object"?null:(j=p&&j[p]||j["@@iterator"],typeof j=="function"?j:null)}var y={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},b=Object.assign,x={};function g(j,X,re){this.props=j,this.context=X,this.refs=x,this.updater=re||y}g.prototype.isReactComponent={},g.prototype.setState=function(j,X){if(typeof j!="object"&&typeof j!="function"&&j!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,j,X,"setState")},g.prototype.forceUpdate=function(j){this.updater.enqueueForceUpdate(this,j,"forceUpdate")};function M(){}M.prototype=g.prototype;function w(j,X,re){this.props=j,this.context=X,this.refs=x,this.updater=re||y}var E=w.prototype=new M;E.constructor=w,b(E,g.prototype),E.isPureReactComponent=!0;var A=Array.isArray;function P(){}var C={H:null,A:null,T:null,S:null},F=Object.prototype.hasOwnProperty;function N(j,X,re){var ce=re.ref;return{$$typeof:t,type:j,key:X,ref:ce!==void 0?ce:null,props:re}}function O(j,X){return N(j.type,X,j.props)}function B(j){return typeof j=="object"&&j!==null&&j.$$typeof===t}function I(j){var X={"=":"=0",":":"=2"};return"$"+j.replace(/[=:]/g,function(re){return X[re]})}var G=/\/+/g;function L(j,X){return typeof j=="object"&&j!==null&&j.key!=null?I(""+j.key):X.toString(36)}function $(j){switch(j.status){case"fulfilled":return j.value;case"rejected":throw j.reason;default:switch(typeof j.status=="string"?j.then(P,P):(j.status="pending",j.then(function(X){j.status==="pending"&&(j.status="fulfilled",j.value=X)},function(X){j.status==="pending"&&(j.status="rejected",j.reason=X)})),j.status){case"fulfilled":return j.value;case"rejected":throw j.reason}}throw j}function q(j,X,re,ce,xe){var me=typeof j;(me==="undefined"||me==="boolean")&&(j=null);var ye=!1;if(j===null)ye=!0;else switch(me){case"bigint":case"string":case"number":ye=!0;break;case"object":switch(j.$$typeof){case t:case e:ye=!0;break;case d:return ye=j._init,q(ye(j._payload),X,re,ce,xe)}}if(ye)return xe=xe(j),ye=ce===""?"."+L(j,0):ce,A(xe)?(re="",ye!=null&&(re=ye.replace(G,"$&/")+"/"),q(xe,X,re,"",function(we){return we})):xe!=null&&(B(xe)&&(xe=O(xe,re+(xe.key==null||j&&j.key===xe.key?"":(""+xe.key).replace(G,"$&/")+"/")+ye)),X.push(xe)),1;ye=0;var De=ce===""?".":ce+":";if(A(j))for(var he=0;he>>1,se=q[J];if(0>>1;Jr(re,Y))cer(xe,re)?(q[J]=xe,q[ce]=Y,J=ce):(q[J]=re,q[X]=Y,J=X);else if(cer(xe,Y))q[J]=xe,q[ce]=Y,J=ce;else break e}}return W}function r(q,W){var Y=q.sortIndex-W.sortIndex;return Y!==0?Y:q.id-W.id}if(t.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var a=performance;t.unstable_now=function(){return a.now()}}else{var s=Date,o=s.now();t.unstable_now=function(){return s.now()-o}}var l=[],c=[],d=1,f=null,p=3,m=!1,y=!1,b=!1,x=!1,g=typeof setTimeout=="function"?setTimeout:null,M=typeof clearTimeout=="function"?clearTimeout:null,w=typeof setImmediate<"u"?setImmediate:null;function E(q){for(var W=n(c);W!==null;){if(W.callback===null)i(c);else if(W.startTime<=q)i(c),W.sortIndex=W.expirationTime,e(l,W);else break;W=n(c)}}function A(q){if(b=!1,E(q),!y)if(n(l)!==null)y=!0,P||(P=!0,I());else{var W=n(c);W!==null&&$(A,W.startTime-q)}}var P=!1,C=-1,F=5,N=-1;function O(){return x?!0:!(t.unstable_now()-Nq&&O());){var J=f.callback;if(typeof J=="function"){f.callback=null,p=f.priorityLevel;var se=J(f.expirationTime<=q);if(q=t.unstable_now(),typeof se=="function"){f.callback=se,E(q),W=!0;break t}f===n(l)&&i(l),E(q)}else i(l);f=n(l)}if(f!==null)W=!0;else{var j=n(c);j!==null&&$(A,j.startTime-q),W=!1}}break e}finally{f=null,p=Y,m=!1}W=void 0}}finally{W?I():P=!1}}}var I;if(typeof w=="function")I=function(){w(B)};else if(typeof MessageChannel<"u"){var G=new MessageChannel,L=G.port2;G.port1.onmessage=B,I=function(){L.postMessage(null)}}else I=function(){g(B,0)};function $(q,W){C=g(function(){q(t.unstable_now())},W)}t.unstable_IdlePriority=5,t.unstable_ImmediatePriority=1,t.unstable_LowPriority=4,t.unstable_NormalPriority=3,t.unstable_Profiling=null,t.unstable_UserBlockingPriority=2,t.unstable_cancelCallback=function(q){q.callback=null},t.unstable_forceFrameRate=function(q){0>q||125J?(q.sortIndex=Y,e(c,q),n(l)===null&&q===n(c)&&(b?(M(C),C=-1):b=!0,$(A,Y-J))):(q.sortIndex=se,e(l,q),y||m||(y=!0,P||(P=!0,I()))),q},t.unstable_shouldYield=O,t.unstable_wrapCallback=function(q){var W=p;return function(){var Y=p;p=W;try{return q.apply(this,arguments)}finally{p=Y}}}})(GE)),GE}var i4;function wee(){return i4||(i4=1,VE.exports=See()),VE.exports}var $E={exports:{}},Gr={};var r4;function Mee(){if(r4)return Gr;r4=1;var t=Qp();function e(l){var c="https://react.dev/errors/"+l;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(t)}catch(e){console.error(e)}}return t(),$E.exports=Mee(),$E.exports}var s4;function Eee(){if(s4)return fg;s4=1;var t=wee(),e=Qp(),n=$V();function i(u){var h="https://react.dev/errors/"+u;if(1se||(u.current=J[se],J[se]=null,se--)}function re(u,h){se++,J[se]=u.current,u.current=h}var ce=j(null),xe=j(null),me=j(null),ye=j(null);function De(u,h){switch(re(me,h),re(xe,u),re(ce,null),h.nodeType){case 9:case 11:u=(u=h.documentElement)&&(u=u.namespaceURI)?_U(u):0;break;default:if(u=h.tagName,h=h.namespaceURI)h=_U(h),u=SU(h,u);else switch(u){case"svg":u=1;break;case"math":u=2;break;default:u=0}}X(ce),re(ce,u)}function he(){X(ce),X(xe),X(me)}function we(u){u.memoizedState!==null&&re(ye,u);var h=ce.current,v=SU(h,u.type);h!==v&&(re(xe,u),re(ce,v))}function je(u){xe.current===u&&(X(ce),X(xe)),ye.current===u&&(X(ye),og._currentValue=Y)}var le,ut;function z(u){if(le===void 0)try{throw Error()}catch(v){var h=v.stack.trim().match(/\n( *(at )?)/);le=h&&h[1]||"",ut=-1i[r]})}}}return Object.freeze(Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}))}(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const r of document.querySelectorAll('link[rel="modulepreload"]'))i(r);new MutationObserver(r=>{for(const a of r)if(a.type==="childList")for(const s of a.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&i(s)}).observe(document,{childList:!0,subtree:!0});function n(r){const a={};return r.integrity&&(a.integrity=r.integrity),r.referrerPolicy&&(a.referrerPolicy=r.referrerPolicy),r.crossOrigin==="use-credentials"?a.credentials="include":r.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function i(r){if(r.ep)return;r.ep=!0;const a=n(r);fetch(r.href,a)}})();var ny=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function $s(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var HE={exports:{}},dg={};var t4;function wee(){if(t4)return dg;t4=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.fragment");function n(i,r,a){var s=null;if(a!==void 0&&(s=""+a),r.key!==void 0&&(s=""+r.key),"key"in r){a={};for(var o in r)o!=="key"&&(a[o]=r[o])}else a=r;return r=a.ref,{$$typeof:t,type:i,key:s,ref:r!==void 0?r:null,props:a}}return dg.Fragment=e,dg.jsx=n,dg.jsxs=n,dg}var n4;function Mee(){return n4||(n4=1,HE.exports=wee()),HE.exports}var x=Mee(),VE={exports:{}},$t={};var i4;function Eee(){if(i4)return $t;i4=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.portal"),n=Symbol.for("react.fragment"),i=Symbol.for("react.strict_mode"),r=Symbol.for("react.profiler"),a=Symbol.for("react.consumer"),s=Symbol.for("react.context"),o=Symbol.for("react.forward_ref"),l=Symbol.for("react.suspense"),c=Symbol.for("react.memo"),d=Symbol.for("react.lazy"),f=Symbol.for("react.activity"),p=Symbol.iterator;function m(j){return j===null||typeof j!="object"?null:(j=p&&j[p]||j["@@iterator"],typeof j=="function"?j:null)}var _={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},b=Object.assign,y={};function g(j,X,re){this.props=j,this.context=X,this.refs=y,this.updater=re||_}g.prototype.isReactComponent={},g.prototype.setState=function(j,X){if(typeof j!="object"&&typeof j!="function"&&j!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,j,X,"setState")},g.prototype.forceUpdate=function(j){this.updater.enqueueForceUpdate(this,j,"forceUpdate")};function M(){}M.prototype=g.prototype;function w(j,X,re){this.props=j,this.context=X,this.refs=y,this.updater=re||_}var A=w.prototype=new M;A.constructor=w,b(A,g.prototype),A.isPureReactComponent=!0;var E=Array.isArray;function P(){}var N={H:null,A:null,T:null,S:null},F=Object.prototype.hasOwnProperty;function R(j,X,re){var ce=re.ref;return{$$typeof:t,type:j,key:X,ref:ce!==void 0?ce:null,props:re}}function O(j,X){return R(j.type,X,j.props)}function B(j){return typeof j=="object"&&j!==null&&j.$$typeof===t}function I(j){var X={"=":"=0",":":"=2"};return"$"+j.replace(/[=:]/g,function(re){return X[re]})}var G=/\/+/g;function L(j,X){return typeof j=="object"&&j!==null&&j.key!=null?I(""+j.key):X.toString(36)}function H(j){switch(j.status){case"fulfilled":return j.value;case"rejected":throw j.reason;default:switch(typeof j.status=="string"?j.then(P,P):(j.status="pending",j.then(function(X){j.status==="pending"&&(j.status="fulfilled",j.value=X)},function(X){j.status==="pending"&&(j.status="rejected",j.reason=X)})),j.status){case"fulfilled":return j.value;case"rejected":throw j.reason}}throw j}function q(j,X,re,ce,ve){var me=typeof j;(me==="undefined"||me==="boolean")&&(j=null);var ye=!1;if(j===null)ye=!0;else switch(me){case"bigint":case"string":case"number":ye=!0;break;case"object":switch(j.$$typeof){case t:case e:ye=!0;break;case d:return ye=j._init,q(ye(j._payload),X,re,ce,ve)}}if(ye)return ve=ve(j),ye=ce===""?"."+L(j,0):ce,E(ve)?(re="",ye!=null&&(re=ye.replace(G,"$&/")+"/"),q(ve,X,re,"",function(we){return we})):ve!=null&&(B(ve)&&(ve=O(ve,re+(ve.key==null||j&&j.key===ve.key?"":(""+ve.key).replace(G,"$&/")+"/")+ye)),X.push(ve)),1;ye=0;var De=ce===""?".":ce+":";if(E(j))for(var he=0;he>>1,se=q[J];if(0>>1;Jr(re,K))cer(ve,re)?(q[J]=ve,q[ce]=K,J=ce):(q[J]=re,q[X]=K,J=X);else if(cer(ve,K))q[J]=ve,q[ce]=K,J=ce;else break e}}return W}function r(q,W){var K=q.sortIndex-W.sortIndex;return K!==0?K:q.id-W.id}if(t.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var a=performance;t.unstable_now=function(){return a.now()}}else{var s=Date,o=s.now();t.unstable_now=function(){return s.now()-o}}var l=[],c=[],d=1,f=null,p=3,m=!1,_=!1,b=!1,y=!1,g=typeof setTimeout=="function"?setTimeout:null,M=typeof clearTimeout=="function"?clearTimeout:null,w=typeof setImmediate<"u"?setImmediate:null;function A(q){for(var W=n(c);W!==null;){if(W.callback===null)i(c);else if(W.startTime<=q)i(c),W.sortIndex=W.expirationTime,e(l,W);else break;W=n(c)}}function E(q){if(b=!1,A(q),!_)if(n(l)!==null)_=!0,P||(P=!0,I());else{var W=n(c);W!==null&&H(E,W.startTime-q)}}var P=!1,N=-1,F=5,R=-1;function O(){return y?!0:!(t.unstable_now()-Rq&&O());){var J=f.callback;if(typeof J=="function"){f.callback=null,p=f.priorityLevel;var se=J(f.expirationTime<=q);if(q=t.unstable_now(),typeof se=="function"){f.callback=se,A(q),W=!0;break t}f===n(l)&&i(l),A(q)}else i(l);f=n(l)}if(f!==null)W=!0;else{var j=n(c);j!==null&&H(E,j.startTime-q),W=!1}}break e}finally{f=null,p=K,m=!1}W=void 0}}finally{W?I():P=!1}}}var I;if(typeof w=="function")I=function(){w(B)};else if(typeof MessageChannel<"u"){var G=new MessageChannel,L=G.port2;G.port1.onmessage=B,I=function(){L.postMessage(null)}}else I=function(){g(B,0)};function H(q,W){N=g(function(){q(t.unstable_now())},W)}t.unstable_IdlePriority=5,t.unstable_ImmediatePriority=1,t.unstable_LowPriority=4,t.unstable_NormalPriority=3,t.unstable_Profiling=null,t.unstable_UserBlockingPriority=2,t.unstable_cancelCallback=function(q){q.callback=null},t.unstable_forceFrameRate=function(q){0>q||125J?(q.sortIndex=K,e(c,q),n(l)===null&&q===n(c)&&(b?(M(N),N=-1):b=!0,H(E,K-J))):(q.sortIndex=se,e(l,q),_||m||(_=!0,P||(P=!0,I()))),q},t.unstable_shouldYield=O,t.unstable_wrapCallback=function(q){var W=p;return function(){var K=p;p=W;try{return q.apply(this,arguments)}finally{p=K}}}})(WE)),WE}var s4;function Cee(){return s4||(s4=1,$E.exports=Tee()),$E.exports}var qE={exports:{}},Gr={};var o4;function Nee(){if(o4)return Gr;o4=1;var t=Jp();function e(l){var c="https://react.dev/errors/"+l;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(t)}catch(e){console.error(e)}}return t(),qE.exports=Nee(),qE.exports}var c4;function Ree(){if(c4)return fg;c4=1;var t=Cee(),e=Jp(),n=XV();function i(u){var h="https://react.dev/errors/"+u;if(1se||(u.current=J[se],J[se]=null,se--)}function re(u,h){se++,J[se]=u.current,u.current=h}var ce=j(null),ve=j(null),me=j(null),ye=j(null);function De(u,h){switch(re(me,h),re(ve,u),re(ce,null),h.nodeType){case 9:case 11:u=(u=h.documentElement)&&(u=u.namespaceURI)?MU(u):0;break;default:if(u=h.tagName,h=h.namespaceURI)h=MU(h),u=EU(h,u);else switch(u){case"svg":u=1;break;case"math":u=2;break;default:u=0}}X(ce),re(ce,u)}function he(){X(ce),X(ve),X(me)}function we(u){u.memoizedState!==null&&re(ye,u);var h=ce.current,v=EU(h,u.type);h!==v&&(re(ve,u),re(ce,v))}function je(u){ve.current===u&&(X(ce),X(ve)),ye.current===u&&(X(ye),og._currentValue=K)}var le,ft;function z(u){if(le===void 0)try{throw Error()}catch(v){var h=v.stack.trim().match(/\n( *(at )?)/);le=h&&h[1]||"",ft=-1)":-1R||_e[S]!==ze[R]){var Qe=` -`+_e[S].replace(" at new "," at ");return u.displayName&&Qe.includes("")&&(Qe=Qe.replace("",u.displayName)),Qe}while(1<=S&&0<=R);break}}}finally{V=!1,Error.prepareStackTrace=v}return(v=u?u.displayName||u.name:"")?z(v):""}function Q(u,h){switch(u.tag){case 26:case 27:case 5:return z(u.type);case 16:return z("Lazy");case 13:return u.child!==h&&h!==null?z("Suspense Fallback"):z("Suspense");case 19:return z("SuspenseList");case 0:case 15:return ie(u.type,!1);case 11:return ie(u.type.render,!1);case 1:return ie(u.type,!0);case 31:return z("Activity");default:return""}}function ee(u){try{var h="",v=null;do h+=Q(u,v),v=u,u=u.return;while(u);return h}catch(S){return` +`);for(C=S=0;S<_e.length&&!_e[S].includes("DetermineComponentFrameRoot");)S++;for(;CC||_e[S]!==ze[C]){var Qe=` +`+_e[S].replace(" at new "," at ");return u.displayName&&Qe.includes("")&&(Qe=Qe.replace("",u.displayName)),Qe}while(1<=S&&0<=C);break}}}finally{$=!1,Error.prepareStackTrace=v}return(v=u?u.displayName||u.name:"")?z(v):""}function Q(u,h){switch(u.tag){case 26:case 27:case 5:return z(u.type);case 16:return z("Lazy");case 13:return u.child!==h&&h!==null?z("Suspense Fallback"):z("Suspense");case 19:return z("SuspenseList");case 0:case 15:return ie(u.type,!1);case 11:return ie(u.type.render,!1);case 1:return ie(u.type,!0);case 31:return z("Activity");default:return""}}function ee(u){try{var h="",v=null;do h+=Q(u,v),v=u,u=u.return;while(u);return h}catch(S){return` Error generating stack: `+S.message+` -`+S.stack}}var ae=Object.prototype.hasOwnProperty,Pe=t.unstable_scheduleCallback,Re=t.unstable_cancelCallback,te=t.unstable_shouldYield,U=t.unstable_requestPaint,k=t.unstable_now,K=t.unstable_getCurrentPriorityLevel,pe=t.unstable_ImmediatePriority,ve=t.unstable_UserBlockingPriority,fe=t.unstable_NormalPriority,Ye=t.unstable_LowPriority,Le=t.unstable_IdlePriority,qe=t.log,it=t.unstable_setDisableYieldValue,Ie=null,Ne=null;function ft(u){if(typeof qe=="function"&&it(u),Ne&&typeof Ne.setStrictMode=="function")try{Ne.setStrictMode(Ie,u)}catch{}}var et=Math.clz32?Math.clz32:ue,Je=Math.log,st=Math.LN2;function ue(u){return u>>>=0,u===0?32:31-(Je(u)/st|0)|0}var tt=256,de=262144,ke=4194304;function Me(u){var h=u&42;if(h!==0)return h;switch(u&-u){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return u&261888;case 262144:case 524288:case 1048576:case 2097152:return u&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return u&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return u}}function ge(u,h,v){var S=u.pendingLanes;if(S===0)return 0;var R=0,D=u.suspendedLanes,H=u.pingedLanes;u=u.warmLanes;var ne=S&134217727;return ne!==0?(S=ne&~D,S!==0?R=Me(S):(H&=ne,H!==0?R=Me(H):v||(v=ne&~u,v!==0&&(R=Me(v))))):(ne=S&~D,ne!==0?R=Me(ne):H!==0?R=Me(H):v||(v=S&~u,v!==0&&(R=Me(v)))),R===0?0:h!==0&&h!==R&&(h&D)===0&&(D=R&-R,v=h&-h,D>=v||D===32&&(v&4194048)!==0)?h:R}function Be(u,h){return(u.pendingLanes&~(u.suspendedLanes&~u.pingedLanes)&h)===0}function at(u,h){switch(u){case 1:case 2:case 4:case 8:case 64:return h+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return h+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function yt(){var u=ke;return ke<<=1,(ke&62914560)===0&&(ke=4194304),u}function Ct(u){for(var h=[],v=0;31>v;v++)h.push(u);return h}function tn(u,h){u.pendingLanes|=h,h!==268435456&&(u.suspendedLanes=0,u.pingedLanes=0,u.warmLanes=0)}function Dt(u,h,v,S,R,D){var H=u.pendingLanes;u.pendingLanes=v,u.suspendedLanes=0,u.pingedLanes=0,u.warmLanes=0,u.expiredLanes&=v,u.entangledLanes&=v,u.errorRecoveryDisabledLanes&=v,u.shellSuspendCounter=0;var ne=u.entanglements,_e=u.expirationTimes,ze=u.hiddenUpdates;for(v=H&~v;0"u")return null;try{return u.activeElement||u.body}catch{return u.body}}var Ei=/[\n"\\]/g;function En(u){return u.replace(Ei,function(h){return"\\"+h.charCodeAt(0).toString(16)+" "})}function Xs(u,h,v,S,R,D,H,ne){u.name="",H!=null&&typeof H!="function"&&typeof H!="symbol"&&typeof H!="boolean"?u.type=H:u.removeAttribute("type"),h!=null?H==="number"?(h===0&&u.value===""||u.value!=h)&&(u.value=""+rn(h)):u.value!==""+rn(h)&&(u.value=""+rn(h)):H!=="submit"&&H!=="reset"||u.removeAttribute("value"),h!=null?hr(u,H,rn(h)):v!=null?hr(u,H,rn(v)):S!=null&&u.removeAttribute("value"),R==null&&D!=null&&(u.defaultChecked=!!D),R!=null&&(u.checked=R&&typeof R!="function"&&typeof R!="symbol"),ne!=null&&typeof ne!="function"&&typeof ne!="symbol"&&typeof ne!="boolean"?u.name=""+rn(ne):u.removeAttribute("name")}function Cn(u,h,v,S,R,D,H,ne){if(D!=null&&typeof D!="function"&&typeof D!="symbol"&&typeof D!="boolean"&&(u.type=D),h!=null||v!=null){if(!(D!=="submit"&&D!=="reset"||h!=null)){Gt(u);return}v=v!=null?""+rn(v):"",h=h!=null?""+rn(h):v,ne||h===u.value||(u.value=h),u.defaultValue=h}S=S??R,S=typeof S!="function"&&typeof S!="symbol"&&!!S,u.checked=ne?u.checked:!!S,u.defaultChecked=!!S,H!=null&&typeof H!="function"&&typeof H!="symbol"&&typeof H!="boolean"&&(u.name=H),Gt(u)}function hr(u,h,v){h==="number"&&vi(u.ownerDocument)===u||u.defaultValue===""+v||(u.defaultValue=""+v)}function Yi(u,h,v,S){if(u=u.options,h){h={};for(var R=0;R"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),jw=!1;if(il)try{var Em={};Object.defineProperty(Em,"passive",{get:function(){jw=!0}}),window.addEventListener("test",Em,Em),window.removeEventListener("test",Em,Em)}catch{jw=!1}var Sc=null,Bw=null,Wv=null;function Sk(){if(Wv)return Wv;var u,h=Bw,v=h.length,S,R="value"in Sc?Sc.value:Sc.textContent,D=R.length;for(u=0;u=Cm),Ck=" ",Rk=!1;function Nk(u,h){switch(u){case"keyup":return zQ.indexOf(h.keyCode)!==-1;case"keydown":return h.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Pk(u){return u=u.detail,typeof u=="object"&&"data"in u?u.data:null}var zf=!1;function BQ(u,h){switch(u){case"compositionend":return Pk(h);case"keypress":return h.which!==32?null:(Rk=!0,Ck);case"textInput":return u=h.data,u===Ck&&Rk?null:u;default:return null}}function HQ(u,h){if(zf)return u==="compositionend"||!Ww&&Nk(u,h)?(u=Sk(),Wv=Bw=Sc=null,zf=!1,u):null;switch(u){case"paste":return null;case"keypress":if(!(h.ctrlKey||h.altKey||h.metaKey)||h.ctrlKey&&h.altKey){if(h.char&&1=h)return{node:v,offset:h-u};u=S}e:{for(;v;){if(v.nextSibling){v=v.nextSibling;break e}v=v.parentNode}v=void 0}v=zk(v)}}function Bk(u,h){return u&&h?u===h?!0:u&&u.nodeType===3?!1:h&&h.nodeType===3?Bk(u,h.parentNode):"contains"in u?u.contains(h):u.compareDocumentPosition?!!(u.compareDocumentPosition(h)&16):!1:!1}function Hk(u){u=u!=null&&u.ownerDocument!=null&&u.ownerDocument.defaultView!=null?u.ownerDocument.defaultView:window;for(var h=vi(u.document);h instanceof u.HTMLIFrameElement;){try{var v=typeof h.contentWindow.location.href=="string"}catch{v=!1}if(v)u=h.contentWindow;else break;h=vi(u.document)}return h}function Kw(u){var h=u&&u.nodeName&&u.nodeName.toLowerCase();return h&&(h==="input"&&(u.type==="text"||u.type==="search"||u.type==="tel"||u.type==="url"||u.type==="password")||h==="textarea"||u.contentEditable==="true")}var YQ=il&&"documentMode"in document&&11>=document.documentMode,jf=null,Yw=null,Dm=null,Zw=!1;function Vk(u,h,v){var S=v.window===v?v.document:v.nodeType===9?v:v.ownerDocument;Zw||jf==null||jf!==vi(S)||(S=jf,"selectionStart"in S&&Kw(S)?S={start:S.selectionStart,end:S.selectionEnd}:(S=(S.ownerDocument&&S.ownerDocument.defaultView||window).getSelection(),S={anchorNode:S.anchorNode,anchorOffset:S.anchorOffset,focusNode:S.focusNode,focusOffset:S.focusOffset}),Dm&&Pm(Dm,S)||(Dm=S,S=zx(Yw,"onSelect"),0>=H,R-=H,So=1<<32-et(h)+R|v<Xt?(dn=Pt,Pt=null):dn=Pt.sibling;var yn=Ve(Oe,Pt,Fe[Xt],nt);if(yn===null){Pt===null&&(Pt=dn);break}u&&Pt&&yn.alternate===null&&h(Oe,Pt),Ee=D(yn,Ee,Xt),xn===null?Lt=yn:xn.sibling=yn,xn=yn,Pt=dn}if(Xt===Fe.length)return v(Oe,Pt),pn&&al(Oe,Xt),Lt;if(Pt===null){for(;XtXt?(dn=Pt,Pt=null):dn=Pt.sibling;var Gc=Ve(Oe,Pt,yn.value,nt);if(Gc===null){Pt===null&&(Pt=dn);break}u&&Pt&&Gc.alternate===null&&h(Oe,Pt),Ee=D(Gc,Ee,Xt),xn===null?Lt=Gc:xn.sibling=Gc,xn=Gc,Pt=dn}if(yn.done)return v(Oe,Pt),pn&&al(Oe,Xt),Lt;if(Pt===null){for(;!yn.done;Xt++,yn=Fe.next())yn=rt(Oe,yn.value,nt),yn!==null&&(Ee=D(yn,Ee,Xt),xn===null?Lt=yn:xn.sibling=yn,xn=yn);return pn&&al(Oe,Xt),Lt}for(Pt=S(Pt);!yn.done;Xt++,yn=Fe.next())yn=Ke(Pt,Oe,Xt,yn.value,nt),yn!==null&&(u&&yn.alternate!==null&&Pt.delete(yn.key===null?Xt:yn.key),Ee=D(yn,Ee,Xt),xn===null?Lt=yn:xn.sibling=yn,xn=yn);return u&&Pt.forEach(function(gee){return h(Oe,gee)}),pn&&al(Oe,Xt),Lt}function Ln(Oe,Ee,Fe,nt){if(typeof Fe=="object"&&Fe!==null&&Fe.type===b&&Fe.key===null&&(Fe=Fe.props.children),typeof Fe=="object"&&Fe!==null){switch(Fe.$$typeof){case m:e:{for(var Lt=Fe.key;Ee!==null;){if(Ee.key===Lt){if(Lt=Fe.type,Lt===b){if(Ee.tag===7){v(Oe,Ee.sibling),nt=R(Ee,Fe.props.children),nt.return=Oe,Oe=nt;break e}}else if(Ee.elementType===Lt||typeof Lt=="object"&&Lt!==null&&Lt.$$typeof===F&&ud(Lt)===Ee.type){v(Oe,Ee.sibling),nt=R(Ee,Fe.props),Fm(nt,Fe),nt.return=Oe,Oe=nt;break e}v(Oe,Ee);break}else h(Oe,Ee);Ee=Ee.sibling}Fe.type===b?(nt=ad(Fe.props.children,Oe.mode,nt,Fe.key),nt.return=Oe,Oe=nt):(nt=nx(Fe.type,Fe.key,Fe.props,null,Oe.mode,nt),Fm(nt,Fe),nt.return=Oe,Oe=nt)}return H(Oe);case y:e:{for(Lt=Fe.key;Ee!==null;){if(Ee.key===Lt)if(Ee.tag===4&&Ee.stateNode.containerInfo===Fe.containerInfo&&Ee.stateNode.implementation===Fe.implementation){v(Oe,Ee.sibling),nt=R(Ee,Fe.children||[]),nt.return=Oe,Oe=nt;break e}else{v(Oe,Ee);break}else h(Oe,Ee);Ee=Ee.sibling}nt=rM(Fe,Oe.mode,nt),nt.return=Oe,Oe=nt}return H(Oe);case F:return Fe=ud(Fe),Ln(Oe,Ee,Fe,nt)}if($(Fe))return Tt(Oe,Ee,Fe,nt);if(I(Fe)){if(Lt=I(Fe),typeof Lt!="function")throw Error(i(150));return Fe=Lt.call(Fe),zt(Oe,Ee,Fe,nt)}if(typeof Fe.then=="function")return Ln(Oe,Ee,cx(Fe),nt);if(Fe.$$typeof===w)return Ln(Oe,Ee,ax(Oe,Fe),nt);ux(Oe,Fe)}return typeof Fe=="string"&&Fe!==""||typeof Fe=="number"||typeof Fe=="bigint"?(Fe=""+Fe,Ee!==null&&Ee.tag===6?(v(Oe,Ee.sibling),nt=R(Ee,Fe),nt.return=Oe,Oe=nt):(v(Oe,Ee),nt=iM(Fe,Oe.mode,nt),nt.return=Oe,Oe=nt),H(Oe)):v(Oe,Ee)}return function(Oe,Ee,Fe,nt){try{Um=0;var Lt=Ln(Oe,Ee,Fe,nt);return Zf=null,Lt}catch(Pt){if(Pt===Yf||Pt===ox)throw Pt;var xn=Ba(29,Pt,null,Oe.mode);return xn.lanes=nt,xn.return=Oe,xn}finally{}}}var fd=fL(!0),hL=fL(!1),Tc=!1;function gM(u){u.updateQueue={baseState:u.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function vM(u,h){u=u.updateQueue,h.updateQueue===u&&(h.updateQueue={baseState:u.baseState,firstBaseUpdate:u.firstBaseUpdate,lastBaseUpdate:u.lastBaseUpdate,shared:u.shared,callbacks:null})}function Cc(u){return{lane:u,tag:0,payload:null,callback:null,next:null}}function Rc(u,h,v){var S=u.updateQueue;if(S===null)return null;if(S=S.shared,(Mn&2)!==0){var R=S.pending;return R===null?h.next=h:(h.next=R.next,R.next=h),S.pending=h,h=tx(u),Yk(u,null,v),h}return ex(u,S,h,v),tx(u)}function zm(u,h,v){if(h=h.updateQueue,h!==null&&(h=h.shared,(v&4194048)!==0)){var S=h.lanes;S&=u.pendingLanes,v|=S,h.lanes=v,$n(u,v)}}function xM(u,h){var v=u.updateQueue,S=u.alternate;if(S!==null&&(S=S.updateQueue,v===S)){var R=null,D=null;if(v=v.firstBaseUpdate,v!==null){do{var H={lane:v.lane,tag:v.tag,payload:v.payload,callback:null,next:null};D===null?R=D=H:D=D.next=H,v=v.next}while(v!==null);D===null?R=D=h:D=D.next=h}else R=D=h;v={baseState:S.baseState,firstBaseUpdate:R,lastBaseUpdate:D,shared:S.shared,callbacks:S.callbacks},u.updateQueue=v;return}u=v.lastBaseUpdate,u===null?v.firstBaseUpdate=h:u.next=h,v.lastBaseUpdate=h}var yM=!1;function jm(){if(yM){var u=Kf;if(u!==null)throw u}}function Bm(u,h,v,S){yM=!1;var R=u.updateQueue;Tc=!1;var D=R.firstBaseUpdate,H=R.lastBaseUpdate,ne=R.shared.pending;if(ne!==null){R.shared.pending=null;var _e=ne,ze=_e.next;_e.next=null,H===null?D=ze:H.next=ze,H=_e;var Qe=u.alternate;Qe!==null&&(Qe=Qe.updateQueue,ne=Qe.lastBaseUpdate,ne!==H&&(ne===null?Qe.firstBaseUpdate=ze:ne.next=ze,Qe.lastBaseUpdate=_e))}if(D!==null){var rt=R.baseState;H=0,Qe=ze=_e=null,ne=D;do{var Ve=ne.lane&-536870913,Ke=Ve!==ne.lane;if(Ke?(un&Ve)===Ve:(S&Ve)===Ve){Ve!==0&&Ve===Xf&&(yM=!0),Qe!==null&&(Qe=Qe.next={lane:0,tag:ne.tag,payload:ne.payload,callback:null,next:null});e:{var Tt=u,zt=ne;Ve=h;var Ln=v;switch(zt.tag){case 1:if(Tt=zt.payload,typeof Tt=="function"){rt=Tt.call(Ln,rt,Ve);break e}rt=Tt;break e;case 3:Tt.flags=Tt.flags&-65537|128;case 0:if(Tt=zt.payload,Ve=typeof Tt=="function"?Tt.call(Ln,rt,Ve):Tt,Ve==null)break e;rt=f({},rt,Ve);break e;case 2:Tc=!0}}Ve=ne.callback,Ve!==null&&(u.flags|=64,Ke&&(u.flags|=8192),Ke=R.callbacks,Ke===null?R.callbacks=[Ve]:Ke.push(Ve))}else Ke={lane:Ve,tag:ne.tag,payload:ne.payload,callback:ne.callback,next:null},Qe===null?(ze=Qe=Ke,_e=rt):Qe=Qe.next=Ke,H|=Ve;if(ne=ne.next,ne===null){if(ne=R.shared.pending,ne===null)break;Ke=ne,ne=Ke.next,Ke.next=null,R.lastBaseUpdate=Ke,R.shared.pending=null}}while(!0);Qe===null&&(_e=rt),R.baseState=_e,R.firstBaseUpdate=ze,R.lastBaseUpdate=Qe,D===null&&(R.shared.lanes=0),kc|=H,u.lanes=H,u.memoizedState=rt}}function pL(u,h){if(typeof u!="function")throw Error(i(191,u));u.call(h)}function mL(u,h){var v=u.callbacks;if(v!==null)for(u.callbacks=null,u=0;uD?D:8;var H=q.T,ne={};q.T=ne,FM(u,!1,h,v);try{var _e=R(),ze=q.S;if(ze!==null&&ze(ne,_e),_e!==null&&typeof _e=="object"&&typeof _e.then=="function"){var Qe=aJ(_e,S);Gm(u,h,Qe,Wa(u))}else Gm(u,h,S,Wa(u))}catch(rt){Gm(u,h,{then:function(){},status:"rejected",reason:rt},Wa())}finally{W.p=D,H!==null&&ne.types!==null&&(H.types=ne.types),q.T=H}}function dJ(){}function IM(u,h,v,S){if(u.tag!==5)throw Error(i(476));var R=qL(u).queue;WL(u,R,h,Y,v===null?dJ:function(){return XL(u),v(S)})}function qL(u){var h=u.memoizedState;if(h!==null)return h;h={memoizedState:Y,baseState:Y,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:cl,lastRenderedState:Y},next:null};var v={};return h.next={memoizedState:v,baseState:v,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:cl,lastRenderedState:v},next:null},u.memoizedState=h,u=u.alternate,u!==null&&(u.memoizedState=h),h}function XL(u){var h=qL(u);h.next===null&&(h=u.alternate.memoizedState),Gm(u,h.next.queue,{},Wa())}function UM(){return Nr(og)}function KL(){return Ti().memoizedState}function YL(){return Ti().memoizedState}function fJ(u){for(var h=u.return;h!==null;){switch(h.tag){case 24:case 3:var v=Wa();u=Cc(v);var S=Rc(h,u,v);S!==null&&(Ma(S,h,v),zm(S,h,v)),h={cache:fM()},u.payload=h;return}h=h.return}}function hJ(u,h,v){var S=Wa();v={lane:S,revertLane:0,gesture:null,action:v,hasEagerState:!1,eagerState:null,next:null},bx(u)?QL(h,v):(v=tM(u,h,v,S),v!==null&&(Ma(v,u,S),JL(v,h,S)))}function ZL(u,h,v){var S=Wa();Gm(u,h,v,S)}function Gm(u,h,v,S){var R={lane:S,revertLane:0,gesture:null,action:v,hasEagerState:!1,eagerState:null,next:null};if(bx(u))QL(h,R);else{var D=u.alternate;if(u.lanes===0&&(D===null||D.lanes===0)&&(D=h.lastRenderedReducer,D!==null))try{var H=h.lastRenderedState,ne=D(H,v);if(R.hasEagerState=!0,R.eagerState=ne,ja(ne,H))return ex(u,h,R,0),Wn===null&&Jv(),!1}catch{}finally{}if(v=tM(u,h,R,S),v!==null)return Ma(v,u,S),JL(v,h,S),!0}return!1}function FM(u,h,v,S){if(S={lane:2,revertLane:gE(),gesture:null,action:S,hasEagerState:!1,eagerState:null,next:null},bx(u)){if(h)throw Error(i(479))}else h=tM(u,v,S,2),h!==null&&Ma(h,u,2)}function bx(u){var h=u.alternate;return u===qt||h!==null&&h===qt}function QL(u,h){Jf=hx=!0;var v=u.pending;v===null?h.next=h:(h.next=v.next,v.next=h),u.pending=h}function JL(u,h,v){if((v&4194048)!==0){var S=h.lanes;S&=u.pendingLanes,v|=S,h.lanes=v,$n(u,v)}}var $m={readContext:Nr,use:gx,useCallback:xi,useContext:xi,useEffect:xi,useImperativeHandle:xi,useLayoutEffect:xi,useInsertionEffect:xi,useMemo:xi,useReducer:xi,useRef:xi,useState:xi,useDebugValue:xi,useDeferredValue:xi,useTransition:xi,useSyncExternalStore:xi,useId:xi,useHostTransitionStatus:xi,useFormState:xi,useActionState:xi,useOptimistic:xi,useMemoCache:xi,useCacheRefresh:xi};$m.useEffectEvent=xi;var eI={readContext:Nr,use:gx,useCallback:function(u,h){return aa().memoizedState=[u,h===void 0?null:h],u},useContext:Nr,useEffect:UL,useImperativeHandle:function(u,h,v){v=v!=null?v.concat([u]):null,xx(4194308,4,BL.bind(null,h,u),v)},useLayoutEffect:function(u,h){return xx(4194308,4,u,h)},useInsertionEffect:function(u,h){xx(4,2,u,h)},useMemo:function(u,h){var v=aa();h=h===void 0?null:h;var S=u();if(hd){ft(!0);try{u()}finally{ft(!1)}}return v.memoizedState=[S,h],S},useReducer:function(u,h,v){var S=aa();if(v!==void 0){var R=v(h);if(hd){ft(!0);try{v(h)}finally{ft(!1)}}}else R=h;return S.memoizedState=S.baseState=R,u={pending:null,lanes:0,dispatch:null,lastRenderedReducer:u,lastRenderedState:R},S.queue=u,u=u.dispatch=hJ.bind(null,qt,u),[S.memoizedState,u]},useRef:function(u){var h=aa();return u={current:u},h.memoizedState=u},useState:function(u){u=PM(u);var h=u.queue,v=ZL.bind(null,qt,h);return h.dispatch=v,[u.memoizedState,v]},useDebugValue:kM,useDeferredValue:function(u,h){var v=aa();return LM(v,u,h)},useTransition:function(){var u=PM(!1);return u=WL.bind(null,qt,u.queue,!0,!1),aa().memoizedState=u,[!1,u]},useSyncExternalStore:function(u,h,v){var S=qt,R=aa();if(pn){if(v===void 0)throw Error(i(407));v=v()}else{if(v=h(),Wn===null)throw Error(i(349));(un&127)!==0||_L(S,h,v)}R.memoizedState=v;var D={value:v,getSnapshot:h};return R.queue=D,UL(wL.bind(null,S,D,u),[u]),S.flags|=2048,th(9,{destroy:void 0},SL.bind(null,S,D,v,h),null),v},useId:function(){var u=aa(),h=Wn.identifierPrefix;if(pn){var v=wo,S=So;v=(S&~(1<<32-et(S)-1)).toString(32)+v,h="_"+h+"R_"+v,v=px++,0<\/script>",D=D.removeChild(D.firstChild);break;case"select":D=typeof S.is=="string"?H.createElement("select",{is:S.is}):H.createElement("select"),S.multiple?D.multiple=!0:S.size&&(D.size=S.size);break;default:D=typeof S.is=="string"?H.createElement(R,{is:S.is}):H.createElement(R)}}D[di]=h,D[Mi]=S;e:for(H=h.child;H!==null;){if(H.tag===5||H.tag===6)D.appendChild(H.stateNode);else if(H.tag!==4&&H.tag!==27&&H.child!==null){H.child.return=H,H=H.child;continue}if(H===h)break e;for(;H.sibling===null;){if(H.return===null||H.return===h)break e;H=H.return}H.sibling.return=H.return,H=H.sibling}h.stateNode=D;e:switch(Dr(D,R,S),R){case"button":case"input":case"select":case"textarea":S=!!S.autoFocus;break e;case"img":S=!0;break e;default:S=!1}S&&dl(h)}}return ei(h),QM(h,h.type,u===null?null:u.memoizedProps,h.pendingProps,v),null;case 6:if(u&&h.stateNode!=null)u.memoizedProps!==S&&dl(h);else{if(typeof S!="string"&&h.stateNode===null)throw Error(i(166));if(u=me.current,Wf(h)){if(u=h.stateNode,v=h.memoizedProps,S=null,R=Rr,R!==null)switch(R.tag){case 27:case 5:S=R.memoizedProps}u[di]=h,u=!!(u.nodeValue===v||S!==null&&S.suppressHydrationWarning===!0||yU(u.nodeValue,v)),u||Ec(h,!0)}else u=jx(u).createTextNode(S),u[di]=h,h.stateNode=u}return ei(h),null;case 31:if(v=h.memoizedState,u===null||u.memoizedState!==null){if(S=Wf(h),v!==null){if(u===null){if(!S)throw Error(i(318));if(u=h.memoizedState,u=u!==null?u.dehydrated:null,!u)throw Error(i(557));u[di]=h}else sd(),(h.flags&128)===0&&(h.memoizedState=null),h.flags|=4;ei(h),u=!1}else v=lM(),u!==null&&u.memoizedState!==null&&(u.memoizedState.hydrationErrors=v),u=!0;if(!u)return h.flags&256?(Va(h),h):(Va(h),null);if((h.flags&128)!==0)throw Error(i(558))}return ei(h),null;case 13:if(S=h.memoizedState,u===null||u.memoizedState!==null&&u.memoizedState.dehydrated!==null){if(R=Wf(h),S!==null&&S.dehydrated!==null){if(u===null){if(!R)throw Error(i(318));if(R=h.memoizedState,R=R!==null?R.dehydrated:null,!R)throw Error(i(317));R[di]=h}else sd(),(h.flags&128)===0&&(h.memoizedState=null),h.flags|=4;ei(h),R=!1}else R=lM(),u!==null&&u.memoizedState!==null&&(u.memoizedState.hydrationErrors=R),R=!0;if(!R)return h.flags&256?(Va(h),h):(Va(h),null)}return Va(h),(h.flags&128)!==0?(h.lanes=v,h):(v=S!==null,u=u!==null&&u.memoizedState!==null,v&&(S=h.child,R=null,S.alternate!==null&&S.alternate.memoizedState!==null&&S.alternate.memoizedState.cachePool!==null&&(R=S.alternate.memoizedState.cachePool.pool),D=null,S.memoizedState!==null&&S.memoizedState.cachePool!==null&&(D=S.memoizedState.cachePool.pool),D!==R&&(S.flags|=2048)),v!==u&&v&&(h.child.flags|=8192),Ex(h,h.updateQueue),ei(h),null);case 4:return he(),u===null&&bE(h.stateNode.containerInfo),ei(h),null;case 10:return ol(h.type),ei(h),null;case 19:if(X(Ai),S=h.memoizedState,S===null)return ei(h),null;if(R=(h.flags&128)!==0,D=S.rendering,D===null)if(R)qm(S,!1);else{if(yi!==0||u!==null&&(u.flags&128)!==0)for(u=h.child;u!==null;){if(D=fx(u),D!==null){for(h.flags|=128,qm(S,!1),u=D.updateQueue,h.updateQueue=u,Ex(h,u),h.subtreeFlags=0,u=v,v=h.child;v!==null;)Zk(v,u),v=v.sibling;return re(Ai,Ai.current&1|2),pn&&al(h,S.treeForkCount),h.child}u=u.sibling}S.tail!==null&&k()>Nx&&(h.flags|=128,R=!0,qm(S,!1),h.lanes=4194304)}else{if(!R)if(u=fx(D),u!==null){if(h.flags|=128,R=!0,u=u.updateQueue,h.updateQueue=u,Ex(h,u),qm(S,!0),S.tail===null&&S.tailMode==="hidden"&&!D.alternate&&!pn)return ei(h),null}else 2*k()-S.renderingStartTime>Nx&&v!==536870912&&(h.flags|=128,R=!0,qm(S,!1),h.lanes=4194304);S.isBackwards?(D.sibling=h.child,h.child=D):(u=S.last,u!==null?u.sibling=D:h.child=D,S.last=D)}return S.tail!==null?(u=S.tail,S.rendering=u,S.tail=u.sibling,S.renderingStartTime=k(),u.sibling=null,v=Ai.current,re(Ai,R?v&1|2:v&1),pn&&al(h,S.treeForkCount),u):(ei(h),null);case 22:case 23:return Va(h),_M(),S=h.memoizedState!==null,u!==null?u.memoizedState!==null!==S&&(h.flags|=8192):S&&(h.flags|=8192),S?(v&536870912)!==0&&(h.flags&128)===0&&(ei(h),h.subtreeFlags&6&&(h.flags|=8192)):ei(h),v=h.updateQueue,v!==null&&Ex(h,v.retryQueue),v=null,u!==null&&u.memoizedState!==null&&u.memoizedState.cachePool!==null&&(v=u.memoizedState.cachePool.pool),S=null,h.memoizedState!==null&&h.memoizedState.cachePool!==null&&(S=h.memoizedState.cachePool.pool),S!==v&&(h.flags|=2048),u!==null&&X(cd),null;case 24:return v=null,u!==null&&(v=u.memoizedState.cache),h.memoizedState.cache!==v&&(h.flags|=2048),ol(Oi),ei(h),null;case 25:return null;case 30:return null}throw Error(i(156,h.tag))}function xJ(u,h){switch(sM(h),h.tag){case 1:return u=h.flags,u&65536?(h.flags=u&-65537|128,h):null;case 3:return ol(Oi),he(),u=h.flags,(u&65536)!==0&&(u&128)===0?(h.flags=u&-65537|128,h):null;case 26:case 27:case 5:return je(h),null;case 31:if(h.memoizedState!==null){if(Va(h),h.alternate===null)throw Error(i(340));sd()}return u=h.flags,u&65536?(h.flags=u&-65537|128,h):null;case 13:if(Va(h),u=h.memoizedState,u!==null&&u.dehydrated!==null){if(h.alternate===null)throw Error(i(340));sd()}return u=h.flags,u&65536?(h.flags=u&-65537|128,h):null;case 19:return X(Ai),null;case 4:return he(),null;case 10:return ol(h.type),null;case 22:case 23:return Va(h),_M(),u!==null&&X(cd),u=h.flags,u&65536?(h.flags=u&-65537|128,h):null;case 24:return ol(Oi),null;case 25:return null;default:return null}}function MI(u,h){switch(sM(h),h.tag){case 3:ol(Oi),he();break;case 26:case 27:case 5:je(h);break;case 4:he();break;case 31:h.memoizedState!==null&&Va(h);break;case 13:Va(h);break;case 19:X(Ai);break;case 10:ol(h.type);break;case 22:case 23:Va(h),_M(),u!==null&&X(cd);break;case 24:ol(Oi)}}function Xm(u,h){try{var v=h.updateQueue,S=v!==null?v.lastEffect:null;if(S!==null){var R=S.next;v=R;do{if((v.tag&u)===u){S=void 0;var D=v.create,H=v.inst;S=D(),H.destroy=S}v=v.next}while(v!==R)}}catch(ne){Nn(h,h.return,ne)}}function Dc(u,h,v){try{var S=h.updateQueue,R=S!==null?S.lastEffect:null;if(R!==null){var D=R.next;S=D;do{if((S.tag&u)===u){var H=S.inst,ne=H.destroy;if(ne!==void 0){H.destroy=void 0,R=h;var _e=v,ze=ne;try{ze()}catch(Qe){Nn(R,_e,Qe)}}}S=S.next}while(S!==D)}}catch(Qe){Nn(h,h.return,Qe)}}function EI(u){var h=u.updateQueue;if(h!==null){var v=u.stateNode;try{mL(h,v)}catch(S){Nn(u,u.return,S)}}}function AI(u,h,v){v.props=pd(u.type,u.memoizedProps),v.state=u.memoizedState;try{v.componentWillUnmount()}catch(S){Nn(u,h,S)}}function Km(u,h){try{var v=u.ref;if(v!==null){switch(u.tag){case 26:case 27:case 5:var S=u.stateNode;break;case 30:S=u.stateNode;break;default:S=u.stateNode}typeof v=="function"?u.refCleanup=v(S):v.current=S}}catch(R){Nn(u,h,R)}}function Mo(u,h){var v=u.ref,S=u.refCleanup;if(v!==null)if(typeof S=="function")try{S()}catch(R){Nn(u,h,R)}finally{u.refCleanup=null,u=u.alternate,u!=null&&(u.refCleanup=null)}else if(typeof v=="function")try{v(null)}catch(R){Nn(u,h,R)}else v.current=null}function TI(u){var h=u.type,v=u.memoizedProps,S=u.stateNode;try{e:switch(h){case"button":case"input":case"select":case"textarea":v.autoFocus&&S.focus();break e;case"img":v.src?S.src=v.src:v.srcSet&&(S.srcset=v.srcSet)}}catch(R){Nn(u,u.return,R)}}function JM(u,h,v){try{var S=u.stateNode;jJ(S,u.type,v,h),S[Mi]=h}catch(R){Nn(u,u.return,R)}}function CI(u){return u.tag===5||u.tag===3||u.tag===26||u.tag===27&&zc(u.type)||u.tag===4}function eE(u){e:for(;;){for(;u.sibling===null;){if(u.return===null||CI(u.return))return null;u=u.return}for(u.sibling.return=u.return,u=u.sibling;u.tag!==5&&u.tag!==6&&u.tag!==18;){if(u.tag===27&&zc(u.type)||u.flags&2||u.child===null||u.tag===4)continue e;u.child.return=u,u=u.child}if(!(u.flags&2))return u.stateNode}}function tE(u,h,v){var S=u.tag;if(S===5||S===6)u=u.stateNode,h?(v.nodeType===9?v.body:v.nodeName==="HTML"?v.ownerDocument.body:v).insertBefore(u,h):(h=v.nodeType===9?v.body:v.nodeName==="HTML"?v.ownerDocument.body:v,h.appendChild(u),v=v._reactRootContainer,v!=null||h.onclick!==null||(h.onclick=nl));else if(S!==4&&(S===27&&zc(u.type)&&(v=u.stateNode,h=null),u=u.child,u!==null))for(tE(u,h,v),u=u.sibling;u!==null;)tE(u,h,v),u=u.sibling}function Ax(u,h,v){var S=u.tag;if(S===5||S===6)u=u.stateNode,h?v.insertBefore(u,h):v.appendChild(u);else if(S!==4&&(S===27&&zc(u.type)&&(v=u.stateNode),u=u.child,u!==null))for(Ax(u,h,v),u=u.sibling;u!==null;)Ax(u,h,v),u=u.sibling}function RI(u){var h=u.stateNode,v=u.memoizedProps;try{for(var S=u.type,R=h.attributes;R.length;)h.removeAttributeNode(R[0]);Dr(h,S,v),h[di]=u,h[Mi]=v}catch(D){Nn(u,u.return,D)}}var fl=!1,Ii=!1,nE=!1,NI=typeof WeakSet=="function"?WeakSet:Set,mr=null;function yJ(u,h){if(u=u.containerInfo,wE=qx,u=Hk(u),Kw(u)){if("selectionStart"in u)var v={start:u.selectionStart,end:u.selectionEnd};else e:{v=(v=u.ownerDocument)&&v.defaultView||window;var S=v.getSelection&&v.getSelection();if(S&&S.rangeCount!==0){v=S.anchorNode;var R=S.anchorOffset,D=S.focusNode;S=S.focusOffset;try{v.nodeType,D.nodeType}catch{v=null;break e}var H=0,ne=-1,_e=-1,ze=0,Qe=0,rt=u,Ve=null;t:for(;;){for(var Ke;rt!==v||R!==0&&rt.nodeType!==3||(ne=H+R),rt!==D||S!==0&&rt.nodeType!==3||(_e=H+S),rt.nodeType===3&&(H+=rt.nodeValue.length),(Ke=rt.firstChild)!==null;)Ve=rt,rt=Ke;for(;;){if(rt===u)break t;if(Ve===v&&++ze===R&&(ne=H),Ve===D&&++Qe===S&&(_e=H),(Ke=rt.nextSibling)!==null)break;rt=Ve,Ve=rt.parentNode}rt=Ke}v=ne===-1||_e===-1?null:{start:ne,end:_e}}else v=null}v=v||{start:0,end:0}}else v=null;for(ME={focusedElem:u,selectionRange:v},qx=!1,mr=h;mr!==null;)if(h=mr,u=h.child,(h.subtreeFlags&1028)!==0&&u!==null)u.return=h,mr=u;else for(;mr!==null;){switch(h=mr,D=h.alternate,u=h.flags,h.tag){case 0:if((u&4)!==0&&(u=h.updateQueue,u=u!==null?u.events:null,u!==null))for(v=0;v title"))),Dr(D,S,v),D[di]=u,Te(D),S=D;break e;case"link":var H=IU("link","href",R).get(S+(v.href||""));if(H){for(var ne=0;neLn&&(H=Ln,Ln=zt,zt=H);var Oe=jk(ne,zt),Ee=jk(ne,Ln);if(Oe&&Ee&&(Ke.rangeCount!==1||Ke.anchorNode!==Oe.node||Ke.anchorOffset!==Oe.offset||Ke.focusNode!==Ee.node||Ke.focusOffset!==Ee.offset)){var Fe=rt.createRange();Fe.setStart(Oe.node,Oe.offset),Ke.removeAllRanges(),zt>Ln?(Ke.addRange(Fe),Ke.extend(Ee.node,Ee.offset)):(Fe.setEnd(Ee.node,Ee.offset),Ke.addRange(Fe))}}}}for(rt=[],Ke=ne;Ke=Ke.parentNode;)Ke.nodeType===1&&rt.push({element:Ke,left:Ke.scrollLeft,top:Ke.scrollTop});for(typeof ne.focus=="function"&&ne.focus(),ne=0;nev?32:v,q.T=null,v=cE,cE=null;var D=Ic,H=vl;if(Zi=0,sh=Ic=null,vl=0,(Mn&6)!==0)throw Error(i(331));var ne=Mn;if(Mn|=4,BI(D.current),FI(D,D.current,H,v),Mn=ne,tg(0,!1),Ne&&typeof Ne.onPostCommitFiberRoot=="function")try{Ne.onPostCommitFiberRoot(Ie,D)}catch{}return!0}finally{W.p=R,q.T=S,aU(u,h)}}function oU(u,h,v){h=fs(v,h),h=HM(u.stateNode,h,2),u=Rc(u,h,2),u!==null&&(tn(u,2),Eo(u))}function Nn(u,h,v){if(u.tag===3)oU(u,u,v);else for(;h!==null;){if(h.tag===3){oU(h,u,v);break}else if(h.tag===1){var S=h.stateNode;if(typeof h.type.getDerivedStateFromError=="function"||typeof S.componentDidCatch=="function"&&(Lc===null||!Lc.has(S))){u=fs(v,u),v=lI(2),S=Rc(h,v,2),S!==null&&(cI(v,S,h,u),tn(S,2),Eo(S));break}}h=h.return}}function hE(u,h,v){var S=u.pingCache;if(S===null){S=u.pingCache=new SJ;var R=new Set;S.set(h,R)}else R=S.get(h),R===void 0&&(R=new Set,S.set(h,R));R.has(v)||(aE=!0,R.add(v),u=TJ.bind(null,u,h,v),h.then(u,u))}function TJ(u,h,v){var S=u.pingCache;S!==null&&S.delete(h),u.pingedLanes|=u.suspendedLanes&v,u.warmLanes&=~v,Wn===u&&(un&v)===v&&(yi===4||yi===3&&(un&62914560)===un&&300>k()-Rx?(Mn&2)===0&&oh(u,0):sE|=v,ah===un&&(ah=0)),Eo(u)}function lU(u,h){h===0&&(h=yt()),u=rd(u,h),u!==null&&(tn(u,h),Eo(u))}function CJ(u){var h=u.memoizedState,v=0;h!==null&&(v=h.retryLane),lU(u,v)}function RJ(u,h){var v=0;switch(u.tag){case 31:case 13:var S=u.stateNode,R=u.memoizedState;R!==null&&(v=R.retryLane);break;case 19:S=u.stateNode;break;case 22:S=u.stateNode._retryCache;break;default:throw Error(i(314))}S!==null&&S.delete(h),lU(u,v)}function NJ(u,h){return Pe(u,h)}var Ix=null,ch=null,pE=!1,Ux=!1,mE=!1,Fc=0;function Eo(u){u!==ch&&u.next===null&&(ch===null?Ix=ch=u:ch=ch.next=u),Ux=!0,pE||(pE=!0,DJ())}function tg(u,h){if(!mE&&Ux){mE=!0;do for(var v=!1,S=Ix;S!==null;){if(u!==0){var R=S.pendingLanes;if(R===0)var D=0;else{var H=S.suspendedLanes,ne=S.pingedLanes;D=(1<<31-et(42|u)+1)-1,D&=R&~(H&~ne),D=D&201326741?D&201326741|1:D?D|2:0}D!==0&&(v=!0,fU(S,D))}else D=un,D=ge(S,S===Wn?D:0,S.cancelPendingCommit!==null||S.timeoutHandle!==-1),(D&3)===0||Be(S,D)||(v=!0,fU(S,D));S=S.next}while(v);mE=!1}}function PJ(){cU()}function cU(){Ux=pE=!1;var u=0;Fc!==0&&HJ()&&(u=Fc);for(var h=k(),v=null,S=Ix;S!==null;){var R=S.next,D=uU(S,h);D===0?(S.next=null,v===null?Ix=R:v.next=R,R===null&&(ch=v)):(v=S,(u!==0||(D&3)!==0)&&(Ux=!0)),S=R}Zi!==0&&Zi!==5||tg(u),Fc!==0&&(Fc=0)}function uU(u,h){for(var v=u.suspendedLanes,S=u.pingedLanes,R=u.expirationTimes,D=u.pendingLanes&-62914561;0ne)break;var Qe=_e.transferSize,rt=_e.initiatorType;Qe&&bU(rt)&&(_e=_e.responseEnd,H+=Qe*(_e"u"?null:document;function DU(u,h,v){var S=uh;if(S&&typeof h=="string"&&h){var R=En(h);R='link[rel="'+u+'"][href="'+R+'"]',typeof v=="string"&&(R+='[crossorigin="'+v+'"]'),PU.has(R)||(PU.add(R),u={rel:u,crossOrigin:v,href:h},S.querySelector(R)===null&&(h=S.createElement("link"),Dr(h,"link",u),Te(h),S.head.appendChild(h)))}}function ZJ(u){xl.D(u),DU("dns-prefetch",u,null)}function QJ(u,h){xl.C(u,h),DU("preconnect",u,h)}function JJ(u,h,v){xl.L(u,h,v);var S=uh;if(S&&u&&h){var R='link[rel="preload"][as="'+En(h)+'"]';h==="image"&&v&&v.imageSrcSet?(R+='[imagesrcset="'+En(v.imageSrcSet)+'"]',typeof v.imageSizes=="string"&&(R+='[imagesizes="'+En(v.imageSizes)+'"]')):R+='[href="'+En(u)+'"]';var D=R;switch(h){case"style":D=dh(u);break;case"script":D=fh(u)}xs.has(D)||(u=f({rel:"preload",href:h==="image"&&v&&v.imageSrcSet?void 0:u,as:h},v),xs.set(D,u),S.querySelector(R)!==null||h==="style"&&S.querySelector(ag(D))||h==="script"&&S.querySelector(sg(D))||(h=S.createElement("link"),Dr(h,"link",u),Te(h),S.head.appendChild(h)))}}function eee(u,h){xl.m(u,h);var v=uh;if(v&&u){var S=h&&typeof h.as=="string"?h.as:"script",R='link[rel="modulepreload"][as="'+En(S)+'"][href="'+En(u)+'"]',D=R;switch(S){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":D=fh(u)}if(!xs.has(D)&&(u=f({rel:"modulepreload",href:u},h),xs.set(D,u),v.querySelector(R)===null)){switch(S){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(v.querySelector(sg(D)))return}S=v.createElement("link"),Dr(S,"link",u),Te(S),v.head.appendChild(S)}}}function tee(u,h,v){xl.S(u,h,v);var S=uh;if(S&&u){var R=Ce(S).hoistableStyles,D=dh(u);h=h||"default";var H=R.get(D);if(!H){var ne={loading:0,preload:null};if(H=S.querySelector(ag(D)))ne.loading=5;else{u=f({rel:"stylesheet",href:u,"data-precedence":h},v),(v=xs.get(D))&&PE(u,v);var _e=H=S.createElement("link");Te(_e),Dr(_e,"link",u),_e._p=new Promise(function(ze,Qe){_e.onload=ze,_e.onerror=Qe}),_e.addEventListener("load",function(){ne.loading|=1}),_e.addEventListener("error",function(){ne.loading|=2}),ne.loading|=4,Hx(H,h,S)}H={type:"stylesheet",instance:H,count:1,state:ne},R.set(D,H)}}}function nee(u,h){xl.X(u,h);var v=uh;if(v&&u){var S=Ce(v).hoistableScripts,R=fh(u),D=S.get(R);D||(D=v.querySelector(sg(R)),D||(u=f({src:u,async:!0},h),(h=xs.get(R))&&DE(u,h),D=v.createElement("script"),Te(D),Dr(D,"link",u),v.head.appendChild(D)),D={type:"script",instance:D,count:1,state:null},S.set(R,D))}}function iee(u,h){xl.M(u,h);var v=uh;if(v&&u){var S=Ce(v).hoistableScripts,R=fh(u),D=S.get(R);D||(D=v.querySelector(sg(R)),D||(u=f({src:u,async:!0,type:"module"},h),(h=xs.get(R))&&DE(u,h),D=v.createElement("script"),Te(D),Dr(D,"link",u),v.head.appendChild(D)),D={type:"script",instance:D,count:1,state:null},S.set(R,D))}}function OU(u,h,v,S){var R=(R=me.current)?Bx(R):null;if(!R)throw Error(i(446));switch(u){case"meta":case"title":return null;case"style":return typeof v.precedence=="string"&&typeof v.href=="string"?(h=dh(v.href),v=Ce(R).hoistableStyles,S=v.get(h),S||(S={type:"style",instance:null,count:0,state:null},v.set(h,S)),S):{type:"void",instance:null,count:0,state:null};case"link":if(v.rel==="stylesheet"&&typeof v.href=="string"&&typeof v.precedence=="string"){u=dh(v.href);var D=Ce(R).hoistableStyles,H=D.get(u);if(H||(R=R.ownerDocument||R,H={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},D.set(u,H),(D=R.querySelector(ag(u)))&&!D._p&&(H.instance=D,H.state.loading=5),xs.has(u)||(v={rel:"preload",as:"style",href:v.href,crossOrigin:v.crossOrigin,integrity:v.integrity,media:v.media,hrefLang:v.hrefLang,referrerPolicy:v.referrerPolicy},xs.set(u,v),D||ree(R,u,v,H.state))),h&&S===null)throw Error(i(528,""));return H}if(h&&S!==null)throw Error(i(529,""));return null;case"script":return h=v.async,v=v.src,typeof v=="string"&&h&&typeof h!="function"&&typeof h!="symbol"?(h=fh(v),v=Ce(R).hoistableScripts,S=v.get(h),S||(S={type:"script",instance:null,count:0,state:null},v.set(h,S)),S):{type:"void",instance:null,count:0,state:null};default:throw Error(i(444,u))}}function dh(u){return'href="'+En(u)+'"'}function ag(u){return'link[rel="stylesheet"]['+u+"]"}function kU(u){return f({},u,{"data-precedence":u.precedence,precedence:null})}function ree(u,h,v,S){u.querySelector('link[rel="preload"][as="style"]['+h+"]")?S.loading=1:(h=u.createElement("link"),S.preload=h,h.addEventListener("load",function(){return S.loading|=1}),h.addEventListener("error",function(){return S.loading|=2}),Dr(h,"link",v),Te(h),u.head.appendChild(h))}function fh(u){return'[src="'+En(u)+'"]'}function sg(u){return"script[async]"+u}function LU(u,h,v){if(h.count++,h.instance===null)switch(h.type){case"style":var S=u.querySelector('style[data-href~="'+En(v.href)+'"]');if(S)return h.instance=S,Te(S),S;var R=f({},v,{"data-href":v.href,"data-precedence":v.precedence,href:null,precedence:null});return S=(u.ownerDocument||u).createElement("style"),Te(S),Dr(S,"style",R),Hx(S,v.precedence,u),h.instance=S;case"stylesheet":R=dh(v.href);var D=u.querySelector(ag(R));if(D)return h.state.loading|=4,h.instance=D,Te(D),D;S=kU(v),(R=xs.get(R))&&PE(S,R),D=(u.ownerDocument||u).createElement("link"),Te(D);var H=D;return H._p=new Promise(function(ne,_e){H.onload=ne,H.onerror=_e}),Dr(D,"link",S),h.state.loading|=4,Hx(D,v.precedence,u),h.instance=D;case"script":return D=fh(v.src),(R=u.querySelector(sg(D)))?(h.instance=R,Te(R),R):(S=v,(R=xs.get(D))&&(S=f({},v),DE(S,R)),u=u.ownerDocument||u,R=u.createElement("script"),Te(R),Dr(R,"link",S),u.head.appendChild(R),h.instance=R);case"void":return null;default:throw Error(i(443,h.type))}else h.type==="stylesheet"&&(h.state.loading&4)===0&&(S=h.instance,h.state.loading|=4,Hx(S,v.precedence,u));return h.instance}function Hx(u,h,v){for(var S=v.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),R=S.length?S[S.length-1]:null,D=R,H=0;H title"):null)}function aee(u,h,v){if(v===1||h.itemProp!=null)return!1;switch(u){case"meta":case"title":return!0;case"style":if(typeof h.precedence!="string"||typeof h.href!="string"||h.href==="")break;return!0;case"link":if(typeof h.rel!="string"||typeof h.href!="string"||h.href===""||h.onLoad||h.onError)break;switch(h.rel){case"stylesheet":return u=h.disabled,typeof h.precedence=="string"&&u==null;default:return!0}case"script":if(h.async&&typeof h.async!="function"&&typeof h.async!="symbol"&&!h.onLoad&&!h.onError&&h.src&&typeof h.src=="string")return!0}return!1}function FU(u){return!(u.type==="stylesheet"&&(u.state.loading&3)===0)}function see(u,h,v,S){if(v.type==="stylesheet"&&(typeof S.media!="string"||matchMedia(S.media).matches!==!1)&&(v.state.loading&4)===0){if(v.instance===null){var R=dh(S.href),D=h.querySelector(ag(R));if(D){h=D._p,h!==null&&typeof h=="object"&&typeof h.then=="function"&&(u.count++,u=Gx.bind(u),h.then(u,u)),v.state.loading|=4,v.instance=D,Te(D);return}D=h.ownerDocument||h,S=kU(S),(R=xs.get(R))&&PE(S,R),D=D.createElement("link"),Te(D);var H=D;H._p=new Promise(function(ne,_e){H.onload=ne,H.onerror=_e}),Dr(D,"link",S),v.instance=D}u.stylesheets===null&&(u.stylesheets=new Map),u.stylesheets.set(v,h),(h=v.state.preload)&&(v.state.loading&3)===0&&(u.count++,v=Gx.bind(u),h.addEventListener("load",v),h.addEventListener("error",v))}}var OE=0;function oee(u,h){return u.stylesheets&&u.count===0&&Wx(u,u.stylesheets),0OE?50:800)+h);return u.unsuspend=v,function(){u.unsuspend=null,clearTimeout(S),clearTimeout(R)}}:null}function Gx(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)Wx(this,this.stylesheets);else if(this.unsuspend){var u=this.unsuspend;this.unsuspend=null,u()}}}var $x=null;function Wx(u,h){u.stylesheets=null,u.unsuspend!==null&&(u.count++,$x=new Map,h.forEach(lee,u),$x=null,Gx.call(u))}function lee(u,h){if(!(h.state.loading&4)){var v=$x.get(u);if(v)var S=v.get(null);else{v=new Map,$x.set(u,v);for(var R=u.querySelectorAll("link[data-precedence],style[data-precedence]"),D=0;D"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(t)}catch(e){console.error(e)}}return t(),HE.exports=Eee(),HE.exports}var Tee=Aee();var l4="popstate";function Cee(t={}){function e(i,r){let{pathname:a,search:s,hash:o}=i.location;return R2("",{pathname:a,search:s,hash:o},r.state&&r.state.usr||null,r.state&&r.state.key||"default")}function n(i,r){return typeof r=="string"?r:m0(r)}return Nee(e,n,null,t)}function hi(t,e){if(t===!1||t===null||typeof t>"u")throw new Error(e)}function go(t,e){if(!t){typeof console<"u"&&console.warn(e);try{throw new Error(e)}catch{}}}function Ree(){return Math.random().toString(36).substring(2,10)}function c4(t,e){return{usr:t.state,key:t.key,idx:e}}function R2(t,e,n=null,i){return{pathname:typeof t=="string"?t:t.pathname,search:"",hash:"",...typeof e=="string"?Jp(e):e,state:n,key:e&&e.key||i||Ree()}}function m0({pathname:t="/",search:e="",hash:n=""}){return e&&e!=="?"&&(t+=e.charAt(0)==="?"?e:"?"+e),n&&n!=="#"&&(t+=n.charAt(0)==="#"?n:"#"+n),t}function Jp(t){let e={};if(t){let n=t.indexOf("#");n>=0&&(e.hash=t.substring(n),t=t.substring(0,n));let i=t.indexOf("?");i>=0&&(e.search=t.substring(i),t=t.substring(0,i)),t&&(e.pathname=t)}return e}function Nee(t,e,n,i={}){let{window:r=document.defaultView,v5Compat:a=!1}=i,s=r.history,o="POP",l=null,c=d();c==null&&(c=0,s.replaceState({...s.state,idx:c},""));function d(){return(s.state||{idx:null}).idx}function f(){o="POP";let x=d(),g=x==null?null:x-c;c=x,l&&l({action:o,location:b.location,delta:g})}function p(x,g){o="PUSH";let M=R2(b.location,x,g);c=d()+1;let w=c4(M,c),E=b.createHref(M);try{s.pushState(w,"",E)}catch(A){if(A instanceof DOMException&&A.name==="DataCloneError")throw A;r.location.assign(E)}a&&l&&l({action:o,location:b.location,delta:1})}function m(x,g){o="REPLACE";let M=R2(b.location,x,g);c=d();let w=c4(M,c),E=b.createHref(M);s.replaceState(w,"",E),a&&l&&l({action:o,location:b.location,delta:0})}function y(x){return Pee(x)}let b={get action(){return o},get location(){return t(r,s)},listen(x){if(l)throw new Error("A history only accepts one active listener");return r.addEventListener(l4,f),l=x,()=>{r.removeEventListener(l4,f),l=null}},createHref(x){return e(r,x)},createURL:y,encodeLocation(x){let g=y(x);return{pathname:g.pathname,search:g.search,hash:g.hash}},push:p,replace:m,go(x){return s.go(x)}};return b}function Pee(t,e=!1){let n="http://localhost";typeof window<"u"&&(n=window.location.origin!=="null"?window.location.origin:window.location.href),hi(n,"No window.location.(origin|href) available to create URL");let i=typeof t=="string"?t:m0(t);return i=i.replace(/ $/,"%20"),!e&&i.startsWith("//")&&(i=n+i),new URL(i,n)}function WV(t,e,n="/"){return Dee(t,e,n,!1)}function Dee(t,e,n,i){let r=typeof e=="string"?Jp(e):e,a=nc(r.pathname||"/",n);if(a==null)return null;let s=qV(t);Oee(s);let o=null;for(let l=0;o==null&&l{let d={relativePath:c===void 0?s.path||"":c,caseSensitive:s.caseSensitive===!0,childrenIndex:o,route:s};if(d.relativePath.startsWith("/")){if(!d.relativePath.startsWith(i)&&l)return;hi(d.relativePath.startsWith(i),`Absolute route path "${d.relativePath}" nested under path "${i}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),d.relativePath=d.relativePath.slice(i.length)}let f=Xl([i,d.relativePath]),p=n.concat(d);s.children&&s.children.length>0&&(hi(s.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${f}".`),qV(s.children,e,p,f,l)),!(s.path==null&&!s.index)&&e.push({path:f,score:jee(f,s.index),routesMeta:p})};return t.forEach((s,o)=>{if(s.path===""||!s.path?.includes("?"))a(s,o);else for(let l of XV(s.path))a(s,o,!0,l)}),e}function XV(t){let e=t.split("/");if(e.length===0)return[];let[n,...i]=e,r=n.endsWith("?"),a=n.replace(/\?$/,"");if(i.length===0)return r?[a,""]:[a];let s=XV(i.join("/")),o=[];return o.push(...s.map(l=>l===""?a:[a,l].join("/"))),r&&o.push(...s),o.map(l=>t.startsWith("/")&&l===""?"/":l)}function Oee(t){t.sort((e,n)=>e.score!==n.score?n.score-e.score:Bee(e.routesMeta.map(i=>i.childrenIndex),n.routesMeta.map(i=>i.childrenIndex)))}var kee=/^:[\w-]+$/,Lee=3,Iee=2,Uee=1,Fee=10,zee=-2,u4=t=>t==="*";function jee(t,e){let n=t.split("/"),i=n.length;return n.some(u4)&&(i+=zee),e&&(i+=Iee),n.filter(r=>!u4(r)).reduce((r,a)=>r+(kee.test(a)?Lee:a===""?Uee:Fee),i)}function Bee(t,e){return t.length===e.length&&t.slice(0,-1).every((i,r)=>i===e[r])?t[t.length-1]-e[e.length-1]:0}function Hee(t,e,n=!1){let{routesMeta:i}=t,r={},a="/",s=[];for(let o=0;o{if(d==="*"){let y=o[p]||"";s=a.slice(0,a.length-y.length).replace(/(.)\/+$/,"$1")}const m=o[p];return f&&!m?c[d]=void 0:c[d]=(m||"").replace(/%2F/g,"/"),c},{}),pathname:a,pathnameBase:s,pattern:t}}function Vee(t,e=!1,n=!0){go(t==="*"||!t.endsWith("*")||t.endsWith("/*"),`Route path "${t}" will be treated as if it were "${t.replace(/\*$/,"/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${t.replace(/\*$/,"/*")}".`);let i=[],r="^"+t.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(s,o,l)=>(i.push({paramName:o,isOptional:l!=null}),l?"/?([^\\/]+)?":"/([^\\/]+)")).replace(/\/([\w-]+)\?(\/|$)/g,"(/$1)?$2");return t.endsWith("*")?(i.push({paramName:"*"}),r+=t==="*"||t==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):n?r+="\\/*$":t!==""&&t!=="/"&&(r+="(?:(?=\\/|$))"),[new RegExp(r,e?void 0:"i"),i]}function Gee(t){try{return t.split("/").map(e=>decodeURIComponent(e).replace(/\//g,"%2F")).join("/")}catch(e){return go(!1,`The URL path "${t}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${e}).`),t}}function nc(t,e){if(e==="/")return t;if(!t.toLowerCase().startsWith(e.toLowerCase()))return null;let n=e.endsWith("/")?e.length-1:e.length,i=t.charAt(n);return i&&i!=="/"?null:t.slice(n)||"/"}var $ee=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,Wee=t=>$ee.test(t);function qee(t,e="/"){let{pathname:n,search:i="",hash:r=""}=typeof t=="string"?Jp(t):t,a;if(n)if(Wee(n))a=n;else{if(n.includes("//")){let s=n;n=n.replace(/\/\/+/g,"/"),go(!1,`Pathnames cannot have embedded double slashes - normalizing ${s} -> ${n}`)}n.startsWith("/")?a=d4(n.substring(1),"/"):a=d4(n,e)}else a=e;return{pathname:a,search:Yee(i),hash:Zee(r)}}function d4(t,e){let n=e.replace(/\/+$/,"").split("/");return t.split("/").forEach(r=>{r===".."?n.length>1&&n.pop():r!=="."&&n.push(r)}),n.length>1?n.join("/"):"/"}function WE(t,e,n,i){return`Cannot include a '${t}' character in a manually specified \`to.${e}\` field [${JSON.stringify(i)}]. Please separate it out to the \`to.${n}\` field. Alternatively you may provide the full path as a string in and the router will parse it for you.`}function Xee(t){return t.filter((e,n)=>n===0||e.route.path&&e.route.path.length>0)}function KV(t){let e=Xee(t);return e.map((n,i)=>i===e.length-1?n.pathname:n.pathnameBase)}function YV(t,e,n,i=!1){let r;typeof t=="string"?r=Jp(t):(r={...t},hi(!r.pathname||!r.pathname.includes("?"),WE("?","pathname","search",r)),hi(!r.pathname||!r.pathname.includes("#"),WE("#","pathname","hash",r)),hi(!r.search||!r.search.includes("#"),WE("#","search","hash",r)));let a=t===""||r.pathname==="",s=a?"/":r.pathname,o;if(s==null)o=n;else{let f=e.length-1;if(!i&&s.startsWith("..")){let p=s.split("/");for(;p[0]==="..";)p.shift(),f-=1;r.pathname=p.join("/")}o=f>=0?e[f]:"/"}let l=qee(r,o),c=s&&s!=="/"&&s.endsWith("/"),d=(a||s===".")&&n.endsWith("/");return!l.pathname.endsWith("/")&&(c||d)&&(l.pathname+="/"),l}var Xl=t=>t.join("/").replace(/\/\/+/g,"/"),Kee=t=>t.replace(/\/+$/,"").replace(/^\/*/,"/"),Yee=t=>!t||t==="?"?"":t.startsWith("?")?t:"?"+t,Zee=t=>!t||t==="#"?"":t.startsWith("#")?t:"#"+t;function Qee(t){return t!=null&&typeof t.status=="number"&&typeof t.statusText=="string"&&typeof t.internal=="boolean"&&"data"in t}Object.getOwnPropertyNames(Object.prototype).sort().join("\0");var ZV=["POST","PUT","PATCH","DELETE"];new Set(ZV);var Jee=["GET",...ZV];new Set(Jee);var em=T.createContext(null);em.displayName="DataRouter";var cS=T.createContext(null);cS.displayName="DataRouterState";T.createContext(!1);var QV=T.createContext({isTransitioning:!1});QV.displayName="ViewTransition";var ete=T.createContext(new Map);ete.displayName="Fetchers";var tte=T.createContext(null);tte.displayName="Await";var Yo=T.createContext(null);Yo.displayName="Navigation";var av=T.createContext(null);av.displayName="Location";var Zo=T.createContext({outlet:null,matches:[],isDataRoute:!1});Zo.displayName="Route";var ON=T.createContext(null);ON.displayName="RouteError";function nte(t,{relative:e}={}){hi(sv(),"useHref() may be used only in the context of a component.");let{basename:n,navigator:i}=T.useContext(Yo),{hash:r,pathname:a,search:s}=ov(t,{relative:e}),o=a;return n!=="/"&&(o=a==="/"?n:Xl([n,a])),i.createHref({pathname:o,search:s,hash:r})}function sv(){return T.useContext(av)!=null}function qu(){return hi(sv(),"useLocation() may be used only in the context of a component."),T.useContext(av).location}var JV="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function eG(t){T.useContext(Yo).static||T.useLayoutEffect(t)}function tG(){let{isDataRoute:t}=T.useContext(Zo);return t?vte():ite()}function ite(){hi(sv(),"useNavigate() may be used only in the context of a component.");let t=T.useContext(em),{basename:e,navigator:n}=T.useContext(Yo),{matches:i}=T.useContext(Zo),{pathname:r}=qu(),a=JSON.stringify(KV(i)),s=T.useRef(!1);return eG(()=>{s.current=!0}),T.useCallback((l,c={})=>{if(go(s.current,JV),!s.current)return;if(typeof l=="number"){n.go(l);return}let d=YV(l,JSON.parse(a),r,c.relative==="path");t==null&&e!=="/"&&(d.pathname=d.pathname==="/"?e:Xl([e,d.pathname])),(c.replace?n.replace:n.push)(d,c.state,c)},[e,n,a,r,t])}var rte=T.createContext(null);function ate(t){let e=T.useContext(Zo).outlet;return T.useMemo(()=>e&&T.createElement(rte.Provider,{value:t},e),[e,t])}function ov(t,{relative:e}={}){let{matches:n}=T.useContext(Zo),{pathname:i}=qu(),r=JSON.stringify(KV(n));return T.useMemo(()=>YV(t,JSON.parse(r),i,e==="path"),[t,r,i,e])}function ste(t,e){return nG(t,e)}function nG(t,e,n,i,r){hi(sv(),"useRoutes() may be used only in the context of a component.");let{navigator:a}=T.useContext(Yo),{matches:s}=T.useContext(Zo),o=s[s.length-1],l=o?o.params:{},c=o?o.pathname:"/",d=o?o.pathnameBase:"/",f=o&&o.route;{let M=f&&f.path||"";iG(c,!f||M.endsWith("*")||M.endsWith("*?"),`You rendered descendant (or called \`useRoutes()\`) at "${c}" (under ) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render. +`+S.stack}}var ae=Object.prototype.hasOwnProperty,Pe=t.unstable_scheduleCallback,Ne=t.unstable_cancelCallback,te=t.unstable_shouldYield,U=t.unstable_requestPaint,k=t.unstable_now,Y=t.unstable_getCurrentPriorityLevel,pe=t.unstable_ImmediatePriority,xe=t.unstable_UserBlockingPriority,fe=t.unstable_NormalPriority,Ye=t.unstable_LowPriority,Le=t.unstable_IdlePriority,qe=t.log,it=t.unstable_setDisableYieldValue,Ie=null,Re=null;function ht(u){if(typeof qe=="function"&&it(u),Re&&typeof Re.setStrictMode=="function")try{Re.setStrictMode(Ie,u)}catch{}}var et=Math.clz32?Math.clz32:ue,Je=Math.log,ot=Math.LN2;function ue(u){return u>>>=0,u===0?32:31-(Je(u)/ot|0)|0}var tt=256,de=262144,ke=4194304;function Me(u){var h=u&42;if(h!==0)return h;switch(u&-u){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return u&261888;case 262144:case 524288:case 1048576:case 2097152:return u&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return u&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return u}}function ge(u,h,v){var S=u.pendingLanes;if(S===0)return 0;var C=0,D=u.suspendedLanes,V=u.pingedLanes;u=u.warmLanes;var ne=S&134217727;return ne!==0?(S=ne&~D,S!==0?C=Me(S):(V&=ne,V!==0?C=Me(V):v||(v=ne&~u,v!==0&&(C=Me(v))))):(ne=S&~D,ne!==0?C=Me(ne):V!==0?C=Me(V):v||(v=S&~u,v!==0&&(C=Me(v)))),C===0?0:h!==0&&h!==C&&(h&D)===0&&(D=C&-C,v=h&-h,D>=v||D===32&&(v&4194048)!==0)?h:C}function Be(u,h){return(u.pendingLanes&~(u.suspendedLanes&~u.pingedLanes)&h)===0}function at(u,h){switch(u){case 1:case 2:case 4:case 8:case 64:return h+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return h+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function yt(){var u=ke;return ke<<=1,(ke&62914560)===0&&(ke=4194304),u}function Nt(u){for(var h=[],v=0;31>v;v++)h.push(u);return h}function nn(u,h){u.pendingLanes|=h,h!==268435456&&(u.suspendedLanes=0,u.pingedLanes=0,u.warmLanes=0)}function Dt(u,h,v,S,C,D){var V=u.pendingLanes;u.pendingLanes=v,u.suspendedLanes=0,u.pingedLanes=0,u.warmLanes=0,u.expiredLanes&=v,u.entangledLanes&=v,u.errorRecoveryDisabledLanes&=v,u.shellSuspendCounter=0;var ne=u.entanglements,_e=u.expirationTimes,ze=u.hiddenUpdates;for(v=V&~v;0"u")return null;try{return u.activeElement||u.body}catch{return u.body}}var Ai=/[\n"\\]/g;function Tn(u){return u.replace(Ai,function(h){return"\\"+h.charCodeAt(0).toString(16)+" "})}function Xs(u,h,v,S,C,D,V,ne){u.name="",V!=null&&typeof V!="function"&&typeof V!="symbol"&&typeof V!="boolean"?u.type=V:u.removeAttribute("type"),h!=null?V==="number"?(h===0&&u.value===""||u.value!=h)&&(u.value=""+an(h)):u.value!==""+an(h)&&(u.value=""+an(h)):V!=="submit"&&V!=="reset"||u.removeAttribute("value"),h!=null?hr(u,V,an(h)):v!=null?hr(u,V,an(v)):S!=null&&u.removeAttribute("value"),C==null&&D!=null&&(u.defaultChecked=!!D),C!=null&&(u.checked=C&&typeof C!="function"&&typeof C!="symbol"),ne!=null&&typeof ne!="function"&&typeof ne!="symbol"&&typeof ne!="boolean"?u.name=""+an(ne):u.removeAttribute("name")}function Nn(u,h,v,S,C,D,V,ne){if(D!=null&&typeof D!="function"&&typeof D!="symbol"&&typeof D!="boolean"&&(u.type=D),h!=null||v!=null){if(!(D!=="submit"&&D!=="reset"||h!=null)){Gt(u);return}v=v!=null?""+an(v):"",h=h!=null?""+an(h):v,ne||h===u.value||(u.value=h),u.defaultValue=h}S=S??C,S=typeof S!="function"&&typeof S!="symbol"&&!!S,u.checked=ne?u.checked:!!S,u.defaultChecked=!!S,V!=null&&typeof V!="function"&&typeof V!="symbol"&&typeof V!="boolean"&&(u.name=V),Gt(u)}function hr(u,h,v){h==="number"&&xi(u.ownerDocument)===u||u.defaultValue===""+v||(u.defaultValue=""+v)}function Yi(u,h,v,S){if(u=u.options,h){h={};for(var C=0;C"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Hw=!1;if(il)try{var Em={};Object.defineProperty(Em,"passive",{get:function(){Hw=!0}}),window.addEventListener("test",Em,Em),window.removeEventListener("test",Em,Em)}catch{Hw=!1}var Sc=null,Vw=null,Xx=null;function Ek(){if(Xx)return Xx;var u,h=Vw,v=h.length,S,C="value"in Sc?Sc.value:Sc.textContent,D=C.length;for(u=0;u=Cm),Pk=" ",Dk=!1;function Ok(u,h){switch(u){case"keyup":return GQ.indexOf(h.keyCode)!==-1;case"keydown":return h.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function kk(u){return u=u.detail,typeof u=="object"&&"data"in u?u.data:null}var Bf=!1;function WQ(u,h){switch(u){case"compositionend":return kk(h);case"keypress":return h.which!==32?null:(Dk=!0,Pk);case"textInput":return u=h.data,u===Pk&&Dk?null:u;default:return null}}function qQ(u,h){if(Bf)return u==="compositionend"||!Xw&&Ok(u,h)?(u=Ek(),Xx=Vw=Sc=null,Bf=!1,u):null;switch(u){case"paste":return null;case"keypress":if(!(h.ctrlKey||h.altKey||h.metaKey)||h.ctrlKey&&h.altKey){if(h.char&&1=h)return{node:v,offset:h-u};u=S}e:{for(;v;){if(v.nextSibling){v=v.nextSibling;break e}v=v.parentNode}v=void 0}v=Hk(v)}}function Gk(u,h){return u&&h?u===h?!0:u&&u.nodeType===3?!1:h&&h.nodeType===3?Gk(u,h.parentNode):"contains"in u?u.contains(h):u.compareDocumentPosition?!!(u.compareDocumentPosition(h)&16):!1:!1}function $k(u){u=u!=null&&u.ownerDocument!=null&&u.ownerDocument.defaultView!=null?u.ownerDocument.defaultView:window;for(var h=xi(u.document);h instanceof u.HTMLIFrameElement;){try{var v=typeof h.contentWindow.location.href=="string"}catch{v=!1}if(v)u=h.contentWindow;else break;h=xi(u.document)}return h}function Zw(u){var h=u&&u.nodeName&&u.nodeName.toLowerCase();return h&&(h==="input"&&(u.type==="text"||u.type==="search"||u.type==="tel"||u.type==="url"||u.type==="password")||h==="textarea"||u.contentEditable==="true")}var tJ=il&&"documentMode"in document&&11>=document.documentMode,Hf=null,Qw=null,Dm=null,Jw=!1;function Wk(u,h,v){var S=v.window===v?v.document:v.nodeType===9?v:v.ownerDocument;Jw||Hf==null||Hf!==xi(S)||(S=Hf,"selectionStart"in S&&Zw(S)?S={start:S.selectionStart,end:S.selectionEnd}:(S=(S.ownerDocument&&S.ownerDocument.defaultView||window).getSelection(),S={anchorNode:S.anchorNode,anchorOffset:S.anchorOffset,focusNode:S.focusNode,focusOffset:S.focusOffset}),Dm&&Pm(Dm,S)||(Dm=S,S=Bv(Qw,"onSelect"),0>=V,C-=V,wo=1<<32-et(h)+C|v<Kt?(hn=Pt,Pt=null):hn=Pt.sibling;var bn=Ve(Oe,Pt,Fe[Kt],nt);if(bn===null){Pt===null&&(Pt=hn);break}u&&Pt&&bn.alternate===null&&h(Oe,Pt),Ee=D(bn,Ee,Kt),yn===null?Lt=bn:yn.sibling=bn,yn=bn,Pt=hn}if(Kt===Fe.length)return v(Oe,Pt),mn&&al(Oe,Kt),Lt;if(Pt===null){for(;KtKt?(hn=Pt,Pt=null):hn=Pt.sibling;var Gc=Ve(Oe,Pt,bn.value,nt);if(Gc===null){Pt===null&&(Pt=hn);break}u&&Pt&&Gc.alternate===null&&h(Oe,Pt),Ee=D(Gc,Ee,Kt),yn===null?Lt=Gc:yn.sibling=Gc,yn=Gc,Pt=hn}if(bn.done)return v(Oe,Pt),mn&&al(Oe,Kt),Lt;if(Pt===null){for(;!bn.done;Kt++,bn=Fe.next())bn=rt(Oe,bn.value,nt),bn!==null&&(Ee=D(bn,Ee,Kt),yn===null?Lt=bn:yn.sibling=bn,yn=bn);return mn&&al(Oe,Kt),Lt}for(Pt=S(Pt);!bn.done;Kt++,bn=Fe.next())bn=Ke(Pt,Oe,Kt,bn.value,nt),bn!==null&&(u&&bn.alternate!==null&&Pt.delete(bn.key===null?Kt:bn.key),Ee=D(bn,Ee,Kt),yn===null?Lt=bn:yn.sibling=bn,yn=bn);return u&&Pt.forEach(function(_ee){return h(Oe,_ee)}),mn&&al(Oe,Kt),Lt}function Un(Oe,Ee,Fe,nt){if(typeof Fe=="object"&&Fe!==null&&Fe.type===b&&Fe.key===null&&(Fe=Fe.props.children),typeof Fe=="object"&&Fe!==null){switch(Fe.$$typeof){case m:e:{for(var Lt=Fe.key;Ee!==null;){if(Ee.key===Lt){if(Lt=Fe.type,Lt===b){if(Ee.tag===7){v(Oe,Ee.sibling),nt=C(Ee,Fe.props.children),nt.return=Oe,Oe=nt;break e}}else if(Ee.elementType===Lt||typeof Lt=="object"&&Lt!==null&&Lt.$$typeof===F&&fd(Lt)===Ee.type){v(Oe,Ee.sibling),nt=C(Ee,Fe.props),Fm(nt,Fe),nt.return=Oe,Oe=nt;break e}v(Oe,Ee);break}else h(Oe,Ee);Ee=Ee.sibling}Fe.type===b?(nt=od(Fe.props.children,Oe.mode,nt,Fe.key),nt.return=Oe,Oe=nt):(nt=rv(Fe.type,Fe.key,Fe.props,null,Oe.mode,nt),Fm(nt,Fe),nt.return=Oe,Oe=nt)}return V(Oe);case _:e:{for(Lt=Fe.key;Ee!==null;){if(Ee.key===Lt)if(Ee.tag===4&&Ee.stateNode.containerInfo===Fe.containerInfo&&Ee.stateNode.implementation===Fe.implementation){v(Oe,Ee.sibling),nt=C(Ee,Fe.children||[]),nt.return=Oe,Oe=nt;break e}else{v(Oe,Ee);break}else h(Oe,Ee);Ee=Ee.sibling}nt=sM(Fe,Oe.mode,nt),nt.return=Oe,Oe=nt}return V(Oe);case F:return Fe=fd(Fe),Un(Oe,Ee,Fe,nt)}if(H(Fe))return Ct(Oe,Ee,Fe,nt);if(I(Fe)){if(Lt=I(Fe),typeof Lt!="function")throw Error(i(150));return Fe=Lt.call(Fe),zt(Oe,Ee,Fe,nt)}if(typeof Fe.then=="function")return Un(Oe,Ee,dv(Fe),nt);if(Fe.$$typeof===w)return Un(Oe,Ee,ov(Oe,Fe),nt);fv(Oe,Fe)}return typeof Fe=="string"&&Fe!==""||typeof Fe=="number"||typeof Fe=="bigint"?(Fe=""+Fe,Ee!==null&&Ee.tag===6?(v(Oe,Ee.sibling),nt=C(Ee,Fe),nt.return=Oe,Oe=nt):(v(Oe,Ee),nt=aM(Fe,Oe.mode,nt),nt.return=Oe,Oe=nt),V(Oe)):v(Oe,Ee)}return function(Oe,Ee,Fe,nt){try{Um=0;var Lt=Un(Oe,Ee,Fe,nt);return Jf=null,Lt}catch(Pt){if(Pt===Qf||Pt===cv)throw Pt;var yn=Va(29,Pt,null,Oe.mode);return yn.lanes=nt,yn.return=Oe,yn}finally{}}}var pd=mL(!0),gL=mL(!1),Tc=!1;function vM(u){u.updateQueue={baseState:u.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function yM(u,h){u=u.updateQueue,h.updateQueue===u&&(h.updateQueue={baseState:u.baseState,firstBaseUpdate:u.firstBaseUpdate,lastBaseUpdate:u.lastBaseUpdate,shared:u.shared,callbacks:null})}function Cc(u){return{lane:u,tag:0,payload:null,callback:null,next:null}}function Nc(u,h,v){var S=u.updateQueue;if(S===null)return null;if(S=S.shared,(An&2)!==0){var C=S.pending;return C===null?h.next=h:(h.next=C.next,C.next=h),S.pending=h,h=iv(u),Jk(u,null,v),h}return nv(u,S,h,v),iv(u)}function zm(u,h,v){if(h=h.updateQueue,h!==null&&(h=h.shared,(v&4194048)!==0)){var S=h.lanes;S&=u.pendingLanes,v|=S,h.lanes=v,$n(u,v)}}function bM(u,h){var v=u.updateQueue,S=u.alternate;if(S!==null&&(S=S.updateQueue,v===S)){var C=null,D=null;if(v=v.firstBaseUpdate,v!==null){do{var V={lane:v.lane,tag:v.tag,payload:v.payload,callback:null,next:null};D===null?C=D=V:D=D.next=V,v=v.next}while(v!==null);D===null?C=D=h:D=D.next=h}else C=D=h;v={baseState:S.baseState,firstBaseUpdate:C,lastBaseUpdate:D,shared:S.shared,callbacks:S.callbacks},u.updateQueue=v;return}u=v.lastBaseUpdate,u===null?v.firstBaseUpdate=h:u.next=h,v.lastBaseUpdate=h}var _M=!1;function jm(){if(_M){var u=Zf;if(u!==null)throw u}}function Bm(u,h,v,S){_M=!1;var C=u.updateQueue;Tc=!1;var D=C.firstBaseUpdate,V=C.lastBaseUpdate,ne=C.shared.pending;if(ne!==null){C.shared.pending=null;var _e=ne,ze=_e.next;_e.next=null,V===null?D=ze:V.next=ze,V=_e;var Qe=u.alternate;Qe!==null&&(Qe=Qe.updateQueue,ne=Qe.lastBaseUpdate,ne!==V&&(ne===null?Qe.firstBaseUpdate=ze:ne.next=ze,Qe.lastBaseUpdate=_e))}if(D!==null){var rt=C.baseState;V=0,Qe=ze=_e=null,ne=D;do{var Ve=ne.lane&-536870913,Ke=Ve!==ne.lane;if(Ke?(fn&Ve)===Ve:(S&Ve)===Ve){Ve!==0&&Ve===Yf&&(_M=!0),Qe!==null&&(Qe=Qe.next={lane:0,tag:ne.tag,payload:ne.payload,callback:null,next:null});e:{var Ct=u,zt=ne;Ve=h;var Un=v;switch(zt.tag){case 1:if(Ct=zt.payload,typeof Ct=="function"){rt=Ct.call(Un,rt,Ve);break e}rt=Ct;break e;case 3:Ct.flags=Ct.flags&-65537|128;case 0:if(Ct=zt.payload,Ve=typeof Ct=="function"?Ct.call(Un,rt,Ve):Ct,Ve==null)break e;rt=f({},rt,Ve);break e;case 2:Tc=!0}}Ve=ne.callback,Ve!==null&&(u.flags|=64,Ke&&(u.flags|=8192),Ke=C.callbacks,Ke===null?C.callbacks=[Ve]:Ke.push(Ve))}else Ke={lane:Ve,tag:ne.tag,payload:ne.payload,callback:ne.callback,next:null},Qe===null?(ze=Qe=Ke,_e=rt):Qe=Qe.next=Ke,V|=Ve;if(ne=ne.next,ne===null){if(ne=C.shared.pending,ne===null)break;Ke=ne,ne=Ke.next,Ke.next=null,C.lastBaseUpdate=Ke,C.shared.pending=null}}while(!0);Qe===null&&(_e=rt),C.baseState=_e,C.firstBaseUpdate=ze,C.lastBaseUpdate=Qe,D===null&&(C.shared.lanes=0),kc|=V,u.lanes=V,u.memoizedState=rt}}function xL(u,h){if(typeof u!="function")throw Error(i(191,u));u.call(h)}function vL(u,h){var v=u.callbacks;if(v!==null)for(u.callbacks=null,u=0;uD?D:8;var V=q.T,ne={};q.T=ne,jM(u,!1,h,v);try{var _e=C(),ze=q.S;if(ze!==null&&ze(ne,_e),_e!==null&&typeof _e=="object"&&typeof _e.then=="function"){var Qe=uJ(_e,S);Gm(u,h,Qe,Xa(u))}else Gm(u,h,S,Xa(u))}catch(rt){Gm(u,h,{then:function(){},status:"rejected",reason:rt},Xa())}finally{W.p=D,V!==null&&ne.types!==null&&(V.types=ne.types),q.T=V}}function gJ(){}function FM(u,h,v,S){if(u.tag!==5)throw Error(i(476));var C=YL(u).queue;KL(u,C,h,K,v===null?gJ:function(){return ZL(u),v(S)})}function YL(u){var h=u.memoizedState;if(h!==null)return h;h={memoizedState:K,baseState:K,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:cl,lastRenderedState:K},next:null};var v={};return h.next={memoizedState:v,baseState:v,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:cl,lastRenderedState:v},next:null},u.memoizedState=h,u=u.alternate,u!==null&&(u.memoizedState=h),h}function ZL(u){var h=YL(u);h.next===null&&(h=u.alternate.memoizedState),Gm(u,h.next.queue,{},Xa())}function zM(){return Rr(og)}function QL(){return Ci().memoizedState}function JL(){return Ci().memoizedState}function xJ(u){for(var h=u.return;h!==null;){switch(h.tag){case 24:case 3:var v=Xa();u=Cc(v);var S=Nc(h,u,v);S!==null&&(Ma(S,h,v),zm(S,h,v)),h={cache:pM()},u.payload=h;return}h=h.return}}function vJ(u,h,v){var S=Xa();v={lane:S,revertLane:0,gesture:null,action:v,hasEagerState:!1,eagerState:null,next:null},Sv(u)?tI(h,v):(v=iM(u,h,v,S),v!==null&&(Ma(v,u,S),nI(v,h,S)))}function eI(u,h,v){var S=Xa();Gm(u,h,v,S)}function Gm(u,h,v,S){var C={lane:S,revertLane:0,gesture:null,action:v,hasEagerState:!1,eagerState:null,next:null};if(Sv(u))tI(h,C);else{var D=u.alternate;if(u.lanes===0&&(D===null||D.lanes===0)&&(D=h.lastRenderedReducer,D!==null))try{var V=h.lastRenderedState,ne=D(V,v);if(C.hasEagerState=!0,C.eagerState=ne,Ha(ne,V))return nv(u,h,C,0),Wn===null&&tv(),!1}catch{}finally{}if(v=iM(u,h,C,S),v!==null)return Ma(v,u,S),nI(v,h,S),!0}return!1}function jM(u,h,v,S){if(S={lane:2,revertLane:vE(),gesture:null,action:S,hasEagerState:!1,eagerState:null,next:null},Sv(u)){if(h)throw Error(i(479))}else h=iM(u,v,S,2),h!==null&&Ma(h,u,2)}function Sv(u){var h=u.alternate;return u===qt||h!==null&&h===qt}function tI(u,h){th=mv=!0;var v=u.pending;v===null?h.next=h:(h.next=v.next,v.next=h),u.pending=h}function nI(u,h,v){if((v&4194048)!==0){var S=h.lanes;S&=u.pendingLanes,v|=S,h.lanes=v,$n(u,v)}}var $m={readContext:Rr,use:vv,useCallback:vi,useContext:vi,useEffect:vi,useImperativeHandle:vi,useLayoutEffect:vi,useInsertionEffect:vi,useMemo:vi,useReducer:vi,useRef:vi,useState:vi,useDebugValue:vi,useDeferredValue:vi,useTransition:vi,useSyncExternalStore:vi,useId:vi,useHostTransitionStatus:vi,useFormState:vi,useActionState:vi,useOptimistic:vi,useMemoCache:vi,useCacheRefresh:vi};$m.useEffectEvent=vi;var iI={readContext:Rr,use:vv,useCallback:function(u,h){return aa().memoizedState=[u,h===void 0?null:h],u},useContext:Rr,useEffect:jL,useImperativeHandle:function(u,h,v){v=v!=null?v.concat([u]):null,bv(4194308,4,GL.bind(null,h,u),v)},useLayoutEffect:function(u,h){return bv(4194308,4,u,h)},useInsertionEffect:function(u,h){bv(4,2,u,h)},useMemo:function(u,h){var v=aa();h=h===void 0?null:h;var S=u();if(md){ht(!0);try{u()}finally{ht(!1)}}return v.memoizedState=[S,h],S},useReducer:function(u,h,v){var S=aa();if(v!==void 0){var C=v(h);if(md){ht(!0);try{v(h)}finally{ht(!1)}}}else C=h;return S.memoizedState=S.baseState=C,u={pending:null,lanes:0,dispatch:null,lastRenderedReducer:u,lastRenderedState:C},S.queue=u,u=u.dispatch=vJ.bind(null,qt,u),[S.memoizedState,u]},useRef:function(u){var h=aa();return u={current:u},h.memoizedState=u},useState:function(u){u=OM(u);var h=u.queue,v=eI.bind(null,qt,h);return h.dispatch=v,[u.memoizedState,v]},useDebugValue:IM,useDeferredValue:function(u,h){var v=aa();return UM(v,u,h)},useTransition:function(){var u=OM(!1);return u=KL.bind(null,qt,u.queue,!0,!1),aa().memoizedState=u,[!1,u]},useSyncExternalStore:function(u,h,v){var S=qt,C=aa();if(mn){if(v===void 0)throw Error(i(407));v=v()}else{if(v=h(),Wn===null)throw Error(i(349));(fn&127)!==0||ML(S,h,v)}C.memoizedState=v;var D={value:v,getSnapshot:h};return C.queue=D,jL(AL.bind(null,S,D,u),[u]),S.flags|=2048,ih(9,{destroy:void 0},EL.bind(null,S,D,v,h),null),v},useId:function(){var u=aa(),h=Wn.identifierPrefix;if(mn){var v=Mo,S=wo;v=(S&~(1<<32-et(S)-1)).toString(32)+v,h="_"+h+"R_"+v,v=gv++,0<\/script>",D=D.removeChild(D.firstChild);break;case"select":D=typeof S.is=="string"?V.createElement("select",{is:S.is}):V.createElement("select"),S.multiple?D.multiple=!0:S.size&&(D.size=S.size);break;default:D=typeof S.is=="string"?V.createElement(C,{is:S.is}):V.createElement(C)}}D[di]=h,D[Ei]=S;e:for(V=h.child;V!==null;){if(V.tag===5||V.tag===6)D.appendChild(V.stateNode);else if(V.tag!==4&&V.tag!==27&&V.child!==null){V.child.return=V,V=V.child;continue}if(V===h)break e;for(;V.sibling===null;){if(V.return===null||V.return===h)break e;V=V.return}V.sibling.return=V.return,V=V.sibling}h.stateNode=D;e:switch(Dr(D,C,S),C){case"button":case"input":case"select":case"textarea":S=!!S.autoFocus;break e;case"img":S=!0;break e;default:S=!1}S&&dl(h)}}return ei(h),eE(h,h.type,u===null?null:u.memoizedProps,h.pendingProps,v),null;case 6:if(u&&h.stateNode!=null)u.memoizedProps!==S&&dl(h);else{if(typeof S!="string"&&h.stateNode===null)throw Error(i(166));if(u=me.current,Xf(h)){if(u=h.stateNode,v=h.memoizedProps,S=null,C=Nr,C!==null)switch(C.tag){case 27:case 5:S=C.memoizedProps}u[di]=h,u=!!(u.nodeValue===v||S!==null&&S.suppressHydrationWarning===!0||SU(u.nodeValue,v)),u||Ec(h,!0)}else u=Hv(u).createTextNode(S),u[di]=h,h.stateNode=u}return ei(h),null;case 31:if(v=h.memoizedState,u===null||u.memoizedState!==null){if(S=Xf(h),v!==null){if(u===null){if(!S)throw Error(i(318));if(u=h.memoizedState,u=u!==null?u.dehydrated:null,!u)throw Error(i(557));u[di]=h}else ld(),(h.flags&128)===0&&(h.memoizedState=null),h.flags|=4;ei(h),u=!1}else v=uM(),u!==null&&u.memoizedState!==null&&(u.memoizedState.hydrationErrors=v),u=!0;if(!u)return h.flags&256?($a(h),h):($a(h),null);if((h.flags&128)!==0)throw Error(i(558))}return ei(h),null;case 13:if(S=h.memoizedState,u===null||u.memoizedState!==null&&u.memoizedState.dehydrated!==null){if(C=Xf(h),S!==null&&S.dehydrated!==null){if(u===null){if(!C)throw Error(i(318));if(C=h.memoizedState,C=C!==null?C.dehydrated:null,!C)throw Error(i(317));C[di]=h}else ld(),(h.flags&128)===0&&(h.memoizedState=null),h.flags|=4;ei(h),C=!1}else C=uM(),u!==null&&u.memoizedState!==null&&(u.memoizedState.hydrationErrors=C),C=!0;if(!C)return h.flags&256?($a(h),h):($a(h),null)}return $a(h),(h.flags&128)!==0?(h.lanes=v,h):(v=S!==null,u=u!==null&&u.memoizedState!==null,v&&(S=h.child,C=null,S.alternate!==null&&S.alternate.memoizedState!==null&&S.alternate.memoizedState.cachePool!==null&&(C=S.alternate.memoizedState.cachePool.pool),D=null,S.memoizedState!==null&&S.memoizedState.cachePool!==null&&(D=S.memoizedState.cachePool.pool),D!==C&&(S.flags|=2048)),v!==u&&v&&(h.child.flags|=8192),Tv(h,h.updateQueue),ei(h),null);case 4:return he(),u===null&&SE(h.stateNode.containerInfo),ei(h),null;case 10:return ol(h.type),ei(h),null;case 19:if(X(Ti),S=h.memoizedState,S===null)return ei(h),null;if(C=(h.flags&128)!==0,D=S.rendering,D===null)if(C)qm(S,!1);else{if(yi!==0||u!==null&&(u.flags&128)!==0)for(u=h.child;u!==null;){if(D=pv(u),D!==null){for(h.flags|=128,qm(S,!1),u=D.updateQueue,h.updateQueue=u,Tv(h,u),h.subtreeFlags=0,u=v,v=h.child;v!==null;)eL(v,u),v=v.sibling;return re(Ti,Ti.current&1|2),mn&&al(h,S.treeForkCount),h.child}u=u.sibling}S.tail!==null&&k()>Dv&&(h.flags|=128,C=!0,qm(S,!1),h.lanes=4194304)}else{if(!C)if(u=pv(D),u!==null){if(h.flags|=128,C=!0,u=u.updateQueue,h.updateQueue=u,Tv(h,u),qm(S,!0),S.tail===null&&S.tailMode==="hidden"&&!D.alternate&&!mn)return ei(h),null}else 2*k()-S.renderingStartTime>Dv&&v!==536870912&&(h.flags|=128,C=!0,qm(S,!1),h.lanes=4194304);S.isBackwards?(D.sibling=h.child,h.child=D):(u=S.last,u!==null?u.sibling=D:h.child=D,S.last=D)}return S.tail!==null?(u=S.tail,S.rendering=u,S.tail=u.sibling,S.renderingStartTime=k(),u.sibling=null,v=Ti.current,re(Ti,C?v&1|2:v&1),mn&&al(h,S.treeForkCount),u):(ei(h),null);case 22:case 23:return $a(h),wM(),S=h.memoizedState!==null,u!==null?u.memoizedState!==null!==S&&(h.flags|=8192):S&&(h.flags|=8192),S?(v&536870912)!==0&&(h.flags&128)===0&&(ei(h),h.subtreeFlags&6&&(h.flags|=8192)):ei(h),v=h.updateQueue,v!==null&&Tv(h,v.retryQueue),v=null,u!==null&&u.memoizedState!==null&&u.memoizedState.cachePool!==null&&(v=u.memoizedState.cachePool.pool),S=null,h.memoizedState!==null&&h.memoizedState.cachePool!==null&&(S=h.memoizedState.cachePool.pool),S!==v&&(h.flags|=2048),u!==null&&X(dd),null;case 24:return v=null,u!==null&&(v=u.memoizedState.cache),h.memoizedState.cache!==v&&(h.flags|=2048),ol(ki),ei(h),null;case 25:return null;case 30:return null}throw Error(i(156,h.tag))}function wJ(u,h){switch(lM(h),h.tag){case 1:return u=h.flags,u&65536?(h.flags=u&-65537|128,h):null;case 3:return ol(ki),he(),u=h.flags,(u&65536)!==0&&(u&128)===0?(h.flags=u&-65537|128,h):null;case 26:case 27:case 5:return je(h),null;case 31:if(h.memoizedState!==null){if($a(h),h.alternate===null)throw Error(i(340));ld()}return u=h.flags,u&65536?(h.flags=u&-65537|128,h):null;case 13:if($a(h),u=h.memoizedState,u!==null&&u.dehydrated!==null){if(h.alternate===null)throw Error(i(340));ld()}return u=h.flags,u&65536?(h.flags=u&-65537|128,h):null;case 19:return X(Ti),null;case 4:return he(),null;case 10:return ol(h.type),null;case 22:case 23:return $a(h),wM(),u!==null&&X(dd),u=h.flags,u&65536?(h.flags=u&-65537|128,h):null;case 24:return ol(ki),null;case 25:return null;default:return null}}function TI(u,h){switch(lM(h),h.tag){case 3:ol(ki),he();break;case 26:case 27:case 5:je(h);break;case 4:he();break;case 31:h.memoizedState!==null&&$a(h);break;case 13:$a(h);break;case 19:X(Ti);break;case 10:ol(h.type);break;case 22:case 23:$a(h),wM(),u!==null&&X(dd);break;case 24:ol(ki)}}function Xm(u,h){try{var v=h.updateQueue,S=v!==null?v.lastEffect:null;if(S!==null){var C=S.next;v=C;do{if((v.tag&u)===u){S=void 0;var D=v.create,V=v.inst;S=D(),V.destroy=S}v=v.next}while(v!==C)}}catch(ne){Pn(h,h.return,ne)}}function Dc(u,h,v){try{var S=h.updateQueue,C=S!==null?S.lastEffect:null;if(C!==null){var D=C.next;S=D;do{if((S.tag&u)===u){var V=S.inst,ne=V.destroy;if(ne!==void 0){V.destroy=void 0,C=h;var _e=v,ze=ne;try{ze()}catch(Qe){Pn(C,_e,Qe)}}}S=S.next}while(S!==D)}}catch(Qe){Pn(h,h.return,Qe)}}function CI(u){var h=u.updateQueue;if(h!==null){var v=u.stateNode;try{vL(h,v)}catch(S){Pn(u,u.return,S)}}}function NI(u,h,v){v.props=gd(u.type,u.memoizedProps),v.state=u.memoizedState;try{v.componentWillUnmount()}catch(S){Pn(u,h,S)}}function Km(u,h){try{var v=u.ref;if(v!==null){switch(u.tag){case 26:case 27:case 5:var S=u.stateNode;break;case 30:S=u.stateNode;break;default:S=u.stateNode}typeof v=="function"?u.refCleanup=v(S):v.current=S}}catch(C){Pn(u,h,C)}}function Eo(u,h){var v=u.ref,S=u.refCleanup;if(v!==null)if(typeof S=="function")try{S()}catch(C){Pn(u,h,C)}finally{u.refCleanup=null,u=u.alternate,u!=null&&(u.refCleanup=null)}else if(typeof v=="function")try{v(null)}catch(C){Pn(u,h,C)}else v.current=null}function RI(u){var h=u.type,v=u.memoizedProps,S=u.stateNode;try{e:switch(h){case"button":case"input":case"select":case"textarea":v.autoFocus&&S.focus();break e;case"img":v.src?S.src=v.src:v.srcSet&&(S.srcset=v.srcSet)}}catch(C){Pn(u,u.return,C)}}function tE(u,h,v){try{var S=u.stateNode;$J(S,u.type,v,h),S[Ei]=h}catch(C){Pn(u,u.return,C)}}function PI(u){return u.tag===5||u.tag===3||u.tag===26||u.tag===27&&zc(u.type)||u.tag===4}function nE(u){e:for(;;){for(;u.sibling===null;){if(u.return===null||PI(u.return))return null;u=u.return}for(u.sibling.return=u.return,u=u.sibling;u.tag!==5&&u.tag!==6&&u.tag!==18;){if(u.tag===27&&zc(u.type)||u.flags&2||u.child===null||u.tag===4)continue e;u.child.return=u,u=u.child}if(!(u.flags&2))return u.stateNode}}function iE(u,h,v){var S=u.tag;if(S===5||S===6)u=u.stateNode,h?(v.nodeType===9?v.body:v.nodeName==="HTML"?v.ownerDocument.body:v).insertBefore(u,h):(h=v.nodeType===9?v.body:v.nodeName==="HTML"?v.ownerDocument.body:v,h.appendChild(u),v=v._reactRootContainer,v!=null||h.onclick!==null||(h.onclick=nl));else if(S!==4&&(S===27&&zc(u.type)&&(v=u.stateNode,h=null),u=u.child,u!==null))for(iE(u,h,v),u=u.sibling;u!==null;)iE(u,h,v),u=u.sibling}function Cv(u,h,v){var S=u.tag;if(S===5||S===6)u=u.stateNode,h?v.insertBefore(u,h):v.appendChild(u);else if(S!==4&&(S===27&&zc(u.type)&&(v=u.stateNode),u=u.child,u!==null))for(Cv(u,h,v),u=u.sibling;u!==null;)Cv(u,h,v),u=u.sibling}function DI(u){var h=u.stateNode,v=u.memoizedProps;try{for(var S=u.type,C=h.attributes;C.length;)h.removeAttributeNode(C[0]);Dr(h,S,v),h[di]=u,h[Ei]=v}catch(D){Pn(u,u.return,D)}}var fl=!1,Ui=!1,rE=!1,OI=typeof WeakSet=="function"?WeakSet:Set,mr=null;function MJ(u,h){if(u=u.containerInfo,EE=Kv,u=$k(u),Zw(u)){if("selectionStart"in u)var v={start:u.selectionStart,end:u.selectionEnd};else e:{v=(v=u.ownerDocument)&&v.defaultView||window;var S=v.getSelection&&v.getSelection();if(S&&S.rangeCount!==0){v=S.anchorNode;var C=S.anchorOffset,D=S.focusNode;S=S.focusOffset;try{v.nodeType,D.nodeType}catch{v=null;break e}var V=0,ne=-1,_e=-1,ze=0,Qe=0,rt=u,Ve=null;t:for(;;){for(var Ke;rt!==v||C!==0&&rt.nodeType!==3||(ne=V+C),rt!==D||S!==0&&rt.nodeType!==3||(_e=V+S),rt.nodeType===3&&(V+=rt.nodeValue.length),(Ke=rt.firstChild)!==null;)Ve=rt,rt=Ke;for(;;){if(rt===u)break t;if(Ve===v&&++ze===C&&(ne=V),Ve===D&&++Qe===S&&(_e=V),(Ke=rt.nextSibling)!==null)break;rt=Ve,Ve=rt.parentNode}rt=Ke}v=ne===-1||_e===-1?null:{start:ne,end:_e}}else v=null}v=v||{start:0,end:0}}else v=null;for(AE={focusedElem:u,selectionRange:v},Kv=!1,mr=h;mr!==null;)if(h=mr,u=h.child,(h.subtreeFlags&1028)!==0&&u!==null)u.return=h,mr=u;else for(;mr!==null;){switch(h=mr,D=h.alternate,u=h.flags,h.tag){case 0:if((u&4)!==0&&(u=h.updateQueue,u=u!==null?u.events:null,u!==null))for(v=0;v title"))),Dr(D,S,v),D[di]=u,Te(D),S=D;break e;case"link":var V=zU("link","href",C).get(S+(v.href||""));if(V){for(var ne=0;neUn&&(V=Un,Un=zt,zt=V);var Oe=Vk(ne,zt),Ee=Vk(ne,Un);if(Oe&&Ee&&(Ke.rangeCount!==1||Ke.anchorNode!==Oe.node||Ke.anchorOffset!==Oe.offset||Ke.focusNode!==Ee.node||Ke.focusOffset!==Ee.offset)){var Fe=rt.createRange();Fe.setStart(Oe.node,Oe.offset),Ke.removeAllRanges(),zt>Un?(Ke.addRange(Fe),Ke.extend(Ee.node,Ee.offset)):(Fe.setEnd(Ee.node,Ee.offset),Ke.addRange(Fe))}}}}for(rt=[],Ke=ne;Ke=Ke.parentNode;)Ke.nodeType===1&&rt.push({element:Ke,left:Ke.scrollLeft,top:Ke.scrollTop});for(typeof ne.focus=="function"&&ne.focus(),ne=0;nev?32:v,q.T=null,v=dE,dE=null;var D=Ic,V=xl;if(Zi=0,lh=Ic=null,xl=0,(An&6)!==0)throw Error(i(331));var ne=An;if(An|=4,GI(D.current),BI(D,D.current,V,v),An=ne,tg(0,!1),Re&&typeof Re.onPostCommitFiberRoot=="function")try{Re.onPostCommitFiberRoot(Ie,D)}catch{}return!0}finally{W.p=C,q.T=S,lU(u,h)}}function uU(u,h,v){h=fs(v,h),h=GM(u.stateNode,h,2),u=Nc(u,h,2),u!==null&&(nn(u,2),Ao(u))}function Pn(u,h,v){if(u.tag===3)uU(u,u,v);else for(;h!==null;){if(h.tag===3){uU(h,u,v);break}else if(h.tag===1){var S=h.stateNode;if(typeof h.type.getDerivedStateFromError=="function"||typeof S.componentDidCatch=="function"&&(Lc===null||!Lc.has(S))){u=fs(v,u),v=dI(2),S=Nc(h,v,2),S!==null&&(fI(v,S,h,u),nn(S,2),Ao(S));break}}h=h.return}}function mE(u,h,v){var S=u.pingCache;if(S===null){S=u.pingCache=new TJ;var C=new Set;S.set(h,C)}else C=S.get(h),C===void 0&&(C=new Set,S.set(h,C));C.has(v)||(oE=!0,C.add(v),u=DJ.bind(null,u,h,v),h.then(u,u))}function DJ(u,h,v){var S=u.pingCache;S!==null&&S.delete(h),u.pingedLanes|=u.suspendedLanes&v,u.warmLanes&=~v,Wn===u&&(fn&v)===v&&(yi===4||yi===3&&(fn&62914560)===fn&&300>k()-Pv?(An&2)===0&&ch(u,0):lE|=v,oh===fn&&(oh=0)),Ao(u)}function dU(u,h){h===0&&(h=yt()),u=sd(u,h),u!==null&&(nn(u,h),Ao(u))}function OJ(u){var h=u.memoizedState,v=0;h!==null&&(v=h.retryLane),dU(u,v)}function kJ(u,h){var v=0;switch(u.tag){case 31:case 13:var S=u.stateNode,C=u.memoizedState;C!==null&&(v=C.retryLane);break;case 19:S=u.stateNode;break;case 22:S=u.stateNode._retryCache;break;default:throw Error(i(314))}S!==null&&S.delete(h),dU(u,v)}function LJ(u,h){return Pe(u,h)}var Fv=null,dh=null,gE=!1,zv=!1,xE=!1,Fc=0;function Ao(u){u!==dh&&u.next===null&&(dh===null?Fv=dh=u:dh=dh.next=u),zv=!0,gE||(gE=!0,UJ())}function tg(u,h){if(!xE&&zv){xE=!0;do for(var v=!1,S=Fv;S!==null;){if(u!==0){var C=S.pendingLanes;if(C===0)var D=0;else{var V=S.suspendedLanes,ne=S.pingedLanes;D=(1<<31-et(42|u)+1)-1,D&=C&~(V&~ne),D=D&201326741?D&201326741|1:D?D|2:0}D!==0&&(v=!0,mU(S,D))}else D=fn,D=ge(S,S===Wn?D:0,S.cancelPendingCommit!==null||S.timeoutHandle!==-1),(D&3)===0||Be(S,D)||(v=!0,mU(S,D));S=S.next}while(v);xE=!1}}function IJ(){fU()}function fU(){zv=gE=!1;var u=0;Fc!==0&&qJ()&&(u=Fc);for(var h=k(),v=null,S=Fv;S!==null;){var C=S.next,D=hU(S,h);D===0?(S.next=null,v===null?Fv=C:v.next=C,C===null&&(dh=v)):(v=S,(u!==0||(D&3)!==0)&&(zv=!0)),S=C}Zi!==0&&Zi!==5||tg(u),Fc!==0&&(Fc=0)}function hU(u,h){for(var v=u.suspendedLanes,S=u.pingedLanes,C=u.expirationTimes,D=u.pendingLanes&-62914561;0ne)break;var Qe=_e.transferSize,rt=_e.initiatorType;Qe&&wU(rt)&&(_e=_e.responseEnd,V+=Qe*(_e"u"?null:document;function LU(u,h,v){var S=fh;if(S&&typeof h=="string"&&h){var C=Tn(h);C='link[rel="'+u+'"][href="'+C+'"]',typeof v=="string"&&(C+='[crossorigin="'+v+'"]'),kU.has(C)||(kU.add(C),u={rel:u,crossOrigin:v,href:h},S.querySelector(C)===null&&(h=S.createElement("link"),Dr(h,"link",u),Te(h),S.head.appendChild(h)))}}function nee(u){vl.D(u),LU("dns-prefetch",u,null)}function iee(u,h){vl.C(u,h),LU("preconnect",u,h)}function ree(u,h,v){vl.L(u,h,v);var S=fh;if(S&&u&&h){var C='link[rel="preload"][as="'+Tn(h)+'"]';h==="image"&&v&&v.imageSrcSet?(C+='[imagesrcset="'+Tn(v.imageSrcSet)+'"]',typeof v.imageSizes=="string"&&(C+='[imagesizes="'+Tn(v.imageSizes)+'"]')):C+='[href="'+Tn(u)+'"]';var D=C;switch(h){case"style":D=hh(u);break;case"script":D=ph(u)}vs.has(D)||(u=f({rel:"preload",href:h==="image"&&v&&v.imageSrcSet?void 0:u,as:h},v),vs.set(D,u),S.querySelector(C)!==null||h==="style"&&S.querySelector(ag(D))||h==="script"&&S.querySelector(sg(D))||(h=S.createElement("link"),Dr(h,"link",u),Te(h),S.head.appendChild(h)))}}function aee(u,h){vl.m(u,h);var v=fh;if(v&&u){var S=h&&typeof h.as=="string"?h.as:"script",C='link[rel="modulepreload"][as="'+Tn(S)+'"][href="'+Tn(u)+'"]',D=C;switch(S){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":D=ph(u)}if(!vs.has(D)&&(u=f({rel:"modulepreload",href:u},h),vs.set(D,u),v.querySelector(C)===null)){switch(S){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(v.querySelector(sg(D)))return}S=v.createElement("link"),Dr(S,"link",u),Te(S),v.head.appendChild(S)}}}function see(u,h,v){vl.S(u,h,v);var S=fh;if(S&&u){var C=Ce(S).hoistableStyles,D=hh(u);h=h||"default";var V=C.get(D);if(!V){var ne={loading:0,preload:null};if(V=S.querySelector(ag(D)))ne.loading=5;else{u=f({rel:"stylesheet",href:u,"data-precedence":h},v),(v=vs.get(D))&&OE(u,v);var _e=V=S.createElement("link");Te(_e),Dr(_e,"link",u),_e._p=new Promise(function(ze,Qe){_e.onload=ze,_e.onerror=Qe}),_e.addEventListener("load",function(){ne.loading|=1}),_e.addEventListener("error",function(){ne.loading|=2}),ne.loading|=4,Gv(V,h,S)}V={type:"stylesheet",instance:V,count:1,state:ne},C.set(D,V)}}}function oee(u,h){vl.X(u,h);var v=fh;if(v&&u){var S=Ce(v).hoistableScripts,C=ph(u),D=S.get(C);D||(D=v.querySelector(sg(C)),D||(u=f({src:u,async:!0},h),(h=vs.get(C))&&kE(u,h),D=v.createElement("script"),Te(D),Dr(D,"link",u),v.head.appendChild(D)),D={type:"script",instance:D,count:1,state:null},S.set(C,D))}}function lee(u,h){vl.M(u,h);var v=fh;if(v&&u){var S=Ce(v).hoistableScripts,C=ph(u),D=S.get(C);D||(D=v.querySelector(sg(C)),D||(u=f({src:u,async:!0,type:"module"},h),(h=vs.get(C))&&kE(u,h),D=v.createElement("script"),Te(D),Dr(D,"link",u),v.head.appendChild(D)),D={type:"script",instance:D,count:1,state:null},S.set(C,D))}}function IU(u,h,v,S){var C=(C=me.current)?Vv(C):null;if(!C)throw Error(i(446));switch(u){case"meta":case"title":return null;case"style":return typeof v.precedence=="string"&&typeof v.href=="string"?(h=hh(v.href),v=Ce(C).hoistableStyles,S=v.get(h),S||(S={type:"style",instance:null,count:0,state:null},v.set(h,S)),S):{type:"void",instance:null,count:0,state:null};case"link":if(v.rel==="stylesheet"&&typeof v.href=="string"&&typeof v.precedence=="string"){u=hh(v.href);var D=Ce(C).hoistableStyles,V=D.get(u);if(V||(C=C.ownerDocument||C,V={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},D.set(u,V),(D=C.querySelector(ag(u)))&&!D._p&&(V.instance=D,V.state.loading=5),vs.has(u)||(v={rel:"preload",as:"style",href:v.href,crossOrigin:v.crossOrigin,integrity:v.integrity,media:v.media,hrefLang:v.hrefLang,referrerPolicy:v.referrerPolicy},vs.set(u,v),D||cee(C,u,v,V.state))),h&&S===null)throw Error(i(528,""));return V}if(h&&S!==null)throw Error(i(529,""));return null;case"script":return h=v.async,v=v.src,typeof v=="string"&&h&&typeof h!="function"&&typeof h!="symbol"?(h=ph(v),v=Ce(C).hoistableScripts,S=v.get(h),S||(S={type:"script",instance:null,count:0,state:null},v.set(h,S)),S):{type:"void",instance:null,count:0,state:null};default:throw Error(i(444,u))}}function hh(u){return'href="'+Tn(u)+'"'}function ag(u){return'link[rel="stylesheet"]['+u+"]"}function UU(u){return f({},u,{"data-precedence":u.precedence,precedence:null})}function cee(u,h,v,S){u.querySelector('link[rel="preload"][as="style"]['+h+"]")?S.loading=1:(h=u.createElement("link"),S.preload=h,h.addEventListener("load",function(){return S.loading|=1}),h.addEventListener("error",function(){return S.loading|=2}),Dr(h,"link",v),Te(h),u.head.appendChild(h))}function ph(u){return'[src="'+Tn(u)+'"]'}function sg(u){return"script[async]"+u}function FU(u,h,v){if(h.count++,h.instance===null)switch(h.type){case"style":var S=u.querySelector('style[data-href~="'+Tn(v.href)+'"]');if(S)return h.instance=S,Te(S),S;var C=f({},v,{"data-href":v.href,"data-precedence":v.precedence,href:null,precedence:null});return S=(u.ownerDocument||u).createElement("style"),Te(S),Dr(S,"style",C),Gv(S,v.precedence,u),h.instance=S;case"stylesheet":C=hh(v.href);var D=u.querySelector(ag(C));if(D)return h.state.loading|=4,h.instance=D,Te(D),D;S=UU(v),(C=vs.get(C))&&OE(S,C),D=(u.ownerDocument||u).createElement("link"),Te(D);var V=D;return V._p=new Promise(function(ne,_e){V.onload=ne,V.onerror=_e}),Dr(D,"link",S),h.state.loading|=4,Gv(D,v.precedence,u),h.instance=D;case"script":return D=ph(v.src),(C=u.querySelector(sg(D)))?(h.instance=C,Te(C),C):(S=v,(C=vs.get(D))&&(S=f({},v),kE(S,C)),u=u.ownerDocument||u,C=u.createElement("script"),Te(C),Dr(C,"link",S),u.head.appendChild(C),h.instance=C);case"void":return null;default:throw Error(i(443,h.type))}else h.type==="stylesheet"&&(h.state.loading&4)===0&&(S=h.instance,h.state.loading|=4,Gv(S,v.precedence,u));return h.instance}function Gv(u,h,v){for(var S=v.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),C=S.length?S[S.length-1]:null,D=C,V=0;V title"):null)}function uee(u,h,v){if(v===1||h.itemProp!=null)return!1;switch(u){case"meta":case"title":return!0;case"style":if(typeof h.precedence!="string"||typeof h.href!="string"||h.href==="")break;return!0;case"link":if(typeof h.rel!="string"||typeof h.href!="string"||h.href===""||h.onLoad||h.onError)break;switch(h.rel){case"stylesheet":return u=h.disabled,typeof h.precedence=="string"&&u==null;default:return!0}case"script":if(h.async&&typeof h.async!="function"&&typeof h.async!="symbol"&&!h.onLoad&&!h.onError&&h.src&&typeof h.src=="string")return!0}return!1}function BU(u){return!(u.type==="stylesheet"&&(u.state.loading&3)===0)}function dee(u,h,v,S){if(v.type==="stylesheet"&&(typeof S.media!="string"||matchMedia(S.media).matches!==!1)&&(v.state.loading&4)===0){if(v.instance===null){var C=hh(S.href),D=h.querySelector(ag(C));if(D){h=D._p,h!==null&&typeof h=="object"&&typeof h.then=="function"&&(u.count++,u=Wv.bind(u),h.then(u,u)),v.state.loading|=4,v.instance=D,Te(D);return}D=h.ownerDocument||h,S=UU(S),(C=vs.get(C))&&OE(S,C),D=D.createElement("link"),Te(D);var V=D;V._p=new Promise(function(ne,_e){V.onload=ne,V.onerror=_e}),Dr(D,"link",S),v.instance=D}u.stylesheets===null&&(u.stylesheets=new Map),u.stylesheets.set(v,h),(h=v.state.preload)&&(v.state.loading&3)===0&&(u.count++,v=Wv.bind(u),h.addEventListener("load",v),h.addEventListener("error",v))}}var LE=0;function fee(u,h){return u.stylesheets&&u.count===0&&Xv(u,u.stylesheets),0LE?50:800)+h);return u.unsuspend=v,function(){u.unsuspend=null,clearTimeout(S),clearTimeout(C)}}:null}function Wv(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)Xv(this,this.stylesheets);else if(this.unsuspend){var u=this.unsuspend;this.unsuspend=null,u()}}}var qv=null;function Xv(u,h){u.stylesheets=null,u.unsuspend!==null&&(u.count++,qv=new Map,h.forEach(hee,u),qv=null,Wv.call(u))}function hee(u,h){if(!(h.state.loading&4)){var v=qv.get(u);if(v)var S=v.get(null);else{v=new Map,qv.set(u,v);for(var C=u.querySelectorAll("link[data-precedence],style[data-precedence]"),D=0;D"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(t)}catch(e){console.error(e)}}return t(),GE.exports=Ree(),GE.exports}var Dee=Pee();var d4="popstate";function Oee(t={}){function e(i,r){let{pathname:a,search:s,hash:o}=i.location;return PC("",{pathname:a,search:s,hash:o},r.state&&r.state.usr||null,r.state&&r.state.key||"default")}function n(i,r){return typeof r=="string"?r:m0(r)}return Lee(e,n,null,t)}function hi(t,e){if(t===!1||t===null||typeof t>"u")throw new Error(e)}function go(t,e){if(!t){typeof console<"u"&&console.warn(e);try{throw new Error(e)}catch{}}}function kee(){return Math.random().toString(36).substring(2,10)}function f4(t,e){return{usr:t.state,key:t.key,idx:e}}function PC(t,e,n=null,i){return{pathname:typeof t=="string"?t:t.pathname,search:"",hash:"",...typeof e=="string"?em(e):e,state:n,key:e&&e.key||i||kee()}}function m0({pathname:t="/",search:e="",hash:n=""}){return e&&e!=="?"&&(t+=e.charAt(0)==="?"?e:"?"+e),n&&n!=="#"&&(t+=n.charAt(0)==="#"?n:"#"+n),t}function em(t){let e={};if(t){let n=t.indexOf("#");n>=0&&(e.hash=t.substring(n),t=t.substring(0,n));let i=t.indexOf("?");i>=0&&(e.search=t.substring(i),t=t.substring(0,i)),t&&(e.pathname=t)}return e}function Lee(t,e,n,i={}){let{window:r=document.defaultView,v5Compat:a=!1}=i,s=r.history,o="POP",l=null,c=d();c==null&&(c=0,s.replaceState({...s.state,idx:c},""));function d(){return(s.state||{idx:null}).idx}function f(){o="POP";let y=d(),g=y==null?null:y-c;c=y,l&&l({action:o,location:b.location,delta:g})}function p(y,g){o="PUSH";let M=PC(b.location,y,g);c=d()+1;let w=f4(M,c),A=b.createHref(M);try{s.pushState(w,"",A)}catch(E){if(E instanceof DOMException&&E.name==="DataCloneError")throw E;r.location.assign(A)}a&&l&&l({action:o,location:b.location,delta:1})}function m(y,g){o="REPLACE";let M=PC(b.location,y,g);c=d();let w=f4(M,c),A=b.createHref(M);s.replaceState(w,"",A),a&&l&&l({action:o,location:b.location,delta:0})}function _(y){return Iee(y)}let b={get action(){return o},get location(){return t(r,s)},listen(y){if(l)throw new Error("A history only accepts one active listener");return r.addEventListener(d4,f),l=y,()=>{r.removeEventListener(d4,f),l=null}},createHref(y){return e(r,y)},createURL:_,encodeLocation(y){let g=_(y);return{pathname:g.pathname,search:g.search,hash:g.hash}},push:p,replace:m,go(y){return s.go(y)}};return b}function Iee(t,e=!1){let n="http://localhost";typeof window<"u"&&(n=window.location.origin!=="null"?window.location.origin:window.location.href),hi(n,"No window.location.(origin|href) available to create URL");let i=typeof t=="string"?t:m0(t);return i=i.replace(/ $/,"%20"),!e&&i.startsWith("//")&&(i=n+i),new URL(i,n)}function KV(t,e,n="/"){return Uee(t,e,n,!1)}function Uee(t,e,n,i){let r=typeof e=="string"?em(e):e,a=nc(r.pathname||"/",n);if(a==null)return null;let s=YV(t);Fee(s);let o=null;for(let l=0;o==null&&l{let d={relativePath:c===void 0?s.path||"":c,caseSensitive:s.caseSensitive===!0,childrenIndex:o,route:s};if(d.relativePath.startsWith("/")){if(!d.relativePath.startsWith(i)&&l)return;hi(d.relativePath.startsWith(i),`Absolute route path "${d.relativePath}" nested under path "${i}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),d.relativePath=d.relativePath.slice(i.length)}let f=Xl([i,d.relativePath]),p=n.concat(d);s.children&&s.children.length>0&&(hi(s.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${f}".`),YV(s.children,e,p,f,l)),!(s.path==null&&!s.index)&&e.push({path:f,score:$ee(f,s.index),routesMeta:p})};return t.forEach((s,o)=>{if(s.path===""||!s.path?.includes("?"))a(s,o);else for(let l of ZV(s.path))a(s,o,!0,l)}),e}function ZV(t){let e=t.split("/");if(e.length===0)return[];let[n,...i]=e,r=n.endsWith("?"),a=n.replace(/\?$/,"");if(i.length===0)return r?[a,""]:[a];let s=ZV(i.join("/")),o=[];return o.push(...s.map(l=>l===""?a:[a,l].join("/"))),r&&o.push(...s),o.map(l=>t.startsWith("/")&&l===""?"/":l)}function Fee(t){t.sort((e,n)=>e.score!==n.score?n.score-e.score:Wee(e.routesMeta.map(i=>i.childrenIndex),n.routesMeta.map(i=>i.childrenIndex)))}var zee=/^:[\w-]+$/,jee=3,Bee=2,Hee=1,Vee=10,Gee=-2,h4=t=>t==="*";function $ee(t,e){let n=t.split("/"),i=n.length;return n.some(h4)&&(i+=Gee),e&&(i+=Bee),n.filter(r=>!h4(r)).reduce((r,a)=>r+(zee.test(a)?jee:a===""?Hee:Vee),i)}function Wee(t,e){return t.length===e.length&&t.slice(0,-1).every((i,r)=>i===e[r])?t[t.length-1]-e[e.length-1]:0}function qee(t,e,n=!1){let{routesMeta:i}=t,r={},a="/",s=[];for(let o=0;o{if(d==="*"){let _=o[p]||"";s=a.slice(0,a.length-_.length).replace(/(.)\/+$/,"$1")}const m=o[p];return f&&!m?c[d]=void 0:c[d]=(m||"").replace(/%2F/g,"/"),c},{}),pathname:a,pathnameBase:s,pattern:t}}function Xee(t,e=!1,n=!0){go(t==="*"||!t.endsWith("*")||t.endsWith("/*"),`Route path "${t}" will be treated as if it were "${t.replace(/\*$/,"/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${t.replace(/\*$/,"/*")}".`);let i=[],r="^"+t.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(s,o,l)=>(i.push({paramName:o,isOptional:l!=null}),l?"/?([^\\/]+)?":"/([^\\/]+)")).replace(/\/([\w-]+)\?(\/|$)/g,"(/$1)?$2");return t.endsWith("*")?(i.push({paramName:"*"}),r+=t==="*"||t==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):n?r+="\\/*$":t!==""&&t!=="/"&&(r+="(?:(?=\\/|$))"),[new RegExp(r,e?void 0:"i"),i]}function Kee(t){try{return t.split("/").map(e=>decodeURIComponent(e).replace(/\//g,"%2F")).join("/")}catch(e){return go(!1,`The URL path "${t}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${e}).`),t}}function nc(t,e){if(e==="/")return t;if(!t.toLowerCase().startsWith(e.toLowerCase()))return null;let n=e.endsWith("/")?e.length-1:e.length,i=t.charAt(n);return i&&i!=="/"?null:t.slice(n)||"/"}var Yee=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,Zee=t=>Yee.test(t);function Qee(t,e="/"){let{pathname:n,search:i="",hash:r=""}=typeof t=="string"?em(t):t,a;if(n)if(Zee(n))a=n;else{if(n.includes("//")){let s=n;n=n.replace(/\/\/+/g,"/"),go(!1,`Pathnames cannot have embedded double slashes - normalizing ${s} -> ${n}`)}n.startsWith("/")?a=p4(n.substring(1),"/"):a=p4(n,e)}else a=e;return{pathname:a,search:tte(i),hash:nte(r)}}function p4(t,e){let n=e.replace(/\/+$/,"").split("/");return t.split("/").forEach(r=>{r===".."?n.length>1&&n.pop():r!=="."&&n.push(r)}),n.length>1?n.join("/"):"/"}function XE(t,e,n,i){return`Cannot include a '${t}' character in a manually specified \`to.${e}\` field [${JSON.stringify(i)}]. Please separate it out to the \`to.${n}\` field. Alternatively you may provide the full path as a string in and the router will parse it for you.`}function Jee(t){return t.filter((e,n)=>n===0||e.route.path&&e.route.path.length>0)}function QV(t){let e=Jee(t);return e.map((n,i)=>i===e.length-1?n.pathname:n.pathnameBase)}function JV(t,e,n,i=!1){let r;typeof t=="string"?r=em(t):(r={...t},hi(!r.pathname||!r.pathname.includes("?"),XE("?","pathname","search",r)),hi(!r.pathname||!r.pathname.includes("#"),XE("#","pathname","hash",r)),hi(!r.search||!r.search.includes("#"),XE("#","search","hash",r)));let a=t===""||r.pathname==="",s=a?"/":r.pathname,o;if(s==null)o=n;else{let f=e.length-1;if(!i&&s.startsWith("..")){let p=s.split("/");for(;p[0]==="..";)p.shift(),f-=1;r.pathname=p.join("/")}o=f>=0?e[f]:"/"}let l=Qee(r,o),c=s&&s!=="/"&&s.endsWith("/"),d=(a||s===".")&&n.endsWith("/");return!l.pathname.endsWith("/")&&(c||d)&&(l.pathname+="/"),l}var Xl=t=>t.join("/").replace(/\/\/+/g,"/"),ete=t=>t.replace(/\/+$/,"").replace(/^\/*/,"/"),tte=t=>!t||t==="?"?"":t.startsWith("?")?t:"?"+t,nte=t=>!t||t==="#"?"":t.startsWith("#")?t:"#"+t;function ite(t){return t!=null&&typeof t.status=="number"&&typeof t.statusText=="string"&&typeof t.internal=="boolean"&&"data"in t}Object.getOwnPropertyNames(Object.prototype).sort().join("\0");var eG=["POST","PUT","PATCH","DELETE"];new Set(eG);var rte=["GET",...eG];new Set(rte);var tm=T.createContext(null);tm.displayName="DataRouter";var fS=T.createContext(null);fS.displayName="DataRouterState";T.createContext(!1);var tG=T.createContext({isTransitioning:!1});tG.displayName="ViewTransition";var ate=T.createContext(new Map);ate.displayName="Fetchers";var ste=T.createContext(null);ste.displayName="Await";var Zo=T.createContext(null);Zo.displayName="Navigation";var ax=T.createContext(null);ax.displayName="Location";var Qo=T.createContext({outlet:null,matches:[],isDataRoute:!1});Qo.displayName="Route";var I3=T.createContext(null);I3.displayName="RouteError";function ote(t,{relative:e}={}){hi(sx(),"useHref() may be used only in the context of a component.");let{basename:n,navigator:i}=T.useContext(Zo),{hash:r,pathname:a,search:s}=ox(t,{relative:e}),o=a;return n!=="/"&&(o=a==="/"?n:Xl([n,a])),i.createHref({pathname:o,search:s,hash:r})}function sx(){return T.useContext(ax)!=null}function Xu(){return hi(sx(),"useLocation() may be used only in the context of a component."),T.useContext(ax).location}var nG="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function iG(t){T.useContext(Zo).static||T.useLayoutEffect(t)}function rG(){let{isDataRoute:t}=T.useContext(Qo);return t?Ste():lte()}function lte(){hi(sx(),"useNavigate() may be used only in the context of a component.");let t=T.useContext(tm),{basename:e,navigator:n}=T.useContext(Zo),{matches:i}=T.useContext(Qo),{pathname:r}=Xu(),a=JSON.stringify(QV(i)),s=T.useRef(!1);return iG(()=>{s.current=!0}),T.useCallback((l,c={})=>{if(go(s.current,nG),!s.current)return;if(typeof l=="number"){n.go(l);return}let d=JV(l,JSON.parse(a),r,c.relative==="path");t==null&&e!=="/"&&(d.pathname=d.pathname==="/"?e:Xl([e,d.pathname])),(c.replace?n.replace:n.push)(d,c.state,c)},[e,n,a,r,t])}var cte=T.createContext(null);function ute(t){let e=T.useContext(Qo).outlet;return T.useMemo(()=>e&&T.createElement(cte.Provider,{value:t},e),[e,t])}function ox(t,{relative:e}={}){let{matches:n}=T.useContext(Qo),{pathname:i}=Xu(),r=JSON.stringify(QV(n));return T.useMemo(()=>JV(t,JSON.parse(r),i,e==="path"),[t,r,i,e])}function dte(t,e){return aG(t,e)}function aG(t,e,n,i,r){hi(sx(),"useRoutes() may be used only in the context of a component.");let{navigator:a}=T.useContext(Zo),{matches:s}=T.useContext(Qo),o=s[s.length-1],l=o?o.params:{},c=o?o.pathname:"/",d=o?o.pathnameBase:"/",f=o&&o.route;{let M=f&&f.path||"";sG(c,!f||M.endsWith("*")||M.endsWith("*?"),`You rendered descendant (or called \`useRoutes()\`) at "${c}" (under ) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render. -Please change the parent to .`)}let p=qu(),m;if(e){let M=typeof e=="string"?Jp(e):e;hi(d==="/"||M.pathname?.startsWith(d),`When overriding the location using \`\` or \`useRoutes(routes, location)\`, the location pathname must begin with the portion of the URL pathname that was matched by all parent routes. The current pathname base is "${d}" but pathname "${M.pathname}" was given in the \`location\` prop.`),m=M}else m=p;let y=m.pathname||"/",b=y;if(d!=="/"){let M=d.replace(/^\//,"").split("/");b="/"+y.replace(/^\//,"").split("/").slice(M.length).join("/")}let x=WV(t,{pathname:b});go(f||x!=null,`No routes matched location "${m.pathname}${m.search}${m.hash}" `),go(x==null||x[x.length-1].route.element!==void 0||x[x.length-1].route.Component!==void 0||x[x.length-1].route.lazy!==void 0,`Matched leaf route at location "${m.pathname}${m.search}${m.hash}" does not have an element or Component. This means it will render an with a null value by default resulting in an "empty" page.`);let g=dte(x&&x.map(M=>Object.assign({},M,{params:Object.assign({},l,M.params),pathname:Xl([d,a.encodeLocation?a.encodeLocation(M.pathname.replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:M.pathname]),pathnameBase:M.pathnameBase==="/"?d:Xl([d,a.encodeLocation?a.encodeLocation(M.pathnameBase.replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:M.pathnameBase])})),s,n,i,r);return e&&g?T.createElement(av.Provider,{value:{location:{pathname:"/",search:"",hash:"",state:null,key:"default",...m},navigationType:"POP"}},g):g}function ote(){let t=gte(),e=Qee(t)?`${t.status} ${t.statusText}`:t instanceof Error?t.message:JSON.stringify(t),n=t instanceof Error?t.stack:null,i="rgba(200,200,200, 0.5)",r={padding:"0.5rem",backgroundColor:i},a={padding:"2px 4px",backgroundColor:i},s=null;return console.error("Error handled by React Router default ErrorBoundary:",t),s=T.createElement(T.Fragment,null,T.createElement("p",null,"💿 Hey developer 👋"),T.createElement("p",null,"You can provide a way better UX than this when your app throws errors by providing your own ",T.createElement("code",{style:a},"ErrorBoundary")," or"," ",T.createElement("code",{style:a},"errorElement")," prop on your route.")),T.createElement(T.Fragment,null,T.createElement("h2",null,"Unexpected Application Error!"),T.createElement("h3",{style:{fontStyle:"italic"}},e),n?T.createElement("pre",{style:r},n):null,s)}var lte=T.createElement(ote,null),cte=class extends T.Component{constructor(t){super(t),this.state={location:t.location,revalidation:t.revalidation,error:t.error}}static getDerivedStateFromError(t){return{error:t}}static getDerivedStateFromProps(t,e){return e.location!==t.location||e.revalidation!=="idle"&&t.revalidation==="idle"?{error:t.error,location:t.location,revalidation:t.revalidation}:{error:t.error!==void 0?t.error:e.error,location:e.location,revalidation:t.revalidation||e.revalidation}}componentDidCatch(t,e){this.props.onError?this.props.onError(t,e):console.error("React Router caught the following error during render",t)}render(){return this.state.error!==void 0?T.createElement(Zo.Provider,{value:this.props.routeContext},T.createElement(ON.Provider,{value:this.state.error,children:this.props.component})):this.props.children}};function ute({routeContext:t,match:e,children:n}){let i=T.useContext(em);return i&&i.static&&i.staticContext&&(e.route.errorElement||e.route.ErrorBoundary)&&(i.staticContext._deepestRenderedBoundaryId=e.route.id),T.createElement(Zo.Provider,{value:t},n)}function dte(t,e=[],n=null,i=null,r=null){if(t==null){if(!n)return null;if(n.errors)t=n.matches;else if(e.length===0&&!n.initialized&&n.matches.length>0)t=n.matches;else return null}let a=t,s=n?.errors;if(s!=null){let d=a.findIndex(f=>f.route.id&&s?.[f.route.id]!==void 0);hi(d>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(s).join(",")}`),a=a.slice(0,Math.min(a.length,d+1))}let o=!1,l=-1;if(n)for(let d=0;d=0?a=a.slice(0,l+1):a=[a[0]];break}}}let c=n&&i?(d,f)=>{i(d,{location:n.location,params:n.matches?.[0]?.params??{},errorInfo:f})}:void 0;return a.reduceRight((d,f,p)=>{let m,y=!1,b=null,x=null;n&&(m=s&&f.route.id?s[f.route.id]:void 0,b=f.route.errorElement||lte,o&&(l<0&&p===0?(iG("route-fallback",!1,"No `HydrateFallback` element provided to render during initial hydration"),y=!0,x=null):l===p&&(y=!0,x=f.route.hydrateFallbackElement||null)));let g=e.concat(a.slice(0,p+1)),M=()=>{let w;return m?w=b:y?w=x:f.route.Component?w=T.createElement(f.route.Component,null):f.route.element?w=f.route.element:w=d,T.createElement(ute,{match:f,routeContext:{outlet:d,matches:g,isDataRoute:n!=null},children:w})};return n&&(f.route.ErrorBoundary||f.route.errorElement||p===0)?T.createElement(cte,{location:n.location,revalidation:n.revalidation,component:b,error:m,children:M(),routeContext:{outlet:null,matches:g,isDataRoute:!0},onError:c}):M()},null)}function kN(t){return`${t} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function fte(t){let e=T.useContext(em);return hi(e,kN(t)),e}function hte(t){let e=T.useContext(cS);return hi(e,kN(t)),e}function pte(t){let e=T.useContext(Zo);return hi(e,kN(t)),e}function LN(t){let e=pte(t),n=e.matches[e.matches.length-1];return hi(n.route.id,`${t} can only be used on routes that contain a unique "id"`),n.route.id}function mte(){return LN("useRouteId")}function gte(){let t=T.useContext(ON),e=hte("useRouteError"),n=LN("useRouteError");return t!==void 0?t:e.errors?.[n]}function vte(){let{router:t}=fte("useNavigate"),e=LN("useNavigate"),n=T.useRef(!1);return eG(()=>{n.current=!0}),T.useCallback(async(r,a={})=>{go(n.current,JV),n.current&&(typeof r=="number"?t.navigate(r):await t.navigate(r,{fromRouteId:e,...a}))},[t,e])}var f4={};function iG(t,e,n){!e&&!f4[t]&&(f4[t]=!0,go(!1,n))}T.memo(xte);function xte({routes:t,future:e,state:n,unstable_onError:i}){return nG(t,void 0,n,i,e)}function yte(t){return ate(t.context)}function au(t){hi(!1,"A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .")}function bte({basename:t="/",children:e=null,location:n,navigationType:i="POP",navigator:r,static:a=!1}){hi(!sv(),"You cannot render a inside another . You should never have more than one in your app.");let s=t.replace(/^\/*/,"/"),o=T.useMemo(()=>({basename:s,navigator:r,static:a,future:{}}),[s,r,a]);typeof n=="string"&&(n=Jp(n));let{pathname:l="/",search:c="",hash:d="",state:f=null,key:p="default"}=n,m=T.useMemo(()=>{let y=nc(l,s);return y==null?null:{location:{pathname:y,search:c,hash:d,state:f,key:p},navigationType:i}},[s,l,c,d,f,p,i]);return go(m!=null,` is not able to match the URL "${l}${c}${d}" because it does not start with the basename, so the won't render anything.`),m==null?null:T.createElement(Yo.Provider,{value:o},T.createElement(av.Provider,{children:e,value:m}))}function _te({children:t,location:e}){return ste(N2(t),e)}function N2(t,e=[]){let n=[];return T.Children.forEach(t,(i,r)=>{if(!T.isValidElement(i))return;let a=[...e,r];if(i.type===T.Fragment){n.push.apply(n,N2(i.props.children,a));return}hi(i.type===au,`[${typeof i.type=="string"?i.type:i.type.name}] is not a component. All component children of must be a or `),hi(!i.props.index||!i.props.children,"An index route cannot have child routes.");let s={id:i.props.id||a.join("-"),caseSensitive:i.props.caseSensitive,element:i.props.element,Component:i.props.Component,index:i.props.index,path:i.props.path,middleware:i.props.middleware,loader:i.props.loader,action:i.props.action,hydrateFallbackElement:i.props.hydrateFallbackElement,HydrateFallback:i.props.HydrateFallback,errorElement:i.props.errorElement,ErrorBoundary:i.props.ErrorBoundary,hasErrorBoundary:i.props.hasErrorBoundary===!0||i.props.ErrorBoundary!=null||i.props.errorElement!=null,shouldRevalidate:i.props.shouldRevalidate,handle:i.props.handle,lazy:i.props.lazy};i.props.children&&(s.children=N2(i.props.children,a)),n.push(s)}),n}var Ob="get",kb="application/x-www-form-urlencoded";function uS(t){return t!=null&&typeof t.tagName=="string"}function Ste(t){return uS(t)&&t.tagName.toLowerCase()==="button"}function wte(t){return uS(t)&&t.tagName.toLowerCase()==="form"}function Mte(t){return uS(t)&&t.tagName.toLowerCase()==="input"}function Ete(t){return!!(t.metaKey||t.altKey||t.ctrlKey||t.shiftKey)}function Ate(t,e){return t.button===0&&(!e||e==="_self")&&!Ete(t)}var ty=null;function Tte(){if(ty===null)try{new FormData(document.createElement("form"),0),ty=!1}catch{ty=!0}return ty}var Cte=new Set(["application/x-www-form-urlencoded","multipart/form-data","text/plain"]);function qE(t){return t!=null&&!Cte.has(t)?(go(!1,`"${t}" is not a valid \`encType\` for \`
\`/\`\` and will default to "${kb}"`),null):t}function Rte(t,e){let n,i,r,a,s;if(wte(t)){let o=t.getAttribute("action");i=o?nc(o,e):null,n=t.getAttribute("method")||Ob,r=qE(t.getAttribute("enctype"))||kb,a=new FormData(t)}else if(Ste(t)||Mte(t)&&(t.type==="submit"||t.type==="image")){let o=t.form;if(o==null)throw new Error('Cannot submit a