diff --git a/.gitignore b/.gitignore index aeb91a2..29de579 100644 --- a/.gitignore +++ b/.gitignore @@ -73,4 +73,6 @@ dmypy.json # Pyre .pyre/ -!fastapi_radar/dashboard/dist/ \ No newline at end of file +!fastapi_radar/dashboard/dist/ +radar.duckdb.wal +radar.duckdb diff --git a/README.md b/README.md index a027e28..75f15ee 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,7 @@ Access your dashboard at: **http://localhost:8000/\_\_radar/** radar = Radar( app, db_engine=engine, # Optional: SQLAlchemy engine for SQL query monitoring + storage_engine=None, # Optional: custom SQLAlchemy engine for Radar's own storage dashboard_path="/__radar", # Custom dashboard path (default: "/__radar") max_requests=1000, # Max requests to store (default: 1000) retention_hours=24, # Data retention period (default: 24) @@ -201,6 +202,47 @@ radar = Radar(app, db_path="./data") If the specified path cannot be created, FastAPI Radar will fallback to using the current directory with a warning. +### Custom Storage Engine + +For full control over where Radar's monitoring data is stored, pass any SQLAlchemy-compatible engine via the `storage_engine` parameter. This lets you store Radar data in PostgreSQL, MySQL, SQLite, or any other supported database instead of the default DuckDB file. + +```python +from fastapi import FastAPI +from fastapi_radar import Radar +from sqlalchemy import create_engine + +app = FastAPI() + +# Store Radar data in PostgreSQL +radar_storage = create_engine("postgresql+psycopg2://user:password@localhost/radar_db") +radar = Radar(app, storage_engine=radar_storage) +radar.create_tables() +``` + +```python +# Store Radar data in MySQL +radar_storage = create_engine("mysql+pymysql://user:password@localhost/radar_db") +radar = Radar(app, storage_engine=radar_storage) +radar.create_tables() +``` + +```python +# Async engine (e.g. asyncpg) is supported too +from sqlalchemy.ext.asyncio import create_async_engine + +radar_storage = create_async_engine("postgresql+asyncpg://user:password@localhost/radar_db") +radar = Radar(app, storage_engine=radar_storage) +radar.create_tables() +``` + +You can also set the `RADAR_STORAGE_URL` environment variable to any SQLAlchemy URL and Radar will use it automatically without any code changes: + +```bash +export RADAR_STORAGE_URL="postgresql+psycopg2://user:password@localhost/radar_db" +``` + +The `storage_engine` parameter takes precedence over `RADAR_STORAGE_URL`, which in turn takes precedence over `db_path`. + ### Development Mode with Auto-Reload When running your FastAPI application with `fastapi dev` (which uses auto-reload), FastAPI Radar automatically switches to an in-memory database to avoid file locking issues. This means: @@ -218,6 +260,43 @@ radar.create_tables() # Safe to call - handles multiple processes gracefully This behavior only applies when using the development server with auto-reload (`fastapi dev`). In production or when using `fastapi run`, the standard file-based DuckDB storage is used. +## Capturing Logs + +FastAPI Radar can capture Python log records and display them in the dashboard. Call `attach_logger()` after creating the `Radar` instance to register a logging handler. + +### Attach to the root logger (captures everything) + +```python +import logging +from fastapi import FastAPI +from fastapi_radar import Radar + +app = FastAPI() +radar = Radar(app) +radar.create_tables() + +# Capture all log records at DEBUG level and above +radar.attach_logger() +``` + +### Attach to a specific logger + +```python +import logging +from fastapi import FastAPI +from fastapi_radar import Radar + +app = FastAPI() +radar = Radar(app) +radar.create_tables() + +# Only capture records from your application's logger +app_logger = logging.getLogger("myapp") +radar.attach_logger(logger=app_logger, level=logging.WARNING) +``` + +`attach_logger()` returns the `logging.Handler` instance in case you need to remove it later. The `level` parameter filters which records are stored (default: `logging.DEBUG`). + ## What Gets Captured? - ✅ HTTP requests and responses @@ -227,6 +306,7 @@ This behavior only applies when using the development server with auto-reload (` - ✅ Slow query detection - ✅ Exceptions with stack traces - ✅ Request/response bodies and headers +- ✅ Python log records (via `attach_logger()`) ## Contributing diff --git a/example_app.py b/example_app.py index ad47027..95a295a 100644 --- a/example_app.py +++ b/example_app.py @@ -1,19 +1,23 @@ """Example FastAPI application with Radar integration.""" -from typing import List, Optional from datetime import datetime -from fastapi import FastAPI, Depends, HTTPException, Query +from typing import List, Optional + +from fastapi import Depends, FastAPI, HTTPException, Query from pydantic import BaseModel -from sqlalchemy import create_engine, Column, Integer, String, Float, DateTime, Boolean +from sqlalchemy import Boolean, Column, DateTime, Float, Integer, String, create_engine try: from sqlalchemy.orm import declarative_base except ImportError: from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import sessionmaker, Session -from fastapi_radar import Radar, track_background_task +import logging + from fastapi import BackgroundTasks +from sqlalchemy.orm import Session, sessionmaker + +from fastapi_radar import Radar, track_background_task # Database setup engine = create_engine("sqlite:///./example.db", connect_args={"check_same_thread": False}) @@ -22,6 +26,8 @@ # Models +logger = logging.getLogger(__name__) + class Product(Base): __tablename__ = "products" @@ -120,6 +126,7 @@ class Config: theme="auto", # auth_dependency=verify_radar_credentials, # Uncomment to enable authentication ) +radar.attach_logger(logger, level=logging.INFO) # Capture INFO and above logs radar.create_tables() # Dependency @@ -139,6 +146,7 @@ def get_db(): @app.get("/") async def root(): """Root endpoint.""" + logger.info("Root endpoint called") return { "message": "Welcome to the Example API", "dashboard": "Visit /__radar to see the debugging dashboard", @@ -153,6 +161,7 @@ async def list_products( db: Session = Depends(get_db), ): """List all products with pagination.""" + logger.info("Listing products: skip=%d, limit=%d, in_stock_only=%s", skip, limit, in_stock_only) query = db.query(Product) if in_stock_only: @@ -165,9 +174,11 @@ async def list_products( @app.get("/products/{product_id}", response_model=ProductResponse) async def get_product(product_id: int, db: Session = Depends(get_db)): """Get a specific product by ID.""" + logger.info("Fetching product id=%d", product_id) product = db.query(Product).filter(Product.id == product_id).first() if not product: + logger.warning("Product id=%d not found", product_id) raise HTTPException(status_code=404, detail="Product not found") return product @@ -176,19 +187,23 @@ async def get_product(product_id: int, db: Session = Depends(get_db)): @app.post("/products", response_model=ProductResponse, status_code=201) async def create_product(product: ProductCreate, db: Session = Depends(get_db)): """Create a new product.""" + logger.info("Creating product: name=%s", product.name) db_product = Product(**product.dict()) db.add(db_product) db.commit() db.refresh(db_product) + logger.info("Created product id=%d", db_product.id) return db_product @app.put("/products/{product_id}", response_model=ProductResponse) async def update_product(product_id: int, product: ProductCreate, db: Session = Depends(get_db)): """Update an existing product.""" + logger.info("Updating product id=%d", product_id) db_product = db.query(Product).filter(Product.id == product_id).first() if not db_product: + logger.warning("Product id=%d not found for update", product_id) raise HTTPException(status_code=404, detail="Product not found") for key, value in product.dict().items(): @@ -196,19 +211,23 @@ async def update_product(product_id: int, product: ProductCreate, db: Session = db.commit() db.refresh(db_product) + logger.info("Updated product id=%d", product_id) return db_product @app.delete("/products/{product_id}") async def delete_product(product_id: int, db: Session = Depends(get_db)): """Delete a product.""" + logger.info("Deleting product id=%d", product_id) db_product = db.query(Product).filter(Product.id == product_id).first() if not db_product: + logger.warning("Product id=%d not found for deletion", product_id) raise HTTPException(status_code=404, detail="Product not found") db.delete(db_product) db.commit() + logger.info("Deleted product id=%d", product_id) return {"message": "Product deleted successfully"} @@ -219,6 +238,7 @@ async def list_users( db: Session = Depends(get_db), ): """List all users with pagination.""" + logger.info("Listing users: skip=%d, limit=%d", skip, limit) users = db.query(User).offset(skip).limit(limit).all() return users @@ -226,9 +246,11 @@ async def list_users( @app.get("/users/{user_id}", response_model=UserResponse) async def get_user(user_id: int, db: Session = Depends(get_db)): """Get a specific user by ID.""" + logger.info("Fetching user id=%d", user_id) user = db.query(User).filter(User.id == user_id).first() if not user: + logger.warning("User id=%d not found", user_id) raise HTTPException(status_code=404, detail="User not found") return user @@ -237,12 +259,15 @@ async def get_user(user_id: int, db: Session = Depends(get_db)): @app.post("/users", response_model=UserResponse, status_code=201) async def create_user(user: UserCreate, db: Session = Depends(get_db)): """Create a new user.""" + logger.info("Creating user: username=%s", user.username) # Check for existing user existing_user = ( db.query(User).filter((User.username == user.username) | (User.email == user.email)).first() ) if existing_user: + logger.warning("User creation failed: username=%s or email=%s already exists", + user.username, user.email) raise HTTPException( status_code=400, detail="User with this username or email already exists" ) @@ -251,12 +276,14 @@ async def create_user(user: UserCreate, db: Session = Depends(get_db)): db.add(db_user) db.commit() db.refresh(db_user) + logger.info("Created user id=%d", db_user.id) return db_user @app.get("/slow-query") async def slow_query_example(db: Session = Depends(get_db)): """Example endpoint that performs a slow query.""" + logger.info("Slow query endpoint called") # This query will be highlighted as slow in Radar import time @@ -277,6 +304,7 @@ async def slow_query_example(db: Session = Depends(get_db)): @app.get("/error") async def trigger_error(): """Example endpoint that raises an exception.""" + logger.error("Error endpoint called") # This will be captured in the Exceptions tab raise ValueError("This is an example error for demonstration purposes") @@ -329,6 +357,7 @@ async def failing_task(): @app.post("/send-email") async def send_email(email: str, subject: str, background_tasks: BackgroundTasks): """Example endpoint that triggers a background task.""" + logger.info("Scheduling send_email_task for email=%s", email) background_tasks.add_task(send_email_task, email, subject) return {"message": "Email will be sent in the background"} @@ -336,6 +365,7 @@ async def send_email(email: str, subject: str, background_tasks: BackgroundTasks @app.post("/process-report/{user_id}") async def process_user_report(user_id: int, background_tasks: BackgroundTasks): """Example endpoint that triggers a long-running background task.""" + logger.info("Scheduling process_report for user_id=%d", user_id) background_tasks.add_task(process_report, user_id) return {"message": "Report processing started"} @@ -345,6 +375,7 @@ async def generate_analytics_endpoint( background_tasks: BackgroundTasks, days: int = Query(7, ge=1, le=365) ): """Generate analytics for the specified number of days.""" + logger.info("Scheduling generate_analytics for days=%d", days) background_tasks.add_task(generate_analytics, days) return {"message": f"Analytics generation started for last {days} days"} @@ -352,6 +383,7 @@ async def generate_analytics_endpoint( @app.post("/sync-inventory") async def sync_inventory(background_tasks: BackgroundTasks): """Synchronize inventory (sync task example).""" + logger.info("Scheduling sync_inventory_task") background_tasks.add_task(sync_inventory_task) return {"message": "Inventory sync started"} @@ -359,6 +391,7 @@ async def sync_inventory(background_tasks: BackgroundTasks): @app.post("/test-failure") async def test_task_failure(background_tasks: BackgroundTasks): """Test a failing background task.""" + logger.info("Scheduling failing_task") background_tasks.add_task(failing_task) return {"message": "Failing task started (check background tasks page)"} @@ -366,13 +399,15 @@ async def test_task_failure(background_tasks: BackgroundTasks): @app.get("/health") async def health_check(): """Health check endpoint (excluded from Radar by default).""" + logger.debug("Health check called") return {"status": "healthy"} if __name__ == "__main__": - import uvicorn from pathlib import Path + import uvicorn + # Check if dashboard is built dashboard_dist = Path(__file__).parent / "fastapi_radar" / "dashboard" / "dist" if not dashboard_dist.exists(): diff --git a/fastapi_radar/__init__.py b/fastapi_radar/__init__.py index 80969c4..e8e4fe9 100644 --- a/fastapi_radar/__init__.py +++ b/fastapi_radar/__init__.py @@ -1,7 +1,8 @@ """FastAPI Radar - Debugging dashboard for FastAPI applications.""" from .background import track_background_task +from .log_handler import RadarLoggingHandler from .radar import Radar -__version__ = "0.3.4" -__all__ = ["Radar", "track_background_task"] +__version__ = "0.4.0" +__all__ = ["Radar", "RadarLoggingHandler", "track_background_task"] diff --git a/fastapi_radar/api.py b/fastapi_radar/api.py index 1f7fcf5..e8ab466 100644 --- a/fastapi_radar/api.py +++ b/fastapi_radar/api.py @@ -13,11 +13,13 @@ from .models import ( BackgroundTask, CapturedException, + CapturedLog, CapturedQuery, CapturedRequest, Span, Trace, ) +from .timeseries import build_timeseries from .tracing import TracingManager @@ -79,6 +81,20 @@ class ExceptionDetail(BaseModel): created_at: datetime +class RequestCounts(BaseModel): + total: int + successful: int + failed: int + slow: int + + +class RequestTimeseriesPoint(BaseModel): + time: str + iso_time: str + requests: int + errors: int + + class DashboardStats(BaseModel): total_requests: int avg_response_time: Optional[float] @@ -114,6 +130,20 @@ class BackgroundTaskSummary(BaseModel): created_at: datetime +class LogRecord(BaseModel): + id: int + logger_name: Optional[str] + level: str + message: str + pathname: Optional[str] + lineno: Optional[int] + func_name: Optional[str] + thread_name: Optional[str] + exc_info: Optional[str] + request_id: Optional[str] + created_at: datetime + + class WaterfallSpan(BaseModel): span_id: str parent_span_id: Optional[str] @@ -142,13 +172,17 @@ class TraceDetail(BaseModel): spans: List[WaterfallSpan] -def create_api_router(get_session_context, auth_dependency: Optional[Callable] = None) -> APIRouter: +def create_api_router( + get_session_context, + auth_dependency: Optional[Callable] = None, + prefix: str = "/__radar/api", +) -> APIRouter: # Build dependencies list for the router dependencies = [] if auth_dependency: dependencies.append(Depends(auth_dependency)) - router = APIRouter(prefix="/__radar/api", tags=["radar"], dependencies=dependencies) + router = APIRouter(prefix=prefix, tags=["radar"], dependencies=dependencies) def get_db(): """Dependency function for FastAPI to get database session.""" @@ -156,14 +190,17 @@ def get_db(): yield session @router.get("/requests", response_model=List[RequestSummary]) - async def get_requests( + def get_requests( limit: int = Query(100, ge=1, le=1000), offset: int = Query(0, ge=0), status_code: Optional[int] = None, + min_status_code: Optional[int] = None, method: Optional[str] = None, search: Optional[str] = None, start_time: Optional[datetime] = None, end_time: Optional[datetime] = None, + slow_only: bool = Query(False), + slow_threshold: int = Query(500), session: Session = Depends(get_db), ): query = session.query(CapturedRequest) @@ -184,6 +221,10 @@ async def get_requests( else: # Exact status code match query = query.filter(CapturedRequest.status_code == status_code) + if min_status_code: + query = query.filter(CapturedRequest.status_code >= min_status_code) + if slow_only: + query = query.filter(CapturedRequest.duration_ms > slow_threshold) if method: query = query.filter(CapturedRequest.method == method) if search: @@ -208,8 +249,65 @@ async def get_requests( for req in requests ] + @router.get("/requests/counts", response_model=RequestCounts) + def get_request_counts( + status_code: Optional[int] = None, + method: Optional[str] = None, + search: Optional[str] = None, + start_time: Optional[datetime] = None, + end_time: Optional[datetime] = None, + slow_threshold: int = Query(500), + session: Session = Depends(get_db), + ): + query = session.query( + func.count().label("total"), + func.sum(case((CapturedRequest.status_code.between(200, 299), 1), else_=0)).label( + "successful" + ), + func.sum(case((CapturedRequest.status_code >= 400, 1), else_=0)).label("failed"), + func.sum(case((CapturedRequest.duration_ms > slow_threshold, 1), else_=0)).label( + "slow" + ), + ) + + if start_time: + query = query.filter(CapturedRequest.created_at >= start_time) + if end_time: + query = query.filter(CapturedRequest.created_at <= end_time) + if status_code: + if status_code in [200, 300, 400, 500]: + lower_bound = status_code + upper_bound = status_code + 100 + query = query.filter( + CapturedRequest.status_code >= lower_bound, + CapturedRequest.status_code < upper_bound, + ) + else: + query = query.filter(CapturedRequest.status_code == status_code) + if method: + query = query.filter(CapturedRequest.method == method) + if search: + query = query.filter(CapturedRequest.path.ilike(f"%{search}%")) + + result = query.one() + return RequestCounts( + total=result.total or 0, + successful=result.successful or 0, + failed=result.failed or 0, + slow=result.slow or 0, + ) + + @router.get("/requests/timeseries", response_model=List[RequestTimeseriesPoint]) + def get_requests_timeseries( + hours: int = Query(24, ge=1, le=87600), + start_time: Optional[datetime] = None, + end_time: Optional[datetime] = None, + session: Session = Depends(get_db), + ): + return build_timeseries(session, hours=hours, start_time=start_time, end_time=end_time) + @router.get("/requests/{request_id}", response_model=RequestDetail) - async def get_request_detail(request_id: str, session: Session = Depends(get_db)): + def get_request_detail(request_id: str, session: Session = Depends(get_db)): request = ( session.query(CapturedRequest).filter(CapturedRequest.request_id == request_id).first() ) @@ -257,7 +355,7 @@ async def get_request_detail(request_id: str, session: Session = Depends(get_db) ) @router.get("/requests/{request_id}/curl") - async def get_request_as_curl(request_id: str, session: Session = Depends(get_db)): + def get_request_as_curl(request_id: str, session: Session = Depends(get_db)): request = ( session.query(CapturedRequest).filter(CapturedRequest.request_id == request_id).first() ) @@ -339,8 +437,8 @@ async def replay_request( replayed_request = CapturedRequest( request_id=str(uuid.uuid4()), method=request.method, - url=request.url, - path=request.path, + url=request.url[:500], + path=request.path[:500], query_params=request.query_params, headers=dict(response.request.headers), body=request_body if isinstance(request_body, str) else None, @@ -367,7 +465,7 @@ async def replay_request( raise HTTPException(status_code=500, detail=f"Replay failed: {str(e)}") @router.get("/queries", response_model=List[QueryDetail]) - async def get_queries( + def get_queries( limit: int = Query(100, ge=1, le=1000), offset: int = Query(0, ge=0), slow_only: bool = Query(False), @@ -398,8 +496,19 @@ async def get_queries( for q in queries ] + @router.get("/exceptions/count") + def get_exceptions_count( + exception_type: Optional[str] = None, + session: Session = Depends(get_db), + ): + query = session.query(func.count()).select_from(CapturedException) + if exception_type: + query = query.filter(CapturedException.exception_type == exception_type) + + return {"count": query.scalar()} + @router.get("/exceptions", response_model=List[ExceptionDetail]) - async def get_exceptions( + def get_exceptions( limit: int = Query(100, ge=1, le=1000), offset: int = Query(0, ge=0), exception_type: Optional[str] = None, @@ -427,7 +536,7 @@ async def get_exceptions( ] @router.get("/stats", response_model=DashboardStats) - async def get_stats( + def get_stats( hours: int = Query(1, ge=1, le=720), # Allow up to 30 days slow_threshold: int = Query(100), session: Session = Depends(get_db), @@ -482,15 +591,27 @@ async def get_stats( requests_per_minute=round_float(requests_per_minute), ) + @router.get("/health") + def health_check(): + return {"status": "ok"} + @router.delete("/clear") - async def clear_data( + def clear_data( older_than_hours: Optional[int] = None, session: Session = Depends(get_db) ): if older_than_hours: cutoff = datetime.now(timezone.utc) - timedelta(hours=older_than_hours) session.query(CapturedRequest).filter(CapturedRequest.created_at < cutoff).delete() + session.query(CapturedException).filter(CapturedException.created_at < cutoff).delete() + session.query(CapturedQuery).filter(CapturedQuery.created_at < cutoff).delete() + session.query(CapturedLog).filter(CapturedLog.created_at < cutoff).delete() + session.query(Trace).filter(Trace.created_at < cutoff).delete() else: session.query(CapturedRequest).delete() + session.query(CapturedException).delete() + session.query(CapturedQuery).delete() + session.query(CapturedLog).delete() + session.query(Trace).delete() session.commit() return {"message": "Data cleared successfully"} @@ -498,7 +619,7 @@ async def clear_data( # Tracing-related API endpoints @router.get("/traces", response_model=List[TraceSummary]) - async def get_traces( + def get_traces( limit: int = Query(100, ge=1, le=1000), offset: int = Query(0, ge=0), status: Optional[str] = Query(None), @@ -536,7 +657,7 @@ async def get_traces( ] @router.get("/traces/{trace_id}", response_model=TraceDetail) - async def get_trace_detail( + def get_trace_detail( trace_id: str, session: Session = Depends(get_db), ): @@ -564,7 +685,7 @@ async def get_trace_detail( ) @router.get("/traces/{trace_id}/waterfall") - async def get_trace_waterfall( + def get_trace_waterfall( trace_id: str, session: Session = Depends(get_db), ): @@ -590,7 +711,7 @@ async def get_trace_waterfall( } @router.get("/spans/{span_id}") - async def get_span_detail( + def get_span_detail( span_id: str, session: Session = Depends(get_db), ): @@ -616,7 +737,7 @@ async def get_span_detail( } @router.get("/background-tasks", response_model=List[BackgroundTaskSummary]) - async def get_background_tasks( + def get_background_tasks( limit: int = Query(100, ge=1, le=1000), offset: int = Query(0, ge=0), status: Optional[str] = None, @@ -649,4 +770,73 @@ async def get_background_tasks( for task in tasks ] + @router.get("/logs/count") + def get_logs_count( + level: Optional[str] = None, + logger_name: Optional[str] = None, + search: Optional[str] = None, + start_time: Optional[datetime] = None, + end_time: Optional[datetime] = None, + session: Session = Depends(get_db), + ): + """Get total count of captured log records matching filters.""" + query = session.query(func.count()).select_from(CapturedLog) + + if level: + query = query.filter(CapturedLog.level == level.upper()) + if logger_name: + query = query.filter(CapturedLog.logger_name == logger_name) + if search: + query = query.filter(CapturedLog.message.ilike(f"%{search}%")) + if start_time: + query = query.filter(CapturedLog.created_at >= start_time) + if end_time: + query = query.filter(CapturedLog.created_at <= end_time) + + return {"count": query.scalar()} + + @router.get("/logs", response_model=List[LogRecord]) + def get_logs( + limit: int = Query(100, ge=1, le=1000), + offset: int = Query(0, ge=0), + level: Optional[str] = None, + logger_name: Optional[str] = None, + search: Optional[str] = None, + start_time: Optional[datetime] = None, + end_time: Optional[datetime] = None, + session: Session = Depends(get_db), + ): + """Get captured log records.""" + query = session.query(CapturedLog) + + if level: + query = query.filter(CapturedLog.level == level.upper()) + if logger_name: + query = query.filter(CapturedLog.logger_name == logger_name) + if search: + query = query.filter(CapturedLog.message.ilike(f"%{search}%")) + if start_time: + query = query.filter(CapturedLog.created_at >= start_time) + if end_time: + query = query.filter(CapturedLog.created_at <= end_time) + + logs = query.order_by(desc(CapturedLog.created_at)).offset(offset).limit(limit).all() + + return [ + LogRecord( + id=log.id, + logger_name=log.logger_name, + level=log.level, + message=log.message, + pathname=log.pathname, + lineno=log.lineno, + func_name=log.func_name, + thread_name=log.thread_name, + exc_info=log.exc_info, + request_id=log.request_id, + created_at=log.created_at, + ) + for log in logs + ] + return router diff --git a/fastapi_radar/dashboard/dist/assets/index-Cp3u_acG.css b/fastapi_radar/dashboard/dist/assets/index-Cp3u_acG.css new file mode 100644 index 0000000..8295458 --- /dev/null +++ b/fastapi_radar/dashboard/dist/assets/index-Cp3u_acG.css @@ -0,0 +1 @@ +*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}:root{--background: 0 0% 100%;--foreground: 0 0% 9%;--card: 0 0% 100%;--card-foreground: 0 0% 9%;--popover: 0 0% 100%;--popover-foreground: 0 0% 9%;--primary: 0 0% 9%;--primary-foreground: 0 0% 100%;--secondary: 0 0% 96%;--secondary-foreground: 0 0% 9%;--muted: 0 0% 96%;--muted-foreground: 0 0% 45%;--accent: 0 0% 96%;--accent-foreground: 0 0% 9%;--destructive: 0 84.2% 60.2%;--destructive-foreground: 0 0% 100%;--border: 0 0% 92%;--input: 0 0% 92%;--ring: 0 0% 9%;--radius: .375rem;--chart-1: 0 0% 15%;--chart-2: 0 0% 30%;--chart-3: 0 0% 45%;--chart-4: 0 0% 60%;--chart-5: 0 0% 75%}.dark{--background: 0 0% 3%;--foreground: 0 0% 98%;--card: 0 0% 5%;--card-foreground: 0 0% 98%;--popover: 0 0% 3%;--popover-foreground: 0 0% 98%;--primary: 0 0% 98%;--primary-foreground: 0 0% 3%;--secondary: 0 0% 10%;--secondary-foreground: 0 0% 98%;--muted: 0 0% 10%;--muted-foreground: 0 0% 60%;--accent: 0 0% 10%;--accent-foreground: 0 0% 98%;--destructive: 0 62.8% 30.6%;--destructive-foreground: 0 0% 98%;--border: 0 0% 14%;--input: 0 0% 14%;--ring: 0 0% 98%;--chart-1: 0 0% 85%;--chart-2: 0 0% 70%;--chart-3: 0 0% 55%;--chart-4: 0 0% 40%;--chart-5: 0 0% 25%}*{border-color:hsl(var(--border))}body{background-color:hsl(var(--background));color:hsl(var(--foreground))}.container{width:100%;margin-right:auto;margin-left:auto;padding-right:2rem;padding-left:2rem}@media (min-width: 1400px){.container{max-width:1400px}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.invisible{visibility:hidden}.collapse{visibility:collapse}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bottom:0;left:0}.inset-x-0{left:0;right:0}.inset-y-0{top:0;bottom:0}.-bottom-5{bottom:-1.25rem}.bottom-0{bottom:0}.left-0{left:0}.left-1{left:.25rem}.left-1\/2{left:50%}.left-1\/4{left:25%}.left-2{left:.5rem}.left-2\.5{left:.625rem}.left-3{left:.75rem}.left-3\/4{left:75%}.left-\[50\%\]{left:50%}.right-0{right:0}.right-1{right:.25rem}.right-2{right:.5rem}.right-4{right:1rem}.top-0{top:0}.top-1\/2{top:50%}.top-2\.5{top:.625rem}.top-4{top:1rem}.top-\[50\%\]{top:50%}.z-50{z-index:50}.-mx-1{margin-left:-.25rem;margin-right:-.25rem}.-mx-2{margin-left:-.5rem;margin-right:-.5rem}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-4{margin-top:1rem;margin-bottom:1rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.ml-1{margin-left:.25rem}.ml-1\.5{margin-left:.375rem}.ml-2{margin-left:.5rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-1\.5{margin-right:.375rem}.mr-2{margin-right:.5rem}.mr-4{margin-right:1rem}.mt-0{margin-top:0}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.block{display:block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.aspect-square{aspect-ratio:1 / 1}.h-1{height:.25rem}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-11{height:2.75rem}.h-12{height:3rem}.h-16{height:4rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-\[100px\]{height:100px}.h-\[120px\]{height:120px}.h-\[150px\]{height:150px}.h-\[1px\]{height:1px}.h-\[200px\]{height:200px}.h-\[300px\]{height:300px}.h-\[400px\]{height:400px}.h-\[500px\]{height:500px}.h-\[80vh\]{height:80vh}.h-\[var\(--radix-select-trigger-height\)\]{height:var(--radix-select-trigger-height)}.h-auto{height:auto}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-40{max-height:10rem}.max-h-\[--radix-select-content-available-height\]{max-height:var(--radix-select-content-available-height)}.max-h-\[var\(--radix-dropdown-menu-content-available-height\)\]{max-height:var(--radix-dropdown-menu-content-available-height)}.w-1\/3{width:33.333333%}.w-10{width:2.5rem}.w-12{width:3rem}.w-16{width:4rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-20{width:5rem}.w-24{width:6rem}.w-28{width:7rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-3\/4{width:75%}.w-32{width:8rem}.w-4{width:1rem}.w-40{width:10rem}.w-48{width:12rem}.w-5{width:1.25rem}.w-64{width:16rem}.w-7{width:1.75rem}.w-72{width:18rem}.w-8{width:2rem}.w-9{width:2.25rem}.w-\[150px\]{width:150px}.w-\[160px\]{width:160px}.w-\[190px\]{width:190px}.w-\[1px\]{width:1px}.w-auto{width:auto}.w-full{width:100%}.w-px{width:1px}.min-w-0{min-width:0px}.min-w-\[200px\]{min-width:200px}.min-w-\[8rem\]{min-width:8rem}.min-w-\[var\(--radix-select-trigger-width\)\]{min-width:var(--radix-select-trigger-width)}.max-w-7xl{max-width:80rem}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.max-w-sm{max-width:24rem}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.origin-\[--radix-dropdown-menu-content-transform-origin\]{transform-origin:var(--radix-dropdown-menu-content-transform-origin)}.origin-\[--radix-select-content-transform-origin\]{transform-origin:var(--radix-select-content-transform-origin)}.origin-\[--radix-tooltip-content-transform-origin\]{transform-origin:var(--radix-tooltip-content-transform-origin)}.-translate-x-1\/2{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-\[-50\%\]{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-\[-50\%\]{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-rotate-90{--tw-rotate: -90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes ping{75%,to{transform:scale(2);opacity:0}}.animate-ping{animation:ping 1s cubic-bezier(0,0,.2,1) infinite}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.25rem * var(--tw-space-x-reverse));margin-left:calc(.25rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.75rem * var(--tw-space-x-reverse));margin-left:calc(.75rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(1rem * var(--tw-space-x-reverse));margin-left:calc(1rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.375rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-x-hidden{overflow-x:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-\[inherit\]{border-radius:inherit}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-destructive{border-color:hsl(var(--destructive))}.border-destructive\/20{border-color:hsl(var(--destructive) / .2)}.border-destructive\/50{border-color:hsl(var(--destructive) / .5)}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity, 1))}.border-green-500{--tw-border-opacity: 1;border-color:rgb(34 197 94 / var(--tw-border-opacity, 1))}.border-input{border-color:hsl(var(--input))}.border-red-400{--tw-border-opacity: 1;border-color:rgb(248 113 113 / var(--tw-border-opacity, 1))}.border-transparent{border-color:transparent}.border-l-border{border-left-color:hsl(var(--border))}.border-l-destructive{border-left-color:hsl(var(--destructive))}.border-l-foreground{border-left-color:hsl(var(--foreground))}.border-l-muted-foreground{border-left-color:hsl(var(--muted-foreground))}.border-l-transparent{border-left-color:transparent}.border-t-transparent{border-top-color:transparent}.bg-accent{background-color:hsl(var(--accent))}.bg-background{background-color:hsl(var(--background))}.bg-background\/95{background-color:hsl(var(--background) / .95)}.bg-black\/80{background-color:#000c}.bg-blue-400{--tw-bg-opacity: 1;background-color:rgb(96 165 250 / var(--tw-bg-opacity, 1))}.bg-blue-50{--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity, 1))}.bg-blue-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.bg-blue-600{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.bg-border{background-color:hsl(var(--border))}.bg-card{background-color:hsl(var(--card))}.bg-destructive{background-color:hsl(var(--destructive))}.bg-destructive\/10{background-color:hsl(var(--destructive) / .1)}.bg-foreground{background-color:hsl(var(--foreground))}.bg-gray-400{--tw-bg-opacity: 1;background-color:rgb(156 163 175 / var(--tw-bg-opacity, 1))}.bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-muted{background-color:hsl(var(--muted))}.bg-muted-foreground{background-color:hsl(var(--muted-foreground))}.bg-muted-foreground\/60{background-color:hsl(var(--muted-foreground) / .6)}.bg-muted\/10{background-color:hsl(var(--muted) / .1)}.bg-orange-500{--tw-bg-opacity: 1;background-color:rgb(249 115 22 / var(--tw-bg-opacity, 1))}.bg-popover{background-color:hsl(var(--popover))}.bg-primary{background-color:hsl(var(--primary))}.bg-primary\/10{background-color:hsl(var(--primary) / .1)}.bg-primary\/20{background-color:hsl(var(--primary) / .2)}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.bg-secondary{background-color:hsl(var(--secondary))}.bg-transparent{background-color:transparent}.bg-yellow-500{--tw-bg-opacity: 1;background-color:rgb(234 179 8 / var(--tw-bg-opacity, 1))}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.from-transparent{--tw-gradient-from: transparent var(--tw-gradient-from-position);--tw-gradient-to: rgb(0 0 0 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.to-muted\/5{--tw-gradient-to: hsl(var(--muted) / .05) var(--tw-gradient-to-position)}.fill-current{fill:currentColor}.p-0{padding:0}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-\[1px\]{padding:1px}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0{padding-top:0;padding-bottom:0}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-0{padding-bottom:0}.pb-1{padding-bottom:.25rem}.pb-12{padding-bottom:3rem}.pb-2{padding-bottom:.5rem}.pb-3{padding-bottom:.75rem}.pb-4{padding-bottom:1rem}.pl-10{padding-left:2.5rem}.pl-2{padding-left:.5rem}.pl-4{padding-left:1rem}.pl-8{padding-left:2rem}.pl-9{padding-left:2.25rem}.pr-2{padding-right:.5rem}.pr-4{padding-right:1rem}.pr-8{padding-right:2rem}.pt-0{padding-top:0}.pt-1{padding-top:.25rem}.pt-2{padding-top:.5rem}.pt-4{padding-top:1rem}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-\[0\.8rem\]{font-size:.8rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-none{line-height:1}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.tracking-widest{letter-spacing:.1em}.text-accent-foreground{color:hsl(var(--accent-foreground))}.text-blue-500{--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity, 1))}.text-card-foreground{color:hsl(var(--card-foreground))}.text-destructive{color:hsl(var(--destructive))}.text-destructive-foreground{color:hsl(var(--destructive-foreground))}.text-foreground{color:hsl(var(--foreground))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-muted{color:hsl(var(--muted))}.text-muted-foreground{color:hsl(var(--muted-foreground))}.text-muted-foreground\/20{color:hsl(var(--muted-foreground) / .2)}.text-muted-foreground\/60{color:hsl(var(--muted-foreground) / .6)}.text-popover-foreground{color:hsl(var(--popover-foreground))}.text-primary{color:hsl(var(--primary))}.text-primary-foreground{color:hsl(var(--primary-foreground))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-secondary-foreground{color:hsl(var(--secondary-foreground))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-yellow-500{--tw-text-opacity: 1;color:rgb(234 179 8 / var(--tw-text-opacity, 1))}.text-yellow-600{--tw-text-opacity: 1;color:rgb(202 138 4 / var(--tw-text-opacity, 1))}.underline-offset-4{text-underline-offset:4px}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-75{opacity:.75}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.ring-0{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-offset-background{--tw-ring-offset-color: hsl(var(--background))}.grayscale{--tw-grayscale: grayscale(100%);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur{--tw-backdrop-blur: blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}@keyframes enter{0%{opacity:var(--tw-enter-opacity, 1);transform:translate3d(var(--tw-enter-translate-x, 0),var(--tw-enter-translate-y, 0),0) scale3d(var(--tw-enter-scale, 1),var(--tw-enter-scale, 1),var(--tw-enter-scale, 1)) rotate(var(--tw-enter-rotate, 0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity, 1);transform:translate3d(var(--tw-exit-translate-x, 0),var(--tw-exit-translate-y, 0),0) scale3d(var(--tw-exit-scale, 1),var(--tw-exit-scale, 1),var(--tw-exit-scale, 1)) rotate(var(--tw-exit-rotate, 0))}}.animate-in{animation-name:enter;animation-duration:.15s;--tw-enter-opacity: initial;--tw-enter-scale: initial;--tw-enter-rotate: initial;--tw-enter-translate-x: initial;--tw-enter-translate-y: initial}.fade-in-0{--tw-enter-opacity: 0}.zoom-in-95{--tw-enter-scale: .95}.duration-200{animation-duration:.2s}.duration-300{animation-duration:.3s}.duration-500{animation-duration:.5s}.ease-in-out{animation-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{animation-timing-function:cubic-bezier(0,0,.2,1)}.running{animation-play-state:running}.file\:border-0::file-selector-button{border-width:0px}.file\:bg-transparent::file-selector-button{background-color:transparent}.file\:text-sm::file-selector-button{font-size:.875rem;line-height:1.25rem}.file\:font-medium::file-selector-button{font-weight:500}.file\:text-foreground::file-selector-button{color:hsl(var(--foreground))}.placeholder\:text-muted-foreground::-moz-placeholder{color:hsl(var(--muted-foreground))}.placeholder\:text-muted-foreground::placeholder{color:hsl(var(--muted-foreground))}.last\:border-b-0:last-child{border-bottom-width:0px}.focus-within\:relative:focus-within{position:relative}.focus-within\:z-20:focus-within{z-index:20}.hover\:bg-accent:hover{background-color:hsl(var(--accent))}.hover\:bg-destructive\/80:hover{background-color:hsl(var(--destructive) / .8)}.hover\:bg-destructive\/90:hover{background-color:hsl(var(--destructive) / .9)}.hover\:bg-muted\/30:hover{background-color:hsl(var(--muted) / .3)}.hover\:bg-muted\/50:hover{background-color:hsl(var(--muted) / .5)}.hover\:bg-primary:hover{background-color:hsl(var(--primary))}.hover\:bg-primary\/80:hover{background-color:hsl(var(--primary) / .8)}.hover\:bg-primary\/90:hover{background-color:hsl(var(--primary) / .9)}.hover\:bg-secondary\/80:hover{background-color:hsl(var(--secondary) / .8)}.hover\:text-accent-foreground:hover{color:hsl(var(--accent-foreground))}.hover\:text-foreground:hover{color:hsl(var(--foreground))}.hover\:text-primary-foreground:hover{color:hsl(var(--primary-foreground))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.hover\:opacity-80:hover{opacity:.8}.focus\:bg-accent:focus{background-color:hsl(var(--accent))}.focus\:bg-primary:focus{background-color:hsl(var(--primary))}.focus\:text-accent-foreground:focus{color:hsl(var(--accent-foreground))}.focus\:text-primary-foreground:focus{color:hsl(var(--primary-foreground))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-1:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-ring:focus{--tw-ring-color: hsl(var(--ring))}.focus\:ring-offset-2:focus{--tw-ring-offset-width: 2px}.focus-visible\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\:ring-1:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color: hsl(var(--ring))}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width: 2px}.focus-visible\:ring-offset-background:focus-visible{--tw-ring-offset-color: hsl(var(--background))}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.peer:disabled~.peer-disabled\:cursor-not-allowed{cursor:not-allowed}.peer:disabled~.peer-disabled\:opacity-70{opacity:.7}.aria-selected\:bg-accent[aria-selected=true]{background-color:hsl(var(--accent))}.aria-selected\:text-accent-foreground[aria-selected=true]{color:hsl(var(--accent-foreground))}.aria-selected\:opacity-100[aria-selected=true]{opacity:1}.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[side\=bottom\]\:translate-y-1[data-side=bottom]{--tw-translate-y: .25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=left\]\:-translate-x-1[data-side=left]{--tw-translate-x: -.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=right\]\:translate-x-1[data-side=right]{--tw-translate-x: .25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=top\]\:-translate-y-1[data-side=top]{--tw-translate-y: -.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[state\=checked\]\:translate-x-4[data-state=checked]{--tw-translate-x: 1rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[state\=unchecked\]\:translate-x-0[data-state=unchecked]{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[state\=active\]\:bg-background[data-state=active]{background-color:hsl(var(--background))}.data-\[state\=checked\]\:bg-primary[data-state=checked]{background-color:hsl(var(--primary))}.data-\[state\=open\]\:bg-accent[data-state=open]{background-color:hsl(var(--accent))}.data-\[state\=open\]\:bg-secondary[data-state=open]{background-color:hsl(var(--secondary))}.data-\[state\=unchecked\]\:bg-input[data-state=unchecked]{background-color:hsl(var(--input))}.data-\[placeholder\]\:text-muted-foreground[data-placeholder]{color:hsl(var(--muted-foreground))}.data-\[state\=active\]\:text-foreground[data-state=active]{color:hsl(var(--foreground))}.data-\[state\=open\]\:text-muted-foreground[data-state=open]{color:hsl(var(--muted-foreground))}.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}.data-\[state\=active\]\:shadow-sm[data-state=active]{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.data-\[state\=closed\]\:duration-300[data-state=closed]{transition-duration:.3s}.data-\[state\=open\]\:duration-500[data-state=open]{transition-duration:.5s}.data-\[state\=open\]\:animate-in[data-state=open]{animation-name:enter;animation-duration:.15s;--tw-enter-opacity: initial;--tw-enter-scale: initial;--tw-enter-rotate: initial;--tw-enter-translate-x: initial;--tw-enter-translate-y: initial}.data-\[state\=closed\]\:animate-out[data-state=closed]{animation-name:exit;animation-duration:.15s;--tw-exit-opacity: initial;--tw-exit-scale: initial;--tw-exit-rotate: initial;--tw-exit-translate-x: initial;--tw-exit-translate-y: initial}.data-\[state\=closed\]\:fade-out-0[data-state=closed]{--tw-exit-opacity: 0}.data-\[state\=open\]\:fade-in-0[data-state=open]{--tw-enter-opacity: 0}.data-\[state\=closed\]\:zoom-out-95[data-state=closed]{--tw-exit-scale: .95}.data-\[state\=open\]\:zoom-in-95[data-state=open]{--tw-enter-scale: .95}.data-\[side\=bottom\]\:slide-in-from-top-2[data-side=bottom]{--tw-enter-translate-y: -.5rem}.data-\[side\=left\]\:slide-in-from-right-2[data-side=left]{--tw-enter-translate-x: .5rem}.data-\[side\=right\]\:slide-in-from-left-2[data-side=right]{--tw-enter-translate-x: -.5rem}.data-\[side\=top\]\:slide-in-from-bottom-2[data-side=top]{--tw-enter-translate-y: .5rem}.data-\[state\=closed\]\:slide-out-to-bottom[data-state=closed]{--tw-exit-translate-y: 100%}.data-\[state\=closed\]\:slide-out-to-left[data-state=closed]{--tw-exit-translate-x: -100%}.data-\[state\=closed\]\:slide-out-to-left-1\/2[data-state=closed]{--tw-exit-translate-x: -50%}.data-\[state\=closed\]\:slide-out-to-right[data-state=closed]{--tw-exit-translate-x: 100%}.data-\[state\=closed\]\:slide-out-to-top[data-state=closed]{--tw-exit-translate-y: -100%}.data-\[state\=closed\]\:slide-out-to-top-\[48\%\][data-state=closed]{--tw-exit-translate-y: -48%}.data-\[state\=open\]\:slide-in-from-bottom[data-state=open]{--tw-enter-translate-y: 100%}.data-\[state\=open\]\:slide-in-from-left[data-state=open]{--tw-enter-translate-x: -100%}.data-\[state\=open\]\:slide-in-from-left-1\/2[data-state=open]{--tw-enter-translate-x: -50%}.data-\[state\=open\]\:slide-in-from-right[data-state=open]{--tw-enter-translate-x: 100%}.data-\[state\=open\]\:slide-in-from-top[data-state=open]{--tw-enter-translate-y: -100%}.data-\[state\=open\]\:slide-in-from-top-\[48\%\][data-state=open]{--tw-enter-translate-y: -48%}.data-\[state\=closed\]\:duration-300[data-state=closed]{animation-duration:.3s}.data-\[state\=open\]\:duration-500[data-state=open]{animation-duration:.5s}.dark\:border-gray-700:is(.dark *){--tw-border-opacity: 1;border-color:rgb(55 65 81 / var(--tw-border-opacity, 1))}@media (min-width: 640px){.sm\:flex{display:flex}.sm\:max-w-sm{max-width:24rem}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:justify-end{justify-content:flex-end}.sm\:space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.sm\:rounded-lg{border-radius:var(--radius)}.sm\:text-left{text-align:left}}@media (min-width: 768px){.md\:col-span-2{grid-column:span 2 / span 2}.md\:flex{display:flex}.md\:hidden{display:none}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width: 1024px){.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}.\[\&\:has\(\[aria-selected\]\)\]\:bg-accent:has([aria-selected]){background-color:hsl(var(--accent))}.first\:\[\&\:has\(\[aria-selected\]\)\]\:rounded-l-md:has([aria-selected]):first-child{border-top-left-radius:calc(var(--radius) - 2px);border-bottom-left-radius:calc(var(--radius) - 2px)}.last\:\[\&\:has\(\[aria-selected\]\)\]\:rounded-r-md:has([aria-selected]):last-child{border-top-right-radius:calc(var(--radius) - 2px);border-bottom-right-radius:calc(var(--radius) - 2px)}.\[\&\>span\]\:line-clamp-1>span{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1}.\[\&\>svg\]\:size-4>svg{width:1rem;height:1rem}.\[\&\>svg\]\:shrink-0>svg{flex-shrink:0}.\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.\[\&_svg\]\:size-4 svg{width:1rem;height:1rem}.\[\&_svg\]\:shrink-0 svg{flex-shrink:0} diff --git a/fastapi_radar/dashboard/dist/assets/index-D51YrvFG.css b/fastapi_radar/dashboard/dist/assets/index-D51YrvFG.css deleted file mode 100644 index 66adc8f..0000000 --- a/fastapi_radar/dashboard/dist/assets/index-D51YrvFG.css +++ /dev/null @@ -1 +0,0 @@ -*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}:root{--background: 0 0% 100%;--foreground: 0 0% 9%;--card: 0 0% 100%;--card-foreground: 0 0% 9%;--popover: 0 0% 100%;--popover-foreground: 0 0% 9%;--primary: 0 0% 9%;--primary-foreground: 0 0% 100%;--secondary: 0 0% 96%;--secondary-foreground: 0 0% 9%;--muted: 0 0% 96%;--muted-foreground: 0 0% 45%;--accent: 0 0% 96%;--accent-foreground: 0 0% 9%;--destructive: 0 84.2% 60.2%;--destructive-foreground: 0 0% 100%;--border: 0 0% 92%;--input: 0 0% 92%;--ring: 0 0% 9%;--radius: .375rem;--chart-1: 0 0% 15%;--chart-2: 0 0% 30%;--chart-3: 0 0% 45%;--chart-4: 0 0% 60%;--chart-5: 0 0% 75%}.dark{--background: 0 0% 3%;--foreground: 0 0% 98%;--card: 0 0% 5%;--card-foreground: 0 0% 98%;--popover: 0 0% 3%;--popover-foreground: 0 0% 98%;--primary: 0 0% 98%;--primary-foreground: 0 0% 3%;--secondary: 0 0% 10%;--secondary-foreground: 0 0% 98%;--muted: 0 0% 10%;--muted-foreground: 0 0% 60%;--accent: 0 0% 10%;--accent-foreground: 0 0% 98%;--destructive: 0 62.8% 30.6%;--destructive-foreground: 0 0% 98%;--border: 0 0% 14%;--input: 0 0% 14%;--ring: 0 0% 98%;--chart-1: 0 0% 85%;--chart-2: 0 0% 70%;--chart-3: 0 0% 55%;--chart-4: 0 0% 40%;--chart-5: 0 0% 25%}*{border-color:hsl(var(--border))}body{background-color:hsl(var(--background));color:hsl(var(--foreground))}.container{width:100%;margin-right:auto;margin-left:auto;padding-right:2rem;padding-left:2rem}@media (min-width: 1400px){.container{max-width:1400px}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.collapse{visibility:collapse}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bottom:0;left:0}.inset-x-0{left:0;right:0}.inset-y-0{top:0;bottom:0}.-bottom-5{bottom:-1.25rem}.bottom-0{bottom:0}.left-0{left:0}.left-1\/2{left:50%}.left-1\/4{left:25%}.left-2{left:.5rem}.left-3{left:.75rem}.left-3\/4{left:75%}.left-\[50\%\]{left:50%}.right-0{right:0}.right-2{right:.5rem}.right-4{right:1rem}.top-0{top:0}.top-1\/2{top:50%}.top-4{top:1rem}.top-\[50\%\]{top:50%}.z-50{z-index:50}.-mx-1{margin-left:-.25rem;margin-right:-.25rem}.-mx-2{margin-left:-.5rem;margin-right:-.5rem}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-4{margin-top:1rem;margin-bottom:1rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mr-4{margin-right:1rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.block{display:block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.aspect-square{aspect-ratio:1 / 1}.h-1{height:.25rem}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-11{height:2.75rem}.h-12{height:3rem}.h-16{height:4rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-\[100px\]{height:100px}.h-\[120px\]{height:120px}.h-\[150px\]{height:150px}.h-\[1px\]{height:1px}.h-\[200px\]{height:200px}.h-\[300px\]{height:300px}.h-\[400px\]{height:400px}.h-\[500px\]{height:500px}.h-\[80vh\]{height:80vh}.h-\[var\(--radix-select-trigger-height\)\]{height:var(--radix-select-trigger-height)}.h-auto{height:auto}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-40{max-height:10rem}.max-h-\[--radix-select-content-available-height\]{max-height:var(--radix-select-content-available-height)}.max-h-\[var\(--radix-dropdown-menu-content-available-height\)\]{max-height:var(--radix-dropdown-menu-content-available-height)}.w-1\/3{width:33.333333%}.w-10{width:2.5rem}.w-12{width:3rem}.w-16{width:4rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-20{width:5rem}.w-24{width:6rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-3\/4{width:75%}.w-32{width:8rem}.w-4{width:1rem}.w-40{width:10rem}.w-5{width:1.25rem}.w-64{width:16rem}.w-8{width:2rem}.w-9{width:2.25rem}.w-\[150px\]{width:150px}.w-\[1px\]{width:1px}.w-full{width:100%}.w-px{width:1px}.min-w-0{min-width:0px}.min-w-\[200px\]{min-width:200px}.min-w-\[8rem\]{min-width:8rem}.min-w-\[var\(--radix-select-trigger-width\)\]{min-width:var(--radix-select-trigger-width)}.max-w-7xl{max-width:80rem}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.max-w-sm{max-width:24rem}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.origin-\[--radix-dropdown-menu-content-transform-origin\]{transform-origin:var(--radix-dropdown-menu-content-transform-origin)}.origin-\[--radix-select-content-transform-origin\]{transform-origin:var(--radix-select-content-transform-origin)}.origin-\[--radix-tooltip-content-transform-origin\]{transform-origin:var(--radix-tooltip-content-transform-origin)}.-translate-x-1\/2{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-\[-50\%\]{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-\[-50\%\]{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-rotate-90{--tw-rotate: -90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes ping{75%,to{transform:scale(2);opacity:0}}.animate-ping{animation:ping 1s cubic-bezier(0,0,.2,1) infinite}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.25rem * var(--tw-space-x-reverse));margin-left:calc(.25rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.75rem * var(--tw-space-x-reverse));margin-left:calc(.75rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(1rem * var(--tw-space-x-reverse));margin-left:calc(1rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.375rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-x-hidden{overflow-x:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-\[inherit\]{border-radius:inherit}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-destructive{border-color:hsl(var(--destructive))}.border-destructive\/20{border-color:hsl(var(--destructive) / .2)}.border-destructive\/50{border-color:hsl(var(--destructive) / .5)}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity, 1))}.border-green-500{--tw-border-opacity: 1;border-color:rgb(34 197 94 / var(--tw-border-opacity, 1))}.border-input{border-color:hsl(var(--input))}.border-transparent{border-color:transparent}.border-l-border{border-left-color:hsl(var(--border))}.border-l-destructive{border-left-color:hsl(var(--destructive))}.border-l-foreground{border-left-color:hsl(var(--foreground))}.border-l-muted-foreground{border-left-color:hsl(var(--muted-foreground))}.border-l-transparent{border-left-color:transparent}.border-t-transparent{border-top-color:transparent}.bg-background{background-color:hsl(var(--background))}.bg-background\/95{background-color:hsl(var(--background) / .95)}.bg-black\/80{background-color:#000c}.bg-blue-400{--tw-bg-opacity: 1;background-color:rgb(96 165 250 / var(--tw-bg-opacity, 1))}.bg-blue-50{--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity, 1))}.bg-blue-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.bg-blue-600{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.bg-border{background-color:hsl(var(--border))}.bg-card{background-color:hsl(var(--card))}.bg-destructive{background-color:hsl(var(--destructive))}.bg-destructive\/10{background-color:hsl(var(--destructive) / .1)}.bg-foreground{background-color:hsl(var(--foreground))}.bg-gray-400{--tw-bg-opacity: 1;background-color:rgb(156 163 175 / var(--tw-bg-opacity, 1))}.bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-muted{background-color:hsl(var(--muted))}.bg-muted-foreground{background-color:hsl(var(--muted-foreground))}.bg-muted-foreground\/60{background-color:hsl(var(--muted-foreground) / .6)}.bg-muted\/10{background-color:hsl(var(--muted) / .1)}.bg-orange-500{--tw-bg-opacity: 1;background-color:rgb(249 115 22 / var(--tw-bg-opacity, 1))}.bg-popover{background-color:hsl(var(--popover))}.bg-primary{background-color:hsl(var(--primary))}.bg-primary\/10{background-color:hsl(var(--primary) / .1)}.bg-primary\/20{background-color:hsl(var(--primary) / .2)}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.bg-secondary{background-color:hsl(var(--secondary))}.bg-transparent{background-color:transparent}.bg-yellow-500{--tw-bg-opacity: 1;background-color:rgb(234 179 8 / var(--tw-bg-opacity, 1))}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.from-transparent{--tw-gradient-from: transparent var(--tw-gradient-from-position);--tw-gradient-to: rgb(0 0 0 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.to-muted\/5{--tw-gradient-to: hsl(var(--muted) / .05) var(--tw-gradient-to-position)}.fill-current{fill:currentColor}.p-0{padding:0}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-\[1px\]{padding:1px}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-1{padding-bottom:.25rem}.pb-12{padding-bottom:3rem}.pb-2{padding-bottom:.5rem}.pb-3{padding-bottom:.75rem}.pb-4{padding-bottom:1rem}.pl-10{padding-left:2.5rem}.pl-2{padding-left:.5rem}.pl-4{padding-left:1rem}.pl-8{padding-left:2rem}.pl-9{padding-left:2.25rem}.pr-2{padding-right:.5rem}.pr-4{padding-right:1rem}.pr-8{padding-right:2rem}.pt-0{padding-top:0}.pt-2{padding-top:.5rem}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-none{line-height:1}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.tracking-widest{letter-spacing:.1em}.text-blue-500{--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity, 1))}.text-card-foreground{color:hsl(var(--card-foreground))}.text-destructive{color:hsl(var(--destructive))}.text-destructive-foreground{color:hsl(var(--destructive-foreground))}.text-foreground{color:hsl(var(--foreground))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-muted{color:hsl(var(--muted))}.text-muted-foreground{color:hsl(var(--muted-foreground))}.text-muted-foreground\/20{color:hsl(var(--muted-foreground) / .2)}.text-muted-foreground\/60{color:hsl(var(--muted-foreground) / .6)}.text-popover-foreground{color:hsl(var(--popover-foreground))}.text-primary{color:hsl(var(--primary))}.text-primary-foreground{color:hsl(var(--primary-foreground))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-secondary-foreground{color:hsl(var(--secondary-foreground))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-yellow-500{--tw-text-opacity: 1;color:rgb(234 179 8 / var(--tw-text-opacity, 1))}.text-yellow-600{--tw-text-opacity: 1;color:rgb(202 138 4 / var(--tw-text-opacity, 1))}.underline-offset-4{text-underline-offset:4px}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-75{opacity:.75}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.ring-0{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-offset-background{--tw-ring-offset-color: hsl(var(--background))}.grayscale{--tw-grayscale: grayscale(100%);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur{--tw-backdrop-blur: blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}@keyframes enter{0%{opacity:var(--tw-enter-opacity, 1);transform:translate3d(var(--tw-enter-translate-x, 0),var(--tw-enter-translate-y, 0),0) scale3d(var(--tw-enter-scale, 1),var(--tw-enter-scale, 1),var(--tw-enter-scale, 1)) rotate(var(--tw-enter-rotate, 0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity, 1);transform:translate3d(var(--tw-exit-translate-x, 0),var(--tw-exit-translate-y, 0),0) scale3d(var(--tw-exit-scale, 1),var(--tw-exit-scale, 1),var(--tw-exit-scale, 1)) rotate(var(--tw-exit-rotate, 0))}}.animate-in{animation-name:enter;animation-duration:.15s;--tw-enter-opacity: initial;--tw-enter-scale: initial;--tw-enter-rotate: initial;--tw-enter-translate-x: initial;--tw-enter-translate-y: initial}.fade-in-0{--tw-enter-opacity: 0}.zoom-in-95{--tw-enter-scale: .95}.duration-200{animation-duration:.2s}.duration-300{animation-duration:.3s}.duration-500{animation-duration:.5s}.ease-in-out{animation-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{animation-timing-function:cubic-bezier(0,0,.2,1)}.running{animation-play-state:running}.file\:border-0::file-selector-button{border-width:0px}.file\:bg-transparent::file-selector-button{background-color:transparent}.file\:text-sm::file-selector-button{font-size:.875rem;line-height:1.25rem}.file\:font-medium::file-selector-button{font-weight:500}.file\:text-foreground::file-selector-button{color:hsl(var(--foreground))}.placeholder\:text-muted-foreground::-moz-placeholder{color:hsl(var(--muted-foreground))}.placeholder\:text-muted-foreground::placeholder{color:hsl(var(--muted-foreground))}.hover\:bg-accent:hover{background-color:hsl(var(--accent))}.hover\:bg-destructive\/80:hover{background-color:hsl(var(--destructive) / .8)}.hover\:bg-destructive\/90:hover{background-color:hsl(var(--destructive) / .9)}.hover\:bg-muted\/50:hover{background-color:hsl(var(--muted) / .5)}.hover\:bg-primary\/80:hover{background-color:hsl(var(--primary) / .8)}.hover\:bg-primary\/90:hover{background-color:hsl(var(--primary) / .9)}.hover\:bg-secondary\/80:hover{background-color:hsl(var(--secondary) / .8)}.hover\:text-accent-foreground:hover{color:hsl(var(--accent-foreground))}.hover\:text-foreground:hover{color:hsl(var(--foreground))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.hover\:opacity-80:hover{opacity:.8}.focus\:bg-accent:focus{background-color:hsl(var(--accent))}.focus\:text-accent-foreground:focus{color:hsl(var(--accent-foreground))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-1:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-ring:focus{--tw-ring-color: hsl(var(--ring))}.focus\:ring-offset-2:focus{--tw-ring-offset-width: 2px}.focus-visible\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\:ring-1:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color: hsl(var(--ring))}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width: 2px}.focus-visible\:ring-offset-background:focus-visible{--tw-ring-offset-color: hsl(var(--background))}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.peer:disabled~.peer-disabled\:cursor-not-allowed{cursor:not-allowed}.peer:disabled~.peer-disabled\:opacity-70{opacity:.7}.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[side\=bottom\]\:translate-y-1[data-side=bottom]{--tw-translate-y: .25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=left\]\:-translate-x-1[data-side=left]{--tw-translate-x: -.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=right\]\:translate-x-1[data-side=right]{--tw-translate-x: .25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=top\]\:-translate-y-1[data-side=top]{--tw-translate-y: -.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[state\=checked\]\:translate-x-4[data-state=checked]{--tw-translate-x: 1rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[state\=unchecked\]\:translate-x-0[data-state=unchecked]{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[state\=active\]\:bg-background[data-state=active]{background-color:hsl(var(--background))}.data-\[state\=checked\]\:bg-primary[data-state=checked]{background-color:hsl(var(--primary))}.data-\[state\=open\]\:bg-accent[data-state=open]{background-color:hsl(var(--accent))}.data-\[state\=open\]\:bg-secondary[data-state=open]{background-color:hsl(var(--secondary))}.data-\[state\=unchecked\]\:bg-input[data-state=unchecked]{background-color:hsl(var(--input))}.data-\[placeholder\]\:text-muted-foreground[data-placeholder]{color:hsl(var(--muted-foreground))}.data-\[state\=active\]\:text-foreground[data-state=active]{color:hsl(var(--foreground))}.data-\[state\=open\]\:text-muted-foreground[data-state=open]{color:hsl(var(--muted-foreground))}.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}.data-\[state\=active\]\:shadow-sm[data-state=active]{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.data-\[state\=closed\]\:duration-300[data-state=closed]{transition-duration:.3s}.data-\[state\=open\]\:duration-500[data-state=open]{transition-duration:.5s}.data-\[state\=open\]\:animate-in[data-state=open]{animation-name:enter;animation-duration:.15s;--tw-enter-opacity: initial;--tw-enter-scale: initial;--tw-enter-rotate: initial;--tw-enter-translate-x: initial;--tw-enter-translate-y: initial}.data-\[state\=closed\]\:animate-out[data-state=closed]{animation-name:exit;animation-duration:.15s;--tw-exit-opacity: initial;--tw-exit-scale: initial;--tw-exit-rotate: initial;--tw-exit-translate-x: initial;--tw-exit-translate-y: initial}.data-\[state\=closed\]\:fade-out-0[data-state=closed]{--tw-exit-opacity: 0}.data-\[state\=open\]\:fade-in-0[data-state=open]{--tw-enter-opacity: 0}.data-\[state\=closed\]\:zoom-out-95[data-state=closed]{--tw-exit-scale: .95}.data-\[state\=open\]\:zoom-in-95[data-state=open]{--tw-enter-scale: .95}.data-\[side\=bottom\]\:slide-in-from-top-2[data-side=bottom]{--tw-enter-translate-y: -.5rem}.data-\[side\=left\]\:slide-in-from-right-2[data-side=left]{--tw-enter-translate-x: .5rem}.data-\[side\=right\]\:slide-in-from-left-2[data-side=right]{--tw-enter-translate-x: -.5rem}.data-\[side\=top\]\:slide-in-from-bottom-2[data-side=top]{--tw-enter-translate-y: .5rem}.data-\[state\=closed\]\:slide-out-to-bottom[data-state=closed]{--tw-exit-translate-y: 100%}.data-\[state\=closed\]\:slide-out-to-left[data-state=closed]{--tw-exit-translate-x: -100%}.data-\[state\=closed\]\:slide-out-to-left-1\/2[data-state=closed]{--tw-exit-translate-x: -50%}.data-\[state\=closed\]\:slide-out-to-right[data-state=closed]{--tw-exit-translate-x: 100%}.data-\[state\=closed\]\:slide-out-to-top[data-state=closed]{--tw-exit-translate-y: -100%}.data-\[state\=closed\]\:slide-out-to-top-\[48\%\][data-state=closed]{--tw-exit-translate-y: -48%}.data-\[state\=open\]\:slide-in-from-bottom[data-state=open]{--tw-enter-translate-y: 100%}.data-\[state\=open\]\:slide-in-from-left[data-state=open]{--tw-enter-translate-x: -100%}.data-\[state\=open\]\:slide-in-from-left-1\/2[data-state=open]{--tw-enter-translate-x: -50%}.data-\[state\=open\]\:slide-in-from-right[data-state=open]{--tw-enter-translate-x: 100%}.data-\[state\=open\]\:slide-in-from-top[data-state=open]{--tw-enter-translate-y: -100%}.data-\[state\=open\]\:slide-in-from-top-\[48\%\][data-state=open]{--tw-enter-translate-y: -48%}.data-\[state\=closed\]\:duration-300[data-state=closed]{animation-duration:.3s}.data-\[state\=open\]\:duration-500[data-state=open]{animation-duration:.5s}.dark\:border-gray-700:is(.dark *){--tw-border-opacity: 1;border-color:rgb(55 65 81 / var(--tw-border-opacity, 1))}@media (min-width: 640px){.sm\:flex{display:flex}.sm\:max-w-sm{max-width:24rem}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:justify-end{justify-content:flex-end}.sm\:space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.sm\:rounded-lg{border-radius:var(--radius)}.sm\:text-left{text-align:left}}@media (min-width: 768px){.md\:col-span-2{grid-column:span 2 / span 2}.md\:flex{display:flex}.md\:hidden{display:none}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width: 1024px){.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}.\[\&\>span\]\:line-clamp-1>span{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1}.\[\&\>svg\]\:size-4>svg{width:1rem;height:1rem}.\[\&\>svg\]\:shrink-0>svg{flex-shrink:0}.\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.\[\&_svg\]\:size-4 svg{width:1rem;height:1rem}.\[\&_svg\]\:shrink-0 svg{flex-shrink:0} diff --git a/fastapi_radar/dashboard/dist/assets/index-Dkd4m14x.js b/fastapi_radar/dashboard/dist/assets/index-Dkd4m14x.js new file mode 100644 index 0000000..f44fa9c --- /dev/null +++ b/fastapi_radar/dashboard/dist/assets/index-Dkd4m14x.js @@ -0,0 +1,381 @@ +var o3=Object.defineProperty;var sS=e=>{throw TypeError(e)};var s3=(e,t,r)=>t in e?o3(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r;var Q=(e,t,r)=>s3(e,typeof t!="symbol"?t+"":t,r),pg=(e,t,r)=>t.has(e)||sS("Cannot "+r);var N=(e,t,r)=>(pg(e,t,"read from private field"),r?r.call(e):t.get(e)),ae=(e,t,r)=>t.has(e)?sS("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,r),G=(e,t,r,n)=>(pg(e,t,"write to private field"),n?n.call(e,r):t.set(e,r),r),ge=(e,t,r)=>(pg(e,t,"access private method"),r);var Cd=(e,t,r,n)=>({set _(a){G(e,t,a,r)},get _(){return N(e,t,n)}});function l3(e,t){for(var r=0;rn[a]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const a of document.querySelectorAll('link[rel="modulepreload"]'))n(a);new MutationObserver(a=>{for(const i of a)if(i.type==="childList")for(const o of i.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&n(o)}).observe(document,{childList:!0,subtree:!0});function r(a){const i={};return a.integrity&&(i.integrity=a.integrity),a.referrerPolicy&&(i.referrerPolicy=a.referrerPolicy),a.crossOrigin==="use-credentials"?i.credentials="include":a.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function n(a){if(a.ep)return;a.ep=!0;const i=r(a);fetch(a.href,i)}})();function ba(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var DC={exports:{}},gp={},MC={exports:{}},we={};/** + * @license React + * react.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var zu=Symbol.for("react.element"),c3=Symbol.for("react.portal"),u3=Symbol.for("react.fragment"),d3=Symbol.for("react.strict_mode"),f3=Symbol.for("react.profiler"),h3=Symbol.for("react.provider"),p3=Symbol.for("react.context"),m3=Symbol.for("react.forward_ref"),g3=Symbol.for("react.suspense"),v3=Symbol.for("react.memo"),y3=Symbol.for("react.lazy"),lS=Symbol.iterator;function x3(e){return e===null||typeof e!="object"?null:(e=lS&&e[lS]||e["@@iterator"],typeof e=="function"?e:null)}var RC={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},IC=Object.assign,LC={};function Ul(e,t,r){this.props=e,this.context=t,this.refs=LC,this.updater=r||RC}Ul.prototype.isReactComponent={};Ul.prototype.setState=function(e,t){if(typeof e!="object"&&typeof e!="function"&&e!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")};Ul.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function $C(){}$C.prototype=Ul.prototype;function r0(e,t,r){this.props=e,this.context=t,this.refs=LC,this.updater=r||RC}var n0=r0.prototype=new $C;n0.constructor=r0;IC(n0,Ul.prototype);n0.isPureReactComponent=!0;var cS=Array.isArray,FC=Object.prototype.hasOwnProperty,a0={current:null},BC={key:!0,ref:!0,__self:!0,__source:!0};function zC(e,t,r){var n,a={},i=null,o=null;if(t!=null)for(n in t.ref!==void 0&&(o=t.ref),t.key!==void 0&&(i=""+t.key),t)FC.call(t,n)&&!BC.hasOwnProperty(n)&&(a[n]=t[n]);var s=arguments.length-2;if(s===1)a.children=r;else if(1>>1,Y=_[Z];if(0>>1;Za(je,U))Uea(J,je)?(_[Z]=J,_[Ue]=U,Z=Ue):(_[Z]=je,_[Ae]=U,Z=Ae);else if(Uea(J,U))_[Z]=J,_[Ue]=U,Z=Ue;else break e}}return L}function a(_,L){var U=_.sortIndex-L.sortIndex;return U!==0?U:_.id-L.id}if(typeof performance=="object"&&typeof performance.now=="function"){var i=performance;e.unstable_now=function(){return i.now()}}else{var o=Date,s=o.now();e.unstable_now=function(){return o.now()-s}}var l=[],c=[],u=1,f=null,h=3,m=!1,v=!1,g=!1,y=typeof setTimeout=="function"?setTimeout:null,b=typeof clearTimeout=="function"?clearTimeout:null,x=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function w(_){for(var L=r(c);L!==null;){if(L.callback===null)n(c);else if(L.startTime<=_)n(c),L.sortIndex=L.expirationTime,t(l,L);else break;L=r(c)}}function S(_){if(g=!1,w(_),!v)if(r(l)!==null)v=!0,$(k);else{var L=r(c);L!==null&&W(S,L.startTime-_)}}function k(_,L){v=!1,g&&(g=!1,b(j),j=-1),m=!0;var U=h;try{for(w(L),f=r(l);f!==null&&(!(f.expirationTime>L)||_&&!T());){var Z=f.callback;if(typeof Z=="function"){f.callback=null,h=f.priorityLevel;var Y=Z(f.expirationTime<=L);L=e.unstable_now(),typeof Y=="function"?f.callback=Y:f===r(l)&&n(l),w(L)}else n(l);f=r(l)}if(f!==null)var ye=!0;else{var Ae=r(c);Ae!==null&&W(S,Ae.startTime-L),ye=!1}return ye}finally{f=null,h=U,m=!1}}var P=!1,E=null,j=-1,C=5,O=-1;function T(){return!(e.unstable_now()-O_||125<_?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):C=0<_?Math.floor(1e3/_):5},e.unstable_getCurrentPriorityLevel=function(){return h},e.unstable_getFirstCallbackNode=function(){return r(l)},e.unstable_next=function(_){switch(h){case 1:case 2:case 3:var L=3;break;default:L=h}var U=h;h=L;try{return _()}finally{h=U}},e.unstable_pauseExecution=function(){},e.unstable_requestPaint=function(){},e.unstable_runWithPriority=function(_,L){switch(_){case 1:case 2:case 3:case 4:case 5:break;default:_=3}var U=h;h=_;try{return L()}finally{h=U}},e.unstable_scheduleCallback=function(_,L,U){var Z=e.unstable_now();switch(typeof U=="object"&&U!==null?(U=U.delay,U=typeof U=="number"&&0Z?(_.sortIndex=U,t(c,_),r(l)===null&&_===r(c)&&(g?(b(j),j=-1):g=!0,W(S,U-Z))):(_.sortIndex=Y,t(l,_),v||m||(v=!0,$(k))),_},e.unstable_shouldYield=T,e.unstable_wrapCallback=function(_){var L=h;return function(){var U=h;h=L;try{return _.apply(this,arguments)}finally{h=U}}}})(KC);HC.exports=KC;var T3=HC.exports;/** + * @license React + * react-dom.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var A3=p,Gr=T3;function q(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,r=1;r"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Mv=Object.prototype.hasOwnProperty,_3=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,dS={},fS={};function D3(e){return Mv.call(fS,e)?!0:Mv.call(dS,e)?!1:_3.test(e)?fS[e]=!0:(dS[e]=!0,!1)}function M3(e,t,r,n){if(r!==null&&r.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return n?!1:r!==null?!r.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function R3(e,t,r,n){if(t===null||typeof t>"u"||M3(e,t,r,n))return!0;if(n)return!1;if(r!==null)switch(r.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function xr(e,t,r,n,a,i,o){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=n,this.attributeNamespace=a,this.mustUseProperty=r,this.propertyName=e,this.type=t,this.sanitizeURL=i,this.removeEmptyString=o}var Yt={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){Yt[e]=new xr(e,0,!1,e,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];Yt[t]=new xr(t,1,!1,e[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(e){Yt[e]=new xr(e,2,!1,e.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){Yt[e]=new xr(e,2,!1,e,null,!1,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){Yt[e]=new xr(e,3,!1,e.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(e){Yt[e]=new xr(e,3,!0,e,null,!1,!1)});["capture","download"].forEach(function(e){Yt[e]=new xr(e,4,!1,e,null,!1,!1)});["cols","rows","size","span"].forEach(function(e){Yt[e]=new xr(e,6,!1,e,null,!1,!1)});["rowSpan","start"].forEach(function(e){Yt[e]=new xr(e,5,!1,e.toLowerCase(),null,!1,!1)});var s0=/[\-:]([a-z])/g;function l0(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(s0,l0);Yt[t]=new xr(t,1,!1,e,null,!1,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(s0,l0);Yt[t]=new xr(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)});["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(s0,l0);Yt[t]=new xr(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(e){Yt[e]=new xr(e,1,!1,e.toLowerCase(),null,!1,!1)});Yt.xlinkHref=new xr("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(e){Yt[e]=new xr(e,1,!1,e.toLowerCase(),null,!0,!0)});function c0(e,t,r,n){var a=Yt.hasOwnProperty(t)?Yt[t]:null;(a!==null?a.type!==0:n||!(2s||a[o]!==i[s]){var l=` +`+a[o].replace(" at new "," at ");return e.displayName&&l.includes("")&&(l=l.replace("",e.displayName)),l}while(1<=o&&0<=s);break}}}finally{vg=!1,Error.prepareStackTrace=r}return(e=e?e.displayName||e.name:"")?Dc(e):""}function I3(e){switch(e.tag){case 5:return Dc(e.type);case 16:return Dc("Lazy");case 13:return Dc("Suspense");case 19:return Dc("SuspenseList");case 0:case 2:case 15:return e=yg(e.type,!1),e;case 11:return e=yg(e.type.render,!1),e;case 1:return e=yg(e.type,!0),e;default:return""}}function $v(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case Fs:return"Fragment";case $s:return"Portal";case Rv:return"Profiler";case u0:return"StrictMode";case Iv:return"Suspense";case Lv:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case GC:return(e.displayName||"Context")+".Consumer";case VC:return(e._context.displayName||"Context")+".Provider";case d0:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case f0:return t=e.displayName||null,t!==null?t:$v(e.type)||"Memo";case Ei:t=e._payload,e=e._init;try{return $v(e(t))}catch{}}return null}function L3(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return $v(t);case 8:return t===u0?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function to(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function XC(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function $3(e){var t=XC(e)?"checked":"value",r=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),n=""+e[t];if(!e.hasOwnProperty(t)&&typeof r<"u"&&typeof r.get=="function"&&typeof r.set=="function"){var a=r.get,i=r.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return a.call(this)},set:function(o){n=""+o,i.call(this,o)}}),Object.defineProperty(e,t,{enumerable:r.enumerable}),{getValue:function(){return n},setValue:function(o){n=""+o},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function Td(e){e._valueTracker||(e._valueTracker=$3(e))}function ZC(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var r=t.getValue(),n="";return e&&(n=XC(e)?e.checked?"true":"false":e.value),e=n,e!==r?(t.setValue(e),!0):!1}function qf(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function Fv(e,t){var r=t.checked;return ft({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:r??e._wrapperState.initialChecked})}function pS(e,t){var r=t.defaultValue==null?"":t.defaultValue,n=t.checked!=null?t.checked:t.defaultChecked;r=to(t.value!=null?t.value:r),e._wrapperState={initialChecked:n,initialValue:r,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function JC(e,t){t=t.checked,t!=null&&c0(e,"checked",t,!1)}function Bv(e,t){JC(e,t);var r=to(t.value),n=t.type;if(r!=null)n==="number"?(r===0&&e.value===""||e.value!=r)&&(e.value=""+r):e.value!==""+r&&(e.value=""+r);else if(n==="submit"||n==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?zv(e,t.type,r):t.hasOwnProperty("defaultValue")&&zv(e,t.type,to(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function mS(e,t,r){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var n=t.type;if(!(n!=="submit"&&n!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,r||t===e.value||(e.value=t),e.defaultValue=t}r=e.name,r!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,r!==""&&(e.name=r)}function zv(e,t,r){(t!=="number"||qf(e.ownerDocument)!==e)&&(r==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+r&&(e.defaultValue=""+r))}var Mc=Array.isArray;function Xs(e,t,r,n){if(e=e.options,t){t={};for(var a=0;a"+t.valueOf().toString()+"",t=Ad.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function tu(e,t){if(t){var r=e.firstChild;if(r&&r===e.lastChild&&r.nodeType===3){r.nodeValue=t;return}}e.textContent=t}var Wc={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},F3=["Webkit","ms","Moz","O"];Object.keys(Wc).forEach(function(e){F3.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),Wc[t]=Wc[e]})});function nO(e,t,r){return t==null||typeof t=="boolean"||t===""?"":r||typeof t!="number"||t===0||Wc.hasOwnProperty(e)&&Wc[e]?(""+t).trim():t+"px"}function aO(e,t){e=e.style;for(var r in t)if(t.hasOwnProperty(r)){var n=r.indexOf("--")===0,a=nO(r,t[r],n);r==="float"&&(r="cssFloat"),n?e.setProperty(r,a):e[r]=a}}var B3=ft({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function Uv(e,t){if(t){if(B3[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(q(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(q(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(q(61))}if(t.style!=null&&typeof t.style!="object")throw Error(q(62))}}function Hv(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var Kv=null;function h0(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var Yv=null,Zs=null,Js=null;function yS(e){if(e=Uu(e)){if(typeof Yv!="function")throw Error(q(280));var t=e.stateNode;t&&(t=wp(t),Yv(e.stateNode,e.type,t))}}function iO(e){Zs?Js?Js.push(e):Js=[e]:Zs=e}function oO(){if(Zs){var e=Zs,t=Js;if(Js=Zs=null,yS(e),t)for(e=0;e>>=0,e===0?32:31-(X3(e)/Z3|0)|0}var _d=64,Dd=4194304;function Rc(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function Yf(e,t){var r=e.pendingLanes;if(r===0)return 0;var n=0,a=e.suspendedLanes,i=e.pingedLanes,o=r&268435455;if(o!==0){var s=o&~a;s!==0?n=Rc(s):(i&=o,i!==0&&(n=Rc(i)))}else o=r&~a,o!==0?n=Rc(o):i!==0&&(n=Rc(i));if(n===0)return 0;if(t!==0&&t!==n&&!(t&a)&&(a=n&-n,i=t&-t,a>=i||a===16&&(i&4194240)!==0))return t;if(n&4&&(n|=r&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=n;0r;r++)t.push(e);return t}function Wu(e,t,r){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-In(t),e[t]=r}function rF(e,t){var r=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var n=e.eventTimes;for(e=e.expirationTimes;0=Uc),CS=" ",OS=!1;function jO(e,t){switch(e){case"keyup":return TF.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function CO(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var Bs=!1;function _F(e,t){switch(e){case"compositionend":return CO(t);case"keypress":return t.which!==32?null:(OS=!0,CS);case"textInput":return e=t.data,e===CS&&OS?null:e;default:return null}}function DF(e,t){if(Bs)return e==="compositionend"||!w0&&jO(e,t)?(e=PO(),Sf=y0=Wi=null,Bs=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:r,offset:t-e};e=n}e:{for(;r;){if(r.nextSibling){r=r.nextSibling;break e}r=r.parentNode}r=void 0}r=_S(r)}}function AO(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?AO(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function _O(){for(var e=window,t=qf();t instanceof e.HTMLIFrameElement;){try{var r=typeof t.contentWindow.location.href=="string"}catch{r=!1}if(r)e=t.contentWindow;else break;t=qf(e.document)}return t}function S0(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function WF(e){var t=_O(),r=e.focusedElem,n=e.selectionRange;if(t!==r&&r&&r.ownerDocument&&AO(r.ownerDocument.documentElement,r)){if(n!==null&&S0(r)){if(t=n.start,e=n.end,e===void 0&&(e=t),"selectionStart"in r)r.selectionStart=t,r.selectionEnd=Math.min(e,r.value.length);else if(e=(t=r.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var a=r.textContent.length,i=Math.min(n.start,a);n=n.end===void 0?i:Math.min(n.end,a),!e.extend&&i>n&&(a=n,n=i,i=a),a=DS(r,i);var o=DS(r,n);a&&o&&(e.rangeCount!==1||e.anchorNode!==a.node||e.anchorOffset!==a.offset||e.focusNode!==o.node||e.focusOffset!==o.offset)&&(t=t.createRange(),t.setStart(a.node,a.offset),e.removeAllRanges(),i>n?(e.addRange(t),e.extend(o.node,o.offset)):(t.setEnd(o.node,o.offset),e.addRange(t)))}}for(t=[],e=r;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof r.focus=="function"&&r.focus(),r=0;r=document.documentMode,zs=null,Jv=null,Kc=null,ey=!1;function MS(e,t,r){var n=r.window===r?r.document:r.nodeType===9?r:r.ownerDocument;ey||zs==null||zs!==qf(n)||(n=zs,"selectionStart"in n&&S0(n)?n={start:n.selectionStart,end:n.selectionEnd}:(n=(n.ownerDocument&&n.ownerDocument.defaultView||window).getSelection(),n={anchorNode:n.anchorNode,anchorOffset:n.anchorOffset,focusNode:n.focusNode,focusOffset:n.focusOffset}),Kc&&su(Kc,n)||(Kc=n,n=Qf(Jv,"onSelect"),0Us||(e.current=oy[Us],oy[Us]=null,Us--)}function Ge(e,t){Us++,oy[Us]=e.current,e.current=t}var ro={},sr=co(ro),Nr=co(!1),ns=ro;function wl(e,t){var r=e.type.contextTypes;if(!r)return ro;var n=e.stateNode;if(n&&n.__reactInternalMemoizedUnmaskedChildContext===t)return n.__reactInternalMemoizedMaskedChildContext;var a={},i;for(i in r)a[i]=t[i];return n&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=a),a}function Tr(e){return e=e.childContextTypes,e!=null}function Zf(){Je(Nr),Je(sr)}function zS(e,t,r){if(sr.current!==ro)throw Error(q(168));Ge(sr,t),Ge(Nr,r)}function zO(e,t,r){var n=e.stateNode;if(t=t.childContextTypes,typeof n.getChildContext!="function")return r;n=n.getChildContext();for(var a in n)if(!(a in t))throw Error(q(108,L3(e)||"Unknown",a));return ft({},r,n)}function Jf(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||ro,ns=sr.current,Ge(sr,e),Ge(Nr,Nr.current),!0}function WS(e,t,r){var n=e.stateNode;if(!n)throw Error(q(169));r?(e=zO(e,t,ns),n.__reactInternalMemoizedMergedChildContext=e,Je(Nr),Je(sr),Ge(sr,e)):Je(Nr),Ge(Nr,r)}var Aa=null,Sp=!1,_g=!1;function WO(e){Aa===null?Aa=[e]:Aa.push(e)}function e4(e){Sp=!0,WO(e)}function uo(){if(!_g&&Aa!==null){_g=!0;var e=0,t=qe;try{var r=Aa;for(qe=1;e>=o,a-=o,$a=1<<32-In(t)+a|r<j?(C=E,E=null):C=E.sibling;var O=h(b,E,w[j],S);if(O===null){E===null&&(E=C);break}e&&E&&O.alternate===null&&t(b,E),x=i(O,x,j),P===null?k=O:P.sibling=O,P=O,E=C}if(j===w.length)return r(b,E),at&&Eo(b,j),k;if(E===null){for(;jj?(C=E,E=null):C=E.sibling;var T=h(b,E,O.value,S);if(T===null){E===null&&(E=C);break}e&&E&&T.alternate===null&&t(b,E),x=i(T,x,j),P===null?k=T:P.sibling=T,P=T,E=C}if(O.done)return r(b,E),at&&Eo(b,j),k;if(E===null){for(;!O.done;j++,O=w.next())O=f(b,O.value,S),O!==null&&(x=i(O,x,j),P===null?k=O:P.sibling=O,P=O);return at&&Eo(b,j),k}for(E=n(b,E);!O.done;j++,O=w.next())O=m(E,b,j,O.value,S),O!==null&&(e&&O.alternate!==null&&E.delete(O.key===null?j:O.key),x=i(O,x,j),P===null?k=O:P.sibling=O,P=O);return e&&E.forEach(function(A){return t(b,A)}),at&&Eo(b,j),k}function y(b,x,w,S){if(typeof w=="object"&&w!==null&&w.type===Fs&&w.key===null&&(w=w.props.children),typeof w=="object"&&w!==null){switch(w.$$typeof){case Nd:e:{for(var k=w.key,P=x;P!==null;){if(P.key===k){if(k=w.type,k===Fs){if(P.tag===7){r(b,P.sibling),x=a(P,w.props.children),x.return=b,b=x;break e}}else if(P.elementType===k||typeof k=="object"&&k!==null&&k.$$typeof===Ei&&HS(k)===P.type){r(b,P.sibling),x=a(P,w.props),x.ref=vc(b,P,w),x.return=b,b=x;break e}r(b,P);break}else t(b,P);P=P.sibling}w.type===Fs?(x=Xo(w.props.children,b.mode,S,w.key),x.return=b,b=x):(S=Tf(w.type,w.key,w.props,null,b.mode,S),S.ref=vc(b,x,w),S.return=b,b=S)}return o(b);case $s:e:{for(P=w.key;x!==null;){if(x.key===P)if(x.tag===4&&x.stateNode.containerInfo===w.containerInfo&&x.stateNode.implementation===w.implementation){r(b,x.sibling),x=a(x,w.children||[]),x.return=b,b=x;break e}else{r(b,x);break}else t(b,x);x=x.sibling}x=Bg(w,b.mode,S),x.return=b,b=x}return o(b);case Ei:return P=w._init,y(b,x,P(w._payload),S)}if(Mc(w))return v(b,x,w,S);if(fc(w))return g(b,x,w,S);Bd(b,w)}return typeof w=="string"&&w!==""||typeof w=="number"?(w=""+w,x!==null&&x.tag===6?(r(b,x.sibling),x=a(x,w),x.return=b,b=x):(r(b,x),x=Fg(w,b.mode,S),x.return=b,b=x),o(b)):r(b,x)}return y}var kl=KO(!0),YO=KO(!1),rh=co(null),nh=null,Ys=null,j0=null;function C0(){j0=Ys=nh=null}function O0(e){var t=rh.current;Je(rh),e._currentValue=t}function cy(e,t,r){for(;e!==null;){var n=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,n!==null&&(n.childLanes|=t)):n!==null&&(n.childLanes&t)!==t&&(n.childLanes|=t),e===r)break;e=e.return}}function tl(e,t){nh=e,j0=Ys=null,e=e.dependencies,e!==null&&e.firstContext!==null&&(e.lanes&t&&(Er=!0),e.firstContext=null)}function gn(e){var t=e._currentValue;if(j0!==e)if(e={context:e,memoizedValue:t,next:null},Ys===null){if(nh===null)throw Error(q(308));Ys=e,nh.dependencies={lanes:0,firstContext:e}}else Ys=Ys.next=e;return t}var _o=null;function N0(e){_o===null?_o=[e]:_o.push(e)}function VO(e,t,r,n){var a=t.interleaved;return a===null?(r.next=r,N0(t)):(r.next=a.next,a.next=r),t.interleaved=r,Ja(e,n)}function Ja(e,t){e.lanes|=t;var r=e.alternate;for(r!==null&&(r.lanes|=t),r=e,e=e.return;e!==null;)e.childLanes|=t,r=e.alternate,r!==null&&(r.childLanes|=t),r=e,e=e.return;return r.tag===3?r.stateNode:null}var ji=!1;function T0(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function GO(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function Ha(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function Gi(e,t,r){var n=e.updateQueue;if(n===null)return null;if(n=n.shared,De&2){var a=n.pending;return a===null?t.next=t:(t.next=a.next,a.next=t),n.pending=t,Ja(e,r)}return a=n.interleaved,a===null?(t.next=t,N0(n)):(t.next=a.next,a.next=t),n.interleaved=t,Ja(e,r)}function Pf(e,t,r){if(t=t.updateQueue,t!==null&&(t=t.shared,(r&4194240)!==0)){var n=t.lanes;n&=e.pendingLanes,r|=n,t.lanes=r,m0(e,r)}}function KS(e,t){var r=e.updateQueue,n=e.alternate;if(n!==null&&(n=n.updateQueue,r===n)){var a=null,i=null;if(r=r.firstBaseUpdate,r!==null){do{var o={eventTime:r.eventTime,lane:r.lane,tag:r.tag,payload:r.payload,callback:r.callback,next:null};i===null?a=i=o:i=i.next=o,r=r.next}while(r!==null);i===null?a=i=t:i=i.next=t}else a=i=t;r={baseState:n.baseState,firstBaseUpdate:a,lastBaseUpdate:i,shared:n.shared,effects:n.effects},e.updateQueue=r;return}e=r.lastBaseUpdate,e===null?r.firstBaseUpdate=t:e.next=t,r.lastBaseUpdate=t}function ah(e,t,r,n){var a=e.updateQueue;ji=!1;var i=a.firstBaseUpdate,o=a.lastBaseUpdate,s=a.shared.pending;if(s!==null){a.shared.pending=null;var l=s,c=l.next;l.next=null,o===null?i=c:o.next=c,o=l;var u=e.alternate;u!==null&&(u=u.updateQueue,s=u.lastBaseUpdate,s!==o&&(s===null?u.firstBaseUpdate=c:s.next=c,u.lastBaseUpdate=l))}if(i!==null){var f=a.baseState;o=0,u=c=l=null,s=i;do{var h=s.lane,m=s.eventTime;if((n&h)===h){u!==null&&(u=u.next={eventTime:m,lane:0,tag:s.tag,payload:s.payload,callback:s.callback,next:null});e:{var v=e,g=s;switch(h=t,m=r,g.tag){case 1:if(v=g.payload,typeof v=="function"){f=v.call(m,f,h);break e}f=v;break e;case 3:v.flags=v.flags&-65537|128;case 0:if(v=g.payload,h=typeof v=="function"?v.call(m,f,h):v,h==null)break e;f=ft({},f,h);break e;case 2:ji=!0}}s.callback!==null&&s.lane!==0&&(e.flags|=64,h=a.effects,h===null?a.effects=[s]:h.push(s))}else m={eventTime:m,lane:h,tag:s.tag,payload:s.payload,callback:s.callback,next:null},u===null?(c=u=m,l=f):u=u.next=m,o|=h;if(s=s.next,s===null){if(s=a.shared.pending,s===null)break;h=s,s=h.next,h.next=null,a.lastBaseUpdate=h,a.shared.pending=null}}while(!0);if(u===null&&(l=f),a.baseState=l,a.firstBaseUpdate=c,a.lastBaseUpdate=u,t=a.shared.interleaved,t!==null){a=t;do o|=a.lane,a=a.next;while(a!==t)}else i===null&&(a.shared.lanes=0);os|=o,e.lanes=o,e.memoizedState=f}}function YS(e,t,r){if(e=t.effects,t.effects=null,e!==null)for(t=0;tr?r:4,e(!0);var n=Mg.transition;Mg.transition={};try{e(!1),t()}finally{qe=r,Mg.transition=n}}function fN(){return vn().memoizedState}function a4(e,t,r){var n=Xi(e);if(r={lane:n,action:r,hasEagerState:!1,eagerState:null,next:null},hN(e))pN(t,r);else if(r=VO(e,t,r,n),r!==null){var a=vr();Ln(r,e,n,a),mN(r,t,n)}}function i4(e,t,r){var n=Xi(e),a={lane:n,action:r,hasEagerState:!1,eagerState:null,next:null};if(hN(e))pN(t,a);else{var i=e.alternate;if(e.lanes===0&&(i===null||i.lanes===0)&&(i=t.lastRenderedReducer,i!==null))try{var o=t.lastRenderedState,s=i(o,r);if(a.hasEagerState=!0,a.eagerState=s,$n(s,o)){var l=t.interleaved;l===null?(a.next=a,N0(t)):(a.next=l.next,l.next=a),t.interleaved=a;return}}catch{}finally{}r=VO(e,t,a,n),r!==null&&(a=vr(),Ln(r,e,n,a),mN(r,t,n))}}function hN(e){var t=e.alternate;return e===dt||t!==null&&t===dt}function pN(e,t){Yc=oh=!0;var r=e.pending;r===null?t.next=t:(t.next=r.next,r.next=t),e.pending=t}function mN(e,t,r){if(r&4194240){var n=t.lanes;n&=e.pendingLanes,r|=n,t.lanes=r,m0(e,r)}}var sh={readContext:gn,useCallback:Zt,useContext:Zt,useEffect:Zt,useImperativeHandle:Zt,useInsertionEffect:Zt,useLayoutEffect:Zt,useMemo:Zt,useReducer:Zt,useRef:Zt,useState:Zt,useDebugValue:Zt,useDeferredValue:Zt,useTransition:Zt,useMutableSource:Zt,useSyncExternalStore:Zt,useId:Zt,unstable_isNewReconciler:!1},o4={readContext:gn,useCallback:function(e,t){return Vn().memoizedState=[e,t===void 0?null:t],e},useContext:gn,useEffect:GS,useImperativeHandle:function(e,t,r){return r=r!=null?r.concat([e]):null,jf(4194308,4,sN.bind(null,t,e),r)},useLayoutEffect:function(e,t){return jf(4194308,4,e,t)},useInsertionEffect:function(e,t){return jf(4,2,e,t)},useMemo:function(e,t){var r=Vn();return t=t===void 0?null:t,e=e(),r.memoizedState=[e,t],e},useReducer:function(e,t,r){var n=Vn();return t=r!==void 0?r(t):t,n.memoizedState=n.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},n.queue=e,e=e.dispatch=a4.bind(null,dt,e),[n.memoizedState,e]},useRef:function(e){var t=Vn();return e={current:e},t.memoizedState=e},useState:VS,useDebugValue:$0,useDeferredValue:function(e){return Vn().memoizedState=e},useTransition:function(){var e=VS(!1),t=e[0];return e=n4.bind(null,e[1]),Vn().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,r){var n=dt,a=Vn();if(at){if(r===void 0)throw Error(q(407));r=r()}else{if(r=t(),$t===null)throw Error(q(349));is&30||JO(n,t,r)}a.memoizedState=r;var i={value:r,getSnapshot:t};return a.queue=i,GS(tN.bind(null,n,i,e),[e]),n.flags|=2048,mu(9,eN.bind(null,n,i,r,t),void 0,null),r},useId:function(){var e=Vn(),t=$t.identifierPrefix;if(at){var r=Fa,n=$a;r=(n&~(1<<32-In(n)-1)).toString(32)+r,t=":"+t+"R"+r,r=hu++,0<\/script>",e=e.removeChild(e.firstChild)):typeof n.is=="string"?e=o.createElement(r,{is:n.is}):(e=o.createElement(r),r==="select"&&(o=e,n.multiple?o.multiple=!0:n.size&&(o.size=n.size))):e=o.createElementNS(e,r),e[Jn]=t,e[uu]=n,EN(e,t,!1,!1),t.stateNode=e;e:{switch(o=Hv(r,n),r){case"dialog":Xe("cancel",e),Xe("close",e),a=n;break;case"iframe":case"object":case"embed":Xe("load",e),a=n;break;case"video":case"audio":for(a=0;ajl&&(t.flags|=128,n=!0,yc(i,!1),t.lanes=4194304)}else{if(!n)if(e=ih(o),e!==null){if(t.flags|=128,n=!0,r=e.updateQueue,r!==null&&(t.updateQueue=r,t.flags|=4),yc(i,!0),i.tail===null&&i.tailMode==="hidden"&&!o.alternate&&!at)return Jt(t),null}else 2*vt()-i.renderingStartTime>jl&&r!==1073741824&&(t.flags|=128,n=!0,yc(i,!1),t.lanes=4194304);i.isBackwards?(o.sibling=t.child,t.child=o):(r=i.last,r!==null?r.sibling=o:t.child=o,i.last=o)}return i.tail!==null?(t=i.tail,i.rendering=t,i.tail=t.sibling,i.renderingStartTime=vt(),t.sibling=null,r=ct.current,Ge(ct,n?r&1|2:r&1),t):(Jt(t),null);case 22:case 23:return U0(),n=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==n&&(t.flags|=8192),n&&t.mode&1?Fr&1073741824&&(Jt(t),t.subtreeFlags&6&&(t.flags|=8192)):Jt(t),null;case 24:return null;case 25:return null}throw Error(q(156,t.tag))}function p4(e,t){switch(P0(t),t.tag){case 1:return Tr(t.type)&&Zf(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return Pl(),Je(Nr),Je(sr),D0(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 5:return _0(t),null;case 13:if(Je(ct),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(q(340));Sl()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return Je(ct),null;case 4:return Pl(),null;case 10:return O0(t.type._context),null;case 22:case 23:return U0(),null;case 24:return null;default:return null}}var Wd=!1,nr=!1,m4=typeof WeakSet=="function"?WeakSet:Set,ee=null;function Vs(e,t){var r=e.ref;if(r!==null)if(typeof r=="function")try{r(null)}catch(n){pt(e,t,n)}else r.current=null}function yy(e,t,r){try{r()}catch(n){pt(e,t,n)}}var ok=!1;function g4(e,t){if(ty=Vf,e=_O(),S0(e)){if("selectionStart"in e)var r={start:e.selectionStart,end:e.selectionEnd};else e:{r=(r=e.ownerDocument)&&r.defaultView||window;var n=r.getSelection&&r.getSelection();if(n&&n.rangeCount!==0){r=n.anchorNode;var a=n.anchorOffset,i=n.focusNode;n=n.focusOffset;try{r.nodeType,i.nodeType}catch{r=null;break e}var o=0,s=-1,l=-1,c=0,u=0,f=e,h=null;t:for(;;){for(var m;f!==r||a!==0&&f.nodeType!==3||(s=o+a),f!==i||n!==0&&f.nodeType!==3||(l=o+n),f.nodeType===3&&(o+=f.nodeValue.length),(m=f.firstChild)!==null;)h=f,f=m;for(;;){if(f===e)break t;if(h===r&&++c===a&&(s=o),h===i&&++u===n&&(l=o),(m=f.nextSibling)!==null)break;f=h,h=f.parentNode}f=m}r=s===-1||l===-1?null:{start:s,end:l}}else r=null}r=r||{start:0,end:0}}else r=null;for(ry={focusedElem:e,selectionRange:r},Vf=!1,ee=t;ee!==null;)if(t=ee,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,ee=e;else for(;ee!==null;){t=ee;try{var v=t.alternate;if(t.flags&1024)switch(t.tag){case 0:case 11:case 15:break;case 1:if(v!==null){var g=v.memoizedProps,y=v.memoizedState,b=t.stateNode,x=b.getSnapshotBeforeUpdate(t.elementType===t.type?g:Cn(t.type,g),y);b.__reactInternalSnapshotBeforeUpdate=x}break;case 3:var w=t.stateNode.containerInfo;w.nodeType===1?w.textContent="":w.nodeType===9&&w.documentElement&&w.removeChild(w.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(q(163))}}catch(S){pt(t,t.return,S)}if(e=t.sibling,e!==null){e.return=t.return,ee=e;break}ee=t.return}return v=ok,ok=!1,v}function Vc(e,t,r){var n=t.updateQueue;if(n=n!==null?n.lastEffect:null,n!==null){var a=n=n.next;do{if((a.tag&e)===e){var i=a.destroy;a.destroy=void 0,i!==void 0&&yy(t,r,i)}a=a.next}while(a!==n)}}function Ep(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var r=t=t.next;do{if((r.tag&e)===e){var n=r.create;r.destroy=n()}r=r.next}while(r!==t)}}function xy(e){var t=e.ref;if(t!==null){var r=e.stateNode;switch(e.tag){case 5:e=r;break;default:e=r}typeof t=="function"?t(e):t.current=e}}function ON(e){var t=e.alternate;t!==null&&(e.alternate=null,ON(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[Jn],delete t[uu],delete t[iy],delete t[ZF],delete t[JF])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function NN(e){return e.tag===5||e.tag===3||e.tag===4}function sk(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||NN(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function by(e,t,r){var n=e.tag;if(n===5||n===6)e=e.stateNode,t?r.nodeType===8?r.parentNode.insertBefore(e,t):r.insertBefore(e,t):(r.nodeType===8?(t=r.parentNode,t.insertBefore(e,r)):(t=r,t.appendChild(e)),r=r._reactRootContainer,r!=null||t.onclick!==null||(t.onclick=Xf));else if(n!==4&&(e=e.child,e!==null))for(by(e,t,r),e=e.sibling;e!==null;)by(e,t,r),e=e.sibling}function wy(e,t,r){var n=e.tag;if(n===5||n===6)e=e.stateNode,t?r.insertBefore(e,t):r.appendChild(e);else if(n!==4&&(e=e.child,e!==null))for(wy(e,t,r),e=e.sibling;e!==null;)wy(e,t,r),e=e.sibling}var qt=null,Tn=!1;function xi(e,t,r){for(r=r.child;r!==null;)TN(e,t,r),r=r.sibling}function TN(e,t,r){if(la&&typeof la.onCommitFiberUnmount=="function")try{la.onCommitFiberUnmount(vp,r)}catch{}switch(r.tag){case 5:nr||Vs(r,t);case 6:var n=qt,a=Tn;qt=null,xi(e,t,r),qt=n,Tn=a,qt!==null&&(Tn?(e=qt,r=r.stateNode,e.nodeType===8?e.parentNode.removeChild(r):e.removeChild(r)):qt.removeChild(r.stateNode));break;case 18:qt!==null&&(Tn?(e=qt,r=r.stateNode,e.nodeType===8?Ag(e.parentNode,r):e.nodeType===1&&Ag(e,r),iu(e)):Ag(qt,r.stateNode));break;case 4:n=qt,a=Tn,qt=r.stateNode.containerInfo,Tn=!0,xi(e,t,r),qt=n,Tn=a;break;case 0:case 11:case 14:case 15:if(!nr&&(n=r.updateQueue,n!==null&&(n=n.lastEffect,n!==null))){a=n=n.next;do{var i=a,o=i.destroy;i=i.tag,o!==void 0&&(i&2||i&4)&&yy(r,t,o),a=a.next}while(a!==n)}xi(e,t,r);break;case 1:if(!nr&&(Vs(r,t),n=r.stateNode,typeof n.componentWillUnmount=="function"))try{n.props=r.memoizedProps,n.state=r.memoizedState,n.componentWillUnmount()}catch(s){pt(r,t,s)}xi(e,t,r);break;case 21:xi(e,t,r);break;case 22:r.mode&1?(nr=(n=nr)||r.memoizedState!==null,xi(e,t,r),nr=n):xi(e,t,r);break;default:xi(e,t,r)}}function lk(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var r=e.stateNode;r===null&&(r=e.stateNode=new m4),t.forEach(function(n){var a=E4.bind(null,e,n);r.has(n)||(r.add(n),n.then(a,a))})}}function Pn(e,t){var r=t.deletions;if(r!==null)for(var n=0;na&&(a=o),n&=~i}if(n=a,n=vt()-n,n=(120>n?120:480>n?480:1080>n?1080:1920>n?1920:3e3>n?3e3:4320>n?4320:1960*y4(n/1960))-n,10e?16:e,qi===null)var n=!1;else{if(e=qi,qi=null,uh=0,De&6)throw Error(q(331));var a=De;for(De|=4,ee=e.current;ee!==null;){var i=ee,o=i.child;if(ee.flags&16){var s=i.deletions;if(s!==null){for(var l=0;lvt()-W0?Qo(e,0):z0|=r),Ar(e,t)}function $N(e,t){t===0&&(e.mode&1?(t=Dd,Dd<<=1,!(Dd&130023424)&&(Dd=4194304)):t=1);var r=vr();e=Ja(e,t),e!==null&&(Wu(e,t,r),Ar(e,r))}function P4(e){var t=e.memoizedState,r=0;t!==null&&(r=t.retryLane),$N(e,r)}function E4(e,t){var r=0;switch(e.tag){case 13:var n=e.stateNode,a=e.memoizedState;a!==null&&(r=a.retryLane);break;case 19:n=e.stateNode;break;default:throw Error(q(314))}n!==null&&n.delete(t),$N(e,r)}var FN;FN=function(e,t,r){if(e!==null)if(e.memoizedProps!==t.pendingProps||Nr.current)Er=!0;else{if(!(e.lanes&r)&&!(t.flags&128))return Er=!1,f4(e,t,r);Er=!!(e.flags&131072)}else Er=!1,at&&t.flags&1048576&&qO(t,th,t.index);switch(t.lanes=0,t.tag){case 2:var n=t.type;Cf(e,t),e=t.pendingProps;var a=wl(t,sr.current);tl(t,r),a=R0(null,t,n,e,a,r);var i=I0();return t.flags|=1,typeof a=="object"&&a!==null&&typeof a.render=="function"&&a.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,Tr(n)?(i=!0,Jf(t)):i=!1,t.memoizedState=a.state!==null&&a.state!==void 0?a.state:null,T0(t),a.updater=Pp,t.stateNode=a,a._reactInternals=t,dy(t,n,e,r),t=py(null,t,n,!0,i,r)):(t.tag=0,at&&i&&k0(t),fr(null,t,a,r),t=t.child),t;case 16:n=t.elementType;e:{switch(Cf(e,t),e=t.pendingProps,a=n._init,n=a(n._payload),t.type=n,a=t.tag=C4(n),e=Cn(n,e),a){case 0:t=hy(null,t,n,e,r);break e;case 1:t=nk(null,t,n,e,r);break e;case 11:t=tk(null,t,n,e,r);break e;case 14:t=rk(null,t,n,Cn(n.type,e),r);break e}throw Error(q(306,n,""))}return t;case 0:return n=t.type,a=t.pendingProps,a=t.elementType===n?a:Cn(n,a),hy(e,t,n,a,r);case 1:return n=t.type,a=t.pendingProps,a=t.elementType===n?a:Cn(n,a),nk(e,t,n,a,r);case 3:e:{if(SN(t),e===null)throw Error(q(387));n=t.pendingProps,i=t.memoizedState,a=i.element,GO(e,t),ah(t,n,null,r);var o=t.memoizedState;if(n=o.element,i.isDehydrated)if(i={element:n,isDehydrated:!1,cache:o.cache,pendingSuspenseBoundaries:o.pendingSuspenseBoundaries,transitions:o.transitions},t.updateQueue.baseState=i,t.memoizedState=i,t.flags&256){a=El(Error(q(423)),t),t=ak(e,t,n,r,a);break e}else if(n!==a){a=El(Error(q(424)),t),t=ak(e,t,n,r,a);break e}else for(Hr=Vi(t.stateNode.containerInfo.firstChild),Kr=t,at=!0,_n=null,r=YO(t,null,n,r),t.child=r;r;)r.flags=r.flags&-3|4096,r=r.sibling;else{if(Sl(),n===a){t=ei(e,t,r);break e}fr(e,t,n,r)}t=t.child}return t;case 5:return QO(t),e===null&&ly(t),n=t.type,a=t.pendingProps,i=e!==null?e.memoizedProps:null,o=a.children,ny(n,a)?o=null:i!==null&&ny(n,i)&&(t.flags|=32),wN(e,t),fr(e,t,o,r),t.child;case 6:return e===null&&ly(t),null;case 13:return kN(e,t,r);case 4:return A0(t,t.stateNode.containerInfo),n=t.pendingProps,e===null?t.child=kl(t,null,n,r):fr(e,t,n,r),t.child;case 11:return n=t.type,a=t.pendingProps,a=t.elementType===n?a:Cn(n,a),tk(e,t,n,a,r);case 7:return fr(e,t,t.pendingProps,r),t.child;case 8:return fr(e,t,t.pendingProps.children,r),t.child;case 12:return fr(e,t,t.pendingProps.children,r),t.child;case 10:e:{if(n=t.type._context,a=t.pendingProps,i=t.memoizedProps,o=a.value,Ge(rh,n._currentValue),n._currentValue=o,i!==null)if($n(i.value,o)){if(i.children===a.children&&!Nr.current){t=ei(e,t,r);break e}}else for(i=t.child,i!==null&&(i.return=t);i!==null;){var s=i.dependencies;if(s!==null){o=i.child;for(var l=s.firstContext;l!==null;){if(l.context===n){if(i.tag===1){l=Ha(-1,r&-r),l.tag=2;var c=i.updateQueue;if(c!==null){c=c.shared;var u=c.pending;u===null?l.next=l:(l.next=u.next,u.next=l),c.pending=l}}i.lanes|=r,l=i.alternate,l!==null&&(l.lanes|=r),cy(i.return,r,t),s.lanes|=r;break}l=l.next}}else if(i.tag===10)o=i.type===t.type?null:i.child;else if(i.tag===18){if(o=i.return,o===null)throw Error(q(341));o.lanes|=r,s=o.alternate,s!==null&&(s.lanes|=r),cy(o,r,t),o=i.sibling}else o=i.child;if(o!==null)o.return=i;else for(o=i;o!==null;){if(o===t){o=null;break}if(i=o.sibling,i!==null){i.return=o.return,o=i;break}o=o.return}i=o}fr(e,t,a.children,r),t=t.child}return t;case 9:return a=t.type,n=t.pendingProps.children,tl(t,r),a=gn(a),n=n(a),t.flags|=1,fr(e,t,n,r),t.child;case 14:return n=t.type,a=Cn(n,t.pendingProps),a=Cn(n.type,a),rk(e,t,n,a,r);case 15:return xN(e,t,t.type,t.pendingProps,r);case 17:return n=t.type,a=t.pendingProps,a=t.elementType===n?a:Cn(n,a),Cf(e,t),t.tag=1,Tr(n)?(e=!0,Jf(t)):e=!1,tl(t,r),gN(t,n,a),dy(t,n,a,r),py(null,t,n,!0,e,r);case 19:return PN(e,t,r);case 22:return bN(e,t,r)}throw Error(q(156,t.tag))};function BN(e,t){return hO(e,t)}function j4(e,t,r,n){this.tag=e,this.key=r,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=n,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function hn(e,t,r,n){return new j4(e,t,r,n)}function K0(e){return e=e.prototype,!(!e||!e.isReactComponent)}function C4(e){if(typeof e=="function")return K0(e)?1:0;if(e!=null){if(e=e.$$typeof,e===d0)return 11;if(e===f0)return 14}return 2}function Zi(e,t){var r=e.alternate;return r===null?(r=hn(e.tag,t,e.key,e.mode),r.elementType=e.elementType,r.type=e.type,r.stateNode=e.stateNode,r.alternate=e,e.alternate=r):(r.pendingProps=t,r.type=e.type,r.flags=0,r.subtreeFlags=0,r.deletions=null),r.flags=e.flags&14680064,r.childLanes=e.childLanes,r.lanes=e.lanes,r.child=e.child,r.memoizedProps=e.memoizedProps,r.memoizedState=e.memoizedState,r.updateQueue=e.updateQueue,t=e.dependencies,r.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},r.sibling=e.sibling,r.index=e.index,r.ref=e.ref,r}function Tf(e,t,r,n,a,i){var o=2;if(n=e,typeof e=="function")K0(e)&&(o=1);else if(typeof e=="string")o=5;else e:switch(e){case Fs:return Xo(r.children,a,i,t);case u0:o=8,a|=8;break;case Rv:return e=hn(12,r,t,a|2),e.elementType=Rv,e.lanes=i,e;case Iv:return e=hn(13,r,t,a),e.elementType=Iv,e.lanes=i,e;case Lv:return e=hn(19,r,t,a),e.elementType=Lv,e.lanes=i,e;case QC:return Cp(r,a,i,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case VC:o=10;break e;case GC:o=9;break e;case d0:o=11;break e;case f0:o=14;break e;case Ei:o=16,n=null;break e}throw Error(q(130,e==null?e:typeof e,""))}return t=hn(o,r,t,a),t.elementType=e,t.type=n,t.lanes=i,t}function Xo(e,t,r,n){return e=hn(7,e,n,t),e.lanes=r,e}function Cp(e,t,r,n){return e=hn(22,e,n,t),e.elementType=QC,e.lanes=r,e.stateNode={isHidden:!1},e}function Fg(e,t,r){return e=hn(6,e,null,t),e.lanes=r,e}function Bg(e,t,r){return t=hn(4,e.children!==null?e.children:[],e.key,t),t.lanes=r,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function O4(e,t,r,n,a){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=bg(0),this.expirationTimes=bg(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=bg(0),this.identifierPrefix=n,this.onRecoverableError=a,this.mutableSourceEagerHydrationData=null}function Y0(e,t,r,n,a,i,o,s,l){return e=new O4(e,t,r,s,l),t===1?(t=1,i===!0&&(t|=8)):t=0,i=hn(3,null,null,t),e.current=i,i.stateNode=e,i.memoizedState={element:n,isDehydrated:r,cache:null,transitions:null,pendingSuspenseBoundaries:null},T0(i),e}function N4(e,t,r){var n=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(UN)}catch(e){console.error(e)}}UN(),UC.exports=Zr;var fo=UC.exports;const M4=ba(fo);var gk=fo;Dv.createRoot=gk.createRoot,Dv.hydrateRoot=gk.hydrateRoot;var Yl=class{constructor(){this.listeners=new Set,this.subscribe=this.subscribe.bind(this)}subscribe(e){return this.listeners.add(e),this.onSubscribe(),()=>{this.listeners.delete(e),this.onUnsubscribe()}}hasListeners(){return this.listeners.size>0}onSubscribe(){}onUnsubscribe(){}},R4={setTimeout:(e,t)=>setTimeout(e,t),clearTimeout:e=>clearTimeout(e),setInterval:(e,t)=>setInterval(e,t),clearInterval:e=>clearInterval(e)},Di,t0,SC,I4=(SC=class{constructor(){ae(this,Di,R4);ae(this,t0,!1)}setTimeoutProvider(e){G(this,Di,e)}setTimeout(e,t){return N(this,Di).setTimeout(e,t)}clearTimeout(e){N(this,Di).clearTimeout(e)}setInterval(e,t){return N(this,Di).setInterval(e,t)}clearInterval(e){N(this,Di).clearInterval(e)}},Di=new WeakMap,t0=new WeakMap,SC),Mo=new I4;function L4(e){setTimeout(e,0)}var ls=typeof window>"u"||"Deno"in globalThis;function hr(){}function $4(e,t){return typeof e=="function"?e(t):e}function jy(e){return typeof e=="number"&&e>=0&&e!==1/0}function HN(e,t){return Math.max(e+(t||0)-Date.now(),0)}function Ji(e,t){return typeof e=="function"?e(t):e}function cn(e,t){return typeof e=="function"?e(t):e}function vk(e,t){const{type:r="all",exact:n,fetchStatus:a,predicate:i,queryKey:o,stale:s}=e;if(o){if(n){if(t.queryHash!==X0(o,t.options))return!1}else if(!vu(t.queryKey,o))return!1}if(r!=="all"){const l=t.isActive();if(r==="active"&&!l||r==="inactive"&&l)return!1}return!(typeof s=="boolean"&&t.isStale()!==s||a&&a!==t.state.fetchStatus||i&&!i(t))}function yk(e,t){const{exact:r,status:n,predicate:a,mutationKey:i}=e;if(i){if(!t.options.mutationKey)return!1;if(r){if(cs(t.options.mutationKey)!==cs(i))return!1}else if(!vu(t.options.mutationKey,i))return!1}return!(n&&t.state.status!==n||a&&!a(t))}function X0(e,t){return((t==null?void 0:t.queryKeyHashFn)||cs)(e)}function cs(e){return JSON.stringify(e,(t,r)=>Cy(r)?Object.keys(r).sort().reduce((n,a)=>(n[a]=r[a],n),{}):r)}function vu(e,t){return e===t?!0:typeof e!=typeof t?!1:e&&t&&typeof e=="object"&&typeof t=="object"?Object.keys(t).every(r=>vu(e[r],t[r])):!1}var F4=Object.prototype.hasOwnProperty;function KN(e,t){if(e===t)return e;const r=xk(e)&&xk(t);if(!r&&!(Cy(e)&&Cy(t)))return t;const a=(r?e:Object.keys(e)).length,i=r?t:Object.keys(t),o=i.length,s=r?new Array(o):{};let l=0;for(let c=0;c{Mo.setTimeout(t,e)})}function Oy(e,t,r){return typeof r.structuralSharing=="function"?r.structuralSharing(e,t):r.structuralSharing!==!1?KN(e,t):t}function z4(e,t,r=0){const n=[...e,t];return r&&n.length>r?n.slice(1):n}function W4(e,t,r=0){const n=[t,...e];return r&&n.length>r?n.slice(0,-1):n}var Z0=Symbol();function YN(e,t){return!e.queryFn&&(t!=null&&t.initialPromise)?()=>t.initialPromise:!e.queryFn||e.queryFn===Z0?()=>Promise.reject(new Error(`Missing queryFn: '${e.queryHash}'`)):e.queryFn}function VN(e,t){return typeof e=="function"?e(...t):!!e}var zo,Mi,ll,kC,q4=(kC=class extends Yl{constructor(){super();ae(this,zo);ae(this,Mi);ae(this,ll);G(this,ll,t=>{if(!ls&&window.addEventListener){const r=()=>t();return window.addEventListener("visibilitychange",r,!1),()=>{window.removeEventListener("visibilitychange",r)}}})}onSubscribe(){N(this,Mi)||this.setEventListener(N(this,ll))}onUnsubscribe(){var t;this.hasListeners()||((t=N(this,Mi))==null||t.call(this),G(this,Mi,void 0))}setEventListener(t){var r;G(this,ll,t),(r=N(this,Mi))==null||r.call(this),G(this,Mi,t(n=>{typeof n=="boolean"?this.setFocused(n):this.onFocus()}))}setFocused(t){N(this,zo)!==t&&(G(this,zo,t),this.onFocus())}onFocus(){const t=this.isFocused();this.listeners.forEach(r=>{r(t)})}isFocused(){var t;return typeof N(this,zo)=="boolean"?N(this,zo):((t=globalThis.document)==null?void 0:t.visibilityState)!=="hidden"}},zo=new WeakMap,Mi=new WeakMap,ll=new WeakMap,kC),J0=new q4;function Ny(){let e,t;const r=new Promise((a,i)=>{e=a,t=i});r.status="pending",r.catch(()=>{});function n(a){Object.assign(r,a),delete r.resolve,delete r.reject}return r.resolve=a=>{n({status:"fulfilled",value:a}),e(a)},r.reject=a=>{n({status:"rejected",reason:a}),t(a)},r}var U4=L4;function H4(){let e=[],t=0,r=s=>{s()},n=s=>{s()},a=U4;const i=s=>{t?e.push(s):a(()=>{r(s)})},o=()=>{const s=e;e=[],s.length&&a(()=>{n(()=>{s.forEach(l=>{r(l)})})})};return{batch:s=>{let l;t++;try{l=s()}finally{t--,t||o()}return l},batchCalls:s=>(...l)=>{i(()=>{s(...l)})},schedule:i,setNotifyFunction:s=>{r=s},setBatchNotifyFunction:s=>{n=s},setScheduler:s=>{a=s}}}var Ot=H4(),cl,Ri,ul,PC,K4=(PC=class extends Yl{constructor(){super();ae(this,cl,!0);ae(this,Ri);ae(this,ul);G(this,ul,t=>{if(!ls&&window.addEventListener){const r=()=>t(!0),n=()=>t(!1);return window.addEventListener("online",r,!1),window.addEventListener("offline",n,!1),()=>{window.removeEventListener("online",r),window.removeEventListener("offline",n)}}})}onSubscribe(){N(this,Ri)||this.setEventListener(N(this,ul))}onUnsubscribe(){var t;this.hasListeners()||((t=N(this,Ri))==null||t.call(this),G(this,Ri,void 0))}setEventListener(t){var r;G(this,ul,t),(r=N(this,Ri))==null||r.call(this),G(this,Ri,t(this.setOnline.bind(this)))}setOnline(t){N(this,cl)!==t&&(G(this,cl,t),this.listeners.forEach(n=>{n(t)}))}isOnline(){return N(this,cl)}},cl=new WeakMap,Ri=new WeakMap,ul=new WeakMap,PC),ph=new K4;function Y4(e){return Math.min(1e3*2**e,3e4)}function GN(e){return(e??"online")==="online"?ph.isOnline():!0}var Ty=class extends Error{constructor(e){super("CancelledError"),this.revert=e==null?void 0:e.revert,this.silent=e==null?void 0:e.silent}};function QN(e){let t=!1,r=0,n;const a=Ny(),i=()=>a.status!=="pending",o=g=>{var y;if(!i()){const b=new Ty(g);h(b),(y=e.onCancel)==null||y.call(e,b)}},s=()=>{t=!0},l=()=>{t=!1},c=()=>J0.isFocused()&&(e.networkMode==="always"||ph.isOnline())&&e.canRun(),u=()=>GN(e.networkMode)&&e.canRun(),f=g=>{i()||(n==null||n(),a.resolve(g))},h=g=>{i()||(n==null||n(),a.reject(g))},m=()=>new Promise(g=>{var y;n=b=>{(i()||c())&&g(b)},(y=e.onPause)==null||y.call(e)}).then(()=>{var g;n=void 0,i()||(g=e.onContinue)==null||g.call(e)}),v=()=>{if(i())return;let g;const y=r===0?e.initialPromise:void 0;try{g=y??e.fn()}catch(b){g=Promise.reject(b)}Promise.resolve(g).then(f).catch(b=>{var P;if(i())return;const x=e.retry??(ls?0:3),w=e.retryDelay??Y4,S=typeof w=="function"?w(r,b):w,k=x===!0||typeof x=="number"&&rc()?void 0:m()).then(()=>{t?h(b):v()})})};return{promise:a,status:()=>a.status,cancel:o,continue:()=>(n==null||n(),a),cancelRetry:s,continueRetry:l,canStart:u,start:()=>(u()?v():m().then(v),a)}}var Wo,EC,XN=(EC=class{constructor(){ae(this,Wo)}destroy(){this.clearGcTimeout()}scheduleGc(){this.clearGcTimeout(),jy(this.gcTime)&&G(this,Wo,Mo.setTimeout(()=>{this.optionalRemove()},this.gcTime))}updateGcTime(e){this.gcTime=Math.max(this.gcTime||0,e??(ls?1/0:5*60*1e3))}clearGcTimeout(){N(this,Wo)&&(Mo.clearTimeout(N(this,Wo)),G(this,Wo,void 0))}},Wo=new WeakMap,EC),qo,dl,ln,Uo,Mt,Iu,Ho,On,Na,jC,V4=(jC=class extends XN{constructor(t){super();ae(this,On);ae(this,qo);ae(this,dl);ae(this,ln);ae(this,Uo);ae(this,Mt);ae(this,Iu);ae(this,Ho);G(this,Ho,!1),G(this,Iu,t.defaultOptions),this.setOptions(t.options),this.observers=[],G(this,Uo,t.client),G(this,ln,N(this,Uo).getQueryCache()),this.queryKey=t.queryKey,this.queryHash=t.queryHash,G(this,qo,wk(this.options)),this.state=t.state??N(this,qo),this.scheduleGc()}get meta(){return this.options.meta}get promise(){var t;return(t=N(this,Mt))==null?void 0:t.promise}setOptions(t){if(this.options={...N(this,Iu),...t},this.updateGcTime(this.options.gcTime),this.state&&this.state.data===void 0){const r=wk(this.options);r.data!==void 0&&(this.setData(r.data,{updatedAt:r.dataUpdatedAt,manual:!0}),G(this,qo,r))}}optionalRemove(){!this.observers.length&&this.state.fetchStatus==="idle"&&N(this,ln).remove(this)}setData(t,r){const n=Oy(this.state.data,t,this.options);return ge(this,On,Na).call(this,{data:n,type:"success",dataUpdatedAt:r==null?void 0:r.updatedAt,manual:r==null?void 0:r.manual}),n}setState(t,r){ge(this,On,Na).call(this,{type:"setState",state:t,setStateOptions:r})}cancel(t){var n,a;const r=(n=N(this,Mt))==null?void 0:n.promise;return(a=N(this,Mt))==null||a.cancel(t),r?r.then(hr).catch(hr):Promise.resolve()}destroy(){super.destroy(),this.cancel({silent:!0})}reset(){this.destroy(),this.setState(N(this,qo))}isActive(){return this.observers.some(t=>cn(t.options.enabled,this)!==!1)}isDisabled(){return this.getObserversCount()>0?!this.isActive():this.options.queryFn===Z0||this.state.dataUpdateCount+this.state.errorUpdateCount===0}isStatic(){return this.getObserversCount()>0?this.observers.some(t=>Ji(t.options.staleTime,this)==="static"):!1}isStale(){return this.getObserversCount()>0?this.observers.some(t=>t.getCurrentResult().isStale):this.state.data===void 0||this.state.isInvalidated}isStaleByTime(t=0){return this.state.data===void 0?!0:t==="static"?!1:this.state.isInvalidated?!0:!HN(this.state.dataUpdatedAt,t)}onFocus(){var r;const t=this.observers.find(n=>n.shouldFetchOnWindowFocus());t==null||t.refetch({cancelRefetch:!1}),(r=N(this,Mt))==null||r.continue()}onOnline(){var r;const t=this.observers.find(n=>n.shouldFetchOnReconnect());t==null||t.refetch({cancelRefetch:!1}),(r=N(this,Mt))==null||r.continue()}addObserver(t){this.observers.includes(t)||(this.observers.push(t),this.clearGcTimeout(),N(this,ln).notify({type:"observerAdded",query:this,observer:t}))}removeObserver(t){this.observers.includes(t)&&(this.observers=this.observers.filter(r=>r!==t),this.observers.length||(N(this,Mt)&&(N(this,Ho)?N(this,Mt).cancel({revert:!0}):N(this,Mt).cancelRetry()),this.scheduleGc()),N(this,ln).notify({type:"observerRemoved",query:this,observer:t}))}getObserversCount(){return this.observers.length}invalidate(){this.state.isInvalidated||ge(this,On,Na).call(this,{type:"invalidate"})}async fetch(t,r){var l,c,u,f,h,m,v,g,y,b,x,w;if(this.state.fetchStatus!=="idle"&&((l=N(this,Mt))==null?void 0:l.status())!=="rejected"){if(this.state.data!==void 0&&(r!=null&&r.cancelRefetch))this.cancel({silent:!0});else if(N(this,Mt))return N(this,Mt).continueRetry(),N(this,Mt).promise}if(t&&this.setOptions(t),!this.options.queryFn){const S=this.observers.find(k=>k.options.queryFn);S&&this.setOptions(S.options)}const n=new AbortController,a=S=>{Object.defineProperty(S,"signal",{enumerable:!0,get:()=>(G(this,Ho,!0),n.signal)})},i=()=>{const S=YN(this.options,r),P=(()=>{const E={client:N(this,Uo),queryKey:this.queryKey,meta:this.meta};return a(E),E})();return G(this,Ho,!1),this.options.persister?this.options.persister(S,P,this):S(P)},s=(()=>{const S={fetchOptions:r,options:this.options,queryKey:this.queryKey,client:N(this,Uo),state:this.state,fetchFn:i};return a(S),S})();(c=this.options.behavior)==null||c.onFetch(s,this),G(this,dl,this.state),(this.state.fetchStatus==="idle"||this.state.fetchMeta!==((u=s.fetchOptions)==null?void 0:u.meta))&&ge(this,On,Na).call(this,{type:"fetch",meta:(f=s.fetchOptions)==null?void 0:f.meta}),G(this,Mt,QN({initialPromise:r==null?void 0:r.initialPromise,fn:s.fetchFn,onCancel:S=>{S instanceof Ty&&S.revert&&this.setState({...N(this,dl),fetchStatus:"idle"}),n.abort()},onFail:(S,k)=>{ge(this,On,Na).call(this,{type:"failed",failureCount:S,error:k})},onPause:()=>{ge(this,On,Na).call(this,{type:"pause"})},onContinue:()=>{ge(this,On,Na).call(this,{type:"continue"})},retry:s.options.retry,retryDelay:s.options.retryDelay,networkMode:s.options.networkMode,canRun:()=>!0}));try{const S=await N(this,Mt).start();if(S===void 0)throw new Error(`${this.queryHash} data is undefined`);return this.setData(S),(m=(h=N(this,ln).config).onSuccess)==null||m.call(h,S,this),(g=(v=N(this,ln).config).onSettled)==null||g.call(v,S,this.state.error,this),S}catch(S){if(S instanceof Ty){if(S.silent)return N(this,Mt).promise;if(S.revert){if(this.state.data===void 0)throw S;return this.state.data}}throw ge(this,On,Na).call(this,{type:"error",error:S}),(b=(y=N(this,ln).config).onError)==null||b.call(y,S,this),(w=(x=N(this,ln).config).onSettled)==null||w.call(x,this.state.data,S,this),S}finally{this.scheduleGc()}}},qo=new WeakMap,dl=new WeakMap,ln=new WeakMap,Uo=new WeakMap,Mt=new WeakMap,Iu=new WeakMap,Ho=new WeakMap,On=new WeakSet,Na=function(t){const r=n=>{switch(t.type){case"failed":return{...n,fetchFailureCount:t.failureCount,fetchFailureReason:t.error};case"pause":return{...n,fetchStatus:"paused"};case"continue":return{...n,fetchStatus:"fetching"};case"fetch":return{...n,...ZN(n.data,this.options),fetchMeta:t.meta??null};case"success":const a={...n,data:t.data,dataUpdateCount:n.dataUpdateCount+1,dataUpdatedAt:t.dataUpdatedAt??Date.now(),error:null,isInvalidated:!1,status:"success",...!t.manual&&{fetchStatus:"idle",fetchFailureCount:0,fetchFailureReason:null}};return G(this,dl,t.manual?a:void 0),a;case"error":const i=t.error;return{...n,error:i,errorUpdateCount:n.errorUpdateCount+1,errorUpdatedAt:Date.now(),fetchFailureCount:n.fetchFailureCount+1,fetchFailureReason:i,fetchStatus:"idle",status:"error"};case"invalidate":return{...n,isInvalidated:!0};case"setState":return{...n,...t.state}}};this.state=r(this.state),Ot.batch(()=>{this.observers.forEach(n=>{n.onQueryUpdate()}),N(this,ln).notify({query:this,type:"updated",action:t})})},jC);function ZN(e,t){return{fetchFailureCount:0,fetchFailureReason:null,fetchStatus:GN(t.networkMode)?"fetching":"paused",...e===void 0&&{error:null,status:"pending"}}}function wk(e){const t=typeof e.initialData=="function"?e.initialData():e.initialData,r=t!==void 0,n=r?typeof e.initialDataUpdatedAt=="function"?e.initialDataUpdatedAt():e.initialDataUpdatedAt:0;return{data:t,dataUpdateCount:0,dataUpdatedAt:r?n??Date.now():0,error:null,errorUpdateCount:0,errorUpdatedAt:0,fetchFailureCount:0,fetchFailureReason:null,fetchMeta:null,isInvalidated:!1,status:r?"success":"pending",fetchStatus:"idle"}}var br,Ee,Lu,ur,Ko,fl,_a,Ii,$u,hl,pl,Yo,Vo,Li,ml,Fe,Lc,Ay,_y,Dy,My,Ry,Iy,Ly,JN,CC,G4=(CC=class extends Yl{constructor(t,r){super();ae(this,Fe);ae(this,br);ae(this,Ee);ae(this,Lu);ae(this,ur);ae(this,Ko);ae(this,fl);ae(this,_a);ae(this,Ii);ae(this,$u);ae(this,hl);ae(this,pl);ae(this,Yo);ae(this,Vo);ae(this,Li);ae(this,ml,new Set);this.options=r,G(this,br,t),G(this,Ii,null),G(this,_a,Ny()),this.bindMethods(),this.setOptions(r)}bindMethods(){this.refetch=this.refetch.bind(this)}onSubscribe(){this.listeners.size===1&&(N(this,Ee).addObserver(this),Sk(N(this,Ee),this.options)?ge(this,Fe,Lc).call(this):this.updateResult(),ge(this,Fe,My).call(this))}onUnsubscribe(){this.hasListeners()||this.destroy()}shouldFetchOnReconnect(){return $y(N(this,Ee),this.options,this.options.refetchOnReconnect)}shouldFetchOnWindowFocus(){return $y(N(this,Ee),this.options,this.options.refetchOnWindowFocus)}destroy(){this.listeners=new Set,ge(this,Fe,Ry).call(this),ge(this,Fe,Iy).call(this),N(this,Ee).removeObserver(this)}setOptions(t){const r=this.options,n=N(this,Ee);if(this.options=N(this,br).defaultQueryOptions(t),this.options.enabled!==void 0&&typeof this.options.enabled!="boolean"&&typeof this.options.enabled!="function"&&typeof cn(this.options.enabled,N(this,Ee))!="boolean")throw new Error("Expected enabled to be a boolean or a callback that returns a boolean");ge(this,Fe,Ly).call(this),N(this,Ee).setOptions(this.options),r._defaulted&&!hh(this.options,r)&&N(this,br).getQueryCache().notify({type:"observerOptionsUpdated",query:N(this,Ee),observer:this});const a=this.hasListeners();a&&kk(N(this,Ee),n,this.options,r)&&ge(this,Fe,Lc).call(this),this.updateResult(),a&&(N(this,Ee)!==n||cn(this.options.enabled,N(this,Ee))!==cn(r.enabled,N(this,Ee))||Ji(this.options.staleTime,N(this,Ee))!==Ji(r.staleTime,N(this,Ee)))&&ge(this,Fe,Ay).call(this);const i=ge(this,Fe,_y).call(this);a&&(N(this,Ee)!==n||cn(this.options.enabled,N(this,Ee))!==cn(r.enabled,N(this,Ee))||i!==N(this,Li))&&ge(this,Fe,Dy).call(this,i)}getOptimisticResult(t){const r=N(this,br).getQueryCache().build(N(this,br),t),n=this.createResult(r,t);return X4(this,n)&&(G(this,ur,n),G(this,fl,this.options),G(this,Ko,N(this,Ee).state)),n}getCurrentResult(){return N(this,ur)}trackResult(t,r){return new Proxy(t,{get:(n,a)=>(this.trackProp(a),r==null||r(a),a==="promise"&&!this.options.experimental_prefetchInRender&&N(this,_a).status==="pending"&&N(this,_a).reject(new Error("experimental_prefetchInRender feature flag is not enabled")),Reflect.get(n,a))})}trackProp(t){N(this,ml).add(t)}getCurrentQuery(){return N(this,Ee)}refetch({...t}={}){return this.fetch({...t})}fetchOptimistic(t){const r=N(this,br).defaultQueryOptions(t),n=N(this,br).getQueryCache().build(N(this,br),r);return n.fetch().then(()=>this.createResult(n,r))}fetch(t){return ge(this,Fe,Lc).call(this,{...t,cancelRefetch:t.cancelRefetch??!0}).then(()=>(this.updateResult(),N(this,ur)))}createResult(t,r){var C;const n=N(this,Ee),a=this.options,i=N(this,ur),o=N(this,Ko),s=N(this,fl),c=t!==n?t.state:N(this,Lu),{state:u}=t;let f={...u},h=!1,m;if(r._optimisticResults){const O=this.hasListeners(),T=!O&&Sk(t,r),A=O&&kk(t,n,r,a);(T||A)&&(f={...f,...ZN(u.data,t.options)}),r._optimisticResults==="isRestoring"&&(f.fetchStatus="idle")}let{error:v,errorUpdatedAt:g,status:y}=f;m=f.data;let b=!1;if(r.placeholderData!==void 0&&m===void 0&&y==="pending"){let O;i!=null&&i.isPlaceholderData&&r.placeholderData===(s==null?void 0:s.placeholderData)?(O=i.data,b=!0):O=typeof r.placeholderData=="function"?r.placeholderData((C=N(this,pl))==null?void 0:C.state.data,N(this,pl)):r.placeholderData,O!==void 0&&(y="success",m=Oy(i==null?void 0:i.data,O,r),h=!0)}if(r.select&&m!==void 0&&!b)if(i&&m===(o==null?void 0:o.data)&&r.select===N(this,$u))m=N(this,hl);else try{G(this,$u,r.select),m=r.select(m),m=Oy(i==null?void 0:i.data,m,r),G(this,hl,m),G(this,Ii,null)}catch(O){G(this,Ii,O)}N(this,Ii)&&(v=N(this,Ii),m=N(this,hl),g=Date.now(),y="error");const x=f.fetchStatus==="fetching",w=y==="pending",S=y==="error",k=w&&x,P=m!==void 0,j={status:y,fetchStatus:f.fetchStatus,isPending:w,isSuccess:y==="success",isError:S,isInitialLoading:k,isLoading:k,data:m,dataUpdatedAt:f.dataUpdatedAt,error:v,errorUpdatedAt:g,failureCount:f.fetchFailureCount,failureReason:f.fetchFailureReason,errorUpdateCount:f.errorUpdateCount,isFetched:f.dataUpdateCount>0||f.errorUpdateCount>0,isFetchedAfterMount:f.dataUpdateCount>c.dataUpdateCount||f.errorUpdateCount>c.errorUpdateCount,isFetching:x,isRefetching:x&&!w,isLoadingError:S&&!P,isPaused:f.fetchStatus==="paused",isPlaceholderData:h,isRefetchError:S&&P,isStale:eb(t,r),refetch:this.refetch,promise:N(this,_a),isEnabled:cn(r.enabled,t)!==!1};if(this.options.experimental_prefetchInRender){const O=I=>{j.status==="error"?I.reject(j.error):j.data!==void 0&&I.resolve(j.data)},T=()=>{const I=G(this,_a,j.promise=Ny());O(I)},A=N(this,_a);switch(A.status){case"pending":t.queryHash===n.queryHash&&O(A);break;case"fulfilled":(j.status==="error"||j.data!==A.value)&&T();break;case"rejected":(j.status!=="error"||j.error!==A.reason)&&T();break}}return j}updateResult(){const t=N(this,ur),r=this.createResult(N(this,Ee),this.options);if(G(this,Ko,N(this,Ee).state),G(this,fl,this.options),N(this,Ko).data!==void 0&&G(this,pl,N(this,Ee)),hh(r,t))return;G(this,ur,r);const n=()=>{if(!t)return!0;const{notifyOnChangeProps:a}=this.options,i=typeof a=="function"?a():a;if(i==="all"||!i&&!N(this,ml).size)return!0;const o=new Set(i??N(this,ml));return this.options.throwOnError&&o.add("error"),Object.keys(N(this,ur)).some(s=>{const l=s;return N(this,ur)[l]!==t[l]&&o.has(l)})};ge(this,Fe,JN).call(this,{listeners:n()})}onQueryUpdate(){this.updateResult(),this.hasListeners()&&ge(this,Fe,My).call(this)}},br=new WeakMap,Ee=new WeakMap,Lu=new WeakMap,ur=new WeakMap,Ko=new WeakMap,fl=new WeakMap,_a=new WeakMap,Ii=new WeakMap,$u=new WeakMap,hl=new WeakMap,pl=new WeakMap,Yo=new WeakMap,Vo=new WeakMap,Li=new WeakMap,ml=new WeakMap,Fe=new WeakSet,Lc=function(t){ge(this,Fe,Ly).call(this);let r=N(this,Ee).fetch(this.options,t);return t!=null&&t.throwOnError||(r=r.catch(hr)),r},Ay=function(){ge(this,Fe,Ry).call(this);const t=Ji(this.options.staleTime,N(this,Ee));if(ls||N(this,ur).isStale||!jy(t))return;const n=HN(N(this,ur).dataUpdatedAt,t)+1;G(this,Yo,Mo.setTimeout(()=>{N(this,ur).isStale||this.updateResult()},n))},_y=function(){return(typeof this.options.refetchInterval=="function"?this.options.refetchInterval(N(this,Ee)):this.options.refetchInterval)??!1},Dy=function(t){ge(this,Fe,Iy).call(this),G(this,Li,t),!(ls||cn(this.options.enabled,N(this,Ee))===!1||!jy(N(this,Li))||N(this,Li)===0)&&G(this,Vo,Mo.setInterval(()=>{(this.options.refetchIntervalInBackground||J0.isFocused())&&ge(this,Fe,Lc).call(this)},N(this,Li)))},My=function(){ge(this,Fe,Ay).call(this),ge(this,Fe,Dy).call(this,ge(this,Fe,_y).call(this))},Ry=function(){N(this,Yo)&&(Mo.clearTimeout(N(this,Yo)),G(this,Yo,void 0))},Iy=function(){N(this,Vo)&&(Mo.clearInterval(N(this,Vo)),G(this,Vo,void 0))},Ly=function(){const t=N(this,br).getQueryCache().build(N(this,br),this.options);if(t===N(this,Ee))return;const r=N(this,Ee);G(this,Ee,t),G(this,Lu,t.state),this.hasListeners()&&(r==null||r.removeObserver(this),t.addObserver(this))},JN=function(t){Ot.batch(()=>{t.listeners&&this.listeners.forEach(r=>{r(N(this,ur))}),N(this,br).getQueryCache().notify({query:N(this,Ee),type:"observerResultsUpdated"})})},CC);function Q4(e,t){return cn(t.enabled,e)!==!1&&e.state.data===void 0&&!(e.state.status==="error"&&t.retryOnMount===!1)}function Sk(e,t){return Q4(e,t)||e.state.data!==void 0&&$y(e,t,t.refetchOnMount)}function $y(e,t,r){if(cn(t.enabled,e)!==!1&&Ji(t.staleTime,e)!=="static"){const n=typeof r=="function"?r(e):r;return n==="always"||n!==!1&&eb(e,t)}return!1}function kk(e,t,r,n){return(e!==t||cn(n.enabled,e)===!1)&&(!r.suspense||e.state.status!=="error")&&eb(e,r)}function eb(e,t){return cn(t.enabled,e)!==!1&&e.isStaleByTime(Ji(t.staleTime,e))}function X4(e,t){return!hh(e.getCurrentResult(),t)}function Pk(e){return{onFetch:(t,r)=>{var u,f,h,m,v;const n=t.options,a=(h=(f=(u=t.fetchOptions)==null?void 0:u.meta)==null?void 0:f.fetchMore)==null?void 0:h.direction,i=((m=t.state.data)==null?void 0:m.pages)||[],o=((v=t.state.data)==null?void 0:v.pageParams)||[];let s={pages:[],pageParams:[]},l=0;const c=async()=>{let g=!1;const y=w=>{Object.defineProperty(w,"signal",{enumerable:!0,get:()=>(t.signal.aborted?g=!0:t.signal.addEventListener("abort",()=>{g=!0}),t.signal)})},b=YN(t.options,t.fetchOptions),x=async(w,S,k)=>{if(g)return Promise.reject();if(S==null&&w.pages.length)return Promise.resolve(w);const E=(()=>{const T={client:t.client,queryKey:t.queryKey,pageParam:S,direction:k?"backward":"forward",meta:t.options.meta};return y(T),T})(),j=await b(E),{maxPages:C}=t.options,O=k?W4:z4;return{pages:O(w.pages,j,C),pageParams:O(w.pageParams,S,C)}};if(a&&i.length){const w=a==="backward",S=w?Z4:Ek,k={pages:i,pageParams:o},P=S(n,k);s=await x(k,P,w)}else{const w=e??i.length;do{const S=l===0?o[0]??n.initialPageParam:Ek(n,s);if(l>0&&S==null)break;s=await x(s,S),l++}while(l{var g,y;return(y=(g=t.options).persister)==null?void 0:y.call(g,c,{client:t.client,queryKey:t.queryKey,meta:t.options.meta,signal:t.signal},r)}:t.fetchFn=c}}}function Ek(e,{pages:t,pageParams:r}){const n=t.length-1;return t.length>0?e.getNextPageParam(t[n],t,r[n],r):void 0}function Z4(e,{pages:t,pageParams:r}){var n;return t.length>0?(n=e.getPreviousPageParam)==null?void 0:n.call(e,t[0],t,r[0],r):void 0}var Fu,Qn,dr,Go,Xn,ki,OC,J4=(OC=class extends XN{constructor(t){super();ae(this,Xn);ae(this,Fu);ae(this,Qn);ae(this,dr);ae(this,Go);G(this,Fu,t.client),this.mutationId=t.mutationId,G(this,dr,t.mutationCache),G(this,Qn,[]),this.state=t.state||eT(),this.setOptions(t.options),this.scheduleGc()}setOptions(t){this.options=t,this.updateGcTime(this.options.gcTime)}get meta(){return this.options.meta}addObserver(t){N(this,Qn).includes(t)||(N(this,Qn).push(t),this.clearGcTimeout(),N(this,dr).notify({type:"observerAdded",mutation:this,observer:t}))}removeObserver(t){G(this,Qn,N(this,Qn).filter(r=>r!==t)),this.scheduleGc(),N(this,dr).notify({type:"observerRemoved",mutation:this,observer:t})}optionalRemove(){N(this,Qn).length||(this.state.status==="pending"?this.scheduleGc():N(this,dr).remove(this))}continue(){var t;return((t=N(this,Go))==null?void 0:t.continue())??this.execute(this.state.variables)}async execute(t){var o,s,l,c,u,f,h,m,v,g,y,b,x,w,S,k,P,E,j,C;const r=()=>{ge(this,Xn,ki).call(this,{type:"continue"})},n={client:N(this,Fu),meta:this.options.meta,mutationKey:this.options.mutationKey};G(this,Go,QN({fn:()=>this.options.mutationFn?this.options.mutationFn(t,n):Promise.reject(new Error("No mutationFn found")),onFail:(O,T)=>{ge(this,Xn,ki).call(this,{type:"failed",failureCount:O,error:T})},onPause:()=>{ge(this,Xn,ki).call(this,{type:"pause"})},onContinue:r,retry:this.options.retry??0,retryDelay:this.options.retryDelay,networkMode:this.options.networkMode,canRun:()=>N(this,dr).canRun(this)}));const a=this.state.status==="pending",i=!N(this,Go).canStart();try{if(a)r();else{ge(this,Xn,ki).call(this,{type:"pending",variables:t,isPaused:i}),await((s=(o=N(this,dr).config).onMutate)==null?void 0:s.call(o,t,this,n));const T=await((c=(l=this.options).onMutate)==null?void 0:c.call(l,t,n));T!==this.state.context&&ge(this,Xn,ki).call(this,{type:"pending",context:T,variables:t,isPaused:i})}const O=await N(this,Go).start();return await((f=(u=N(this,dr).config).onSuccess)==null?void 0:f.call(u,O,t,this.state.context,this,n)),await((m=(h=this.options).onSuccess)==null?void 0:m.call(h,O,t,this.state.context,n)),await((g=(v=N(this,dr).config).onSettled)==null?void 0:g.call(v,O,null,this.state.variables,this.state.context,this,n)),await((b=(y=this.options).onSettled)==null?void 0:b.call(y,O,null,t,this.state.context,n)),ge(this,Xn,ki).call(this,{type:"success",data:O}),O}catch(O){try{throw await((w=(x=N(this,dr).config).onError)==null?void 0:w.call(x,O,t,this.state.context,this,n)),await((k=(S=this.options).onError)==null?void 0:k.call(S,O,t,this.state.context,n)),await((E=(P=N(this,dr).config).onSettled)==null?void 0:E.call(P,void 0,O,this.state.variables,this.state.context,this,n)),await((C=(j=this.options).onSettled)==null?void 0:C.call(j,void 0,O,t,this.state.context,n)),O}finally{ge(this,Xn,ki).call(this,{type:"error",error:O})}}finally{N(this,dr).runNext(this)}}},Fu=new WeakMap,Qn=new WeakMap,dr=new WeakMap,Go=new WeakMap,Xn=new WeakSet,ki=function(t){const r=n=>{switch(t.type){case"failed":return{...n,failureCount:t.failureCount,failureReason:t.error};case"pause":return{...n,isPaused:!0};case"continue":return{...n,isPaused:!1};case"pending":return{...n,context:t.context,data:void 0,failureCount:0,failureReason:null,error:null,isPaused:t.isPaused,status:"pending",variables:t.variables,submittedAt:Date.now()};case"success":return{...n,data:t.data,failureCount:0,failureReason:null,error:null,status:"success",isPaused:!1};case"error":return{...n,data:void 0,error:t.error,failureCount:n.failureCount+1,failureReason:t.error,isPaused:!1,status:"error"}}};this.state=r(this.state),Ot.batch(()=>{N(this,Qn).forEach(n=>{n.onMutationUpdate(t)}),N(this,dr).notify({mutation:this,type:"updated",action:t})})},OC);function eT(){return{context:void 0,data:void 0,error:null,failureCount:0,failureReason:null,isPaused:!1,status:"idle",variables:void 0,submittedAt:0}}var Da,Nn,Bu,NC,e5=(NC=class extends Yl{constructor(t={}){super();ae(this,Da);ae(this,Nn);ae(this,Bu);this.config=t,G(this,Da,new Set),G(this,Nn,new Map),G(this,Bu,0)}build(t,r,n){const a=new J4({client:t,mutationCache:this,mutationId:++Cd(this,Bu)._,options:t.defaultMutationOptions(r),state:n});return this.add(a),a}add(t){N(this,Da).add(t);const r=Hd(t);if(typeof r=="string"){const n=N(this,Nn).get(r);n?n.push(t):N(this,Nn).set(r,[t])}this.notify({type:"added",mutation:t})}remove(t){if(N(this,Da).delete(t)){const r=Hd(t);if(typeof r=="string"){const n=N(this,Nn).get(r);if(n)if(n.length>1){const a=n.indexOf(t);a!==-1&&n.splice(a,1)}else n[0]===t&&N(this,Nn).delete(r)}}this.notify({type:"removed",mutation:t})}canRun(t){const r=Hd(t);if(typeof r=="string"){const n=N(this,Nn).get(r),a=n==null?void 0:n.find(i=>i.state.status==="pending");return!a||a===t}else return!0}runNext(t){var n;const r=Hd(t);if(typeof r=="string"){const a=(n=N(this,Nn).get(r))==null?void 0:n.find(i=>i!==t&&i.state.isPaused);return(a==null?void 0:a.continue())??Promise.resolve()}else return Promise.resolve()}clear(){Ot.batch(()=>{N(this,Da).forEach(t=>{this.notify({type:"removed",mutation:t})}),N(this,Da).clear(),N(this,Nn).clear()})}getAll(){return Array.from(N(this,Da))}find(t){const r={exact:!0,...t};return this.getAll().find(n=>yk(r,n))}findAll(t={}){return this.getAll().filter(r=>yk(t,r))}notify(t){Ot.batch(()=>{this.listeners.forEach(r=>{r(t)})})}resumePausedMutations(){const t=this.getAll().filter(r=>r.state.isPaused);return Ot.batch(()=>Promise.all(t.map(r=>r.continue().catch(hr))))}},Da=new WeakMap,Nn=new WeakMap,Bu=new WeakMap,NC);function Hd(e){var t;return(t=e.options.scope)==null?void 0:t.id}var Ma,$i,wr,Ra,Qa,Af,Fy,TC,t5=(TC=class extends Yl{constructor(r,n){super();ae(this,Qa);ae(this,Ma);ae(this,$i);ae(this,wr);ae(this,Ra);G(this,Ma,r),this.setOptions(n),this.bindMethods(),ge(this,Qa,Af).call(this)}bindMethods(){this.mutate=this.mutate.bind(this),this.reset=this.reset.bind(this)}setOptions(r){var a;const n=this.options;this.options=N(this,Ma).defaultMutationOptions(r),hh(this.options,n)||N(this,Ma).getMutationCache().notify({type:"observerOptionsUpdated",mutation:N(this,wr),observer:this}),n!=null&&n.mutationKey&&this.options.mutationKey&&cs(n.mutationKey)!==cs(this.options.mutationKey)?this.reset():((a=N(this,wr))==null?void 0:a.state.status)==="pending"&&N(this,wr).setOptions(this.options)}onUnsubscribe(){var r;this.hasListeners()||(r=N(this,wr))==null||r.removeObserver(this)}onMutationUpdate(r){ge(this,Qa,Af).call(this),ge(this,Qa,Fy).call(this,r)}getCurrentResult(){return N(this,$i)}reset(){var r;(r=N(this,wr))==null||r.removeObserver(this),G(this,wr,void 0),ge(this,Qa,Af).call(this),ge(this,Qa,Fy).call(this)}mutate(r,n){var a;return G(this,Ra,n),(a=N(this,wr))==null||a.removeObserver(this),G(this,wr,N(this,Ma).getMutationCache().build(N(this,Ma),this.options)),N(this,wr).addObserver(this),N(this,wr).execute(r)}},Ma=new WeakMap,$i=new WeakMap,wr=new WeakMap,Ra=new WeakMap,Qa=new WeakSet,Af=function(){var n;const r=((n=N(this,wr))==null?void 0:n.state)??eT();G(this,$i,{...r,isPending:r.status==="pending",isSuccess:r.status==="success",isError:r.status==="error",isIdle:r.status==="idle",mutate:this.mutate,reset:this.reset})},Fy=function(r){Ot.batch(()=>{var n,a,i,o,s,l,c,u;if(N(this,Ra)&&this.hasListeners()){const f=N(this,$i).variables,h=N(this,$i).context,m={client:N(this,Ma),meta:this.options.meta,mutationKey:this.options.mutationKey};(r==null?void 0:r.type)==="success"?((a=(n=N(this,Ra)).onSuccess)==null||a.call(n,r.data,f,h,m),(o=(i=N(this,Ra)).onSettled)==null||o.call(i,r.data,null,f,h,m)):(r==null?void 0:r.type)==="error"&&((l=(s=N(this,Ra)).onError)==null||l.call(s,r.error,f,h,m),(u=(c=N(this,Ra)).onSettled)==null||u.call(c,void 0,r.error,f,h,m))}this.listeners.forEach(f=>{f(N(this,$i))})})},TC),Zn,AC,r5=(AC=class extends Yl{constructor(t={}){super();ae(this,Zn);this.config=t,G(this,Zn,new Map)}build(t,r,n){const a=r.queryKey,i=r.queryHash??X0(a,r);let o=this.get(i);return o||(o=new V4({client:t,queryKey:a,queryHash:i,options:t.defaultQueryOptions(r),state:n,defaultOptions:t.getQueryDefaults(a)}),this.add(o)),o}add(t){N(this,Zn).has(t.queryHash)||(N(this,Zn).set(t.queryHash,t),this.notify({type:"added",query:t}))}remove(t){const r=N(this,Zn).get(t.queryHash);r&&(t.destroy(),r===t&&N(this,Zn).delete(t.queryHash),this.notify({type:"removed",query:t}))}clear(){Ot.batch(()=>{this.getAll().forEach(t=>{this.remove(t)})})}get(t){return N(this,Zn).get(t)}getAll(){return[...N(this,Zn).values()]}find(t){const r={exact:!0,...t};return this.getAll().find(n=>vk(r,n))}findAll(t={}){const r=this.getAll();return Object.keys(t).length>0?r.filter(n=>vk(t,n)):r}notify(t){Ot.batch(()=>{this.listeners.forEach(r=>{r(t)})})}onFocus(){Ot.batch(()=>{this.getAll().forEach(t=>{t.onFocus()})})}onOnline(){Ot.batch(()=>{this.getAll().forEach(t=>{t.onOnline()})})}},Zn=new WeakMap,AC),ht,Fi,Bi,gl,vl,zi,yl,xl,_C,n5=(_C=class{constructor(e={}){ae(this,ht);ae(this,Fi);ae(this,Bi);ae(this,gl);ae(this,vl);ae(this,zi);ae(this,yl);ae(this,xl);G(this,ht,e.queryCache||new r5),G(this,Fi,e.mutationCache||new e5),G(this,Bi,e.defaultOptions||{}),G(this,gl,new Map),G(this,vl,new Map),G(this,zi,0)}mount(){Cd(this,zi)._++,N(this,zi)===1&&(G(this,yl,J0.subscribe(async e=>{e&&(await this.resumePausedMutations(),N(this,ht).onFocus())})),G(this,xl,ph.subscribe(async e=>{e&&(await this.resumePausedMutations(),N(this,ht).onOnline())})))}unmount(){var e,t;Cd(this,zi)._--,N(this,zi)===0&&((e=N(this,yl))==null||e.call(this),G(this,yl,void 0),(t=N(this,xl))==null||t.call(this),G(this,xl,void 0))}isFetching(e){return N(this,ht).findAll({...e,fetchStatus:"fetching"}).length}isMutating(e){return N(this,Fi).findAll({...e,status:"pending"}).length}getQueryData(e){var r;const t=this.defaultQueryOptions({queryKey:e});return(r=N(this,ht).get(t.queryHash))==null?void 0:r.state.data}ensureQueryData(e){const t=this.defaultQueryOptions(e),r=N(this,ht).build(this,t),n=r.state.data;return n===void 0?this.fetchQuery(e):(e.revalidateIfStale&&r.isStaleByTime(Ji(t.staleTime,r))&&this.prefetchQuery(t),Promise.resolve(n))}getQueriesData(e){return N(this,ht).findAll(e).map(({queryKey:t,state:r})=>{const n=r.data;return[t,n]})}setQueryData(e,t,r){const n=this.defaultQueryOptions({queryKey:e}),a=N(this,ht).get(n.queryHash),i=a==null?void 0:a.state.data,o=$4(t,i);if(o!==void 0)return N(this,ht).build(this,n).setData(o,{...r,manual:!0})}setQueriesData(e,t,r){return Ot.batch(()=>N(this,ht).findAll(e).map(({queryKey:n})=>[n,this.setQueryData(n,t,r)]))}getQueryState(e){var r;const t=this.defaultQueryOptions({queryKey:e});return(r=N(this,ht).get(t.queryHash))==null?void 0:r.state}removeQueries(e){const t=N(this,ht);Ot.batch(()=>{t.findAll(e).forEach(r=>{t.remove(r)})})}resetQueries(e,t){const r=N(this,ht);return Ot.batch(()=>(r.findAll(e).forEach(n=>{n.reset()}),this.refetchQueries({type:"active",...e},t)))}cancelQueries(e,t={}){const r={revert:!0,...t},n=Ot.batch(()=>N(this,ht).findAll(e).map(a=>a.cancel(r)));return Promise.all(n).then(hr).catch(hr)}invalidateQueries(e,t={}){return Ot.batch(()=>(N(this,ht).findAll(e).forEach(r=>{r.invalidate()}),(e==null?void 0:e.refetchType)==="none"?Promise.resolve():this.refetchQueries({...e,type:(e==null?void 0:e.refetchType)??(e==null?void 0:e.type)??"active"},t)))}refetchQueries(e,t={}){const r={...t,cancelRefetch:t.cancelRefetch??!0},n=Ot.batch(()=>N(this,ht).findAll(e).filter(a=>!a.isDisabled()&&!a.isStatic()).map(a=>{let i=a.fetch(void 0,r);return r.throwOnError||(i=i.catch(hr)),a.state.fetchStatus==="paused"?Promise.resolve():i}));return Promise.all(n).then(hr)}fetchQuery(e){const t=this.defaultQueryOptions(e);t.retry===void 0&&(t.retry=!1);const r=N(this,ht).build(this,t);return r.isStaleByTime(Ji(t.staleTime,r))?r.fetch(t):Promise.resolve(r.state.data)}prefetchQuery(e){return this.fetchQuery(e).then(hr).catch(hr)}fetchInfiniteQuery(e){return e.behavior=Pk(e.pages),this.fetchQuery(e)}prefetchInfiniteQuery(e){return this.fetchInfiniteQuery(e).then(hr).catch(hr)}ensureInfiniteQueryData(e){return e.behavior=Pk(e.pages),this.ensureQueryData(e)}resumePausedMutations(){return ph.isOnline()?N(this,Fi).resumePausedMutations():Promise.resolve()}getQueryCache(){return N(this,ht)}getMutationCache(){return N(this,Fi)}getDefaultOptions(){return N(this,Bi)}setDefaultOptions(e){G(this,Bi,e)}setQueryDefaults(e,t){N(this,gl).set(cs(e),{queryKey:e,defaultOptions:t})}getQueryDefaults(e){const t=[...N(this,gl).values()],r={};return t.forEach(n=>{vu(e,n.queryKey)&&Object.assign(r,n.defaultOptions)}),r}setMutationDefaults(e,t){N(this,vl).set(cs(e),{mutationKey:e,defaultOptions:t})}getMutationDefaults(e){const t=[...N(this,vl).values()],r={};return t.forEach(n=>{vu(e,n.mutationKey)&&Object.assign(r,n.defaultOptions)}),r}defaultQueryOptions(e){if(e._defaulted)return e;const t={...N(this,Bi).queries,...this.getQueryDefaults(e.queryKey),...e,_defaulted:!0};return t.queryHash||(t.queryHash=X0(t.queryKey,t)),t.refetchOnReconnect===void 0&&(t.refetchOnReconnect=t.networkMode!=="always"),t.throwOnError===void 0&&(t.throwOnError=!!t.suspense),!t.networkMode&&t.persister&&(t.networkMode="offlineFirst"),t.queryFn===Z0&&(t.enabled=!1),t}defaultMutationOptions(e){return e!=null&&e._defaulted?e:{...N(this,Bi).mutations,...(e==null?void 0:e.mutationKey)&&this.getMutationDefaults(e.mutationKey),...e,_defaulted:!0}}clear(){N(this,ht).clear(),N(this,Fi).clear()}},ht=new WeakMap,Fi=new WeakMap,Bi=new WeakMap,gl=new WeakMap,vl=new WeakMap,zi=new WeakMap,yl=new WeakMap,xl=new WeakMap,_C),tT=p.createContext(void 0),rT=e=>{const t=p.useContext(tT);if(!t)throw new Error("No QueryClient set, use QueryClientProvider to set one");return t},a5=({client:e,children:t})=>(p.useEffect(()=>(e.mount(),()=>{e.unmount()}),[e]),d.jsx(tT.Provider,{value:e,children:t})),nT=p.createContext(!1),i5=()=>p.useContext(nT);nT.Provider;function o5(){let e=!1;return{clearReset:()=>{e=!1},reset:()=>{e=!0},isReset:()=>e}}var s5=p.createContext(o5()),l5=()=>p.useContext(s5),c5=(e,t)=>{(e.suspense||e.throwOnError||e.experimental_prefetchInRender)&&(t.isReset()||(e.retryOnMount=!1))},u5=e=>{p.useEffect(()=>{e.clearReset()},[e])},d5=({result:e,errorResetBoundary:t,throwOnError:r,query:n,suspense:a})=>e.isError&&!t.isReset()&&!e.isFetching&&n&&(a&&e.data===void 0||VN(r,[e.error,n])),f5=e=>{if(e.suspense){const r=a=>a==="static"?a:Math.max(a??1e3,1e3),n=e.staleTime;e.staleTime=typeof n=="function"?(...a)=>r(n(...a)):r(n),typeof e.gcTime=="number"&&(e.gcTime=Math.max(e.gcTime,1e3))}},h5=(e,t)=>e.isLoading&&e.isFetching&&!t,p5=(e,t)=>(e==null?void 0:e.suspense)&&t.isPending,jk=(e,t,r)=>t.fetchOptimistic(e).catch(()=>{r.clearReset()});function m5(e,t,r){var f,h,m,v,g;const n=i5(),a=l5(),i=rT(),o=i.defaultQueryOptions(e);(h=(f=i.getDefaultOptions().queries)==null?void 0:f._experimental_beforeQuery)==null||h.call(f,o),o._optimisticResults=n?"isRestoring":"optimistic",f5(o),c5(o,a),u5(a);const s=!i.getQueryCache().get(o.queryHash),[l]=p.useState(()=>new t(i,o)),c=l.getOptimisticResult(o),u=!n&&e.subscribed!==!1;if(p.useSyncExternalStore(p.useCallback(y=>{const b=u?l.subscribe(Ot.batchCalls(y)):hr;return l.updateResult(),b},[l,u]),()=>l.getCurrentResult(),()=>l.getCurrentResult()),p.useEffect(()=>{l.setOptions(o)},[o,l]),p5(o,c))throw jk(o,l,a);if(d5({result:c,errorResetBoundary:a,throwOnError:o.throwOnError,query:i.getQueryCache().get(o.queryHash),suspense:o.suspense}))throw c.error;if((v=(m=i.getDefaultOptions().queries)==null?void 0:m._experimental_afterQuery)==null||v.call(m,o,c),o.experimental_prefetchInRender&&!ls&&h5(c,n)){const y=s?jk(o,l,a):(g=i.getQueryCache().get(o.queryHash))==null?void 0:g.promise;y==null||y.catch(hr).finally(()=>{l.updateResult()})}return o.notifyOnChangeProps?c:l.trackResult(c)}function et(e,t){return m5(e,G4)}function g5(e,t){const r=rT(),[n]=p.useState(()=>new t5(r,e));p.useEffect(()=>{n.setOptions(e)},[n,e]);const a=p.useSyncExternalStore(p.useCallback(o=>n.subscribe(Ot.batchCalls(o)),[n]),()=>n.getCurrentResult(),()=>n.getCurrentResult()),i=p.useCallback((o,s)=>{n.mutate(o,s).catch(hr)},[n]);if(a.error&&VN(n.options.throwOnError,[a.error]))throw a.error;return{...a,mutate:i,mutateAsync:a.mutate}}/** + * react-router v7.9.1 + * + * Copyright (c) Remix Software Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE.md file in the root directory of this source tree. + * + * @license MIT + */var Ck="popstate";function v5(e={}){function t(n,a){let{pathname:i,search:o,hash:s}=n.location;return By("",{pathname:i,search:o,hash:s},a.state&&a.state.usr||null,a.state&&a.state.key||"default")}function r(n,a){return typeof a=="string"?a:yu(a)}return x5(t,r,null,e)}function ot(e,t){if(e===!1||e===null||typeof e>"u")throw new Error(t)}function Fn(e,t){if(!e){typeof console<"u"&&console.warn(t);try{throw new Error(t)}catch{}}}function y5(){return Math.random().toString(36).substring(2,10)}function Ok(e,t){return{usr:e.state,key:e.key,idx:t}}function By(e,t,r=null,n){return{pathname:typeof e=="string"?e:e.pathname,search:"",hash:"",...typeof t=="string"?Vl(t):t,state:r,key:t&&t.key||n||y5()}}function yu({pathname:e="/",search:t="",hash:r=""}){return t&&t!=="?"&&(e+=t.charAt(0)==="?"?t:"?"+t),r&&r!=="#"&&(e+=r.charAt(0)==="#"?r:"#"+r),e}function Vl(e){let t={};if(e){let r=e.indexOf("#");r>=0&&(t.hash=e.substring(r),e=e.substring(0,r));let n=e.indexOf("?");n>=0&&(t.search=e.substring(n),e=e.substring(0,n)),e&&(t.pathname=e)}return t}function x5(e,t,r,n={}){let{window:a=document.defaultView,v5Compat:i=!1}=n,o=a.history,s="POP",l=null,c=u();c==null&&(c=0,o.replaceState({...o.state,idx:c},""));function u(){return(o.state||{idx:null}).idx}function f(){s="POP";let y=u(),b=y==null?null:y-c;c=y,l&&l({action:s,location:g.location,delta:b})}function h(y,b){s="PUSH";let x=By(g.location,y,b);c=u()+1;let w=Ok(x,c),S=g.createHref(x);try{o.pushState(w,"",S)}catch(k){if(k instanceof DOMException&&k.name==="DataCloneError")throw k;a.location.assign(S)}i&&l&&l({action:s,location:g.location,delta:1})}function m(y,b){s="REPLACE";let x=By(g.location,y,b);c=u();let w=Ok(x,c),S=g.createHref(x);o.replaceState(w,"",S),i&&l&&l({action:s,location:g.location,delta:0})}function v(y){return b5(y)}let g={get action(){return s},get location(){return e(a,o)},listen(y){if(l)throw new Error("A history only accepts one active listener");return a.addEventListener(Ck,f),l=y,()=>{a.removeEventListener(Ck,f),l=null}},createHref(y){return t(a,y)},createURL:v,encodeLocation(y){let b=v(y);return{pathname:b.pathname,search:b.search,hash:b.hash}},push:h,replace:m,go(y){return o.go(y)}};return g}function b5(e,t=!1){let r="http://localhost";typeof window<"u"&&(r=window.location.origin!=="null"?window.location.origin:window.location.href),ot(r,"No window.location.(origin|href) available to create URL");let n=typeof e=="string"?e:yu(e);return n=n.replace(/ $/,"%20"),!t&&n.startsWith("//")&&(n=r+n),new URL(n,r)}function aT(e,t,r="/"){return w5(e,t,r,!1)}function w5(e,t,r,n){let a=typeof t=="string"?Vl(t):t,i=ti(a.pathname||"/",r);if(i==null)return null;let o=iT(e);S5(o);let s=null;for(let l=0;s==null&&l{let u={relativePath:c===void 0?o.path||"":c,caseSensitive:o.caseSensitive===!0,childrenIndex:s,route:o};if(u.relativePath.startsWith("/")){if(!u.relativePath.startsWith(n)&&l)return;ot(u.relativePath.startsWith(n),`Absolute route path "${u.relativePath}" nested under path "${n}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),u.relativePath=u.relativePath.slice(n.length)}let f=Ka([n,u.relativePath]),h=r.concat(u);o.children&&o.children.length>0&&(ot(o.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${f}".`),iT(o.children,t,h,f,l)),!(o.path==null&&!o.index)&&t.push({path:f,score:N5(f,o.index),routesMeta:h})};return e.forEach((o,s)=>{var l;if(o.path===""||!((l=o.path)!=null&&l.includes("?")))i(o,s);else for(let c of oT(o.path))i(o,s,!0,c)}),t}function oT(e){let t=e.split("/");if(t.length===0)return[];let[r,...n]=t,a=r.endsWith("?"),i=r.replace(/\?$/,"");if(n.length===0)return a?[i,""]:[i];let o=oT(n.join("/")),s=[];return s.push(...o.map(l=>l===""?i:[i,l].join("/"))),a&&s.push(...o),s.map(l=>e.startsWith("/")&&l===""?"/":l)}function S5(e){e.sort((t,r)=>t.score!==r.score?r.score-t.score:T5(t.routesMeta.map(n=>n.childrenIndex),r.routesMeta.map(n=>n.childrenIndex)))}var k5=/^:[\w-]+$/,P5=3,E5=2,j5=1,C5=10,O5=-2,Nk=e=>e==="*";function N5(e,t){let r=e.split("/"),n=r.length;return r.some(Nk)&&(n+=O5),t&&(n+=E5),r.filter(a=>!Nk(a)).reduce((a,i)=>a+(k5.test(i)?P5:i===""?j5:C5),n)}function T5(e,t){return e.length===t.length&&e.slice(0,-1).every((n,a)=>n===t[a])?e[e.length-1]-t[t.length-1]:0}function A5(e,t,r=!1){let{routesMeta:n}=e,a={},i="/",o=[];for(let s=0;s{if(u==="*"){let v=s[h]||"";o=i.slice(0,i.length-v.length).replace(/(.)\/+$/,"$1")}const m=s[h];return f&&!m?c[u]=void 0:c[u]=(m||"").replace(/%2F/g,"/"),c},{}),pathname:i,pathnameBase:o,pattern:e}}function _5(e,t=!1,r=!0){Fn(e==="*"||!e.endsWith("*")||e.endsWith("/*"),`Route path "${e}" will be treated as if it were "${e.replace(/\*$/,"/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${e.replace(/\*$/,"/*")}".`);let n=[],a="^"+e.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(o,s,l)=>(n.push({paramName:s,isOptional:l!=null}),l?"/?([^\\/]+)?":"/([^\\/]+)")).replace(/\/([\w-]+)\?(\/|$)/g,"(/$1)?$2");return e.endsWith("*")?(n.push({paramName:"*"}),a+=e==="*"||e==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):r?a+="\\/*$":e!==""&&e!=="/"&&(a+="(?:(?=\\/|$))"),[new RegExp(a,t?void 0:"i"),n]}function D5(e){try{return e.split("/").map(t=>decodeURIComponent(t).replace(/\//g,"%2F")).join("/")}catch(t){return Fn(!1,`The URL path "${e}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${t}).`),e}}function ti(e,t){if(t==="/")return e;if(!e.toLowerCase().startsWith(t.toLowerCase()))return null;let r=t.endsWith("/")?t.length-1:t.length,n=e.charAt(r);return n&&n!=="/"?null:e.slice(r)||"/"}function M5(e,t="/"){let{pathname:r,search:n="",hash:a=""}=typeof e=="string"?Vl(e):e;return{pathname:r?r.startsWith("/")?r:R5(r,t):t,search:$5(n),hash:F5(a)}}function R5(e,t){let r=t.replace(/\/+$/,"").split("/");return e.split("/").forEach(a=>{a===".."?r.length>1&&r.pop():a!=="."&&r.push(a)}),r.length>1?r.join("/"):"/"}function zg(e,t,r,n){return`Cannot include a '${e}' character in a manually specified \`to.${t}\` field [${JSON.stringify(n)}]. Please separate it out to the \`to.${r}\` field. Alternatively you may provide the full path as a string in and the router will parse it for you.`}function I5(e){return e.filter((t,r)=>r===0||t.route.path&&t.route.path.length>0)}function tb(e){let t=I5(e);return t.map((r,n)=>n===t.length-1?r.pathname:r.pathnameBase)}function rb(e,t,r,n=!1){let a;typeof e=="string"?a=Vl(e):(a={...e},ot(!a.pathname||!a.pathname.includes("?"),zg("?","pathname","search",a)),ot(!a.pathname||!a.pathname.includes("#"),zg("#","pathname","hash",a)),ot(!a.search||!a.search.includes("#"),zg("#","search","hash",a)));let i=e===""||a.pathname==="",o=i?"/":a.pathname,s;if(o==null)s=r;else{let f=t.length-1;if(!n&&o.startsWith("..")){let h=o.split("/");for(;h[0]==="..";)h.shift(),f-=1;a.pathname=h.join("/")}s=f>=0?t[f]:"/"}let l=M5(a,s),c=o&&o!=="/"&&o.endsWith("/"),u=(i||o===".")&&r.endsWith("/");return!l.pathname.endsWith("/")&&(c||u)&&(l.pathname+="/"),l}var Ka=e=>e.join("/").replace(/\/\/+/g,"/"),L5=e=>e.replace(/\/+$/,"").replace(/^\/*/,"/"),$5=e=>!e||e==="?"?"":e.startsWith("?")?e:"?"+e,F5=e=>!e||e==="#"?"":e.startsWith("#")?e:"#"+e;function B5(e){return e!=null&&typeof e.status=="number"&&typeof e.statusText=="string"&&typeof e.internal=="boolean"&&"data"in e}var sT=["POST","PUT","PATCH","DELETE"];new Set(sT);var z5=["GET",...sT];new Set(z5);var Gl=p.createContext(null);Gl.displayName="DataRouter";var _p=p.createContext(null);_p.displayName="DataRouterState";p.createContext(!1);var lT=p.createContext({isTransitioning:!1});lT.displayName="ViewTransition";var W5=p.createContext(new Map);W5.displayName="Fetchers";var q5=p.createContext(null);q5.displayName="Await";var qn=p.createContext(null);qn.displayName="Navigation";var Ku=p.createContext(null);Ku.displayName="Location";var bn=p.createContext({outlet:null,matches:[],isDataRoute:!1});bn.displayName="Route";var nb=p.createContext(null);nb.displayName="RouteError";function U5(e,{relative:t}={}){ot(Ql(),"useHref() may be used only in the context of a component.");let{basename:r,navigator:n}=p.useContext(qn),{hash:a,pathname:i,search:o}=Yu(e,{relative:t}),s=i;return r!=="/"&&(s=i==="/"?r:Ka([r,i])),n.createHref({pathname:s,search:o,hash:a})}function Ql(){return p.useContext(Ku)!=null}function fi(){return ot(Ql(),"useLocation() may be used only in the context of a component."),p.useContext(Ku).location}var cT="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function uT(e){p.useContext(qn).static||p.useLayoutEffect(e)}function Dp(){let{isDataRoute:e}=p.useContext(bn);return e?oB():H5()}function H5(){ot(Ql(),"useNavigate() may be used only in the context of a component.");let e=p.useContext(Gl),{basename:t,navigator:r}=p.useContext(qn),{matches:n}=p.useContext(bn),{pathname:a}=fi(),i=JSON.stringify(tb(n)),o=p.useRef(!1);return uT(()=>{o.current=!0}),p.useCallback((l,c={})=>{if(Fn(o.current,cT),!o.current)return;if(typeof l=="number"){r.go(l);return}let u=rb(l,JSON.parse(i),a,c.relative==="path");e==null&&t!=="/"&&(u.pathname=u.pathname==="/"?t:Ka([t,u.pathname])),(c.replace?r.replace:r.push)(u,c.state,c)},[t,r,i,a,e])}var K5=p.createContext(null);function Y5(e){let t=p.useContext(bn).outlet;return t&&p.createElement(K5.Provider,{value:e},t)}function V5(){let{matches:e}=p.useContext(bn),t=e[e.length-1];return t?t.params:{}}function Yu(e,{relative:t}={}){let{matches:r}=p.useContext(bn),{pathname:n}=fi(),a=JSON.stringify(tb(r));return p.useMemo(()=>rb(e,JSON.parse(a),n,t==="path"),[e,a,n,t])}function G5(e,t){return dT(e,t)}function dT(e,t,r,n,a){var x;ot(Ql(),"useRoutes() may be used only in the context of a component.");let{navigator:i}=p.useContext(qn),{matches:o}=p.useContext(bn),s=o[o.length-1],l=s?s.params:{},c=s?s.pathname:"/",u=s?s.pathnameBase:"/",f=s&&s.route;{let w=f&&f.path||"";fT(c,!f||w.endsWith("*")||w.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 h=fi(),m;if(t){let w=typeof t=="string"?Vl(t):t;ot(u==="/"||((x=w.pathname)==null?void 0:x.startsWith(u)),`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 "${u}" but pathname "${w.pathname}" was given in the \`location\` prop.`),m=w}else m=h;let v=m.pathname||"/",g=v;if(u!=="/"){let w=u.replace(/^\//,"").split("/");g="/"+v.replace(/^\//,"").split("/").slice(w.length).join("/")}let y=aT(e,{pathname:g});Fn(f||y!=null,`No routes matched location "${m.pathname}${m.search}${m.hash}" `),Fn(y==null||y[y.length-1].route.element!==void 0||y[y.length-1].route.Component!==void 0||y[y.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 b=eB(y&&y.map(w=>Object.assign({},w,{params:Object.assign({},l,w.params),pathname:Ka([u,i.encodeLocation?i.encodeLocation(w.pathname).pathname:w.pathname]),pathnameBase:w.pathnameBase==="/"?u:Ka([u,i.encodeLocation?i.encodeLocation(w.pathnameBase).pathname:w.pathnameBase])})),o,r,n,a);return t&&b?p.createElement(Ku.Provider,{value:{location:{pathname:"/",search:"",hash:"",state:null,key:"default",...m},navigationType:"POP"}},b):b}function Q5(){let e=iB(),t=B5(e)?`${e.status} ${e.statusText}`:e instanceof Error?e.message:JSON.stringify(e),r=e instanceof Error?e.stack:null,n="rgba(200,200,200, 0.5)",a={padding:"0.5rem",backgroundColor:n},i={padding:"2px 4px",backgroundColor:n},o=null;return console.error("Error handled by React Router default ErrorBoundary:",e),o=p.createElement(p.Fragment,null,p.createElement("p",null,"💿 Hey developer 👋"),p.createElement("p",null,"You can provide a way better UX than this when your app throws errors by providing your own ",p.createElement("code",{style:i},"ErrorBoundary")," or"," ",p.createElement("code",{style:i},"errorElement")," prop on your route.")),p.createElement(p.Fragment,null,p.createElement("h2",null,"Unexpected Application Error!"),p.createElement("h3",{style:{fontStyle:"italic"}},t),r?p.createElement("pre",{style:a},r):null,o)}var X5=p.createElement(Q5,null),Z5=class extends p.Component{constructor(e){super(e),this.state={location:e.location,revalidation:e.revalidation,error:e.error}}static getDerivedStateFromError(e){return{error:e}}static getDerivedStateFromProps(e,t){return t.location!==e.location||t.revalidation!=="idle"&&e.revalidation==="idle"?{error:e.error,location:e.location,revalidation:e.revalidation}:{error:e.error!==void 0?e.error:t.error,location:t.location,revalidation:e.revalidation||t.revalidation}}componentDidCatch(e,t){this.props.unstable_onError?this.props.unstable_onError(e,t):console.error("React Router caught the following error during render",e)}render(){return this.state.error!==void 0?p.createElement(bn.Provider,{value:this.props.routeContext},p.createElement(nb.Provider,{value:this.state.error,children:this.props.component})):this.props.children}};function J5({routeContext:e,match:t,children:r}){let n=p.useContext(Gl);return n&&n.static&&n.staticContext&&(t.route.errorElement||t.route.ErrorBoundary)&&(n.staticContext._deepestRenderedBoundaryId=t.route.id),p.createElement(bn.Provider,{value:e},r)}function eB(e,t=[],r=null,n=null,a=null){if(e==null){if(!r)return null;if(r.errors)e=r.matches;else if(t.length===0&&!r.initialized&&r.matches.length>0)e=r.matches;else return null}let i=e,o=r==null?void 0:r.errors;if(o!=null){let c=i.findIndex(u=>u.route.id&&(o==null?void 0:o[u.route.id])!==void 0);ot(c>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(o).join(",")}`),i=i.slice(0,Math.min(i.length,c+1))}let s=!1,l=-1;if(r)for(let c=0;c=0?i=i.slice(0,l+1):i=[i[0]];break}}}return i.reduceRight((c,u,f)=>{let h,m=!1,v=null,g=null;r&&(h=o&&u.route.id?o[u.route.id]:void 0,v=u.route.errorElement||X5,s&&(l<0&&f===0?(fT("route-fallback",!1,"No `HydrateFallback` element provided to render during initial hydration"),m=!0,g=null):l===f&&(m=!0,g=u.route.hydrateFallbackElement||null)));let y=t.concat(i.slice(0,f+1)),b=()=>{let x;return h?x=v:m?x=g:u.route.Component?x=p.createElement(u.route.Component,null):u.route.element?x=u.route.element:x=c,p.createElement(J5,{match:u,routeContext:{outlet:c,matches:y,isDataRoute:r!=null},children:x})};return r&&(u.route.ErrorBoundary||u.route.errorElement||f===0)?p.createElement(Z5,{location:r.location,revalidation:r.revalidation,component:v,error:h,children:b(),routeContext:{outlet:null,matches:y,isDataRoute:!0},unstable_onError:n}):b()},null)}function ab(e){return`${e} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function tB(e){let t=p.useContext(Gl);return ot(t,ab(e)),t}function rB(e){let t=p.useContext(_p);return ot(t,ab(e)),t}function nB(e){let t=p.useContext(bn);return ot(t,ab(e)),t}function ib(e){let t=nB(e),r=t.matches[t.matches.length-1];return ot(r.route.id,`${e} can only be used on routes that contain a unique "id"`),r.route.id}function aB(){return ib("useRouteId")}function iB(){var n;let e=p.useContext(nb),t=rB("useRouteError"),r=ib("useRouteError");return e!==void 0?e:(n=t.errors)==null?void 0:n[r]}function oB(){let{router:e}=tB("useNavigate"),t=ib("useNavigate"),r=p.useRef(!1);return uT(()=>{r.current=!0}),p.useCallback(async(a,i={})=>{Fn(r.current,cT),r.current&&(typeof a=="number"?e.navigate(a):await e.navigate(a,{fromRouteId:t,...i}))},[e,t])}var Tk={};function fT(e,t,r){!t&&!Tk[e]&&(Tk[e]=!0,Fn(!1,r))}p.memo(sB);function sB({routes:e,future:t,state:r,unstable_onError:n}){return dT(e,void 0,r,n,t)}function lB({to:e,replace:t,state:r,relative:n}){ot(Ql()," may be used only in the context of a component.");let{static:a}=p.useContext(qn);Fn(!a," must not be used on the initial render in a . This is a no-op, but you should modify your code so the is only ever rendered in response to some user interaction or state change.");let{matches:i}=p.useContext(bn),{pathname:o}=fi(),s=Dp(),l=rb(e,tb(i),o,n==="path"),c=JSON.stringify(l);return p.useEffect(()=>{s(JSON.parse(c),{replace:t,state:r,relative:n})},[s,c,n,t,r]),null}function cB(e){return Y5(e.context)}function Ir(e){ot(!1,"A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .")}function uB({basename:e="/",children:t=null,location:r,navigationType:n="POP",navigator:a,static:i=!1}){ot(!Ql(),"You cannot render a inside another . You should never have more than one in your app.");let o=e.replace(/^\/*/,"/"),s=p.useMemo(()=>({basename:o,navigator:a,static:i,future:{}}),[o,a,i]);typeof r=="string"&&(r=Vl(r));let{pathname:l="/",search:c="",hash:u="",state:f=null,key:h="default"}=r,m=p.useMemo(()=>{let v=ti(l,o);return v==null?null:{location:{pathname:v,search:c,hash:u,state:f,key:h},navigationType:n}},[o,l,c,u,f,h,n]);return Fn(m!=null,` is not able to match the URL "${l}${c}${u}" because it does not start with the basename, so the won't render anything.`),m==null?null:p.createElement(qn.Provider,{value:s},p.createElement(Ku.Provider,{children:t,value:m}))}function dB({children:e,location:t}){return G5(zy(e),t)}function zy(e,t=[]){let r=[];return p.Children.forEach(e,(n,a)=>{if(!p.isValidElement(n))return;let i=[...t,a];if(n.type===p.Fragment){r.push.apply(r,zy(n.props.children,i));return}ot(n.type===Ir,`[${typeof n.type=="string"?n.type:n.type.name}] is not a component. All component children of must be a or `),ot(!n.props.index||!n.props.children,"An index route cannot have child routes.");let o={id:n.props.id||i.join("-"),caseSensitive:n.props.caseSensitive,element:n.props.element,Component:n.props.Component,index:n.props.index,path:n.props.path,loader:n.props.loader,action:n.props.action,hydrateFallbackElement:n.props.hydrateFallbackElement,HydrateFallback:n.props.HydrateFallback,errorElement:n.props.errorElement,ErrorBoundary:n.props.ErrorBoundary,hasErrorBoundary:n.props.hasErrorBoundary===!0||n.props.ErrorBoundary!=null||n.props.errorElement!=null,shouldRevalidate:n.props.shouldRevalidate,handle:n.props.handle,lazy:n.props.lazy};n.props.children&&(o.children=zy(n.props.children,i)),r.push(o)}),r}var _f="get",Df="application/x-www-form-urlencoded";function Mp(e){return e!=null&&typeof e.tagName=="string"}function fB(e){return Mp(e)&&e.tagName.toLowerCase()==="button"}function hB(e){return Mp(e)&&e.tagName.toLowerCase()==="form"}function pB(e){return Mp(e)&&e.tagName.toLowerCase()==="input"}function mB(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function gB(e,t){return e.button===0&&(!t||t==="_self")&&!mB(e)}var Kd=null;function vB(){if(Kd===null)try{new FormData(document.createElement("form"),0),Kd=!1}catch{Kd=!0}return Kd}var yB=new Set(["application/x-www-form-urlencoded","multipart/form-data","text/plain"]);function Wg(e){return e!=null&&!yB.has(e)?(Fn(!1,`"${e}" is not a valid \`encType\` for \`
\`/\`\` and will default to "${Df}"`),null):e}function xB(e,t){let r,n,a,i,o;if(hB(e)){let s=e.getAttribute("action");n=s?ti(s,t):null,r=e.getAttribute("method")||_f,a=Wg(e.getAttribute("enctype"))||Df,i=new FormData(e)}else if(fB(e)||pB(e)&&(e.type==="submit"||e.type==="image")){let s=e.form;if(s==null)throw new Error('Cannot submit a + + + + {/* Time input */} +
+ Time + +
+
+ + ); +} diff --git a/fastapi_radar/dashboard/src/components/ui/popover.tsx b/fastapi_radar/dashboard/src/components/ui/popover.tsx new file mode 100644 index 0000000..156b42e --- /dev/null +++ b/fastapi_radar/dashboard/src/components/ui/popover.tsx @@ -0,0 +1,32 @@ +"use client"; + +import * as PopoverPrimitive from "@radix-ui/react-popover"; +import * as React from "react"; + +import { cn } from "@/lib/utils"; + +const Popover = PopoverPrimitive.Root; +const PopoverTrigger = PopoverPrimitive.Trigger; +const PopoverAnchor = PopoverPrimitive.Anchor; + +const PopoverContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( + + + +)); +PopoverContent.displayName = PopoverPrimitive.Content.displayName; + +export { Popover, PopoverAnchor, PopoverContent, PopoverTrigger }; + diff --git a/fastapi_radar/dashboard/src/components/ui/refresh-interval-select.tsx b/fastapi_radar/dashboard/src/components/ui/refresh-interval-select.tsx new file mode 100644 index 0000000..9846703 --- /dev/null +++ b/fastapi_radar/dashboard/src/components/ui/refresh-interval-select.tsx @@ -0,0 +1,38 @@ +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { useT } from "@/i18n"; + +interface RefreshIntervalSelectProps { + value: number; + onChange: (value: number) => void; +} + +export function RefreshIntervalSelect({ + value, + onChange, +}: RefreshIntervalSelectProps) { + const t = useT(); + return ( + + ); +} diff --git a/fastapi_radar/dashboard/src/components/ui/table-pagination.tsx b/fastapi_radar/dashboard/src/components/ui/table-pagination.tsx new file mode 100644 index 0000000..66f20c4 --- /dev/null +++ b/fastapi_radar/dashboard/src/components/ui/table-pagination.tsx @@ -0,0 +1,85 @@ +import { Button } from "@/components/ui/button"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { useT } from "@/i18n"; +import { cn } from "@/lib/utils"; +import { ChevronLeft, ChevronRight } from "lucide-react"; + +interface TablePaginationProps { + page: number; + pageSize: number; + itemCount: number; + onPageChange: (page: number) => void; + onPageSizeChange: (pageSize: number) => void; + pageSizeOptions?: number[]; + className?: string; +} + +export function TablePagination({ + page, + pageSize, + itemCount, + onPageChange, + onPageSizeChange, + pageSizeOptions = [10, 25, 50, 100], + className, +}: TablePaginationProps) { + const t = useT(); + + return ( +
+
+ + {t("common.pagination.pageSize")} + + +
+ +
+ + + {t("common.pagination.page")} {page} + + +
+
+ ); +} diff --git a/fastapi_radar/dashboard/src/i18n/translations.ts b/fastapi_radar/dashboard/src/i18n/translations.ts index fad4bed..b160dae 100644 --- a/fastapi_radar/dashboard/src/i18n/translations.ts +++ b/fastapi_radar/dashboard/src/i18n/translations.ts @@ -13,6 +13,7 @@ export interface Translations { exceptions: string; performance: string; backgroundTasks: string; + logs: string; settings: string; }; @@ -59,6 +60,10 @@ export interface Translations { title: string; description: string; }; + logs: { + title: string; + description: string; + }; settings: { title: string; description: string; @@ -110,6 +115,12 @@ export interface Translations { viewAll: string; copy: string; copied: string; + pagination: { + previous: string; + next: string; + pageSize: string; + page: string; + }; }; // Time ranges @@ -160,6 +171,13 @@ export interface Translations { disconnected: string; queries: string; avgQueryTime: string; + loading: string; + totalRequests: string; + totalQueries: string; + totalExceptions: string; + slowQueries: string; + avgResponseTime: string; + requestsPerMinute: string; }; performance: { title: string; @@ -218,6 +236,9 @@ export interface Translations { description: string; timeRange: string; apply: string; + startTime: string; + endTime: string; + clearRange: string; }; tabs: { all: string; @@ -248,6 +269,7 @@ export interface Translations { lastHour: string; last24Hours: string; last7Days: string; + customRange: string; }; descriptions: { all: string; @@ -261,6 +283,12 @@ export interface Translations { failed: string; slow: string; }; + chart: { + title: string; + description: string; + successful: string; + errors: string; + }; }; // Exceptions page @@ -435,7 +463,22 @@ export interface Translations { layout: { connected: string; + disconnected: string; }, + + // Logs page + logs: { + noLogs: string; + records: string; + level: string; + logger: string; + searchPlaceholder: string; + startTime: string; + endTime: string; + levelFilters: { + all: string; + }; + }; }; @@ -449,6 +492,7 @@ const en: Translations = { exceptions: "Exceptions", performance: "Performance", backgroundTasks: "Background Tasks", + logs: "Logs", settings: "Settings", }, @@ -495,6 +539,10 @@ const en: Translations = { title: "Background Tasks", description: "Monitor and track background tasks executed in your application", }, + logs: { + title: "Logs", + description: "Browse Python log records captured from your application", + }, settings: { title: "Settings", description: "Manage your dashboard preferences and data", @@ -544,6 +592,12 @@ const en: Translations = { viewAll: "View all", copy: "Copy", copied: "Copied!", + pagination: { + previous: "Previous", + next: "Next", + pageSize: "Rows per page", + page: "Page", + }, }, timeRange: { @@ -591,6 +645,13 @@ const en: Translations = { disconnected: "Disconnected", queries: "Queries", avgQueryTime: "Avg Query Time", + loading: "Loading statistics...", + totalRequests: "Total Requests", + totalQueries: "Total Queries", + totalExceptions: "Total Exceptions", + slowQueries: "Slow Queries", + avgResponseTime: "Avg Response Time", + requestsPerMinute: "Requests/Minute", }, performance: { title: "Performance Overview", @@ -650,6 +711,9 @@ const en: Translations = { description: "Filter and search through request logs", timeRange: "Time Range", apply: "Apply Filters", + startTime: "Start", + endTime: "End", + clearRange: "Clear Range", }, tabs: { all: "All Requests", @@ -680,6 +744,7 @@ const en: Translations = { lastHour: "Last Hour", last24Hours: "Last 24 Hours", last7Days: "Last 7 Days", + customRange: "Custom Range", }, descriptions: { all: "Complete list of all HTTP requests", @@ -693,6 +758,12 @@ const en: Translations = { failed: "No failed requests", slow: "No slow requests", }, + chart: { + title: "Number of Requests", + description: "Requests and errors", + successful: "Successful", + errors: "Errors", + }, }, exceptions: { @@ -864,6 +935,20 @@ const en: Translations = { layout: { connected: "Connected", + disconnected: "Disconnected", + }, + + logs: { + noLogs: "No log records captured yet", + records: "Log Records", + level: "Level", + logger: "Logger", + searchPlaceholder: "Search log messages...", + startTime: "From", + endTime: "To", + levelFilters: { + all: "All levels", + }, }, }; @@ -877,6 +962,7 @@ const zh: Translations = { exceptions: "异常监控", performance: "性能分析", backgroundTasks: "后台任务", + logs: "日志", settings: "设置", }, @@ -921,6 +1007,10 @@ const zh: Translations = { title: "后台任务", description: "监控和跟踪应用程序中执行的后台任务", }, + logs: { + title: "日志", + description: "浏览应用程序捕获的 Python 日志记录", + }, settings: { title: "设置", description: "管理仪表板偏好设置和数据", @@ -970,6 +1060,12 @@ const zh: Translations = { viewAll: "查看全部", copy: "复制", copied: "已复制!", + pagination: { + previous: "上一页", + next: "下一页", + pageSize: "每页行数", + page: "页", + }, }, timeRange: { @@ -1017,6 +1113,13 @@ const zh: Translations = { disconnected: "未连接", queries: "查询数", avgQueryTime: "平均查询时间", + loading: "加载统计数据中...", + totalRequests: "总请求数", + totalQueries: "总查询数", + totalExceptions: "总异常数", + slowQueries: "慢查询", + avgResponseTime: "平均响应时间", + requestsPerMinute: "每分钟请求数", }, performance: { title: "性能概览", @@ -1075,6 +1178,9 @@ const zh: Translations = { description: "筛选和搜索请求日志", timeRange: "时间范围", apply: "应用筛选器", + startTime: "开始", + endTime: "结束", + clearRange: "清除范围", }, tabs: { all: "所有请求", @@ -1105,6 +1211,7 @@ const zh: Translations = { lastHour: "最近1小时", last24Hours: "最近24小时", last7Days: "最近7天", + customRange: "自定义范围", }, descriptions: { all: "所有 HTTP 请求的完整列表", @@ -1118,6 +1225,12 @@ const zh: Translations = { failed: "没有失败的请求", slow: "没有慢请求", }, + chart: { + title: "请求数量", + description: "请求与错误", + successful: "成功", + errors: "错误", + }, }, exceptions: { @@ -1289,6 +1402,20 @@ const zh: Translations = { layout: { connected: "已连接", + disconnected: "未连接", + }, + + logs: { + noLogs: "尚未捕获任何日志记录", + records: "日志记录", + level: "级别", + logger: "日志器", + searchPlaceholder: "搜索日志消息...", + startTime: "开始时间", + endTime: "结束时间", + levelFilters: { + all: "所有级别", + }, }, }; diff --git a/fastapi_radar/dashboard/src/pages/BackgroundTasksPage.tsx b/fastapi_radar/dashboard/src/pages/BackgroundTasksPage.tsx index f096e9c..d493a40 100644 --- a/fastapi_radar/dashboard/src/pages/BackgroundTasksPage.tsx +++ b/fastapi_radar/dashboard/src/pages/BackgroundTasksPage.tsx @@ -3,19 +3,22 @@ import { apiClient } from "@/api/client"; import { Badge } from "@/components/ui/badge"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; +import { RefreshIntervalSelect } from "@/components/ui/refresh-interval-select"; import { format } from "date-fns"; import { Clock, CheckCircle2, XCircle, Loader2, ExternalLink } from "lucide-react"; +import { useState } from "react"; import { useNavigate } from "react-router-dom"; import { useT } from "@/i18n"; export function BackgroundTasksPage() { const navigate = useNavigate(); const t = useT(); + const [refreshInterval, setRefreshInterval] = useState(5000); const { data: tasks, isLoading, isError, error } = useQuery({ queryKey: ["background-tasks"], queryFn: () => apiClient.getBackgroundTasks({ limit: 100 }), - refetchInterval: 3000, + refetchInterval: refreshInterval || false, }); const getStatusColor = ( @@ -77,11 +80,14 @@ export function BackgroundTasksPage() { return (
-
-

{t("pages.backgroundTasks.title")}

-

- {t("pages.backgroundTasks.description")} -

+
+
+

{t("pages.backgroundTasks.title")}

+

+ {t("pages.backgroundTasks.description")} +

+
+
diff --git a/fastapi_radar/dashboard/src/pages/DashboardPage.tsx b/fastapi_radar/dashboard/src/pages/DashboardPage.tsx index ed6ea1f..21b7569 100644 --- a/fastapi_radar/dashboard/src/pages/DashboardPage.tsx +++ b/fastapi_radar/dashboard/src/pages/DashboardPage.tsx @@ -9,6 +9,7 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; +import { RefreshIntervalSelect } from "@/components/ui/refresh-interval-select"; import { Activity, Database, @@ -48,33 +49,33 @@ import { Badge } from "@/components/ui/badge"; export function DashboardPage() { const [timeRange, setTimeRange] = useState(1); // hours - const [autoRefresh] = useState(true); + const [refreshInterval, setRefreshInterval] = useState(5000); const { openDetail } = useDetailDrawer(); const t = useT(); const { data: stats, refetch: refetchStats } = useQuery({ queryKey: ["stats", timeRange], queryFn: () => apiClient.getStats(timeRange), - refetchInterval: autoRefresh ? 5000 : false, + refetchInterval: refreshInterval || false, }); const { data: recentRequests, refetch: refetchRequests } = useQuery({ queryKey: ["recent-requests"], queryFn: () => apiClient.getRequests({ limit: 100 }), - refetchInterval: autoRefresh ? 5000 : false, + refetchInterval: refreshInterval || false, }); const { data: slowQueries, refetch: refetchQueries } = useQuery({ queryKey: ["slow-queries"], queryFn: () => apiClient.getQueries({ slow_only: true, limit: 10, slow_threshold: 100 }), - refetchInterval: autoRefresh ? 10000 : false, + refetchInterval: refreshInterval || false, }); const { data: recentExceptions, refetch: refetchExceptions } = useQuery({ queryKey: ["recent-exceptions"], queryFn: () => apiClient.getExceptions({ limit: 5 }), - refetchInterval: autoRefresh ? 10000 : false, + refetchInterval: refreshInterval || false, }); // Use centralized metrics calculations @@ -149,6 +150,7 @@ export function DashboardPage() { {t("timeRange.last7Days")} + diff --git a/fastapi_radar/dashboard/src/pages/LogsPage.tsx b/fastapi_radar/dashboard/src/pages/LogsPage.tsx new file mode 100644 index 0000000..fbb7e95 --- /dev/null +++ b/fastapi_radar/dashboard/src/pages/LogsPage.tsx @@ -0,0 +1,262 @@ +import { apiClient, LogRecord } from "@/api/client"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { DateTimePicker } from "@/components/ui/date-time-picker"; +import { Input } from "@/components/ui/input"; +import { RefreshIntervalSelect } from "@/components/ui/refresh-interval-select"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { TablePagination } from "@/components/ui/table-pagination"; +import { useT } from "@/i18n"; +import { useQuery } from "@tanstack/react-query"; +import { format } from "date-fns"; +import { ChevronDown, ChevronUp, RefreshCw, Search } from "lucide-react"; +import { useState } from "react"; + +const LEVEL_COLORS: Record< + string, + "default" | "destructive" | "secondary" | "outline" +> = { + DEBUG: "outline", + INFO: "secondary", + WARNING: "default", + ERROR: "destructive", + CRITICAL: "destructive", +}; + +function LogRow({ log }: { log: LogRecord }) { + const [expanded, setExpanded] = useState(false); + return ( +
+
+ + {log.level} + + +
+
+ + {format(new Date(log.created_at), "HH:mm:ss.SSS")} + + {log.logger_name && ( + + [{log.logger_name}] + + )} +
+

{log.message}

+ {log.pathname && ( +

+ {log.pathname} + {log.lineno ? `:${log.lineno}` : ""} + {log.func_name ? ` in ${log.func_name}` : ""} +

+ )} + {log.exc_info && ( +
+ + {expanded && ( +
+                  {log.exc_info}
+                
+ )} +
+ )} +
+
+
+ ); +} + +export function LogsPage() { + const t = useT(); + const [search, setSearch] = useState(""); + const [level, setLevel] = useState("all"); + const [startTime, setStartTime] = useState(""); + const [endTime, setEndTime] = useState(""); + const [appliedSearch, setAppliedSearch] = useState(""); + const [appliedLevel, setAppliedLevel] = useState("all"); + const [appliedStartTime, setAppliedStartTime] = useState(""); + const [appliedEndTime, setAppliedEndTime] = useState(""); + const [page, setPage] = useState(1); + const [pageSize, setPageSize] = useState(10); + const [refreshInterval, setRefreshInterval] = useState(5000); + + const filterParams = { + level: appliedLevel !== "all" ? appliedLevel : undefined, + search: appliedSearch || undefined, + start_time: appliedStartTime || undefined, + end_time: appliedEndTime || undefined, + }; + + const { data: logsCount } = useQuery({ + queryKey: ["logs-count", appliedLevel, appliedSearch, appliedStartTime, appliedEndTime], + queryFn: () => apiClient.getLogsCount(filterParams), + refetchInterval: refreshInterval || false, + }); + + const { data: logs, isLoading, isError, refetch } = useQuery({ + queryKey: ["logs", appliedLevel, appliedSearch, appliedStartTime, appliedEndTime, page, pageSize], + queryFn: () => + apiClient.getLogs({ + limit: pageSize, + offset: (page - 1) * pageSize, + ...filterParams, + }), + refetchInterval: refreshInterval || false, + }); + + const applyFilters = () => { + setAppliedSearch(search); + setAppliedLevel(level); + setAppliedStartTime(startTime ? new Date(startTime).toISOString() : ""); + setAppliedEndTime(endTime ? new Date(endTime).toISOString() : ""); + setPage(1); + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === "Enter") applyFilters(); + }; + + return ( +
+
+
+

+ {t("pages.logs.title")} +

+

{t("pages.logs.description")}

+
+ +
+ + {/* Filters */} + + +
+
+
+ + setSearch(e.target.value)} + onKeyDown={handleKeyDown} + className="pl-8" + /> +
+
+ + + +
+ {t("logs.startTime")} + +
+ +
+ {t("logs.endTime")} + +
+ + + +
+
+
+ + {/* Log list */} + + + + {t("logs.records")} + {logsCount !== undefined && ( + + ({logsCount.count}) + + )} + + + + {isLoading && ( +
+ {t("common.loading")} +
+ )} + {isError && ( +
+ {t("common.error")} +
+ )} + {!isLoading && !isError && (!logs || logs.length === 0) && ( +
+ {t("logs.noLogs")} +
+ )} + {logs && logs.length > 0 && ( +
+ {logs.map((log) => ( + + ))} +
+ )} + + {/* Pagination */} + +
+
+
+ ); +} diff --git a/fastapi_radar/dashboard/src/pages/PerformancePage.tsx b/fastapi_radar/dashboard/src/pages/PerformancePage.tsx index 2b6fb2c..a8a35dd 100644 --- a/fastapi_radar/dashboard/src/pages/PerformancePage.tsx +++ b/fastapi_radar/dashboard/src/pages/PerformancePage.tsx @@ -9,6 +9,7 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; +import { RefreshIntervalSelect } from "@/components/ui/refresh-interval-select"; import { useDetailDrawer } from "@/context/DetailDrawerContext"; import { useMetrics, formatDuration, formatNumber } from "@/hooks/useMetrics"; import { useT } from "@/i18n"; @@ -174,22 +175,7 @@ export function PerformancePage() { {t("timeRange.last7Days")} - +
diff --git a/fastapi_radar/dashboard/src/pages/RequestsPage.tsx b/fastapi_radar/dashboard/src/pages/RequestsPage.tsx index 894fece..8c059b5 100644 --- a/fastapi_radar/dashboard/src/pages/RequestsPage.tsx +++ b/fastapi_radar/dashboard/src/pages/RequestsPage.tsx @@ -1,15 +1,11 @@ -import { useState } from "react"; -import { useQuery } from "@tanstack/react-query"; import { apiClient } from "@/api/client"; -import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from "@/components/ui/card"; +import { RequestItem } from "@/components/RequestItem"; +import { BarChart } from "@/components/charts"; +import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; -import { Label } from "@/components/ui/label"; +import { DateTimePicker } from "@/components/ui/date-time-picker"; +import { RefreshIntervalSelect } from "@/components/ui/refresh-interval-select"; +import { SearchInput } from "@/components/ui/search-input"; import { Select, SelectContent, @@ -17,90 +13,166 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; -import { Badge } from "@/components/ui/badge"; +import { TablePagination } from "@/components/ui/table-pagination"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { SearchInput } from "@/components/ui/search-input"; -import { RequestItem } from "@/components/RequestItem"; -import { useDebounce } from "@/hooks/useDebounce"; -import { Filter, Download, RefreshCw } from "lucide-react"; import { useDetailDrawer } from "@/context/DetailDrawerContext"; +import { useDebounce } from "@/hooks/useDebounce"; import { useT } from "@/i18n"; +import { useQuery } from "@tanstack/react-query"; +import { Download, Filter, RefreshCw, X } from "lucide-react"; +import { useEffect, useState } from "react"; export function RequestsPage() { const [statusFilter, setStatusFilter] = useState("all"); const [methodFilter, setMethodFilter] = useState("all"); const [searchTerm, setSearchTerm] = useState(""); const [activeTab, setActiveTab] = useState("all"); - const [timeRange, setTimeRange] = useState(null); // hours + const [timeRange, setTimeRange] = useState(null); + const [page, setPage] = useState(1); + const [pageSize, setPageSize] = useState(10); + const [refreshInterval, setRefreshInterval] = useState(30000); + const [startTime, setStartTime] = useState(""); + const [endTime, setEndTime] = useState(""); + const [appliedStartTime, setAppliedStartTime] = useState(""); + const [appliedEndTime, setAppliedEndTime] = useState(""); const { openDetail } = useDetailDrawer(); const t = useT(); - const debouncedSearchTerm = useDebounce(searchTerm, 300); - // Get all requests - const { data: allRequests, refetch } = useQuery({ - queryKey: ["all-requests", statusFilter, methodFilter, debouncedSearchTerm, timeRange], - queryFn: () => { - const params: any = { - limit: 200, - status_code: - statusFilter !== "all" ? getStatusCode(statusFilter) : undefined, - method: methodFilter !== "all" ? methodFilter : undefined, - search: debouncedSearchTerm || undefined, - }; + function toDatetimeLocal(date: Date): string { + const offset = date.getTimezoneOffset(); + return new Date(date.getTime() - offset * 60000).toISOString().slice(0, 16); + } + + useEffect(() => { + setPage(1); + }, [statusFilter, methodFilter, debouncedSearchTerm, appliedStartTime, appliedEndTime, activeTab]); - if (timeRange) { - params.start_time = new Date(Date.now() - timeRange * 60 * 60 * 1000).toISOString(); - } + const getStatusCode = (filter: string) => { + switch (filter) { + case "2xx": return 200; + case "3xx": return 300; + case "4xx": return 400; + case "5xx": return 500; + default: return undefined; + } + }; + const getCountsParams = () => { + const params: any = { + status_code: statusFilter !== "all" ? getStatusCode(statusFilter) : undefined, + method: methodFilter !== "all" ? methodFilter : undefined, + search: debouncedSearchTerm || undefined, + }; + if (appliedStartTime) params.start_time = appliedStartTime; + if (appliedEndTime) params.end_time = appliedEndTime; + return params; + }; + + const getTabParams = (tab: string) => { + switch (tab) { + case "successful": return { status_code: 200 }; + case "failed": return { min_status_code: 400 }; + case "slow": return { slow_only: true, slow_threshold: 500 }; + default: return {}; + } + }; + + const chartHours = (() => { + if (appliedStartTime && appliedEndTime) { + const diffMs = new Date(appliedEndTime).getTime() - new Date(appliedStartTime).getTime(); + return Math.max(1, Math.ceil(diffMs / (1000 * 3600))); + } + return timeRange ?? 24; + })(); + + const chartTimeLabel = (() => { + switch (timeRange) { + case 1: return t('requests.timeRangeFilters.lastHour'); + case 24: return t('requests.timeRangeFilters.last24Hours'); + case 168: return t('requests.timeRangeFilters.last7Days'); + } + if (appliedStartTime || appliedEndTime) { + const parts: string[] = []; + if (appliedStartTime) parts.push(new Date(appliedStartTime).toLocaleString()); + if (appliedEndTime) parts.push(new Date(appliedEndTime).toLocaleString()); + return parts.length === 2 + ? `${t('requests.timeRangeFilters.customRange')}: ${parts[0]} – ${parts[1]}` + : `${t('requests.timeRangeFilters.customRange')}: ${parts[0]}`; + } + return t('requests.timeRangeFilters.last24Hours'); + })(); + + const { data: timeseriesData } = useQuery({ + queryKey: ["requests-timeseries", chartHours, appliedStartTime, appliedEndTime], + queryFn: () => apiClient.getRequestTimeseries(chartHours, appliedStartTime || undefined, appliedEndTime || undefined), + refetchInterval: refreshInterval || false, + }); + + const chartData = timeseriesData?.map((point) => { + const d = new Date(point.iso_time); + const localTime = chartHours > 48 + ? d.toLocaleDateString([], { weekday: "short", month: "numeric", day: "numeric" }) + : d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", hour12: false }); + return { time: localTime, isoTime: point.iso_time, successful: Math.max(0, point.requests - point.errors), errors: point.errors }; + }); + + const getBucketDurationMs = () => { + if (chartHours === 1) return 60 * 1000; + if (chartHours > 24) return 24 * 60 * 60 * 1000; + return 60 * 60 * 1000; + }; + + const handleChartBrush = (startIdx: number, endIdx: number) => { + if (!chartData?.length) return; + const startIso = chartData[startIdx]?.isoTime; + const endIso = chartData[endIdx]?.isoTime; + if (!startIso || !endIso) return; + const endDate = new Date(new Date(endIso).getTime() + getBucketDurationMs()); + const newStart = new Date(startIso).toISOString(); + const newEnd = endDate.toISOString(); + setStartTime(toDatetimeLocal(new Date(newStart))); + setEndTime(toDatetimeLocal(new Date(newEnd))); + setAppliedStartTime(newStart); + setAppliedEndTime(newEnd); + setTimeRange(null); + setPage(1); + }; + + const { data: allRequests, refetch } = useQuery({ + queryKey: ["all-requests", statusFilter, methodFilter, debouncedSearchTerm, appliedStartTime, appliedEndTime, page, pageSize, activeTab], + queryFn: () => { + const params: any = { limit: pageSize, offset: (page - 1) * pageSize, ...getCountsParams(), ...getTabParams(activeTab) }; return apiClient.getRequests(params); }, - refetchInterval: 5000, + refetchInterval: refreshInterval || false, + placeholderData: (prev) => prev, }); - // Calculate counts for tabs - const successfulCount = - allRequests?.filter( - (r) => r.status_code && r.status_code >= 200 && r.status_code < 300 - ).length || 0; - const failedCount = - allRequests?.filter((r) => r.status_code && r.status_code >= 400).length || - 0; - const slowCount = - allRequests?.filter((r) => r.duration_ms && r.duration_ms > 500).length || - 0; + const { data: requestCounts } = useQuery({ + queryKey: ["request-counts", statusFilter, methodFilter, debouncedSearchTerm, appliedStartTime, appliedEndTime], + queryFn: () => apiClient.getRequestCounts(getCountsParams()), + refetchInterval: refreshInterval || false, + }); - // Filter requests based on active tab - const filteredRequests = - activeTab === "all" - ? allRequests - : activeTab === "successful" - ? allRequests?.filter( - (r) => r.status_code && r.status_code >= 200 && r.status_code < 300 - ) - : activeTab === "failed" - ? allRequests?.filter((r) => r.status_code && r.status_code >= 400) - : activeTab === "slow" - ? allRequests?.filter((r) => r.duration_ms && r.duration_ms > 500) - : allRequests; + const successfulCount = requestCounts?.successful ?? 0; + const failedCount = requestCounts?.failed ?? 0; + const slowCount = requestCounts?.slow ?? 0; + const filteredRequests = allRequests; - const getStatusCode = (filter: string) => { - switch (filter) { - case "2xx": - return 200; - case "3xx": - return 300; - case "4xx": - return 400; - case "5xx": - return 500; - default: - return undefined; - } + const applyFilters = () => { + setTimeRange(null); + setAppliedStartTime(startTime ? new Date(startTime).toISOString() : ""); + setAppliedEndTime(endTime ? new Date(endTime).toISOString() : ""); + setPage(1); }; - const applyFilters = () => { - refetch(); + const clearTimeRange = () => { + setTimeRange(null); + setStartTime(""); + setEndTime(""); + setAppliedStartTime(""); + setAppliedEndTime(""); }; const exportData = () => { @@ -116,201 +188,190 @@ export function RequestsPage() { }; return ( -
-
-

{t('pages.requests.title')}

-

- {t('pages.requests.description')} -

+
+ {/* Header */} +
+

{t('pages.requests.title')}

+
- {/* Filters and Actions */} - - - {t('common.filter')} - - {t('requests.filters.description')} - - - -
-
-
- - -
+ {/* Chart */} + -
- - -
+ {/* Compact Filter Bar */} +
+ -
- - -
+ -
- -
- - - -
-
-
+ -
- -
- - - - -
-
-
- - + {/* Time range quick buttons */} +
+ {[ + { label: t('requests.timeRangeFilters.all'), value: null as number | null }, + { label: "1h", value: 1 }, + { label: "24h", value: 24 }, + { label: "7d", value: 168 }, + ].map(({ label, value }) => ( + + ))} +
+ + {/* Custom datetime range */} + + + + + {(startTime || endTime) && ( + + )} - {/* Request Tabs */} - - - +
+ + + +
+
+ + {/* Tabs + Request List */} + + + {t('requests.tabs.all')} - {allRequests && allRequests.length > 0 && ( - - {allRequests.length} - + {requestCounts && requestCounts.total > 0 && ( + {requestCounts.total} )} - + {t('requests.tabs.successful')} {successfulCount > 0 && ( - - {successfulCount} - + {successfulCount} )} - + {t('requests.tabs.failed')} {failedCount > 0 && ( - - {failedCount} - + {failedCount} )} - + {t('requests.tabs.slow')} {slowCount > 0 && ( - - {slowCount} - + {slowCount} )} - - - - - {activeTab === "all" && t('requests.tabs.all')} - {activeTab === "successful" && t('requests.tabs.successful')} - {activeTab === "failed" && t('requests.tabs.failed')} - {activeTab === "slow" && t('requests.tabs.slow')} - - - {activeTab === "all" && t('requests.descriptions.all')} - {activeTab === "successful" && t('requests.descriptions.successful')} - {activeTab === "failed" && t('requests.descriptions.failed')} - {activeTab === "slow" && t('requests.descriptions.slow')} - - - -
- {filteredRequests?.map((request) => ( - openDetail("request", request.request_id)} - /> - ))} - {(!filteredRequests || filteredRequests.length === 0) && ( -
- {activeTab === "all" && t('requests.empty.all')} - {activeTab === "successful" && t('requests.empty.successful')} - {activeTab === "failed" && t('requests.empty.failed')} - {activeTab === "slow" && t('requests.empty.slow')} -
- )} -
-
-
+ +
+
+ {filteredRequests?.map((request) => ( + openDetail("request", request.request_id)} + /> + ))} + {(!filteredRequests || filteredRequests.length === 0) && ( +
+ {activeTab === "all" && t('requests.empty.all')} + {activeTab === "successful" && t('requests.empty.successful')} + {activeTab === "failed" && t('requests.empty.failed')} + {activeTab === "slow" && t('requests.empty.slow')} +
+ )} +
+
+ +
+
); -} +} \ No newline at end of file diff --git a/fastapi_radar/dashboard/src/pages/TracingPage.tsx b/fastapi_radar/dashboard/src/pages/TracingPage.tsx index fd97f93..77ec9ae 100644 --- a/fastapi_radar/dashboard/src/pages/TracingPage.tsx +++ b/fastapi_radar/dashboard/src/pages/TracingPage.tsx @@ -1,3 +1,4 @@ +import { useState } from "react"; import { Card, CardContent, @@ -5,20 +6,25 @@ import { CardHeader, CardTitle, } from "@/components/ui/card"; +import { RefreshIntervalSelect } from "@/components/ui/refresh-interval-select"; import { TracesList } from "@/components/TracesList"; import { useT } from "@/i18n"; export function TracingPage() { const t = useT(); + const [refreshInterval, setRefreshInterval] = useState(30000); return (
-
-

- {t("pages.tracing.title")} -

-

- {t("pages.tracing.description")} -

+
+
+

+ {t("pages.tracing.title")} +

+

+ {t("pages.tracing.description")} +

+
+
@@ -29,7 +35,7 @@ export function TracingPage() { - +
diff --git a/fastapi_radar/dashboard/vite.config.ts b/fastapi_radar/dashboard/vite.config.ts index cae1fae..cb8fcf7 100644 --- a/fastapi_radar/dashboard/vite.config.ts +++ b/fastapi_radar/dashboard/vite.config.ts @@ -1,9 +1,9 @@ -import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; import path from "path"; +import { defineConfig } from "vite"; -export default defineConfig({ - base: "/__radar/", +export default defineConfig(({ command }) => ({ + base: command === "build" ? "./" : "/__radar/", plugins: [react()], resolve: { alias: { @@ -25,10 +25,10 @@ export default defineConfig({ }, server: { proxy: { - "/api": { + "/__radar/api": { target: "http://localhost:8000", changeOrigin: true, }, }, }, -}); +})); diff --git a/fastapi_radar/log_handler.py b/fastapi_radar/log_handler.py new file mode 100644 index 0000000..9b6db61 --- /dev/null +++ b/fastapi_radar/log_handler.py @@ -0,0 +1,53 @@ +import logging +import traceback +from datetime import datetime, timezone +from typing import Callable, Optional + +from .models import CapturedLog + + +class RadarLoggingHandler(logging.Handler): + """A logging handler that persists log records to the FastAPI Radar database. + + Usage:: + + radar = Radar(app) + handler = RadarLoggingHandler(get_session=radar.get_session) + logging.getLogger().addHandler(handler) + + Or use the convenience method:: + + radar = Radar(app) + radar.attach_logger() # root logger, all levels + radar.attach_logger(level=logging.WARNING) + """ + + def __init__(self, get_session: Callable, level: int = logging.NOTSET): + super().__init__(level) + self._get_session = get_session + + def emit(self, record: logging.LogRecord) -> None: + try: + message = self.format(record) + + exc_text: Optional[str] = None + if record.exc_info: + exc_text = "".join(traceback.format_exception(*record.exc_info)) + + log_entry = CapturedLog( + logger_name=record.name, + level=record.levelname, + message=message, + pathname=record.pathname, + lineno=record.lineno, + func_name=record.funcName, + thread_name=record.threadName, + exc_info=exc_text, + request_id=None, + created_at=datetime.fromtimestamp(record.created, tz=timezone.utc), + ) + with self._get_session() as session: + session.add(log_entry) + session.commit() + except Exception: + self.handleError(record) diff --git a/fastapi_radar/middleware.py b/fastapi_radar/middleware.py index 39d6011..74e940a 100644 --- a/fastapi_radar/middleware.py +++ b/fastapi_radar/middleware.py @@ -13,18 +13,8 @@ from starlette.responses import Response, StreamingResponse from .models import CapturedException, CapturedRequest -from .tracing import ( - TraceContext, - TracingManager, - create_trace_context, - set_trace_context, -) -from .utils import ( - get_client_ip, - redact_sensitive_data, - serialize_headers, - truncate_body, -) +from .tracing import TraceContext, TracingManager, create_trace_context, set_trace_context +from .utils import get_client_ip, redact_sensitive_data, serialize_headers, truncate_body request_context: ContextVar[Optional[str]] = ContextVar("request_id", default=None) @@ -97,8 +87,8 @@ async def dispatch(self, request: Request, call_next) -> Response: captured_request = CapturedRequest( request_id=request_id, method=request.method, - url=str(request.url), - path=request.url.path, + url=str(request.url)[:500], + path=request.url.path[:500], query_params=dict(request.query_params) if request.query_params else None, headers=serialize_headers(request.headers), body=( @@ -187,7 +177,7 @@ async def capture_response(): # Persist trace data if trace_ctx and self.tracing_manager: - self.tracing_manager.save_trace_context(trace_ctx) + self.tracing_manager.save_trace_context(trace_ctx, session=session) session.commit() diff --git a/fastapi_radar/models.py b/fastapi_radar/models.py index 8359471..685b726 100644 --- a/fastapi_radar/models.py +++ b/fastapi_radar/models.py @@ -2,16 +2,7 @@ from datetime import datetime, timezone -from sqlalchemy import ( - JSON, - Column, - DateTime, - Float, - Integer, - Sequence, - String, - Text, -) +from sqlalchemy import JSON, Column, DateTime, Float, Integer, Sequence, String, Text try: from sqlalchemy.orm import declarative_base @@ -177,3 +168,21 @@ class BackgroundTask(Base): created_at = Column( DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), index=True ) + + +class CapturedLog(Base): + __tablename__ = "radar_logs" + + id = Column(Integer, Sequence("radar_logs_id_seq"), primary_key=True, index=True) + logger_name = Column(String(200), index=True) + level = Column(String(20), index=True) + message = Column(Text, nullable=False) + pathname = Column(String(500)) + lineno = Column(Integer) + func_name = Column(String(200)) + thread_name = Column(String(100)) + exc_info = Column(Text) + request_id = Column(String(36), index=True, nullable=True) + created_at = Column( + DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), index=True + ) diff --git a/fastapi_radar/radar.py b/fastapi_radar/radar.py index 3ef01f4..65cbfde 100644 --- a/fastapi_radar/radar.py +++ b/fastapi_radar/radar.py @@ -1,19 +1,22 @@ """Main Radar class for FastAPI Radar.""" import asyncio +import logging import multiprocessing import os import sys +import threading from contextlib import contextmanager from pathlib import Path from typing import Callable, List, Optional, Union from fastapi import FastAPI +from fastapi.responses import HTMLResponse from sqlalchemy import create_engine from sqlalchemy.engine import Engine from sqlalchemy.ext.asyncio import AsyncEngine from sqlalchemy.orm import Session, sessionmaker -from sqlalchemy.pool import StaticPool +from sqlalchemy.pool import NullPool, StaticPool from .api import create_api_router from .capture import QueryCapture @@ -81,6 +84,7 @@ def __init__( self.query_capture = None if dashboard_path not in self.exclude_paths: + self.exclude_paths.append(f"{self.app.root_path}{dashboard_path}") self.exclude_paths.append(dashboard_path) self.exclude_paths.append("/favicon.ico") @@ -147,12 +151,17 @@ def __init__( "read_only": False, "config": {"memory_limit": "500mb"}, }, - poolclass=StaticPool, + poolclass=NullPool, ) # Check if storage_engine is async or sync # If async, we'll use it for DDL operations but keep sessions sync # by accessing the sync engine from the async engine + self._use_session_lock = isinstance( + getattr(self.storage_engine, "pool", None), StaticPool + ) + self._session_lock = threading.Lock() if self._use_session_lock else None + if isinstance(self.storage_engine, AsyncEngine): # For async engines, get the underlying sync engine for session operations # The middleware and other components use sessions synchronously @@ -176,11 +185,34 @@ def __init__( @contextmanager def get_session(self) -> Session: """Get a database session for radar storage.""" + if self._session_lock is not None: + self._session_lock.acquire() session = self.SessionLocal() try: yield session + except Exception: + session.rollback() + raise finally: session.close() + if self._session_lock is not None: + self._session_lock.release() + + def attach_logger( + self, + logger: Optional[logging.Logger] = None, + level: int = logging.DEBUG, + ) -> "logging.Handler": + """Attach a handler to capture Python log records into the Radar database.""" + from .log_handler import RadarLoggingHandler + + handler = RadarLoggingHandler(get_session=self.get_session, level=level) + target = logger if logger is not None else logging.getLogger() + if target.level == logging.NOTSET or target.level > level: + target.setLevel(level) + + target.addHandler(handler) + return handler def _setup_middleware(self) -> None: """Add request capture middleware.""" @@ -209,7 +241,11 @@ def _setup_query_capture(self) -> None: def _setup_api(self, include_in_schema: bool) -> None: """Mount API endpoints.""" - api_router = create_api_router(self.get_session, self.auth_dependency) + api_router = create_api_router( + self.get_session, + self.auth_dependency, + prefix=f"{self.dashboard_path}/api", + ) self.app.include_router(api_router, include_in_schema=include_in_schema) def _setup_dashboard(self, include_in_schema: bool) -> None: @@ -262,7 +298,14 @@ async def serve_dashboard(request: Request, full_path: str = ""): index_path = dashboard_dir / "index.html" if index_path.exists(): - return FileResponse(index_path) + content = index_path.read_text(encoding="utf-8") + root_path = request.scope.get("root_path", "").rstrip("/") + base_path = root_path + self.dashboard_path.rstrip("/") + "/" + injection = ( + f'' + ) + content = content.replace("", f"{injection}\n", 1) + return HTMLResponse(content=content) else: return {"error": "Dashboard not found. Please build the dashboard."} @@ -357,7 +400,8 @@ def _create_placeholder_dashboard(self, dashboard_dir: Path) -> None: