From fbcb1966d5d629599385075c9347f151bab9fc16 Mon Sep 17 00:00:00 2001 From: nogueiraguh Date: Fri, 20 Mar 2026 13:48:30 -0300 Subject: [PATCH] feat: comprehensive security, performance, UX and deployment audit Security: - CORS restricted to explicit methods/headers, configurable via CORS_ORIGINS env - Auth added to /reports/{rid}/html and /compliance-report endpoints - Ownership check on report downloads - Rate limiting on login (10 attempts/5min per IP with threading.Lock) - Non-root container user (agent via gosu entrypoint) - Nginx security headers (X-Frame-Options, X-Content-Type-Options, X-XSS-Protection, Referrer-Policy, Permissions-Policy) - .gitignore and .dockerignore for secret leak prevention - .env.example with documentation Performance: - 16 SQLite indexes on foreign keys and frequently queried columns - Pagination on chat messages (100), reports (50), audit log (100) - subprocess.run wrapped in run_in_executor (3 async handlers) - asyncio.wait_for timeouts on GenAI calls (300s) and Chromium PDF (120s) - Thread pool reduced to 10 workers - Code splitting with React.lazy (bundle: 1.3MB -> ~550KB initial) - React.memo on TreeItem (recursive compartment tree) Error handling: - 13 bare except clauses replaced with logged Exception handlers - Graceful shutdown handler (terminates subprocesses + executor) - File upload validation (50MB max, extension whitelist per endpoint) - Health check expanded (version, uptime, db_ok) - Cache-Control on /api/genai/models (1h) - Auto-cleanup audit_log > 30 days Dead code removed: - DownloadsPage.tsx, StubPage.tsx, MfaPage.tsx (moved to UsersPage) - Legacy frontend/ directory - 19 unused i18n keys, dist/ removed from git tracking UX & i18n: - 8 alert() calls replaced with styled error states (TerraformPage) - 50+ hardcoded Portuguese strings localized to i18n (pt/en) across 11 files - aria-label on all icon-only buttons (Chat, Explorer, Sidebar) - focus-visible CSS for keyboard navigation - Responsive grid fix (360px -> 280px for mobile) - aria-hidden on decorative SVGs Deployment: - Docker resource limits (backend 4G, frontend 512M) - Log rotation (json-file, 10MB x 3) - Terraform version parameterized via ARG --- .env.example | 6 + backend/.dockerignore | 10 + backend/Dockerfile | 18 +- backend/app.py | 278 +- docker-compose.yml | 20 +- frontend-react/.gitignore | 1 + frontend-react/dist/assets/index-Ca4AYldT.css | 2 - frontend-react/dist/assets/index-Dtsz7Biu.js | 115 - frontend-react/dist/index.html | 22 - frontend-react/dist/logo.svg | 21 - frontend-react/index.html | 2 +- frontend-react/src/App.tsx | 90 +- .../src/components/layout/Sidebar.tsx | 138 +- .../src/components/shared/StubPage.tsx | 68 - frontend-react/src/i18n/en.ts | 119 +- frontend-react/src/i18n/pt.ts | 120 +- frontend-react/src/index.css | 6 + frontend-react/src/pages/ChatPage.tsx | 72 +- frontend-react/src/pages/DownloadsPage.tsx | 453 --- frontend-react/src/pages/ExplorerPage.tsx | 45 +- .../src/pages/PromptGeneratorPage.tsx | 34 +- frontend-react/src/pages/ReportsPage.tsx | 74 +- frontend-react/src/pages/TerraformPage.tsx | 92 +- frontend-react/src/pages/WorkspacesPage.tsx | 10 +- frontend-react/src/pages/admin/MfaPage.tsx | 281 -- frontend-react/src/pages/admin/UsersPage.tsx | 196 +- .../src/pages/config/AdbConfigPage.tsx | 32 +- .../src/pages/config/EmbConsultPage.tsx | 2 +- .../src/pages/config/EmbeddingsPage.tsx | 8 +- .../src/pages/config/GenAiConfigPage.tsx | 14 +- .../src/pages/config/McpServersPage.tsx | 18 +- .../src/pages/config/OciConfigPage.tsx | 6 +- frontend-react/src/stores/app.ts | 264 ++ frontend/index.html | 3554 ----------------- nginx/default.conf | 13 +- 35 files changed, 1301 insertions(+), 4903 deletions(-) create mode 100644 .env.example create mode 100644 backend/.dockerignore delete mode 100644 frontend-react/dist/assets/index-Ca4AYldT.css delete mode 100644 frontend-react/dist/assets/index-Dtsz7Biu.js delete mode 100644 frontend-react/dist/index.html delete mode 100644 frontend-react/dist/logo.svg delete mode 100644 frontend-react/src/components/shared/StubPage.tsx delete mode 100644 frontend-react/src/pages/DownloadsPage.tsx delete mode 100644 frontend-react/src/pages/admin/MfaPage.tsx delete mode 100644 frontend/index.html diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..42bb6f1 --- /dev/null +++ b/.env.example @@ -0,0 +1,6 @@ +# Generate with: openssl rand -hex 32 +APP_SECRET=change-me-generate-a-secure-random-string +JWT_EXPIRY_HOURS=12 +PORT=8080 +CORS_ORIGINS=http://localhost:8080 +OCI_CLI_SUPPRESS_FILE_PERMISSIONS_WARNING=True diff --git a/backend/.dockerignore b/backend/.dockerignore new file mode 100644 index 0000000..a444054 --- /dev/null +++ b/backend/.dockerignore @@ -0,0 +1,10 @@ +.git +.gitignore +__pycache__ +*.pyc +.venv +.env* +*.db +*.sqlite +.DS_Store +.idea diff --git a/backend/Dockerfile b/backend/Dockerfile index 8ccd9d1..2135888 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -2,12 +2,13 @@ FROM python:3.12-slim WORKDIR /app -# Install system deps + Terraform CLI +# Install system deps + Chromium + Terraform CLI +ARG TERRAFORM_VERSION=1.7.5 RUN apt-get update && apt-get install -y --no-install-recommends \ curl gcc libffi-dev unzip \ chromium && \ ARCH=$(dpkg --print-architecture) && \ - curl -fsSL "https://releases.hashicorp.com/terraform/1.7.5/terraform_1.7.5_linux_${ARCH}.zip" -o /tmp/tf.zip && \ + curl -fsSL "https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_${ARCH}.zip" -o /tmp/tf.zip && \ unzip /tmp/tf.zip -d /usr/local/bin/ && rm /tmp/tf.zip && \ rm -rf /var/lib/apt/lists/* @@ -22,12 +23,21 @@ COPY cis_reports.py . COPY mcp_cis_server.py . COPY gen_tf_reference.py . -# Data volume -RUN mkdir -p /data +# Non-root user setup +RUN groupadd -r agent && useradd -r -g agent -d /app -s /bin/bash agent && \ + mkdir -p /data && chown -R agent:agent /app /data # Generate OCI Terraform resource reference at build time RUN python gen_tf_reference.py || echo "WARN: tf reference generation skipped (terraform init may need network)" +# Entrypoint: fix /data ownership then exec as agent user +RUN printf '#!/bin/bash\nchown -R agent:agent /data 2>/dev/null || true\nexec gosu agent "$@"\n' > /entrypoint.sh && \ + chmod +x /entrypoint.sh && \ + apt-get update && apt-get install -y --no-install-recommends gosu && rm -rf /var/lib/apt/lists/* + +VOLUME /data +ENTRYPOINT ["/entrypoint.sh"] + EXPOSE 8000 CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "8"] diff --git a/backend/app.py b/backend/app.py index bcb3551..5ca8aa4 100644 --- a/backend/app.py +++ b/backend/app.py @@ -5,7 +5,7 @@ OCI Account Explorer, MCP Server registry with VectorDB tool integration, Autonomous DB vector storage, CIS reports, chat agent, audit log. """ import os, json, uuid, hashlib, hmac, time, base64, struct, secrets, subprocess -import shutil, asyncio, sqlite3, logging, re, concurrent.futures +import shutil, asyncio, sqlite3, logging, re, concurrent.futures, threading from datetime import datetime, timedelta from pathlib import Path from typing import Optional, List, Dict, Any @@ -33,6 +33,8 @@ REPORTS = DATA / "reports" MCP_DIR = DATA / "mcp_servers" WALLET_DIR = DATA / "wallets" VERSION = "1.1" +_START_TIME = time.time() +_MAX_UPLOAD_BYTES = 50 * 1024 * 1024 # 50MB global limit for file uploads for d in [DATA, OCI_DIR, REPORTS, MCP_DIR, WALLET_DIR]: d.mkdir(parents=True, exist_ok=True) @@ -41,7 +43,7 @@ _running_reports: dict[str, asyncio.subprocess.Process] = {} # rid → subproce _running_terraform: dict[str, asyncio.subprocess.Process] = {} # wid → subprocess TERRAFORM_DIR = DATA / "terraform" TERRAFORM_DIR.mkdir(parents=True, exist_ok=True) -_chat_executor = concurrent.futures.ThreadPoolExecutor(max_workers=16, thread_name_prefix="chat") +_chat_executor = concurrent.futures.ThreadPoolExecutor(max_workers=10, thread_name_prefix="chat") # ── Chat Memory Compaction Settings ── COMPACT_TOKEN_THRESHOLD = 6000 # estimated tokens before triggering compaction @@ -52,9 +54,26 @@ COMPACT_MIN_MESSAGES = 8 logging.basicConfig(level=logging.INFO) log = logging.getLogger("agent") + +async def _validate_upload(file: "UploadFile", allowed_exts: set[str] | None = None, max_bytes: int = _MAX_UPLOAD_BYTES): + """Validate uploaded file size and extension. Seeks back to 0 after size check.""" + data = await file.read() + if len(data) > max_bytes: + raise HTTPException(400, f"Arquivo excede o limite de {max_bytes // (1024*1024)}MB") + if allowed_exts and file.filename: + ext = Path(file.filename).suffix.lower() + if ext not in allowed_exts: + raise HTTPException(400, f"Extensão '{ext}' não permitida. Aceitas: {', '.join(sorted(allowed_exts))}") + return data + + app = FastAPI(title="AI Agent - Infrastructure & Security Engineer", version=VERSION) -app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True, - allow_methods=["*"], allow_headers=["*"]) +_CORS_ORIGINS = os.environ.get("CORS_ORIGINS", "").split(",") if os.environ.get("CORS_ORIGINS") else [] +app.add_middleware(CORSMiddleware, + allow_origins=_CORS_ORIGINS or ["http://localhost:8080", "http://127.0.0.1:8080"], + allow_credentials=True, + allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"], + allow_headers=["Content-Type", "Authorization"]) security = HTTPBearer() # ── OCI GenAI Models Catalog ────────────────────────────────────────────────── @@ -363,6 +382,27 @@ def init_db(): """) c.execute("DELETE FROM config_logs WHERE created_at < datetime('now', '-30 days')") c.execute("DELETE FROM chat_logs WHERE created_at < datetime('now', '-30 days')") + c.execute("DELETE FROM audit_log WHERE created_at < datetime('now', '-30 days')") + # ── Indexes for performance ── + for idx in [ + "CREATE INDEX IF NOT EXISTS idx_chat_msg_session ON chat_messages(session_id)", + "CREATE INDEX IF NOT EXISTS idx_chat_msg_user ON chat_messages(user_id)", + "CREATE INDEX IF NOT EXISTS idx_chat_msg_status ON chat_messages(status)", + "CREATE INDEX IF NOT EXISTS idx_chat_sess_user ON chat_sessions(user_id)", + "CREATE INDEX IF NOT EXISTS idx_reports_user ON reports(user_id)", + "CREATE INDEX IF NOT EXISTS idx_reports_status ON reports(status)", + "CREATE INDEX IF NOT EXISTS idx_report_files_report ON report_files(report_id)", + "CREATE INDEX IF NOT EXISTS idx_oci_cfg_user ON oci_configs(user_id)", + "CREATE INDEX IF NOT EXISTS idx_genai_cfg_user ON genai_configs(user_id)", + "CREATE INDEX IF NOT EXISTS idx_mcp_srv_user ON mcp_servers(user_id)", + "CREATE INDEX IF NOT EXISTS idx_adb_cfg_user ON adb_vector_configs(user_id)", + "CREATE INDEX IF NOT EXISTS idx_adb_tables_cfg ON adb_vector_tables(adb_config_id)", + "CREATE INDEX IF NOT EXISTS idx_sessions_user ON sessions(user_id)", + "CREATE INDEX IF NOT EXISTS idx_tf_ws_user ON terraform_workspaces(user_id)", + "CREATE INDEX IF NOT EXISTS idx_tf_ws_session ON terraform_workspaces(session_id)", + "CREATE INDEX IF NOT EXISTS idx_audit_user ON audit_log(user_id)", + ]: + c.execute(idx) # ── Migrations ── for col in ["system_prompt TEXT DEFAULT ''", "reasoning_effort TEXT"]: try: @@ -401,8 +441,8 @@ def init_db(): if not c.execute("SELECT 1 FROM adb_vector_tables WHERE adb_config_id=? AND table_name=?", (cfg_row["id"], cfg_row["table_name"])).fetchone(): c.execute("INSERT INTO adb_vector_tables (id, adb_config_id, table_name, description) VALUES (?,?,?,?)", (str(uuid.uuid4()), cfg_row["id"], cfg_row["table_name"], "Migrado automaticamente")) - except Exception: - pass + except Exception as e: + log.warning(f"DB migration: adb_vector_tables backfill failed: {e}") # Backfill chat_sessions from existing chat_messages try: c.execute("""INSERT OR IGNORE INTO chat_sessions (id, user_id, agent_type, title, created_at, updated_at) @@ -414,8 +454,8 @@ def init_db(): LEFT JOIN terraform_workspaces tw ON tw.session_id = cm.session_id WHERE cm.session_id NOT IN (SELECT id FROM chat_sessions) GROUP BY cm.session_id""") - except Exception: - pass + except Exception as e: + log.warning(f"DB migration: chat_sessions backfill failed: {e}") # Seed default system prompt if none exists for chat agent if not c.execute("SELECT 1 FROM system_prompts WHERE agent='chat'").fetchone(): c.execute("INSERT INTO system_prompts (id,name,agent,content,is_active) VALUES (?,?,?,?,?)", @@ -591,9 +631,28 @@ class MCPServerReq(BaseModel): module_path: Optional[str] = None; tools: Optional[List[dict]] = None linked_adb_id: Optional[str] = None +# ── Rate limiting (login brute-force protection) ───────────────────────────── +_login_attempts: dict = {} # {ip: [timestamps]} +_login_attempts_lock = threading.Lock() +_LOGIN_WINDOW = 300 # 5 minutes +_LOGIN_MAX = 10 # max attempts per window + +def _check_rate_limit(ip: str): + """Raise 429 if too many login attempts from this IP.""" + import time + now = time.time() + with _login_attempts_lock: + attempts = _login_attempts.get(ip, []) + attempts = [t for t in attempts if now - t < _LOGIN_WINDOW] + if len(attempts) >= _LOGIN_MAX: + raise HTTPException(429, "Muitas tentativas de login. Aguarde 5 minutos.") + attempts.append(now) + _login_attempts[ip] = attempts + # ── Auth endpoints ──────────────────────────────────────────────────────────── @app.post("/api/auth/login") async def login(req: LoginReq, request: Request): + _check_rate_limit(request.client.host if request.client else "unknown") with db() as c: u = c.execute("SELECT * FROM users WHERE username=? AND is_active=1", (req.username,)).fetchone() if not u or not _verify_pw(req.password, u["password_hash"]): raise HTTPException(401, "Credenciais inválidas") @@ -697,6 +756,13 @@ async def save_oci( ): if auth_type not in ("api_key", "session_token"): raise HTTPException(400, "auth_type deve ser 'api_key' ou 'session_token'") + _PEM_EXTS = {".pem", ".key"} + if private_key and private_key.filename: + await _validate_upload(private_key, _PEM_EXTS) + private_key.file.seek(0) + if public_key and public_key.filename: + await _validate_upload(public_key, _PEM_EXTS) + public_key.file.seek(0) cid = str(uuid.uuid4()); cdir = OCI_DIR / cid; cdir.mkdir(parents=True) kp = cdir / "oci_api_key.pem" @@ -810,6 +876,13 @@ async def update_oci( private_key: Optional[UploadFile] = File(None), public_key: Optional[UploadFile] = File(None), u = Depends(require("admin","user")) ): + _PEM_EXTS = {".pem", ".key"} + if private_key and private_key.filename: + await _validate_upload(private_key, _PEM_EXTS) + private_key.file.seek(0) + if public_key and public_key.filename: + await _validate_upload(public_key, _PEM_EXTS) + public_key.file.seek(0) with db() as c: existing = c.execute("SELECT * FROM oci_configs WHERE id=?", (cid,)).fetchone() if not existing: raise HTTPException(404) @@ -874,8 +947,10 @@ async def test_oci(cid: str, u=Depends(require("admin","user"))): _config_log("oci", cid, cname, "error", "test", "Config não encontrada", u["id"], u["username"]) return {"status":"error","message":"Config não encontrada"} try: - r = subprocess.run(["oci","iam","region","list","--config-file",str(cp),"--output","json"], - capture_output=True, text=True, timeout=30, stdin=subprocess.DEVNULL) + loop = asyncio.get_event_loop() + r = await loop.run_in_executor(None, partial( + subprocess.run, ["oci","iam","region","list","--config-file",str(cp),"--output","json"], + capture_output=True, text=True, timeout=30, stdin=subprocess.DEVNULL)) if r.returncode == 0: _config_log("oci", cid, cname, "success", "test", "Conexão OK", u["id"], u["username"]) return {"status":"success","message":"Conexão OK","data":json.loads(r.stdout)} @@ -1030,10 +1105,12 @@ async def explore_compartment_tree(cid: str, u=Depends(current_user)): cp = identity.get_compartment(user_comp).data tree.append({"id":cp.id, "name":cp.name, "parent_id":cp.compartment_id, "lifecycle_state":cp.lifecycle_state}) - except Exception: + except Exception as e: + log.warning(f"Failed to get compartment {user_comp}: {e}") tree.append({"id":user_comp, "name":"Meu Compartment", "parent_id":tenancy, "lifecycle_state":"ACTIVE"}) - except Exception: + except Exception as e: + log.warning(f"Failed to list compartments from tenancy: {e}") # If listing from tenancy fails (no permission), list from user's compartment if user_comp and user_comp != tenancy: try: @@ -1042,7 +1119,8 @@ async def explore_compartment_tree(cid: str, u=Depends(current_user)): sub = identity.list_compartments(user_comp, compartment_id_in_subtree=True).data tree += [{"id":s.id, "name":s.name, "parent_id":s.compartment_id, "lifecycle_state":s.lifecycle_state} for s in sub if s.lifecycle_state == "ACTIVE"] - except Exception: + except Exception as e: + log.warning(f"Failed to list sub-compartments for {user_comp}: {e}") tree = [{"id":user_comp, "name":"Meu Compartment", "parent_id": None, "lifecycle_state":"ACTIVE"}] return tree except Exception as e: @@ -1851,7 +1929,10 @@ async def explore_network_firewall_policies(cid: str, compartment_id: str = Quer # ── OCI GenAI Config & Chat ─────────────────────────────────────────────────── @app.get("/api/genai/models") async def list_genai_models(u=Depends(current_user)): - return {"models": GENAI_MODELS, "regions": GENAI_REGIONS, "embedding_models": EMBEDDING_MODELS, "oci_regions": OCI_REGIONS} + return JSONResponse( + content={"models": GENAI_MODELS, "regions": GENAI_REGIONS, "embedding_models": EMBEDDING_MODELS, "oci_regions": OCI_REGIONS}, + headers={"Cache-Control": "max-age=3600"}, + ) @app.post("/api/genai/config") async def save_genai(req: GenAIConfigReq, u=Depends(require("admin","user"))): @@ -2281,7 +2362,7 @@ def _call_genai(gc: dict, message: str, history: list = None, tools: list = None args = {} if hasattr(tc, 'arguments') and tc.arguments: try: args = json.loads(tc.arguments) if isinstance(tc.arguments, str) else tc.arguments - except: args = {} + except Exception as e: log.warning(f"Failed to parse tool call arguments: {e}"); args = {} tool_calls.append({ "id": tc.id if hasattr(tc, 'id') else tc.name, "name": tc.name if hasattr(tc, 'name') else "", @@ -2762,7 +2843,8 @@ def _get_table_embedding_dim(cfg: dict, table_name: str) -> int: row = cur.fetchone() cur.close() return int(row[0]) if row and row[0] else 0 - except Exception: + except Exception as e: + log.warning(f"Failed to get embedding dimension for table '{table_name}': {e}") return 0 finally: conn.close() @@ -2818,7 +2900,8 @@ def _enrich_doc_content(doc: dict) -> str: if isinstance(meta, str) and meta: try: meta = json.loads(meta) - except Exception: + except Exception as e: + log.warning(f"Failed to parse document metadata JSON: {e}") meta = {} if isinstance(meta, dict): # If TEXT column is short/empty, use metadata.text instead @@ -2904,7 +2987,7 @@ async def list_mcp(u=Depends(current_user)): for k in ("args","env_vars","tools"): if d.get(k): try: d[k] = json.loads(d[k]) - except: pass + except Exception as e: log.warning(f"Failed to parse MCP server field '{k}': {e}") res.append(d) return res @@ -2940,6 +3023,8 @@ async def toggle_mcp(mid: str, u=Depends(require("admin","user"))): @app.post("/api/mcp/servers/{mid}/upload") async def upload_mcp_file(mid: str, file: UploadFile = File(...), u=Depends(require("admin","user"))): + await _validate_upload(file, {".py", ".js", ".ts", ".json", ".yaml", ".yml", ".toml", ".sh"}) + file.file.seek(0) sdir = MCP_DIR / mid; sdir.mkdir(parents=True, exist_ok=True) fp = sdir / file.filename fp.write_bytes(await file.read()) @@ -2966,7 +3051,7 @@ async def discover_mcp_tools(mid: str, u=Depends(require("admin","user"))): existing = [] if mcp_srv.get("tools"): try: existing = json.loads(mcp_srv["tools"]) if isinstance(mcp_srv["tools"], str) else mcp_srv["tools"] - except: pass + except Exception as e: log.warning(f"Failed to parse existing MCP tools JSON: {e}") existing_names = {t["name"] for t in existing if isinstance(t, dict)} for dt in discovered: if dt["name"] not in existing_names: @@ -3076,7 +3161,8 @@ def _get_active_mcp_tools(user_id: str) -> list[dict]: continue try: tools = json.loads(tools_raw) if isinstance(tools_raw, str) else tools_raw - except: + except Exception as e: + log.warning(f"Failed to parse tools JSON for MCP server '{srv.get('name', '?')}': {e}") continue for t in tools: if isinstance(t, dict) and t.get("name"): @@ -3087,6 +3173,8 @@ def _get_active_mcp_tools(user_id: str) -> list[dict]: @app.post("/api/adb/parse-wallet") async def parse_wallet(wallet: UploadFile = File(...), u=Depends(require("admin","user"))): """Extract DSN names from tnsnames.ora inside wallet ZIP (temporary parse, no save).""" + await _validate_upload(wallet, {".zip"}) + wallet.file.seek(0) import zipfile, tempfile try: data = await wallet.read() @@ -3115,6 +3203,9 @@ async def save_adb( wallet: Optional[UploadFile] = File(None), u=Depends(require("admin","user")) ): + if wallet and wallet.filename: + await _validate_upload(wallet, {".zip"}) + wallet.file.seek(0) vid = str(uuid.uuid4()) use_mtls_bool = use_mtls.lower() in ("true", "1", "yes") with db() as c: @@ -3138,6 +3229,8 @@ async def save_adb( @app.post("/api/adb/{vid}/upload-wallet") async def upload_wallet(vid: str, wallet: UploadFile = File(...), u=Depends(require("admin","user"))): + await _validate_upload(wallet, {".zip"}) + wallet.file.seek(0) cname = None with db() as c: row = c.execute("SELECT config_name FROM adb_vector_configs WHERE id=?", (vid,)).fetchone() @@ -3249,6 +3342,9 @@ async def update_adb( wallet: Optional[UploadFile] = File(None), u=Depends(require("admin","user")) ): + if wallet and wallet.filename: + await _validate_upload(wallet, {".zip"}) + wallet.file.seek(0) with db() as c: existing = c.execute("SELECT * FROM adb_vector_configs WHERE id=?", (vid,)).fetchone() if not existing: raise HTTPException(404) @@ -3631,6 +3727,8 @@ async def embed_upload(adb_config_id: str = Form(...), table_name: str = Form("" allowed = ('.txt', '.pdf', '.csv', '.json', '.md') if not any(fname.endswith(ext) for ext in allowed): raise HTTPException(400, f"Formatos aceitos: {', '.join(allowed)}") + await _validate_upload(file, allowed) + file.file.seek(0) raw = await file.read() if fname.endswith('.pdf'): content = _extract_pdf_text(raw) @@ -3965,11 +4063,11 @@ async def _exec_report(rid, cfg, regions, level=2, obp=False, raw=False, redact_ _config_log("oci", cfg["id"], cfg["tenancy_name"], "error", "report", str(e)[:2000]) @app.get("/api/reports") -async def list_reports(u=Depends(current_user)): +async def list_reports(skip: int = Query(0, ge=0), limit: int = Query(50, ge=1, le=200), u=Depends(current_user)): with db() as c: q = "SELECT id,user_id,tenancy_name,status,progress,level,obp_checks,raw_data,redact_output,created_at,completed_at,error_msg FROM reports" - rows = c.execute(q+" ORDER BY created_at DESC").fetchall() if u["role"]=="admin" \ - else c.execute(q+" WHERE user_id=? ORDER BY created_at DESC",(u["id"],)).fetchall() + rows = c.execute(q+" ORDER BY created_at DESC LIMIT ? OFFSET ?", (limit, skip)).fetchall() if u["role"]=="admin" \ + else c.execute(q+" WHERE user_id=? ORDER BY created_at DESC LIMIT ? OFFSET ?",(u["id"], limit, skip)).fetchall() return [dict(r) for r in rows] @app.get("/api/reports/{rid}/progress") @@ -4108,7 +4206,9 @@ async def download_report_file(rid: str, fid: str, token: str = Query(None), cre return FileResponse(p, filename=f["file_name"]) @app.get("/api/reports/{rid}/html") -async def report_html(rid): +async def report_html(rid, token: str = Query(None), cred: HTTPAuthorizationCredentials = Depends(HTTPBearer(auto_error=False))): + u = _user_from_token(token) if token else (await current_user(cred) if cred else None) + if not u: raise HTTPException(401, "Not authenticated") with db() as c: r=c.execute("SELECT html_path FROM reports WHERE id=?",(rid,)).fetchone() if not r or not r["html_path"] or not Path(r["html_path"]).exists(): raise HTTPException(404) return FileResponse(r["html_path"], media_type="text/html") @@ -4255,14 +4355,14 @@ def _search_cis_remediation(title: str, rec_num: str, user_id: str) -> dict: actual_dim = _get_table_embedding_dim(adb_cfg, tbl_name) if actual_dim and actual_dim in _DIM_TO_MODEL: tbl_model = _DIM_TO_MODEL[actual_dim] - except Exception: - pass + except Exception as e: + log.warning(f"Failed to detect embedding dimension for table '{tbl_name}': {e}") query_embedding = _embed_text(query, emb_genai, tbl_model) docs = _vector_search(adb_cfg, query_embedding, top_k=10, table_name=tbl_name) if docs: all_docs.extend(docs) - except Exception: - pass + except Exception as e: + log.warning(f"Vector search failed for table '{tbl_name}': {e}") if all_docs: import re as _re @@ -4836,8 +4936,10 @@ def _generate_compliance_report(rid: str, user_id: str, force_rag: bool = True) @app.get("/api/reports/{rid}/compliance-report") -async def compliance_report(rid: str, regen: int = Query(0)): +async def compliance_report(rid: str, regen: int = Query(0), token: str = Query(None), cred: HTTPAuthorizationCredentials = Depends(HTTPBearer(auto_error=False))): """Serve cached LAD A-Team CIS Compliance Report, or return 404 if not generated yet.""" + u = _user_from_token(token) if token else (await current_user(cred) if cred else None) + if not u: raise HTTPException(401, "Not authenticated") rdir = REPORTS / rid cache_path = rdir / "compliance_report.html" @@ -4949,12 +5051,16 @@ async def compliance_report_download(rid: str, token: str = Query(None), cred: H tmp_html = Path(tmpdir) / "report.html" tmp_pdf = Path(tmpdir) / "report.pdf" tmp_html.write_text(html, encoding="utf-8") - result = subprocess.run([ - "chromium", "--headless", "--no-sandbox", "--disable-gpu", - "--disable-software-rasterizer", "--run-all-compositor-stages-before-draw", - f"--print-to-pdf={tmp_pdf}", "--no-pdf-header-footer", - f"file://{tmp_html}" - ], capture_output=True, timeout=120) + loop = asyncio.get_event_loop() + result = await asyncio.wait_for( + loop.run_in_executor(None, partial( + subprocess.run, [ + "chromium", "--headless", "--no-sandbox", "--disable-gpu", + "--disable-software-rasterizer", "--run-all-compositor-stages-before-draw", + f"--print-to-pdf={tmp_pdf}", "--no-pdf-header-footer", + f"file://{tmp_html}" + ], capture_output=True, timeout=120)), + timeout=120) if tmp_pdf.exists(): pdf_bytes = tmp_pdf.read_bytes() log.info(f"Chromium PDF generated: {len(pdf_bytes)} bytes") @@ -4990,6 +5096,7 @@ async def report_dl(rid, fmt: str = Query("json"), token: str = Query(None), cre if not u: raise HTTPException(401, "Not authenticated") with db() as c: r=c.execute("SELECT * FROM reports WHERE id=?",(rid,)).fetchone() if not r: raise HTTPException(404) + if u["role"] != "admin" and r["user_id"] != u["id"]: raise HTTPException(403) p = r["json_path"] if fmt=="json" else r["html_path"] if not p or not Path(p).exists(): raise HTTPException(404) return FileResponse(p, filename=f"cis_{r['tenancy_name']}_{rid[:8]}.{fmt}") @@ -5274,9 +5381,15 @@ async def _chat_background(mid: str, sid: str, msg: ChatMsg, user: dict, genai_c hist = history[:-1] if len(history) > 1 else None loop = asyncio.get_event_loop() - resp_text, tool_calls, tool_calls_raw = await loop.run_in_executor( - _chat_executor, partial(_call_genai, cfg_dict, augmented_message, hist, - tool_defs, None, None, attachments)) + try: + resp_text, tool_calls, tool_calls_raw = await asyncio.wait_for( + loop.run_in_executor( + _chat_executor, partial(_call_genai, cfg_dict, augmented_message, hist, + tool_defs, None, None, attachments)), + timeout=300) + except asyncio.TimeoutError: + log.error(f"GenAI call timed out after 300s for session {sid}") + raise HTTPException(504, "O modelo de IA demorou demais para responder. Tente novamente.") all_tool_results = [] accumulated_msgs = [] @@ -5314,9 +5427,15 @@ async def _chat_background(mid: str, sid: str, msg: ChatMsg, user: dict, genai_c tr_obj.call = tc_obj tr_obj.outputs = [{"result": tr["content"]}] cohere_results.append(tr_obj) - resp_text, tool_calls, tool_calls_raw = await loop.run_in_executor( - _chat_executor, partial(_call_genai, cfg_dict, augmented_message, hist, - tool_defs, cohere_results)) + try: + resp_text, tool_calls, tool_calls_raw = await asyncio.wait_for( + loop.run_in_executor( + _chat_executor, partial(_call_genai, cfg_dict, augmented_message, hist, + tool_defs, cohere_results)), + timeout=300) + except asyncio.TimeoutError: + log.error(f"GenAI tool-use call timed out after 300s (Cohere, iteration {iterations})") + raise HTTPException(504, "O modelo de IA demorou demais para responder. Tente novamente.") else: import oci assistant_msg = oci.generative_ai_inference.models.AssistantMessage() @@ -5329,9 +5448,15 @@ async def _chat_background(mid: str, sid: str, msg: ChatMsg, user: dict, genai_c tool_content.text = tr["content"] tool_msg.content = [tool_content] accumulated_msgs.append(tool_msg) - resp_text, tool_calls, tool_calls_raw = await loop.run_in_executor( - _chat_executor, partial(_call_genai, cfg_dict, augmented_message, hist, - tool_defs, None, accumulated_msgs)) + try: + resp_text, tool_calls, tool_calls_raw = await asyncio.wait_for( + loop.run_in_executor( + _chat_executor, partial(_call_genai, cfg_dict, augmented_message, hist, + tool_defs, None, accumulated_msgs)), + timeout=300) + except asyncio.TimeoutError: + log.error(f"GenAI tool-use call timed out after 300s (Generic, iteration {iterations})") + raise HTTPException(504, "O modelo de IA demorou demais para responder. Tente novamente.") resp = resp_text if all_tool_results: @@ -5416,7 +5541,8 @@ async def chat_with_files( else: try: text = data.decode("utf-8", errors="replace") - except Exception: + except Exception as e: + log.warning(f"UTF-8 decode failed for file attachment, falling back to latin-1: {e}") text = data.decode("latin-1", errors="replace") message = f"{message}\n\n--- Conteúdo de {f.filename} ---\n{text[:50000]}" file_names.append(f.filename) @@ -5507,15 +5633,18 @@ async def list_chat_sessions(agent_type: str = "chat", limit: int = 50, u=Depend @app.get("/api/chat/sessions/{sid}/messages") -async def get_session_messages(sid: str, u=Depends(current_user)): +async def get_session_messages(sid: str, skip: int = Query(0, ge=0), limit: int = Query(100, ge=1, le=500), u=Depends(current_user)): with db() as c: session = c.execute("SELECT * FROM chat_sessions WHERE id=? AND user_id=?", (sid, u["id"])).fetchone() if not session: raise HTTPException(404, "Session not found") + total = c.execute( + "SELECT COUNT(*) as cnt FROM chat_messages " + "WHERE session_id=? AND status='done'", (sid,)).fetchone()["cnt"] msgs = c.execute( "SELECT id, role, content, model_id, status, created_at FROM chat_messages " - "WHERE session_id=? AND status='done' ORDER BY created_at ASC", (sid,)).fetchall() - return {"session": dict(session), "messages": [dict(m) for m in msgs]} + "WHERE session_id=? AND status='done' ORDER BY created_at ASC LIMIT ? OFFSET ?", (sid, limit, skip)).fetchall() + return {"session": dict(session), "messages": [dict(m) for m in msgs], "total": total} @app.put("/api/chat/sessions/{sid}/title") @@ -6105,7 +6234,13 @@ async def tf_delete_workspace(wid: str, u=Depends(current_user)): async def tf_refresh_reference(u=Depends(current_user)): """Regenerate the OCI Terraform resource reference from provider schema (internal, triggered by UI button).""" loop = asyncio.get_event_loop() - result = await loop.run_in_executor(_chat_executor, _regenerate_tf_reference) + try: + result = await asyncio.wait_for( + loop.run_in_executor(_chat_executor, _regenerate_tf_reference), + timeout=300) + except asyncio.TimeoutError: + log.error("TF reference regeneration timed out after 300s") + raise HTTPException(504, "Geração de referência Terraform demorou demais. Tente novamente.") # Add status info p = Path(_TF_RESOURCE_REF_PATH) if p.exists(): @@ -6548,8 +6683,8 @@ provider "oci" {{ # ── Audit ───────────────────────────────────────────────────────────────────── @app.get("/api/audit-log") -async def audit_log(limit:int=Query(100,le=500), u=Depends(require("admin"))): - with db() as c: rows=c.execute("SELECT * FROM audit_log ORDER BY created_at DESC LIMIT ?",(limit,)).fetchall() +async def audit_log(skip: int = Query(0, ge=0), limit: int = Query(100, ge=1, le=500), u=Depends(require("admin"))): + with db() as c: rows=c.execute("SELECT * FROM audit_log ORDER BY created_at DESC LIMIT ? OFFSET ?",(limit, skip)).fetchall() return [dict(r) for r in rows] # ── Config Logs ─────────────────────────────────────────────────────────────── @@ -6693,6 +6828,8 @@ async def export_config(u=Depends(require("admin"))): @app.post("/api/config/import") async def import_config(file: UploadFile = File(...), u=Depends(require("admin"))): """Import configurations from exported JSON. Merges by ID (skip existing).""" + await _validate_upload(file, {".json"}) + file.file.seek(0) content = await file.read() try: data = json.loads(content) @@ -6843,7 +6980,40 @@ async def cis_engine_update(u=Depends(require("admin"))): @app.get("/api/health") async def health(): - return {"status":"ok","ts":datetime.utcnow().isoformat(),"version":VERSION} + db_ok = False + try: + with db() as c: + c.execute("SELECT 1") + db_ok = True + except Exception: + pass + return { + "status": "ok", + "version": VERSION, + "uptime_seconds": int(time.time() - _START_TIME), + "db_ok": db_ok, + "ts": datetime.utcnow().isoformat(), + } + +@app.on_event("shutdown") +async def shutdown(): + """Graceful shutdown: terminate running subprocesses and thread pool.""" + for rid, proc in list(_running_reports.items()): + try: + proc.terminate() + log.info(f"Terminated report process {rid}") + except Exception as e: + log.warning(f"Failed to terminate report process {rid}: {e}") + _running_reports.clear() + for wid, proc in list(_running_terraform.items()): + try: + proc.terminate() + log.info(f"Terminated terraform process {wid}") + except Exception as e: + log.warning(f"Failed to terminate terraform process {wid}: {e}") + _running_terraform.clear() + _chat_executor.shutdown(wait=False) + log.info("Graceful shutdown completed") @app.on_event("startup") async def startup(): @@ -6885,7 +7055,9 @@ async def startup(): ref_path = Path(_TF_RESOURCE_REF_PATH) if not ref_path.exists(): try: - result = subprocess.run(["python", "/app/gen_tf_reference.py"], capture_output=True, text=True, timeout=300) + loop = asyncio.get_event_loop() + result = await loop.run_in_executor(None, partial( + subprocess.run, ["python", "/app/gen_tf_reference.py"], capture_output=True, text=True, timeout=300)) if result.returncode == 0 and ref_path.exists(): log.info(f"Generated TF resource reference at startup: {ref_path.stat().st_size} bytes") else: diff --git a/docker-compose.yml b/docker-compose.yml index ae2f580..1d51f5a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,6 +9,7 @@ services: - APP_SECRET=${APP_SECRET:-change-me-in-production-use-a-long-random-string} - JWT_EXPIRY_HOURS=${JWT_EXPIRY_HOURS:-12} - DATA_DIR=/data + - CORS_ORIGINS=${CORS_ORIGINS:-} - OCI_CLI_SUPPRESS_FILE_PERMISSIONS_WARNING=True volumes: - agent-data:/data @@ -19,6 +20,15 @@ services: interval: 30s timeout: 10s retries: 3 + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + deploy: + resources: + limits: + memory: 4G frontend: image: nginx:alpine @@ -27,13 +37,21 @@ services: volumes: - ./frontend-react/dist:/usr/share/nginx/html/app:ro - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro - # Note: nginx root points to /usr/share/nginx/html/app (React dist) ports: - "${PORT:-8080}:80" depends_on: - backend networks: - agent-net + logging: + driver: "json-file" + options: + max-size: "5m" + max-file: "3" + deploy: + resources: + limits: + memory: 512M volumes: agent-data: diff --git a/frontend-react/.gitignore b/frontend-react/.gitignore index 251ce6d..a547bf3 100644 --- a/frontend-react/.gitignore +++ b/frontend-react/.gitignore @@ -8,6 +8,7 @@ pnpm-debug.log* lerna-debug.log* node_modules +dist dist-ssr *.local diff --git a/frontend-react/dist/assets/index-Ca4AYldT.css b/frontend-react/dist/assets/index-Ca4AYldT.css deleted file mode 100644 index 61a34ef..0000000 --- a/frontend-react/dist/assets/index-Ca4AYldT.css +++ /dev/null @@ -1,2 +0,0 @@ -/*! tailwindcss v4.2.1 | MIT License | https://tailwindcss.com */ -@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-duration:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--container-sm:24rem;--container-md:28rem;--container-2xl:42rem;--container-3xl:48rem;--container-5xl:64rem;--container-6xl:72rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--font-weight-extrabold:800;--tracking-tight:-.025em;--tracking-wide:.025em;--tracking-wider:.05em;--leading-snug:1.375;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--ease-in:cubic-bezier(.4, 0, 1, 1);--ease-out:cubic-bezier(0, 0, .2, 1);--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--blur-2xl:40px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}:root,html.light{--bg:#f4f2ef;--bg1:#fff;--bg2:#faf9f7;--bg3:#efeee9;--bg4:#e5e3dd;--bd:#ddd9d2;--bdf:#c5c0b8;--t1:#1c1917;--t2:#44403c;--t3:#78716c;--t4:#a8a29e;--ac:#c74634;--ach:#b13d2e;--acd:#9a3427;--acl:#c746340f;--acl2:#c746341a;--gn:#16a34a;--gnl:#16a34a14;--rd:#dc2626;--rdl:#dc26260f;--bl:#2563eb;--bll:#2563eb0f;--yl:#ca8a04;--yll:#ca8a040f;--pp:#7c3aed;--ppl:#7c3aed0f;--ok:#16a34a;--w:#ca8a04;--err:#dc2626;--r:12px;--rl:16px;--rr:20px;--sh1:0 1px 2px #1c19170a, 0 1px 3px #1c19170f;--sh2:0 4px 6px -1px #1c19170d, 0 2px 4px -2px #1c19170a;--sh3:0 10px 15px -3px #1c19170f, 0 4px 6px -4px #1c19170a;--shh:0 20px 25px -5px #1c191714, 0 8px 10px -6px #1c19170a;--tb-bg:#fffc;--fm:"JetBrains Mono", monospace;--fs:"Plus Jakarta Sans", -apple-system, system-ui, sans-serif;--trans:cubic-bezier(.22,1,.36,1)}html.dark{--bg:#0d0f14;--bg1:#151820;--bg2:#1a1e28;--bg3:#222733;--bg4:#2a3040;--bd:#2a3040;--bdf:#3a4258;--t1:#f0f0f4;--t2:#c8cad0;--t3:#8b8fa3;--t4:#5a5e6e;--ac:#c74634;--ach:#d9523f;--acd:#a83428;--acl:#c746341a;--acl2:#c7463429;--gn:#34c759;--gnl:#34c7591a;--rd:#ff453a;--rdl:#ff453a1a;--bl:#0a84ff;--bll:#0a84ff1a;--yl:#ffd60a;--yll:#ffd60a1a;--pp:#bf5af2;--ppl:#bf5af21a;--ok:#34c759;--w:#ffd60a;--err:#ff453a;--sh1:0 1px 3px #0000004d, 0 1px 2px #0003;--sh2:0 4px 8px #0000004d, 0 2px 4px #0003;--sh3:0 10px 20px #00000059, 0 4px 8px #0003;--shh:0 20px 30px #0006, 0 8px 12px #00000040;--tb-bg:#151820d9}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}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;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}*,:before,:after{box-sizing:border-box;margin:0;padding:0}body{font-family:var(--fs);background:var(--bg);color:var(--t1);-webkit-font-smoothing:antialiased;height:100vh;overflow:hidden}::selection{color:var(--t1);background:#c7463433}html.light *,html.dark *{transition:background-color .25s var(--trans), border-color .25s var(--trans), color .15s var(--trans)}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background:var(--bd);border-radius:3px}::-webkit-scrollbar-thumb:hover{background:var(--bdf)}@keyframes fadeIn{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}select{appearance:none;background:var(--bg1) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2378716c' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E") no-repeat right 12px center;border:1px solid var(--bd);border-radius:var(--r);font-size:.82rem;font-family:var(--fs);color:var(--t1);cursor:pointer;min-width:0;padding:9px 36px 9px 14px;transition:border-color .2s,box-shadow .2s}select:focus{border-color:var(--ac);box-shadow:0 0 0 3px var(--acl);outline:none}select:hover{border-color:var(--bdf)}input[type=text],input[type=email],input[type=password],input[type=number],input[type=search],textarea{background:var(--bg1);border:1px solid var(--bd);border-radius:var(--r);font-size:.82rem;font-family:var(--fs);color:var(--t1);width:100%;padding:9px 14px;transition:border-color .2s,box-shadow .2s}input[type=text]:focus,input[type=email]:focus,input[type=password]:focus,input[type=number]:focus,input[type=search]:focus,textarea:focus{border-color:var(--ac);box-shadow:0 0 0 3px var(--acl);outline:none}input[type=checkbox]{appearance:none;border:2px solid var(--bd);background:var(--bg1);cursor:pointer;border-radius:5px;flex-shrink:0;width:18px;height:18px;transition:all .15s;position:relative}input[type=checkbox]:checked{background:var(--ac);border-color:var(--ac)}input[type=checkbox]:checked:after{content:"";border:2px solid #fff;border-width:0 2px 2px 0;width:6px;height:10px;position:absolute;top:1px;left:4px;transform:rotate(45deg)}}@layer utilities{.collapse{visibility:collapse}.invisible{visibility:hidden}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.inset-0{inset:calc(var(--spacing) * 0)}.inset-y-0{inset-block:calc(var(--spacing) * 0)}.start{inset-inline-start:var(--spacing)}.start\!{inset-inline-start:var(--spacing)!important}.end{inset-inline-end:var(--spacing)}.top-0{top:calc(var(--spacing) * 0)}.top-1\/2{top:50%}.top-2{top:calc(var(--spacing) * 2)}.top-4{top:calc(var(--spacing) * 4)}.top-\[-30\%\]{top:-30%}.top-full{top:100%}.right-0{right:calc(var(--spacing) * 0)}.right-2{right:calc(var(--spacing) * 2)}.right-4{right:calc(var(--spacing) * 4)}.right-\[-20\%\]{right:-20%}.bottom-0{bottom:calc(var(--spacing) * 0)}.left-0{left:calc(var(--spacing) * 0)}.left-2{left:calc(var(--spacing) * 2)}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.container{width:100%}@media (width>=40rem){.container{max-width:40rem}}@media (width>=48rem){.container{max-width:48rem}}@media (width>=64rem){.container{max-width:64rem}}@media (width>=80rem){.container{max-width:80rem}}@media (width>=96rem){.container{max-width:96rem}}.mx-5{margin-inline:calc(var(--spacing) * 5)}.mx-auto{margin-inline:auto}.my-1{margin-block:calc(var(--spacing) * 1)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mr-0\.5{margin-right:calc(var(--spacing) * .5)}.mr-1\.5{margin-right:calc(var(--spacing) * 1.5)}.mr-2{margin-right:calc(var(--spacing) * 2)}.mb-0\.5{margin-bottom:calc(var(--spacing) * .5)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-5{margin-bottom:calc(var(--spacing) * 5)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.ml-1{margin-left:calc(var(--spacing) * 1)}.ml-2{margin-left:calc(var(--spacing) * 2)}.ml-4{margin-left:calc(var(--spacing) * 4)}.ml-\[260px\]{margin-left:260px}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.h-0\.5{height:calc(var(--spacing) * .5)}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-2{height:calc(var(--spacing) * 2)}.h-2\.5{height:calc(var(--spacing) * 2.5)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-14{height:calc(var(--spacing) * 14)}.h-16{height:calc(var(--spacing) * 16)}.h-20{height:calc(var(--spacing) * 20)}.h-\[120px\]{height:120px}.h-full{height:100%}.h-screen{height:100vh}.max-h-56{max-height:calc(var(--spacing) * 56)}.max-h-60{max-height:calc(var(--spacing) * 60)}.max-h-64{max-height:calc(var(--spacing) * 64)}.max-h-80{max-height:calc(var(--spacing) * 80)}.min-h-0{min-height:calc(var(--spacing) * 0)}.min-h-\[36px\]{min-height:36px}.w-1{width:calc(var(--spacing) * 1)}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-2{width:calc(var(--spacing) * 2)}.w-2\.5{width:calc(var(--spacing) * 2.5)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-4{width:calc(var(--spacing) * 4)}.w-5{width:calc(var(--spacing) * 5)}.w-6{width:calc(var(--spacing) * 6)}.w-7{width:calc(var(--spacing) * 7)}.w-8{width:calc(var(--spacing) * 8)}.w-9{width:calc(var(--spacing) * 9)}.w-14{width:calc(var(--spacing) * 14)}.w-16{width:calc(var(--spacing) * 16)}.w-20{width:calc(var(--spacing) * 20)}.w-28{width:calc(var(--spacing) * 28)}.w-32{width:calc(var(--spacing) * 32)}.w-36{width:calc(var(--spacing) * 36)}.w-60{width:calc(var(--spacing) * 60)}.w-64{width:calc(var(--spacing) * 64)}.w-\[80px\]{width:80px}.w-\[120px\]{width:120px}.w-\[260px\]{width:260px}.w-\[380px\]{width:380px}.w-full{width:100%}.w-px{width:1px}.max-w-2xl{max-width:var(--container-2xl)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-5xl{max-width:var(--container-5xl)}.max-w-6xl{max-width:var(--container-6xl)}.max-w-\[80\%\]{max-width:80%}.max-w-\[85\%\]{max-width:85%}.max-w-\[100px\]{max-width:100px}.max-w-\[140px\]{max-width:140px}.max-w-\[150px\]{max-width:150px}.max-w-\[180px\]{max-width:180px}.max-w-full{max-width:100%}.max-w-md{max-width:var(--container-md)}.max-w-none{max-width:none}.max-w-sm{max-width:var(--container-sm)}.max-w-xs{max-width:var(--container-xs)}.min-w-0{min-width:calc(var(--spacing) * 0)}.min-w-\[180px\]{min-width:180px}.min-w-\[240px\]{min-width:240px}.flex-1{flex:1}.flex-shrink-0,.shrink-0{flex-shrink:0}.-translate-y-1\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.rotate-90{rotate:90deg}.rotate-180{rotate:180deg}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-\[fadeIn_\.2s_ease\]{animation:.2s fadeIn}.animate-\[fadeIn_\.4s_ease\]{animation:.4s fadeIn}.animate-\[fadeIn_\.15s_ease\]{animation:.15s fadeIn}.animate-\[fadeIn_\.25s_ease\]{animation:.25s fadeIn}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-col-resize{cursor:col-resize}.cursor-pointer{cursor:pointer}.cursor-row-resize{cursor:row-resize}.resize{resize:both}.resize-none{resize:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.items-stretch{align-items:stretch}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}:where(.space-y-0\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * .5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * .5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}.self-start{align-self:flex-start}.self-stretch{align-self:stretch}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-none{--tw-border-style:none;border-style:none}.border-t-transparent{border-top-color:#0000}.bg-\[var\(--bg3\)\]{background-color:var(--bg3)}.bg-\[var\(--bll\)\]{background-color:var(--bll)}.bg-\[var\(--gnl\)\]{background-color:var(--gnl)}.bg-\[var\(--ppl\)\]{background-color:var(--ppl)}.bg-\[var\(--rdl\)\]{background-color:var(--rdl)}.bg-transparent{background-color:#0000}.p-0\.5{padding:calc(var(--spacing) * .5)}.p-1{padding:calc(var(--spacing) * 1)}.p-1\.5{padding:calc(var(--spacing) * 1.5)}.p-2{padding:calc(var(--spacing) * 2)}.p-2\.5{padding:calc(var(--spacing) * 2.5)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-6{padding:calc(var(--spacing) * 6)}.p-8{padding:calc(var(--spacing) * 8)}.px-1{padding-inline:calc(var(--spacing) * 1)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.py-0{padding-block:calc(var(--spacing) * 0)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-3\.5{padding-block:calc(var(--spacing) * 3.5)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-6{padding-block:calc(var(--spacing) * 6)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-10{padding-block:calc(var(--spacing) * 10)}.py-12{padding-block:calc(var(--spacing) * 12)}.py-16{padding-block:calc(var(--spacing) * 16)}.pt-1{padding-top:calc(var(--spacing) * 1)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pr-1{padding-right:calc(var(--spacing) * 1)}.pr-2{padding-right:calc(var(--spacing) * 2)}.pr-3{padding-right:calc(var(--spacing) * 3)}.pb-1{padding-bottom:calc(var(--spacing) * 1)}.pb-1\.5{padding-bottom:calc(var(--spacing) * 1.5)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-5{padding-bottom:calc(var(--spacing) * 5)}.pl-1{padding-left:calc(var(--spacing) * 1)}.pl-2{padding-left:calc(var(--spacing) * 2)}.pl-7{padding-left:calc(var(--spacing) * 7)}.pl-9{padding-left:calc(var(--spacing) * 9)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.align-top{vertical-align:top}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[\.5rem\]{font-size:.5rem}.text-\[\.6rem\]{font-size:.6rem}.text-\[\.7rem\]{font-size:.7rem}.text-\[\.8rem\]{font-size:.8rem}.text-\[\.58rem\]{font-size:.58rem}.text-\[\.62rem\]{font-size:.62rem}.text-\[\.64rem\]{font-size:.64rem}.text-\[\.65rem\]{font-size:.65rem}.text-\[\.66rem\]{font-size:.66rem}.text-\[\.68rem\]{font-size:.68rem}.text-\[\.72rem\]{font-size:.72rem}.text-\[\.74rem\]{font-size:.74rem}.text-\[\.75rem\]{font-size:.75rem}.text-\[\.76rem\]{font-size:.76rem}.text-\[\.78rem\]{font-size:.78rem}.text-\[\.82rem\]{font-size:.82rem}.text-\[\.88rem\]{font-size:.88rem}.text-\[0\.7rem\]{font-size:.7rem}.text-\[0\.68rem\]{font-size:.68rem}.text-\[10px\]{font-size:10px}.leading-5{--tw-leading:calc(var(--spacing) * 5);line-height:calc(var(--spacing) * 5)}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.leading-snug{--tw-leading:var(--leading-snug);line-height:var(--leading-snug)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-extrabold{--tw-font-weight:var(--font-weight-extrabold);font-weight:var(--font-weight-extrabold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-\[\.3em\]{--tw-tracking:.3em;letter-spacing:.3em}.tracking-\[\.14em\]{--tw-tracking:.14em;letter-spacing:.14em}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-\[var\(--bl\)\]{color:var(--bl)}.text-\[var\(--gn\)\]{color:var(--gn)}.text-\[var\(--pp\)\]{color:var(--pp)}.text-\[var\(--rd\)\]{color:var(--rd)}.text-\[var\(--t3\)\]{color:var(--t3)}.text-\[var\(--t4\)\]{color:var(--t4)}.text-white{color:var(--color-white)}.text-white\/60{color:#fff9}@supports (color:color-mix(in lab, red, red)){.text-white\/60{color:color-mix(in oklab, var(--color-white) 60%, transparent)}}.uppercase{text-transform:uppercase}.italic{font-style:italic}.no-underline{text-decoration-line:none}.underline{text-decoration-line:underline}.accent-current{accent-color:currentColor}.opacity-0{opacity:0}.opacity-20{opacity:.2}.opacity-30{opacity:.3}.opacity-40{opacity:.4}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-90{opacity:.9}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.blur{--tw-blur:blur(8px);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,)}.blur-2xl{--tw-blur:blur(var(--blur-2xl));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,)}.invert{--tw-invert:invert(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,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-150{--tw-duration:.15s;transition-duration:.15s}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-500{--tw-duration:.5s;transition-duration:.5s}.ease-in{--tw-ease:var(--ease-in);transition-timing-function:var(--ease-in)}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.outline-none{--tw-outline-style:none;outline-style:none}.select-all{-webkit-user-select:all;user-select:all}.select-none{-webkit-user-select:none;user-select:none}@media (hover:hover){.group-hover\:opacity-70:is(:where(.group):hover *){opacity:.7}.group-hover\:opacity-80:is(:where(.group):hover *){opacity:.8}.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}.file\:mr-2::file-selector-button{margin-right:calc(var(--spacing) * 2)}.file\:cursor-pointer::file-selector-button{cursor:pointer}.file\:rounded-lg::file-selector-button{border-radius:var(--radius-lg)}.file\:border-0::file-selector-button{border-style:var(--tw-border-style);border-width:0}.file\:px-3::file-selector-button{padding-inline:calc(var(--spacing) * 3)}.file\:py-1\.5::file-selector-button{padding-block:calc(var(--spacing) * 1.5)}.file\:text-\[\.68rem\]::file-selector-button{font-size:.68rem}.file\:font-semibold::file-selector-button{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.last\:mb-0:last-child{margin-bottom:calc(var(--spacing) * 0)}@media (hover:hover){.hover\:bg-\[var\(--bg2\)\]:hover{background-color:var(--bg2)}.hover\:opacity-80:hover{opacity:.8}.hover\:opacity-100:hover{opacity:1}}.focus\:ring-1:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-60:disabled{opacity:.6}.disabled\:opacity-70:disabled{opacity:.7}@media (width>=48rem){.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))}}@media (width>=64rem){.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}.\[\&_code\]\:text-\[\.72rem\] code{font-size:.72rem}.\[\&_pre\]\:relative pre{position:relative}.\[\&_pre\]\:overflow-auto pre{overflow:auto}.\[\&_pre\]\:overflow-x-auto pre{overflow-x:auto}.\[\&_pre\]\:rounded-lg pre{border-radius:var(--radius-lg)}.\[\&_pre\]\:p-3 pre{padding:calc(var(--spacing) * 3)}.\[\&_pre\]\:text-\[\.72rem\] pre{font-size:.72rem}.\[\&_table\]\:overflow-x-auto table{overflow-x:auto}.\[\&_table\]\:text-\[\.68rem\] table{font-size:.68rem}}nav a:hover{background:var(--bg3);color:var(--t1)!important}.page{max-width:1280px;padding:28px 32px;animation:.35s fadeIn}.page-full{height:100%;animation:.35s fadeIn}.page-header{align-items:center;gap:14px;margin-bottom:24px;display:flex}.page-header h1{color:var(--t1);letter-spacing:-.01em;font-size:1.35rem;font-weight:700}.page-header .subtitle{color:var(--t3);margin-top:2px;font-size:.8rem}.page-header .spacer{flex:1}.page-header .count{color:var(--t3);background:var(--bg3);border-radius:20px;padding:4px 12px;font-size:.75rem;font-weight:600}.card{background:var(--bg1);border:1px solid var(--bd);border-radius:var(--rl);box-shadow:var(--sh1);margin-bottom:16px;padding:20px 24px}.card-header{border-bottom:1px solid var(--bd);align-items:center;gap:10px;margin-bottom:16px;padding-bottom:12px;display:flex}.card-header h2{color:var(--t1);font-size:.92rem;font-weight:700}.card-header .icon{border-radius:10px;flex-shrink:0;justify-content:center;align-items:center;width:36px;height:36px;display:flex}.data-table{border-collapse:separate;border-spacing:0;width:100%;font-size:.82rem}.data-table thead th{text-align:left;text-transform:uppercase;letter-spacing:.06em;color:var(--t3);background:var(--bg2);border-bottom:1px solid var(--bd);padding:10px 14px;font-size:.7rem;font-weight:700}.data-table thead th:first-child{border-radius:var(--r) 0 0 0}.data-table thead th:last-child{border-radius:0 var(--r) 0 0}.data-table tbody td{color:var(--t2);border-bottom:1px solid var(--bg3);vertical-align:middle;padding:12px 14px}.data-table tbody tr:last-child td{border-bottom:none}.data-table tbody tr:hover td{background:var(--bg2)}.btn{border-radius:var(--r);font-size:.82rem;font-weight:600;font-family:var(--fs);cursor:pointer;transition:all .2s var(--trans);white-space:nowrap;border:1px solid #0000;align-items:center;gap:6px;padding:9px 18px;display:inline-flex}.btn:disabled{opacity:.5;cursor:not-allowed}.btn-primary{background:var(--ac);color:#fff;border-color:var(--ac)}.btn-primary:hover:not(:disabled){background:var(--ach)}.btn-secondary{background:var(--bg1);color:var(--t2);border-color:var(--bd)}.btn-secondary:hover:not(:disabled){background:var(--bg3);color:var(--t1)}.btn-danger{color:var(--rd);border-color:var(--rd);background:0 0}.btn-danger:hover:not(:disabled){background:var(--rdl)}.btn-success{background:var(--gn);color:#fff;border-color:var(--gn)}.btn-sm{border-radius:8px;padding:6px 12px;font-size:.75rem}.badge{white-space:nowrap;border-radius:20px;align-items:center;gap:4px;padding:3px 10px;font-size:.68rem;font-weight:600;display:inline-flex}.badge-green{background:var(--gnl);color:var(--gn)}.badge-red{background:var(--rdl);color:var(--rd)}.badge-blue{background:var(--bll);color:var(--bl)}.badge-yellow{background:var(--yll);color:var(--yl)}.badge-purple{background:var(--ppl);color:var(--pp)}.badge-gray{background:var(--bg3);color:var(--t3)}.empty-state{text-align:center;color:var(--t4);flex-direction:column;justify-content:center;align-items:center;padding:60px 20px;display:flex}.empty-state svg{opacity:.35;margin-bottom:12px}.empty-state h3{color:var(--t3);margin-bottom:4px;font-size:.92rem;font-weight:600}.empty-state p{font-size:.78rem}.kpi-grid{grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:16px;margin-bottom:24px;display:grid}.kpi-card{background:var(--bg1);border:1px solid var(--bd);border-radius:var(--rl);box-shadow:var(--sh1);padding:18px 20px}.kpi-card .label{text-transform:uppercase;letter-spacing:.06em;color:var(--t3);margin-bottom:8px;font-size:.7rem;font-weight:600}.kpi-card .value{color:var(--t1);letter-spacing:-.02em;font-size:1.5rem;font-weight:800}.kpi-card .sub{color:var(--t4);margin-top:4px;font-size:.72rem}.form-group{margin-bottom:16px}.form-group label{color:var(--t2);margin-bottom:6px;font-size:.75rem;font-weight:600;display:block}.form-row{grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:16px;display:grid}.tag{background:var(--bg3);color:var(--t2);border:1px solid var(--bd);border-radius:20px;align-items:center;gap:4px;padding:4px 10px;font-size:.72rem;font-weight:500;display:inline-flex}.tag button{cursor:pointer;color:var(--t3);background:0 0;border:none;padding:0;font-size:.82rem;line-height:1}.tag button:hover{color:var(--rd)}.section-title{text-transform:uppercase;letter-spacing:.08em;color:var(--t4);border-bottom:1px solid var(--bg3);margin:24px 0 12px;padding-bottom:8px;font-size:.72rem;font-weight:700}.chat-markdown{color:var(--t1);font-size:.82rem;line-height:1.7}.chat-markdown p{overflow-wrap:break-word;word-break:break-word;margin:0 0 .65em}.chat-markdown p:last-child{margin-bottom:0}.chat-markdown h1,.chat-markdown h2,.chat-markdown h3,.chat-markdown h4,.chat-markdown h5,.chat-markdown h6{color:var(--t1);margin:1em 0 .4em;font-weight:700;line-height:1.3}.chat-markdown h1{font-size:1.15em}.chat-markdown h2{font-size:1.05em}.chat-markdown h3{font-size:.95em}.chat-markdown h4,.chat-markdown h5,.chat-markdown h6{font-size:.88em}.chat-markdown>h1:first-child,.chat-markdown>h2:first-child,.chat-markdown>h3:first-child{margin-top:0}.chat-markdown ul,.chat-markdown ol{margin:.4em 0 .65em;padding-left:1.5em}.chat-markdown ul{list-style:outside}.chat-markdown ol{list-style:decimal}.chat-markdown li{margin:.2em 0}.chat-markdown li>ul,.chat-markdown li>ol,.chat-markdown li>p{margin:.15em 0}.chat-markdown code:not(pre code){background:var(--ac)}@supports (color:color-mix(in lab, red, red)){.chat-markdown code:not(pre code){background:color-mix(in srgb, var(--ac) 10%, transparent)}}.chat-markdown code:not(pre code){color:var(--ac);word-break:break-all;border-radius:4px;padding:.12em .35em;font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,monospace;font-size:.88em}.chat-markdown pre{color:#cdd6f4;border:1px solid var(--bd);background:#1e1e2e;border-radius:8px;max-width:100%;margin:.5em 0 .75em;padding:.85em 1em;font-size:.78rem;line-height:1.55;overflow-x:auto}@supports (color:color-mix(in lab, red, red)){.chat-markdown pre{border:1px solid color-mix(in srgb, var(--bd) 50%, transparent)}}.chat-markdown pre code{word-break:normal;font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,monospace;color:inherit!important;font-size:inherit!important;background:0 0!important;border-radius:0!important;padding:0!important}.chat-markdown table{border-collapse:collapse;max-width:100%;margin:.5em 0 .75em;font-size:.8rem;display:block;overflow-x:auto}.chat-markdown th,.chat-markdown td{border:1px solid var(--bd);text-align:left;padding:.4em .7em}.chat-markdown th{background:var(--ac)}@supports (color:color-mix(in lab, red, red)){.chat-markdown th{background:color-mix(in srgb, var(--ac) 8%, transparent)}}.chat-markdown th{white-space:nowrap;font-size:.78rem;font-weight:700}.chat-markdown tr:nth-child(2n){background:var(--bd)}@supports (color:color-mix(in lab, red, red)){.chat-markdown tr:nth-child(2n){background:color-mix(in srgb, var(--bd) 15%, transparent)}}.chat-markdown blockquote{border-left:3px solid var(--ac);background:var(--ac);margin:.5em 0 .75em;padding:.4em .8em}@supports (color:color-mix(in lab, red, red)){.chat-markdown blockquote{background:color-mix(in srgb, var(--ac) 5%, transparent)}}.chat-markdown blockquote{color:var(--t2);border-radius:0 6px 6px 0}.chat-markdown blockquote p{margin:0}.chat-markdown hr{border:none;border-top:1px solid var(--bd);margin:.8em 0}.chat-markdown a{color:var(--ac);text-underline-offset:2px;text-decoration:underline}.chat-markdown strong{color:var(--t1);font-weight:700}.chat-markdown em{font-style:italic}.hljs-keyword,.hljs-selector-tag{color:#cba6f7}.hljs-string,.hljs-attr{color:#a6e3a1}.hljs-number,.hljs-literal{color:#fab387}.hljs-comment{color:#6c7086;font-style:italic}.hljs-built_in,.hljs-builtin-name{color:#89dceb}.hljs-function .hljs-title,.hljs-title.function_{color:#89b4fa}.hljs-type,.hljs-title.class_{color:#f9e2af}.hljs-variable,.hljs-template-variable{color:#f38ba8}.hljs-meta{color:#fab387}.hljs-punctuation{color:#bac2de}.hljs-tag,.hljs-name{color:#89b4fa}.hljs-attribute{color:#f9e2af}.prose pre{max-width:100%;overflow-x:auto}.prose table{max-width:100%;display:block;overflow-x:auto}.prose code{word-break:break-all}.prose p{overflow-wrap:break-word;word-break:break-word}.field-label{color:var(--t3);text-transform:uppercase;letter-spacing:.04em;font-size:.7rem;font-weight:600}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}} diff --git a/frontend-react/dist/assets/index-Dtsz7Biu.js b/frontend-react/dist/assets/index-Dtsz7Biu.js deleted file mode 100644 index 25455c6..0000000 --- a/frontend-react/dist/assets/index-Dtsz7Biu.js +++ /dev/null @@ -1,115 +0,0 @@ -var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),s=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n||t(r,Symbol.toStringTag,{value:`Module`}),r},c=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;li[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},l=(n,r,a)=>(a=n==null?{}:e(i(n)),c(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var u=o((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.portal`),r=Symbol.for(`react.fragment`),i=Symbol.for(`react.strict_mode`),a=Symbol.for(`react.profiler`),o=Symbol.for(`react.consumer`),s=Symbol.for(`react.context`),c=Symbol.for(`react.forward_ref`),l=Symbol.for(`react.suspense`),u=Symbol.for(`react.memo`),d=Symbol.for(`react.lazy`),f=Symbol.for(`react.activity`),p=Symbol.iterator;function m(e){return typeof e!=`object`||!e?null:(e=p&&e[p]||e[`@@iterator`],typeof e==`function`?e:null)}var h={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},g=Object.assign,_={};function v(e,t,n){this.props=e,this.context=t,this.refs=_,this.updater=n||h}v.prototype.isReactComponent={},v.prototype.setState=function(e,t){if(typeof e!=`object`&&typeof e!=`function`&&e!=null)throw Error(`takes an object of state variables to update or a function which returns an object of state variables.`);this.updater.enqueueSetState(this,e,t,`setState`)},v.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,`forceUpdate`)};function y(){}y.prototype=v.prototype;function b(e,t,n){this.props=e,this.context=t,this.refs=_,this.updater=n||h}var x=b.prototype=new y;x.constructor=b,g(x,v.prototype),x.isPureReactComponent=!0;var S=Array.isArray;function C(){}var w={H:null,A:null,T:null,S:null},T=Object.prototype.hasOwnProperty;function E(e,n,r){var i=r.ref;return{$$typeof:t,type:e,key:n,ref:i===void 0?null:i,props:r}}function D(e,t){return E(e.type,t,e.props)}function O(e){return typeof e==`object`&&!!e&&e.$$typeof===t}function k(e){var t={"=":`=0`,":":`=2`};return`$`+e.replace(/[=:]/g,function(e){return t[e]})}var A=/\/+/g;function j(e,t){return typeof e==`object`&&e&&e.key!=null?k(``+e.key):t.toString(36)}function M(e){switch(e.status){case`fulfilled`:return e.value;case`rejected`:throw e.reason;default:switch(typeof e.status==`string`?e.then(C,C):(e.status=`pending`,e.then(function(t){e.status===`pending`&&(e.status=`fulfilled`,e.value=t)},function(t){e.status===`pending`&&(e.status=`rejected`,e.reason=t)})),e.status){case`fulfilled`:return e.value;case`rejected`:throw e.reason}}throw e}function N(e,r,i,a,o){var s=typeof e;(s===`undefined`||s===`boolean`)&&(e=null);var c=!1;if(e===null)c=!0;else switch(s){case`bigint`:case`string`:case`number`:c=!0;break;case`object`:switch(e.$$typeof){case t:case n:c=!0;break;case d:return c=e._init,N(c(e._payload),r,i,a,o)}}if(c)return o=o(e),c=a===``?`.`+j(e,0):a,S(o)?(i=``,c!=null&&(i=c.replace(A,`$&/`)+`/`),N(o,r,i,``,function(e){return e})):o!=null&&(O(o)&&(o=D(o,i+(o.key==null||e&&e.key===o.key?``:(``+o.key).replace(A,`$&/`)+`/`)+c)),r.push(o)),1;c=0;var l=a===``?`.`:a+`:`;if(S(e))for(var u=0;u{t.exports=u()})),f=o((e=>{function t(e,t){var n=e.length;e.push(t);a:for(;0>>1,a=e[r];if(0>>1;ri(c,n))li(u,c)?(e[r]=u,e[l]=n,r=l):(e[r]=c,e[s]=n,r=s);else if(li(u,n))e[r]=u,e[l]=n,r=l;else break a}}return t}function i(e,t){var n=e.sortIndex-t.sortIndex;return n===0?e.id-t.id:n}if(e.unstable_now=void 0,typeof performance==`object`&&typeof performance.now==`function`){var a=performance;e.unstable_now=function(){return a.now()}}else{var o=Date,s=o.now();e.unstable_now=function(){return o.now()-s}}var c=[],l=[],u=1,d=null,f=3,p=!1,m=!1,h=!1,g=!1,_=typeof setTimeout==`function`?setTimeout:null,v=typeof clearTimeout==`function`?clearTimeout:null,y=typeof setImmediate<`u`?setImmediate:null;function b(e){for(var i=n(l);i!==null;){if(i.callback===null)r(l);else if(i.startTime<=e)r(l),i.sortIndex=i.expirationTime,t(c,i);else break;i=n(l)}}function x(e){if(h=!1,b(e),!m)if(n(c)!==null)m=!0,S||(S=!0,O());else{var t=n(l);t!==null&&j(x,t.startTime-e)}}var S=!1,C=-1,w=5,T=-1;function E(){return g?!0:!(e.unstable_now()-Tt&&E());){var o=d.callback;if(typeof o==`function`){d.callback=null,f=d.priorityLevel;var s=o(d.expirationTime<=t);if(t=e.unstable_now(),typeof s==`function`){d.callback=s,b(t),i=!0;break b}d===n(c)&&r(c),b(t)}else r(c);d=n(c)}if(d!==null)i=!0;else{var u=n(l);u!==null&&j(x,u.startTime-t),i=!1}}break a}finally{d=null,f=a,p=!1}i=void 0}}finally{i?O():S=!1}}}var O;if(typeof y==`function`)O=function(){y(D)};else if(typeof MessageChannel<`u`){var k=new MessageChannel,A=k.port2;k.port1.onmessage=D,O=function(){A.postMessage(null)}}else O=function(){_(D,0)};function j(t,n){C=_(function(){t(e.unstable_now())},n)}e.unstable_IdlePriority=5,e.unstable_ImmediatePriority=1,e.unstable_LowPriority=4,e.unstable_NormalPriority=3,e.unstable_Profiling=null,e.unstable_UserBlockingPriority=2,e.unstable_cancelCallback=function(e){e.callback=null},e.unstable_forceFrameRate=function(e){0>e||125o?(r.sortIndex=a,t(l,r),n(c)===null&&r===n(l)&&(h?(v(C),C=-1):h=!0,j(x,a-o))):(r.sortIndex=s,t(c,r),m||p||(m=!0,S||(S=!0,O()))),r},e.unstable_shouldYield=E,e.unstable_wrapCallback=function(e){var t=f;return function(){var n=f;f=t;try{return e.apply(this,arguments)}finally{f=n}}}})),p=o(((e,t)=>{t.exports=f()})),m=o((e=>{var t=d();function n(e){var t=`https://react.dev/errors/`+e;if(1{function n(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>`u`||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!=`function`))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}n(),t.exports=m()})),g=o((e=>{var t=p(),n=d(),r=h();function i(e){var t=`https://react.dev/errors/`+e;if(1te||(e.current=R[te],R[te]=null,te--)}function B(e,t){te++,R[te]=e.current,e.current=t}var V=ne(null),re=ne(null),ie=ne(null),ae=ne(null);function oe(e,t){switch(B(ie,t),B(re,e),B(V,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?Wd(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=Wd(t),e=Gd(t,e);else switch(e){case`svg`:e=1;break;case`math`:e=2;break;default:e=0}}z(V),B(V,e)}function se(){z(V),z(re),z(ie)}function ce(e){e.memoizedState!==null&&B(ae,e);var t=V.current,n=Gd(t,e.type);t!==n&&(B(re,e),B(V,n))}function H(e){re.current===e&&(z(V),z(re)),ae.current===e&&(z(ae),tp._currentValue=L)}var le,ue;function de(e){if(le===void 0)try{throw Error()}catch(e){var t=e.stack.trim().match(/\n( *(at )?)/);le=t&&t[1]||``,ue=-1)`:-1i||c[r]!==l[i]){var u=` -`+c[r].replace(` at new `,` at `);return e.displayName&&u.includes(``)&&(u=u.replace(``,e.displayName)),u}while(1<=r&&0<=i);break}}}finally{fe=!1,Error.prepareStackTrace=n}return(n=e?e.displayName||e.name:``)?de(n):``}function me(e,t){switch(e.tag){case 26:case 27:case 5:return de(e.type);case 16:return de(`Lazy`);case 13:return e.child!==t&&t!==null?de(`Suspense Fallback`):de(`Suspense`);case 19:return de(`SuspenseList`);case 0:case 15:return pe(e.type,!1);case 11:return pe(e.type.render,!1);case 1:return pe(e.type,!0);case 31:return de(`Activity`);default:return``}}function he(e){try{var t=``,n=null;do t+=me(e,n),n=e,e=e.return;while(e);return t}catch(e){return` -Error generating stack: `+e.message+` -`+e.stack}}var ge=Object.prototype.hasOwnProperty,_e=t.unstable_scheduleCallback,ve=t.unstable_cancelCallback,ye=t.unstable_shouldYield,be=t.unstable_requestPaint,xe=t.unstable_now,Se=t.unstable_getCurrentPriorityLevel,Ce=t.unstable_ImmediatePriority,we=t.unstable_UserBlockingPriority,Te=t.unstable_NormalPriority,Ee=t.unstable_LowPriority,De=t.unstable_IdlePriority,Oe=t.log,ke=t.unstable_setDisableYieldValue,Ae=null,je=null;function Me(e){if(typeof Oe==`function`&&ke(e),je&&typeof je.setStrictMode==`function`)try{je.setStrictMode(Ae,e)}catch{}}var Ne=Math.clz32?Math.clz32:Ie,Pe=Math.log,Fe=Math.LN2;function Ie(e){return e>>>=0,e===0?32:31-(Pe(e)/Fe|0)|0}var Le=256,Re=262144,ze=4194304;function Be(e){var t=e&42;if(t!==0)return t;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:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return e&261888;case 262144:case 524288:case 1048576:case 2097152:return e&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function Ve(e,t,n){var r=e.pendingLanes;if(r===0)return 0;var i=0,a=e.suspendedLanes,o=e.pingedLanes;e=e.warmLanes;var s=r&134217727;return s===0?(s=r&~a,s===0?o===0?n||(n=r&~e,n!==0&&(i=Be(n))):i=Be(o):i=Be(s)):(r=s&~a,r===0?(o&=s,o===0?n||(n=s&~e,n!==0&&(i=Be(n))):i=Be(o)):i=Be(r)),i===0?0:t!==0&&t!==i&&(t&a)===0&&(a=i&-i,n=t&-t,a>=n||a===32&&n&4194048)?t:i}function He(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function Ue(e,t){switch(e){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function We(){var e=ze;return ze<<=1,!(ze&62914560)&&(ze=4194304),e}function Ge(e){for(var t=[],n=0;31>n;n++)t.push(e);return t}function Ke(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function qe(e,t,n,r,i,a){var o=e.pendingLanes;e.pendingLanes=n,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=n,e.entangledLanes&=n,e.errorRecoveryDisabledLanes&=n,e.shellSuspendCounter=0;var s=e.entanglements,c=e.expirationTimes,l=e.hiddenUpdates;for(n=o&~n;0`u`||window.document===void 0||window.document.createElement===void 0),on=!1;if(an)try{var sn={};Object.defineProperty(sn,`passive`,{get:function(){on=!0}}),window.addEventListener(`test`,sn,sn),window.removeEventListener(`test`,sn,sn)}catch{on=!1}var cn=null,ln=null,un=null;function dn(){if(un)return un;var e,t=ln,n=t.length,r,i=`value`in cn?cn.value:cn.textContent,a=i.length;for(e=0;e=Un),Kn=` `,qn=!1;function Jn(e,t){switch(e){case`keyup`:return Vn.indexOf(t.keyCode)!==-1;case`keydown`:return t.keyCode!==229;case`keypress`:case`mousedown`:case`focusout`:return!0;default:return!1}}function Yn(e){return e=e.detail,typeof e==`object`&&`data`in e?e.data:null}var Xn=!1;function Zn(e,t){switch(e){case`compositionend`:return Yn(t);case`keypress`:return t.which===32?(qn=!0,Kn):null;case`textInput`:return e=t.data,e===Kn&&qn?null:e;default:return null}}function Qn(e,t){if(Xn)return e===`compositionend`||!Hn&&Jn(e,t)?(e=dn(),un=ln=cn=null,Xn=!1,e):null;switch(e){case`paste`:return null;case`keypress`:if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}a:{for(;n;){if(n.nextSibling){n=n.nextSibling;break a}n=n.parentNode}n=void 0}n=br(n)}}function Sr(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Sr(e,t.parentNode):`contains`in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Cr(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var t=Mt(e.document);t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href==`string`}catch{n=!1}if(n)e=t.contentWindow;else break;t=Mt(e.document)}return t}function wr(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`)}var Tr=an&&`documentMode`in document&&11>=document.documentMode,Er=null,Dr=null,Or=null,kr=!1;function Ar(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;kr||Er==null||Er!==Mt(r)||(r=Er,`selectionStart`in r&&wr(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),Or&&yr(Or,r)||(Or=r,r=Od(Dr,`onSelect`),0>=o,i-=o,bi=1<<32-Ne(t)+i|n<h?(g=d,d=null):g=d.sibling;var _=p(i,d,s[h],c);if(_===null){d===null&&(d=g);break}e&&d&&_.alternate===null&&t(i,d),a=o(_,a,h),u===null?l=_:u.sibling=_,u=_,d=g}if(h===s.length)return n(i,d),ki&&Si(i,h),l;if(d===null){for(;hg?(_=h,h=null):_=h.sibling;var y=p(a,h,v.value,l);if(y===null){h===null&&(h=_);break}e&&h&&y.alternate===null&&t(a,h),s=o(y,s,g),d===null?u=y:d.sibling=y,d=y,h=_}if(v.done)return n(a,h),ki&&Si(a,g),u;if(h===null){for(;!v.done;g++,v=c.next())v=f(a,v.value,l),v!==null&&(s=o(v,s,g),d===null?u=v:d.sibling=v,d=v);return ki&&Si(a,g),u}for(h=r(h);!v.done;g++,v=c.next())v=m(h,a,g,v.value,l),v!==null&&(e&&v.alternate!==null&&h.delete(v.key===null?g:v.key),s=o(v,s,g),d===null?u=v:d.sibling=v,d=v);return e&&h.forEach(function(e){return t(a,e)}),ki&&Si(a,g),u}function b(e,r,o,c){if(typeof o==`object`&&o&&o.type===y&&o.key===null&&(o=o.props.children),typeof o==`object`&&o){switch(o.$$typeof){case _:a:{for(var l=o.key;r!==null;){if(r.key===l){if(l=o.type,l===y){if(r.tag===7){n(e,r.sibling),c=a(r,o.props.children),c.return=e,e=c;break a}}else if(r.elementType===l||typeof l==`object`&&l&&l.$$typeof===O&&Ca(l)===r.type){n(e,r.sibling),c=a(r,o.props),Aa(c,o),c.return=e,e=c;break a}n(e,r);break}else t(e,r);r=r.sibling}o.type===y?(c=si(o.props.children,e.mode,c,o.key),c.return=e,e=c):(c=oi(o.type,o.key,o.props,null,e.mode,c),Aa(c,o),c.return=e,e=c)}return s(e);case v:a:{for(l=o.key;r!==null;){if(r.key===l)if(r.tag===4&&r.stateNode.containerInfo===o.containerInfo&&r.stateNode.implementation===o.implementation){n(e,r.sibling),c=a(r,o.children||[]),c.return=e,e=c;break a}else{n(e,r);break}else t(e,r);r=r.sibling}c=ui(o,e.mode,c),c.return=e,e=c}return s(e);case O:return o=Ca(o),b(e,r,o,c)}if(ee(o))return h(e,r,o,c);if(M(o)){if(l=M(o),typeof l!=`function`)throw Error(i(150));return o=l.call(o),g(e,r,o,c)}if(typeof o.then==`function`)return b(e,r,ka(o),c);if(o.$$typeof===C)return b(e,r,Zi(e,o),c);ja(e,o)}return typeof o==`string`&&o!==``||typeof o==`number`||typeof o==`bigint`?(o=``+o,r!==null&&r.tag===6?(n(e,r.sibling),c=a(r,o),c.return=e,e=c):(n(e,r),c=ci(o,e.mode,c),c.return=e,e=c),s(e)):n(e,r)}return function(e,t,n,r){try{Oa=0;var i=b(e,t,n,r);return Da=null,i}catch(t){if(t===_a||t===ya)throw t;var a=ni(29,t,null,e.mode);return a.lanes=r,a.return=e,a}}}var Na=Ma(!0),G=Ma(!1),Pa=!1;function Fa(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function Ia(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,callbacks:null})}function La(e){return{lane:e,tag:0,payload:null,callback:null,next:null}}function Ra(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,Fl&2){var i=r.pending;return i===null?t.next=t:(t.next=i.next,i.next=t),r.pending=t,t=$r(e),Qr(e,null,n),t}return U(e,r,t,n),$r(e)}function za(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,n&4194048)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,Ye(e,n)}}function Ba(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var i=null,a=null;if(n=n.firstBaseUpdate,n!==null){do{var o={lane:n.lane,tag:n.tag,payload:n.payload,callback:null,next:null};a===null?i=a=o:a=a.next=o,n=n.next}while(n!==null);a===null?i=a=t:a=a.next=t}else i=a=t;n={baseState:r.baseState,firstBaseUpdate:i,lastBaseUpdate:a,shared:r.shared,callbacks:r.callbacks},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}var Va=!1;function Ha(){if(Va){var e=ca;if(e!==null)throw e}}function Ua(e,t,n,r){Va=!1;var i=e.updateQueue;Pa=!1;var a=i.firstBaseUpdate,o=i.lastBaseUpdate,s=i.shared.pending;if(s!==null){i.shared.pending=null;var c=s,l=c.next;c.next=null,o===null?a=l:o.next=l,o=c;var u=e.alternate;u!==null&&(u=u.updateQueue,s=u.lastBaseUpdate,s!==o&&(s===null?u.firstBaseUpdate=l:s.next=l,u.lastBaseUpdate=c))}if(a!==null){var d=i.baseState;o=0,u=l=c=null,s=a;do{var f=s.lane&-536870913,p=f!==s.lane;if(p?(Rl&f)===f:(r&f)===f){f!==0&&f===sa&&(Va=!0),u!==null&&(u=u.next={lane:0,tag:s.tag,payload:s.payload,callback:null,next:null});a:{var h=e,g=s;f=t;var _=n;switch(g.tag){case 1:if(h=g.payload,typeof h==`function`){d=h.call(_,d,f);break a}d=h;break a;case 3:h.flags=h.flags&-65537|128;case 0:if(h=g.payload,f=typeof h==`function`?h.call(_,d,f):h,f==null)break a;d=m({},d,f);break a;case 2:Pa=!0}}f=s.callback,f!==null&&(e.flags|=64,p&&(e.flags|=8192),p=i.callbacks,p===null?i.callbacks=[f]:p.push(f))}else p={lane:f,tag:s.tag,payload:s.payload,callback:s.callback,next:null},u===null?(l=u=p,c=d):u=u.next=p,o|=f;if(s=s.next,s===null){if(s=i.shared.pending,s===null)break;p=s,s=p.next,p.next=null,i.lastBaseUpdate=p,i.shared.pending=null}}while(1);u===null&&(c=d),i.baseState=c,i.firstBaseUpdate=l,i.lastBaseUpdate=u,a===null&&(i.shared.lanes=0),Kl|=o,e.lanes=o,e.memoizedState=d}}function K(e,t){if(typeof e!=`function`)throw Error(i(191,e));e.call(t)}function Wa(e,t){var n=e.callbacks;if(n!==null)for(e.callbacks=null,e=0;ea?a:8;var o=F.T,s={};F.T=s,As(e,!1,t,n);try{var c=i(),l=F.S;l!==null&&l(s,c),typeof c==`object`&&c&&typeof c.then==`function`?ks(e,t,da(c,r),pu(e)):ks(e,t,r,pu(e))}catch(n){ks(e,t,{then:function(){},status:`rejected`,reason:n},pu())}finally{I.p=a,o!==null&&s.types!==null&&(o.types=s.types),F.T=o}}function ys(){}function bs(e,t,n,r){if(e.tag!==5)throw Error(i(476));var a=xs(e).queue;vs(e,a,t,L,n===null?ys:function(){return Ss(e),n(r)})}function xs(e){var t=e.memoizedState;if(t!==null)return t;t={memoizedState:L,baseState:L,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Mo,lastRenderedState:L},next:null};var n={};return t.next={memoizedState:n,baseState:n,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Mo,lastRenderedState:n},next:null},e.memoizedState=t,e=e.alternate,e!==null&&(e.memoizedState=t),t}function Ss(e){var t=xs(e);t.next===null&&(t=e.alternate.memoizedState),ks(e,t.next.queue,{},pu())}function Cs(){return Xi(tp)}function ws(){return Do().memoizedState}function Ts(){return Do().memoizedState}function Es(e){for(var t=e.return;t!==null;){switch(t.tag){case 24:case 3:var n=pu();e=La(n);var r=Ra(t,e,n);r!==null&&(hu(r,t,n),za(r,t,n)),t={cache:ra()},e.payload=t;return}t=t.return}}function Ds(e,t,n){var r=pu();n={lane:r,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null},js(e)?Ms(t,n):(n=W(e,t,n,r),n!==null&&(hu(n,e,r),Ns(n,t,r)))}function Os(e,t,n){ks(e,t,n,pu())}function ks(e,t,n,r){var i={lane:r,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null};if(js(e))Ms(t,i);else{var a=e.alternate;if(e.lanes===0&&(a===null||a.lanes===0)&&(a=t.lastRenderedReducer,a!==null))try{var o=t.lastRenderedState,s=a(o,n);if(i.hasEagerState=!0,i.eagerState=s,vr(s,o))return U(e,t,i,0),Il===null&&Xr(),!1}catch{}if(n=W(e,t,i,r),n!==null)return hu(n,e,r),Ns(n,t,r),!0}return!1}function As(e,t,n,r){if(r={lane:2,revertLane:fd(),gesture:null,action:r,hasEagerState:!1,eagerState:null,next:null},js(e)){if(t)throw Error(i(479))}else t=W(e,n,r,2),t!==null&&hu(t,e,2)}function js(e){var t=e.alternate;return e===oo||t!==null&&t===oo}function Ms(e,t){uo=lo=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function Ns(e,t,n){if(n&4194048){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,Ye(e,n)}}var Ps={readContext:Xi,use:Ao,useCallback:_o,useContext:_o,useEffect:_o,useImperativeHandle:_o,useLayoutEffect:_o,useInsertionEffect:_o,useMemo:_o,useReducer:_o,useRef:_o,useState:_o,useDebugValue:_o,useDeferredValue:_o,useTransition:_o,useSyncExternalStore:_o,useId:_o,useHostTransitionStatus:_o,useFormState:_o,useActionState:_o,useOptimistic:_o,useMemoCache:_o,useCacheRefresh:_o};Ps.useEffectEvent=_o;var Fs={readContext:Xi,use:Ao,useCallback:function(e,t){return Eo().memoizedState=[e,t===void 0?null:t],e},useContext:Xi,useEffect:as,useImperativeHandle:function(e,t,n){n=n==null?null:n.concat([e]),rs(4194308,4,ds.bind(null,t,e),n)},useLayoutEffect:function(e,t){return rs(4194308,4,e,t)},useInsertionEffect:function(e,t){rs(4,2,e,t)},useMemo:function(e,t){var n=Eo();t=t===void 0?null:t;var r=e();if(fo){Me(!0);try{e()}finally{Me(!1)}}return n.memoizedState=[r,t],r},useReducer:function(e,t,n){var r=Eo();if(n!==void 0){var i=n(t);if(fo){Me(!0);try{n(t)}finally{Me(!1)}}}else i=t;return r.memoizedState=r.baseState=i,e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:i},r.queue=e,e=e.dispatch=Ds.bind(null,oo,e),[r.memoizedState,e]},useRef:function(e){var t=Eo();return e={current:e},t.memoizedState=e},useState:function(e){e=q(e);var t=e.queue,n=Os.bind(null,oo,t);return t.dispatch=n,[e.memoizedState,n]},useDebugValue:ps,useDeferredValue:function(e,t){return gs(Eo(),e,t)},useTransition:function(){var e=q(!1);return e=vs.bind(null,oo,e.queue,!0,!1),Eo().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,t,n){var r=oo,a=Eo();if(ki){if(n===void 0)throw Error(i(407));n=n()}else{if(n=t(),Il===null)throw Error(i(349));Rl&127||Lo(r,t,n)}a.memoizedState=n;var o={value:n,getSnapshot:t};return a.queue=o,as(zo.bind(null,r,o,e),[e]),r.flags|=2048,ts(9,{destroy:void 0},Ro.bind(null,r,o,n,t),null),n},useId:function(){var e=Eo(),t=Il.identifierPrefix;if(ki){var n=xi,r=bi;n=(r&~(1<<32-Ne(r)-1)).toString(32)+n,t=`_`+t+`R_`+n,n=po++,0<\/script>`,o=o.removeChild(o.firstChild);break;case`select`:o=typeof r.is==`string`?s.createElement(`select`,{is:r.is}):s.createElement(`select`),r.multiple?o.multiple=!0:r.size&&(o.size=r.size);break;default:o=typeof r.is==`string`?s.createElement(a,{is:r.is}):s.createElement(a)}}o[nt]=t,o[rt]=r;a:for(s=t.child;s!==null;){if(s.tag===5||s.tag===6)o.appendChild(s.stateNode);else if(s.tag!==4&&s.tag!==27&&s.child!==null){s.child.return=s,s=s.child;continue}if(s===t)break a;for(;s.sibling===null;){if(s.return===null||s.return===t)break a;s=s.return}s.sibling.return=s.return,s=s.sibling}t.stateNode=o;a:switch(Ld(o,a,r),a){case`button`:case`input`:case`select`:case`textarea`:r=!!r.autoFocus;break a;case`img`:r=!0;break a;default:r=!1}r&&Dc(t)}}return Mc(t),Oc(t,t.type,e===null?null:e.memoizedProps,t.pendingProps,n),null;case 6:if(e&&t.stateNode!=null)e.memoizedProps!==r&&Dc(t);else{if(typeof r!=`string`&&t.stateNode===null)throw Error(i(166));if(e=ie.current,Ii(t)){if(e=t.stateNode,n=t.memoizedProps,r=null,a=Di,a!==null)switch(a.tag){case 27:case 5:r=a.memoizedProps}e[nt]=t,e=!!(e.nodeValue===n||r!==null&&!0===r.suppressHydrationWarning||Pd(e.nodeValue,n)),e||Ni(t,!0)}else e=Ud(e).createTextNode(r),e[nt]=t,t.stateNode=e}return Mc(t),null;case 31:if(n=t.memoizedState,e===null||e.memoizedState!==null){if(r=Ii(t),n!==null){if(e===null){if(!r)throw Error(i(318));if(e=t.memoizedState,e=e===null?null:e.dehydrated,!e)throw Error(i(557));e[nt]=t}else Li(),!(t.flags&128)&&(t.memoizedState=null),t.flags|=4;Mc(t),e=!1}else n=Ri(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=n),e=!0;if(!e)return t.flags&256?(no(t),t):(no(t),null);if(t.flags&128)throw Error(i(558))}return Mc(t),null;case 13:if(r=t.memoizedState,e===null||e.memoizedState!==null&&e.memoizedState.dehydrated!==null){if(a=Ii(t),r!==null&&r.dehydrated!==null){if(e===null){if(!a)throw Error(i(318));if(a=t.memoizedState,a=a===null?null:a.dehydrated,!a)throw Error(i(317));a[nt]=t}else Li(),!(t.flags&128)&&(t.memoizedState=null),t.flags|=4;Mc(t),a=!1}else a=Ri(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=a),a=!0;if(!a)return t.flags&256?(no(t),t):(no(t),null)}return no(t),t.flags&128?(t.lanes=n,t):(n=r!==null,e=e!==null&&e.memoizedState!==null,n&&(r=t.child,a=null,r.alternate!==null&&r.alternate.memoizedState!==null&&r.alternate.memoizedState.cachePool!==null&&(a=r.alternate.memoizedState.cachePool.pool),o=null,r.memoizedState!==null&&r.memoizedState.cachePool!==null&&(o=r.memoizedState.cachePool.pool),o!==a&&(r.flags|=2048)),n!==e&&n&&(t.child.flags|=8192),Ac(t,t.updateQueue),Mc(t),null);case 4:return se(),e===null&&wd(t.stateNode.containerInfo),Mc(t),null;case 10:return Wi(t.type),Mc(t),null;case 19:if(z(ro),r=t.memoizedState,r===null)return Mc(t),null;if(a=(t.flags&128)!=0,o=r.rendering,o===null)if(a)jc(r,!1);else{if(Gl!==0||e!==null&&e.flags&128)for(e=t.child;e!==null;){if(o=io(e),o!==null){for(t.flags|=128,jc(r,!1),e=o.updateQueue,t.updateQueue=e,Ac(t,e),t.subtreeFlags=0,e=n,n=t.child;n!==null;)ai(n,e),n=n.sibling;return B(ro,ro.current&1|2),ki&&Si(t,r.treeForkCount),t.child}e=e.sibling}r.tail!==null&&xe()>tu&&(t.flags|=128,a=!0,jc(r,!1),t.lanes=4194304)}else{if(!a)if(e=io(o),e!==null){if(t.flags|=128,a=!0,e=e.updateQueue,t.updateQueue=e,Ac(t,e),jc(r,!0),r.tail===null&&r.tailMode===`hidden`&&!o.alternate&&!ki)return Mc(t),null}else 2*xe()-r.renderingStartTime>tu&&n!==536870912&&(t.flags|=128,a=!0,jc(r,!1),t.lanes=4194304);r.isBackwards?(o.sibling=t.child,t.child=o):(e=r.last,e===null?t.child=o:e.sibling=o,r.last=o)}return r.tail===null?(Mc(t),null):(e=r.tail,r.rendering=e,r.tail=e.sibling,r.renderingStartTime=xe(),e.sibling=null,n=ro.current,B(ro,a?n&1|2:n&1),ki&&Si(t,r.treeForkCount),e);case 22:case 23:return no(t),Ya(),r=t.memoizedState!==null,e===null?r&&(t.flags|=8192):e.memoizedState!==null!==r&&(t.flags|=8192),r?n&536870912&&!(t.flags&128)&&(Mc(t),t.subtreeFlags&6&&(t.flags|=8192)):Mc(t),n=t.updateQueue,n!==null&&Ac(t,n.retryQueue),n=null,e!==null&&e.memoizedState!==null&&e.memoizedState.cachePool!==null&&(n=e.memoizedState.cachePool.pool),r=null,t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(r=t.memoizedState.cachePool.pool),r!==n&&(t.flags|=2048),e!==null&&z(pa),null;case 24:return n=null,e!==null&&(n=e.memoizedState.cache),t.memoizedState.cache!==n&&(t.flags|=2048),Wi(na),Mc(t),null;case 25:return null;case 30:return null}throw Error(i(156,t.tag))}function Pc(e,t){switch(Ti(t),t.tag){case 1:return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return Wi(na),se(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 26:case 27:case 5:return H(t),null;case 31:if(t.memoizedState!==null){if(no(t),t.alternate===null)throw Error(i(340));Li()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 13:if(no(t),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(i(340));Li()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return z(ro),null;case 4:return se(),null;case 10:return Wi(t.type),null;case 22:case 23:return no(t),Ya(),e!==null&&z(pa),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 24:return Wi(na),null;case 25:return null;default:return null}}function Fc(e,t){switch(Ti(t),t.tag){case 3:Wi(na),se();break;case 26:case 27:case 5:H(t);break;case 4:se();break;case 31:t.memoizedState!==null&&no(t);break;case 13:no(t);break;case 19:z(ro);break;case 10:Wi(t.type);break;case 22:case 23:no(t),Ya(),e!==null&&z(pa);break;case 24:Wi(na)}}function Ic(e,t){try{var n=t.updateQueue,r=n===null?null:n.lastEffect;if(r!==null){var i=r.next;n=i;do{if((n.tag&e)===e){r=void 0;var a=n.create,o=n.inst;r=a(),o.destroy=r}n=n.next}while(n!==i)}}catch(e){Gu(t,t.return,e)}}function Lc(e,t,n){try{var r=t.updateQueue,i=r===null?null:r.lastEffect;if(i!==null){var a=i.next;r=a;do{if((r.tag&e)===e){var o=r.inst,s=o.destroy;if(s!==void 0){o.destroy=void 0,i=t;var c=n,l=s;try{l()}catch(e){Gu(i,c,e)}}}r=r.next}while(r!==a)}}catch(e){Gu(t,t.return,e)}}function Rc(e){var t=e.updateQueue;if(t!==null){var n=e.stateNode;try{Wa(t,n)}catch(t){Gu(e,e.return,t)}}}function zc(e,t,n){n.props=J(e.type,e.memoizedProps),n.state=e.memoizedState;try{n.componentWillUnmount()}catch(n){Gu(e,t,n)}}function Bc(e,t){try{var n=e.ref;if(n!==null){switch(e.tag){case 26:case 27:case 5:var r=e.stateNode;break;case 30:r=e.stateNode;break;default:r=e.stateNode}typeof n==`function`?e.refCleanup=n(r):n.current=r}}catch(n){Gu(e,t,n)}}function Vc(e,t){var n=e.ref,r=e.refCleanup;if(n!==null)if(typeof r==`function`)try{r()}catch(n){Gu(e,t,n)}finally{e.refCleanup=null,e=e.alternate,e!=null&&(e.refCleanup=null)}else if(typeof n==`function`)try{n(null)}catch(n){Gu(e,t,n)}else n.current=null}function Hc(e){var t=e.type,n=e.memoizedProps,r=e.stateNode;try{a:switch(t){case`button`:case`input`:case`select`:case`textarea`:n.autoFocus&&r.focus();break a;case`img`:n.src?r.src=n.src:n.srcSet&&(r.srcset=n.srcSet)}}catch(t){Gu(e,e.return,t)}}function Uc(e,t,n){try{var r=e.stateNode;Rd(r,e.type,n,t),r[rt]=t}catch(t){Gu(e,e.return,t)}}function Wc(e){return e.tag===5||e.tag===3||e.tag===26||e.tag===27&&ef(e.type)||e.tag===4}function Gc(e){a:for(;;){for(;e.sibling===null;){if(e.return===null||Wc(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.tag===27&&ef(e.type)||e.flags&2||e.child===null||e.tag===4)continue a;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function Kc(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?(n.nodeType===9?n.body:n.nodeName===`HTML`?n.ownerDocument.body:n).insertBefore(e,t):(t=n.nodeType===9?n.body:n.nodeName===`HTML`?n.ownerDocument.body:n,t.appendChild(e),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=Yt));else if(r!==4&&(r===27&&ef(e.type)&&(n=e.stateNode,t=null),e=e.child,e!==null))for(Kc(e,t,n),e=e.sibling;e!==null;)Kc(e,t,n),e=e.sibling}function qc(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(r===27&&ef(e.type)&&(n=e.stateNode),e=e.child,e!==null))for(qc(e,t,n),e=e.sibling;e!==null;)qc(e,t,n),e=e.sibling}function Jc(e){var t=e.stateNode,n=e.memoizedProps;try{for(var r=e.type,i=t.attributes;i.length;)t.removeAttributeNode(i[0]);Ld(t,r,n),t[nt]=e,t[rt]=n}catch(t){Gu(e,e.return,t)}}var Yc=!1,Xc=!1,Zc=!1,Qc=typeof WeakSet==`function`?WeakSet:Set,$c=null;function el(e,t){if(e=e.containerInfo,Vd=up,e=Cr(e),wr(e)){if(`selectionStart`in e)var n={start:e.selectionStart,end:e.selectionEnd};else a:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var a=r.anchorOffset,o=r.focusNode;r=r.focusOffset;try{n.nodeType,o.nodeType}catch{n=null;break a}var s=0,c=-1,l=-1,u=0,d=0,f=e,p=null;b:for(;;){for(var m;f!==n||a!==0&&f.nodeType!==3||(c=s+a),f!==o||r!==0&&f.nodeType!==3||(l=s+r),f.nodeType===3&&(s+=f.nodeValue.length),(m=f.firstChild)!==null;)p=f,f=m;for(;;){if(f===e)break b;if(p===n&&++u===a&&(c=s),p===o&&++d===r&&(l=s),(m=f.nextSibling)!==null)break;f=p,p=f.parentNode}f=m}n=c===-1||l===-1?null:{start:c,end:l}}else n=null}n||={start:0,end:0}}else n=null;for(Hd={focusedElem:e,selectionRange:n},up=!1,$c=t;$c!==null;)if(t=$c,e=t.child,t.subtreeFlags&1028&&e!==null)e.return=t,$c=e;else for(;$c!==null;){switch(t=$c,o=t.alternate,e=t.flags,t.tag){case 0:if(e&4&&(e=t.updateQueue,e=e===null?null:e.events,e!==null))for(n=0;n title`))),Ld(o,r,n),o[nt]=e,ht(o),r=o;break a;case`link`:var s=Wf(`link`,`href`,a).get(r+(n.href||``));if(s){for(var c=0;cg&&(o=g,g=h,h=o);var _=xr(s,h),v=xr(s,g);if(_&&v&&(p.rangeCount!==1||p.anchorNode!==_.node||p.anchorOffset!==_.offset||p.focusNode!==v.node||p.focusOffset!==v.offset)){var y=d.createRange();y.setStart(_.node,_.offset),p.removeAllRanges(),h>g?(p.addRange(y),p.extend(v.node,v.offset)):(y.setEnd(v.node,v.offset),p.addRange(y))}}}}for(d=[],p=s;p=p.parentNode;)p.nodeType===1&&d.push({element:p,left:p.scrollLeft,top:p.scrollTop});for(typeof s.focus==`function`&&s.focus(),s=0;sn?32:n,F.T=null,n=lu,lu=null;var o=au,s=su;if(iu=0,ou=au=null,su=0,Fl&6)throw Error(i(331));var c=Fl;if(Fl|=4,Al(o.current),Sl(o,o.current,s,n),Fl=c,ad(0,!1),je&&typeof je.onPostCommitFiberRoot==`function`)try{je.onPostCommitFiberRoot(Ae,o)}catch{}return!0}finally{I.p=a,F.T=r,Vu(e,t)}}function Wu(e,t,n){t=fi(n,t),t=qs(e.stateNode,t,2),e=Ra(e,t,2),e!==null&&(Ke(e,2),id(e))}function Gu(e,t,n){if(e.tag===3)Wu(e,e,n);else for(;t!==null;){if(t.tag===3){Wu(t,e,n);break}else if(t.tag===1){var r=t.stateNode;if(typeof t.type.getDerivedStateFromError==`function`||typeof r.componentDidCatch==`function`&&(ru===null||!ru.has(r))){e=fi(n,e),n=Js(2),r=Ra(t,n,2),r!==null&&(Ys(n,r,t,e),Ke(r,2),id(r));break}}t=t.return}}function Ku(e,t,n){var r=e.pingCache;if(r===null){r=e.pingCache=new Pl;var i=new Set;r.set(t,i)}else i=r.get(t),i===void 0&&(i=new Set,r.set(t,i));i.has(n)||(Ul=!0,i.add(n),e=qu.bind(null,e,t,n),t.then(e,e))}function qu(e,t,n){var r=e.pingCache;r!==null&&r.delete(t),e.pingedLanes|=e.suspendedLanes&n,e.warmLanes&=~n,Il===e&&(Rl&n)===n&&(Gl===4||Gl===3&&(Rl&62914560)===Rl&&300>xe()-$l?!(Fl&2)&&Su(e,0):X|=n,Yl===Rl&&(Yl=0)),id(e)}function Ju(e,t){t===0&&(t=We()),e=Zr(e,t),e!==null&&(Ke(e,t),id(e))}function Yu(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),Ju(e,n)}function Xu(e,t){var n=0;switch(e.tag){case 31:case 13:var r=e.stateNode,a=e.memoizedState;a!==null&&(n=a.retryLane);break;case 19:r=e.stateNode;break;case 22:r=e.stateNode._retryCache;break;default:throw Error(i(314))}r!==null&&r.delete(t),Ju(e,n)}function Zu(e,t){return _e(e,t)}var Qu=null,$u=null,ed=!1,td=!1,nd=!1,rd=0;function id(e){e!==$u&&e.next===null&&($u===null?Qu=$u=e:$u=$u.next=e),td=!0,ed||(ed=!0,dd())}function ad(e,t){if(!nd&&td){nd=!0;do for(var n=!1,r=Qu;r!==null;){if(!t)if(e!==0){var i=r.pendingLanes;if(i===0)var a=0;else{var o=r.suspendedLanes,s=r.pingedLanes;a=(1<<31-Ne(42|e)+1)-1,a&=i&~(o&~s),a=a&201326741?a&201326741|1:a?a|2:0}a!==0&&(n=!0,ud(r,a))}else a=Rl,a=Ve(r,r===Il?a:0,r.cancelPendingCommit!==null||r.timeoutHandle!==-1),!(a&3)||He(r,a)||(n=!0,ud(r,a));r=r.next}while(n);nd=!1}}function od(){sd()}function sd(){td=ed=!1;var e=0;rd!==0&&Jd()&&(e=rd);for(var t=xe(),n=null,r=Qu;r!==null;){var i=r.next,a=cd(r,t);a===0?(r.next=null,n===null?Qu=i:n.next=i,i===null&&($u=n)):(n=r,(e!==0||a&3)&&(td=!0)),r=i}iu!==0&&iu!==5||ad(e,!1),rd!==0&&(rd=0)}function cd(e,t){for(var n=e.suspendedLanes,r=e.pingedLanes,i=e.expirationTimes,a=e.pendingLanes&-62914561;0s)break;var u=c.transferSize,d=c.initiatorType;u&&zd(d)&&(c=c.responseEnd,o+=u*(c`u`?null:document;function wf(e,t,n){var r=Cf;if(r&&typeof t==`string`&&t){var i=Pt(t);i=`link[rel="`+e+`"][href="`+i+`"]`,typeof n==`string`&&(i+=`[crossorigin="`+n+`"]`),vf.has(i)||(vf.add(i),e={rel:e,crossOrigin:n,href:t},r.querySelector(i)===null&&(t=r.createElement(`link`),Ld(t,`link`,e),ht(t),r.head.appendChild(t)))}}function Tf(e){bf.D(e),wf(`dns-prefetch`,e,null)}function Ef(e,t){bf.C(e,t),wf(`preconnect`,e,t)}function Df(e,t,n){bf.L(e,t,n);var r=Cf;if(r&&e&&t){var i=`link[rel="preload"][as="`+Pt(t)+`"]`;t===`image`&&n&&n.imageSrcSet?(i+=`[imagesrcset="`+Pt(n.imageSrcSet)+`"]`,typeof n.imageSizes==`string`&&(i+=`[imagesizes="`+Pt(n.imageSizes)+`"]`)):i+=`[href="`+Pt(e)+`"]`;var a=i;switch(t){case`style`:a=Nf(e);break;case`script`:a=Lf(e)}_f.has(a)||(e=m({rel:`preload`,href:t===`image`&&n&&n.imageSrcSet?void 0:e,as:t},n),_f.set(a,e),r.querySelector(i)!==null||t===`style`&&r.querySelector(Pf(a))||t===`script`&&r.querySelector(Rf(a))||(t=r.createElement(`link`),Ld(t,`link`,e),ht(t),r.head.appendChild(t)))}}function Of(e,t){bf.m(e,t);var n=Cf;if(n&&e){var r=t&&typeof t.as==`string`?t.as:`script`,i=`link[rel="modulepreload"][as="`+Pt(r)+`"][href="`+Pt(e)+`"]`,a=i;switch(r){case`audioworklet`:case`paintworklet`:case`serviceworker`:case`sharedworker`:case`worker`:case`script`:a=Lf(e)}if(!_f.has(a)&&(e=m({rel:`modulepreload`,href:e},t),_f.set(a,e),n.querySelector(i)===null)){switch(r){case`audioworklet`:case`paintworklet`:case`serviceworker`:case`sharedworker`:case`worker`:case`script`:if(n.querySelector(Rf(a)))return}r=n.createElement(`link`),Ld(r,`link`,e),ht(r),n.head.appendChild(r)}}}function kf(e,t,n){bf.S(e,t,n);var r=Cf;if(r&&e){var i=mt(r).hoistableStyles,a=Nf(e);t||=`default`;var o=i.get(a);if(!o){var s={loading:0,preload:null};if(o=r.querySelector(Pf(a)))s.loading=5;else{e=m({rel:`stylesheet`,href:e,"data-precedence":t},n),(n=_f.get(a))&&Vf(e,n);var c=o=r.createElement(`link`);ht(c),Ld(c,`link`,e),c._p=new Promise(function(e,t){c.onload=e,c.onerror=t}),c.addEventListener(`load`,function(){s.loading|=1}),c.addEventListener(`error`,function(){s.loading|=2}),s.loading|=4,Bf(o,t,r)}o={type:`stylesheet`,instance:o,count:1,state:s},i.set(a,o)}}}function Af(e,t){bf.X(e,t);var n=Cf;if(n&&e){var r=mt(n).hoistableScripts,i=Lf(e),a=r.get(i);a||(a=n.querySelector(Rf(i)),a||(e=m({src:e,async:!0},t),(t=_f.get(i))&&Hf(e,t),a=n.createElement(`script`),ht(a),Ld(a,`link`,e),n.head.appendChild(a)),a={type:`script`,instance:a,count:1,state:null},r.set(i,a))}}function jf(e,t){bf.M(e,t);var n=Cf;if(n&&e){var r=mt(n).hoistableScripts,i=Lf(e),a=r.get(i);a||(a=n.querySelector(Rf(i)),a||(e=m({src:e,async:!0,type:`module`},t),(t=_f.get(i))&&Hf(e,t),a=n.createElement(`script`),ht(a),Ld(a,`link`,e),n.head.appendChild(a)),a={type:`script`,instance:a,count:1,state:null},r.set(i,a))}}function Mf(e,t,n,r){var a=(a=ie.current)?yf(a):null;if(!a)throw Error(i(446));switch(e){case`meta`:case`title`:return null;case`style`:return typeof n.precedence==`string`&&typeof n.href==`string`?(t=Nf(n.href),n=mt(a).hoistableStyles,r=n.get(t),r||(r={type:`style`,instance:null,count:0,state:null},n.set(t,r)),r):{type:`void`,instance:null,count:0,state:null};case`link`:if(n.rel===`stylesheet`&&typeof n.href==`string`&&typeof n.precedence==`string`){e=Nf(n.href);var o=mt(a).hoistableStyles,s=o.get(e);if(s||(a=a.ownerDocument||a,s={type:`stylesheet`,instance:null,count:0,state:{loading:0,preload:null}},o.set(e,s),(o=a.querySelector(Pf(e)))&&!o._p&&(s.instance=o,s.state.loading=5),_f.has(e)||(n={rel:`preload`,as:`style`,href:n.href,crossOrigin:n.crossOrigin,integrity:n.integrity,media:n.media,hrefLang:n.hrefLang,referrerPolicy:n.referrerPolicy},_f.set(e,n),o||If(a,e,n,s.state))),t&&r===null)throw Error(i(528,``));return s}if(t&&r!==null)throw Error(i(529,``));return null;case`script`:return t=n.async,n=n.src,typeof n==`string`&&t&&typeof t!=`function`&&typeof t!=`symbol`?(t=Lf(n),n=mt(a).hoistableScripts,r=n.get(t),r||(r={type:`script`,instance:null,count:0,state:null},n.set(t,r)),r):{type:`void`,instance:null,count:0,state:null};default:throw Error(i(444,e))}}function Nf(e){return`href="`+Pt(e)+`"`}function Pf(e){return`link[rel="stylesheet"][`+e+`]`}function Ff(e){return m({},e,{"data-precedence":e.precedence,precedence:null})}function If(e,t,n,r){e.querySelector(`link[rel="preload"][as="style"][`+t+`]`)?r.loading=1:(t=e.createElement(`link`),r.preload=t,t.addEventListener(`load`,function(){return r.loading|=1}),t.addEventListener(`error`,function(){return r.loading|=2}),Ld(t,`link`,n),ht(t),e.head.appendChild(t))}function Lf(e){return`[src="`+Pt(e)+`"]`}function Rf(e){return`script[async]`+e}function zf(e,t,n){if(t.count++,t.instance===null)switch(t.type){case`style`:var r=e.querySelector(`style[data-href~="`+Pt(n.href)+`"]`);if(r)return t.instance=r,ht(r),r;var a=m({},n,{"data-href":n.href,"data-precedence":n.precedence,href:null,precedence:null});return r=(e.ownerDocument||e).createElement(`style`),ht(r),Ld(r,`style`,a),Bf(r,n.precedence,e),t.instance=r;case`stylesheet`:a=Nf(n.href);var o=e.querySelector(Pf(a));if(o)return t.state.loading|=4,t.instance=o,ht(o),o;r=Ff(n),(a=_f.get(a))&&Vf(r,a),o=(e.ownerDocument||e).createElement(`link`),ht(o);var s=o;return s._p=new Promise(function(e,t){s.onload=e,s.onerror=t}),Ld(o,`link`,r),t.state.loading|=4,Bf(o,n.precedence,e),t.instance=o;case`script`:return o=Lf(n.src),(a=e.querySelector(Rf(o)))?(t.instance=a,ht(a),a):(r=n,(a=_f.get(o))&&(r=m({},n),Hf(r,a)),e=e.ownerDocument||e,a=e.createElement(`script`),ht(a),Ld(a,`link`,r),e.head.appendChild(a),t.instance=a);case`void`:return null;default:throw Error(i(443,t.type))}else t.type===`stylesheet`&&!(t.state.loading&4)&&(r=t.instance,t.state.loading|=4,Bf(r,n.precedence,e));return t.instance}function Bf(e,t,n){for(var r=n.querySelectorAll(`link[rel="stylesheet"][data-precedence],style[data-precedence]`),i=r.length?r[r.length-1]:null,a=i,o=0;o title`):null)}function Kf(e,t,n){if(n===1||t.itemProp!=null)return!1;switch(e){case`meta`:case`title`:return!0;case`style`:if(typeof t.precedence!=`string`||typeof t.href!=`string`||t.href===``)break;return!0;case`link`:if(typeof t.rel!=`string`||typeof t.href!=`string`||t.href===``||t.onLoad||t.onError)break;switch(t.rel){case`stylesheet`:return e=t.disabled,typeof t.precedence==`string`&&e==null;default:return!0}case`script`:if(t.async&&typeof t.async!=`function`&&typeof t.async!=`symbol`&&!t.onLoad&&!t.onError&&t.src&&typeof t.src==`string`)return!0}return!1}function qf(e){return!(e.type===`stylesheet`&&!(e.state.loading&3))}function Jf(e,t,n,r){if(n.type===`stylesheet`&&(typeof r.media!=`string`||!1!==matchMedia(r.media).matches)&&!(n.state.loading&4)){if(n.instance===null){var i=Nf(r.href),a=t.querySelector(Pf(i));if(a){t=a._p,typeof t==`object`&&t&&typeof t.then==`function`&&(e.count++,e=Zf.bind(e),t.then(e,e)),n.state.loading|=4,n.instance=a,ht(a);return}a=t.ownerDocument||t,r=Ff(r),(i=_f.get(i))&&Vf(r,i),a=a.createElement(`link`),ht(a);var o=a;o._p=new Promise(function(e,t){o.onload=e,o.onerror=t}),Ld(a,`link`,r),n.instance=a}e.stylesheets===null&&(e.stylesheets=new Map),e.stylesheets.set(n,t),(t=n.state.preload)&&!(n.state.loading&3)&&(e.count++,n=Zf.bind(e),t.addEventListener(`load`,n),t.addEventListener(`error`,n))}}var Yf=0;function Xf(e,t){return e.stylesheets&&e.count===0&&$f(e,e.stylesheets),0Yf?50:800)+t);return e.unsuspend=n,function(){e.unsuspend=null,clearTimeout(r),clearTimeout(i)}}:null}function Zf(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)$f(this,this.stylesheets);else if(this.unsuspend){var e=this.unsuspend;this.unsuspend=null,e()}}}var Qf=null;function $f(e,t){e.stylesheets=null,e.unsuspend!==null&&(e.count++,Qf=new Map,t.forEach(ep,e),Qf=null,Zf.call(e))}function ep(e,t){if(!(t.state.loading&4)){var n=Qf.get(e);if(n)var r=n.get(null);else{n=new Map,Qf.set(e,n);for(var i=e.querySelectorAll(`link[data-precedence],style[data-precedence]`),a=0;a{function n(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>`u`||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!=`function`))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}n(),t.exports=g()})),v=`modulepreload`,y=function(e){return`/`+e},b={},x=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e=document.getElementsByTagName(`link`),i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}r=o(t.map(t=>{if(t=y(t,n),t in b)return;b[t]=!0;let r=t.endsWith(`.css`),i=r?`[rel="stylesheet"]`:``;if(n)for(let n=e.length-1;n>=0;n--){let i=e[n];if(i.href===t&&(!r||i.rel===`stylesheet`))return}else if(document.querySelector(`link[href="${t}"]${i}`))return;let o=document.createElement(`link`);if(o.rel=r?`stylesheet`:v,r||(o.as=`script`),o.crossOrigin=``,o.href=t,a&&o.setAttribute(`nonce`,a),document.head.appendChild(o),r)return new Promise((e,n)=>{o.addEventListener(`load`,e),o.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${t}`)))})}))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})},S=l(d(),1),C=`popstate`;function w(e){return typeof e==`object`&&!!e&&`pathname`in e&&`search`in e&&`hash`in e&&`state`in e&&`key`in e}function T(e={}){function t(e,t){let n=t.state?.masked,{pathname:r,search:i,hash:a}=n||e.location;return A(``,{pathname:r,search:i,hash:a},t.state&&t.state.usr||null,t.state&&t.state.key||`default`,n?{pathname:e.location.pathname,search:e.location.search,hash:e.location.hash}:void 0)}function n(e,t){return typeof t==`string`?t:j(t)}return N(t,n,null,e)}function E(e,t){if(e===!1||e==null)throw Error(t)}function D(e,t){if(!e){typeof console<`u`&&console.warn(t);try{throw Error(t)}catch{}}}function O(){return Math.random().toString(36).substring(2,10)}function k(e,t){return{usr:e.state,key:e.key,idx:t,masked:e.unstable_mask?{pathname:e.pathname,search:e.search,hash:e.hash}:void 0}}function A(e,t,n=null,r,i){return{pathname:typeof e==`string`?e:e.pathname,search:``,hash:``,...typeof t==`string`?M(t):t,state:n,key:t&&t.key||r||O(),unstable_mask:i}}function j({pathname:e=`/`,search:t=``,hash:n=``}){return t&&t!==`?`&&(e+=t.charAt(0)===`?`?t:`?`+t),n&&n!==`#`&&(e+=n.charAt(0)===`#`?n:`#`+n),e}function M(e){let t={};if(e){let n=e.indexOf(`#`);n>=0&&(t.hash=e.substring(n),e=e.substring(0,n));let r=e.indexOf(`?`);r>=0&&(t.search=e.substring(r),e=e.substring(0,r)),e&&(t.pathname=e)}return t}function N(e,t,n,r={}){let{window:i=document.defaultView,v5Compat:a=!1}=r,o=i.history,s=`POP`,c=null,l=u();l??(l=0,o.replaceState({...o.state,idx:l},``));function u(){return(o.state||{idx:null}).idx}function d(){s=`POP`;let e=u(),t=e==null?null:e-l;l=e,c&&c({action:s,location:h.location,delta:t})}function f(e,t){s=`PUSH`;let r=w(e)?e:A(h.location,e,t);n&&n(r,e),l=u()+1;let d=k(r,l),f=h.createHref(r.unstable_mask||r);try{o.pushState(d,``,f)}catch(e){if(e instanceof DOMException&&e.name===`DataCloneError`)throw e;i.location.assign(f)}a&&c&&c({action:s,location:h.location,delta:1})}function p(e,t){s=`REPLACE`;let r=w(e)?e:A(h.location,e,t);n&&n(r,e),l=u();let i=k(r,l),d=h.createHref(r.unstable_mask||r);o.replaceState(i,``,d),a&&c&&c({action:s,location:h.location,delta:0})}function m(e){return P(e)}let h={get action(){return s},get location(){return e(i,o)},listen(e){if(c)throw Error(`A history only accepts one active listener`);return i.addEventListener(C,d),c=e,()=>{i.removeEventListener(C,d),c=null}},createHref(e){return t(i,e)},createURL:m,encodeLocation(e){let t=m(e);return{pathname:t.pathname,search:t.search,hash:t.hash}},push:f,replace:p,go(e){return o.go(e)}};return h}function P(e,t=!1){let n=`http://localhost`;typeof window<`u`&&(n=window.location.origin===`null`?window.location.href:window.location.origin),E(n,`No window.location.(origin|href) available to create URL`);let r=typeof e==`string`?e:j(e);return r=r.replace(/ $/,`%20`),!t&&r.startsWith(`//`)&&(r=n+r),new URL(r,n)}function ee(e,t,n=`/`){return F(e,t,n,!1)}function F(e,t,n,r){let i=de((typeof t==`string`?M(t):t).pathname||`/`,n);if(i==null)return null;let a=L(e);te(a);let o=null;for(let e=0;o==null&&e{let c={relativePath:s===void 0?e.path||``:s,caseSensitive:e.caseSensitive===!0,childrenIndex:a,route:e};if(c.relativePath.startsWith(`/`)){if(!c.relativePath.startsWith(r)&&o)return;E(c.relativePath.startsWith(r),`Absolute route path "${c.relativePath}" nested under path "${r}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),c.relativePath=c.relativePath.slice(r.length)}let l=ye([r,c.relativePath]),u=n.concat(c);e.children&&e.children.length>0&&(E(e.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${l}".`),L(e.children,t,u,l,o)),!(e.path==null&&!e.index)&&t.push({path:l,score:oe(l,e.index),routesMeta:u})};return e.forEach((e,t)=>{if(e.path===``||!e.path?.includes(`?`))a(e,t);else for(let n of R(e.path))a(e,t,!0,n)}),t}function R(e){let t=e.split(`/`);if(t.length===0)return[];let[n,...r]=t,i=n.endsWith(`?`),a=n.replace(/\?$/,``);if(r.length===0)return i?[a,``]:[a];let o=R(r.join(`/`)),s=[];return s.push(...o.map(e=>e===``?a:[a,e].join(`/`))),i&&s.push(...o),s.map(t=>e.startsWith(`/`)&&t===``?`/`:t)}function te(e){e.sort((e,t)=>e.score===t.score?se(e.routesMeta.map(e=>e.childrenIndex),t.routesMeta.map(e=>e.childrenIndex)):t.score-e.score)}var ne=/^:[\w-]+$/,z=3,B=2,V=1,re=10,ie=-2,ae=e=>e===`*`;function oe(e,t){let n=e.split(`/`),r=n.length;return n.some(ae)&&(r+=ie),t&&(r+=B),n.filter(e=>!ae(e)).reduce((e,t)=>e+(ne.test(t)?z:t===``?V:re),r)}function se(e,t){return e.length===t.length&&e.slice(0,-1).every((e,n)=>e===t[n])?e[e.length-1]-t[t.length-1]:0}function ce(e,t,n=!1){let{routesMeta:r}=e,i={},a=`/`,o=[];for(let e=0;e{if(t===`*`){let e=s[r]||``;o=a.slice(0,a.length-e.length).replace(/(.)\/+$/,`$1`)}let i=s[r];return n&&!i?e[t]=void 0:e[t]=(i||``).replace(/%2F/g,`/`),e},{}),pathname:a,pathnameBase:o,pattern:e}}function le(e,t=!1,n=!0){D(e===`*`||!e.endsWith(`*`)||e.endsWith(`/*`),`Route path "${e}" will be treated as if it were "${e.replace(/\*$/,`/*`)}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${e.replace(/\*$/,`/*`)}".`);let r=[],i=`^`+e.replace(/\/*\*?$/,``).replace(/^\/*/,`/`).replace(/[\\.*+^${}|()[\]]/g,`\\$&`).replace(/\/:([\w-]+)(\?)?/g,(e,t,n,i,a)=>{if(r.push({paramName:t,isOptional:n!=null}),n){let t=a.charAt(i+e.length);return t&&t!==`/`?`/([^\\/]*)`:`(?:/([^\\/]*))?`}return`/([^\\/]+)`}).replace(/\/([\w-]+)\?(\/|$)/g,`(/$1)?$2`);return e.endsWith(`*`)?(r.push({paramName:`*`}),i+=e===`*`||e===`/*`?`(.*)$`:`(?:\\/(.+)|\\/*)$`):n?i+=`\\/*$`:e!==``&&e!==`/`&&(i+=`(?:(?=\\/|$))`),[new RegExp(i,t?void 0:`i`),r]}function ue(e){try{return e.split(`/`).map(e=>decodeURIComponent(e).replace(/\//g,`%2F`)).join(`/`)}catch(t){return D(!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 de(e,t){if(t===`/`)return e;if(!e.toLowerCase().startsWith(t.toLowerCase()))return null;let n=t.endsWith(`/`)?t.length-1:t.length,r=e.charAt(n);return r&&r!==`/`?null:e.slice(n)||`/`}var fe=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;function pe(e,t=`/`){let{pathname:n,search:r=``,hash:i=``}=typeof e==`string`?M(e):e,a;return n?(n=n.replace(/\/\/+/g,`/`),a=n.startsWith(`/`)?me(n.substring(1),`/`):me(n,t)):a=t,{pathname:a,search:xe(r),hash:Se(i)}}function me(e,t){let n=t.replace(/\/+$/,``).split(`/`);return e.split(`/`).forEach(e=>{e===`..`?n.length>1&&n.pop():e!==`.`&&n.push(e)}),n.length>1?n.join(`/`):`/`}function he(e,t,n,r){return`Cannot include a '${e}' character in a manually specified \`to.${t}\` field [${JSON.stringify(r)}]. Please separate it out to the \`to.${n}\` field. Alternatively you may provide the full path as a string in and the router will parse it for you.`}function ge(e){return e.filter((e,t)=>t===0||e.route.path&&e.route.path.length>0)}function _e(e){let t=ge(e);return t.map((e,n)=>n===t.length-1?e.pathname:e.pathnameBase)}function ve(e,t,n,r=!1){let i;typeof e==`string`?i=M(e):(i={...e},E(!i.pathname||!i.pathname.includes(`?`),he(`?`,`pathname`,`search`,i)),E(!i.pathname||!i.pathname.includes(`#`),he(`#`,`pathname`,`hash`,i)),E(!i.search||!i.search.includes(`#`),he(`#`,`search`,`hash`,i)));let a=e===``||i.pathname===``,o=a?`/`:i.pathname,s;if(o==null)s=n;else{let e=t.length-1;if(!r&&o.startsWith(`..`)){let t=o.split(`/`);for(;t[0]===`..`;)t.shift(),--e;i.pathname=t.join(`/`)}s=e>=0?t[e]:`/`}let c=pe(i,s),l=o&&o!==`/`&&o.endsWith(`/`),u=(a||o===`.`)&&n.endsWith(`/`);return!c.pathname.endsWith(`/`)&&(l||u)&&(c.pathname+=`/`),c}var ye=e=>e.join(`/`).replace(/\/\/+/g,`/`),be=e=>e.replace(/\/+$/,``).replace(/^\/*/,`/`),xe=e=>!e||e===`?`?``:e.startsWith(`?`)?e:`?`+e,Se=e=>!e||e===`#`?``:e.startsWith(`#`)?e:`#`+e,Ce=class{constructor(e,t,n,r=!1){this.status=e,this.statusText=t||``,this.internal=r,n instanceof Error?(this.data=n.toString(),this.error=n):this.data=n}};function we(e){return e!=null&&typeof e.status==`number`&&typeof e.statusText==`string`&&typeof e.internal==`boolean`&&`data`in e}function Te(e){return e.map(e=>e.route.path).filter(Boolean).join(`/`).replace(/\/\/*/g,`/`)||`/`}var Ee=typeof window<`u`&&window.document!==void 0&&window.document.createElement!==void 0;function De(e,t){let n=e;if(typeof n!=`string`||!fe.test(n))return{absoluteURL:void 0,isExternal:!1,to:n};let r=n,i=!1;if(Ee)try{let e=new URL(window.location.href),r=n.startsWith(`//`)?new URL(e.protocol+n):new URL(n),a=de(r.pathname,t);r.origin===e.origin&&a!=null?n=a+r.search+r.hash:i=!0}catch{D(!1,` contains an invalid URL which will probably break when clicked - please update to a valid URL path.`)}return{absoluteURL:r,isExternal:i,to:n}}Object.getOwnPropertyNames(Object.prototype).sort().join(`\0`);var Oe=S.createContext(null);Oe.displayName=`DataRouter`;var ke=S.createContext(null);ke.displayName=`DataRouterState`;var Ae=S.createContext(!1),je=S.createContext({isTransitioning:!1});je.displayName=`ViewTransition`;var Me=S.createContext(new Map);Me.displayName=`Fetchers`;var Ne=S.createContext(null);Ne.displayName=`Await`;var Pe=S.createContext(null);Pe.displayName=`Navigation`;var Fe=S.createContext(null);Fe.displayName=`Location`;var Ie=S.createContext({outlet:null,matches:[],isDataRoute:!1});Ie.displayName=`Route`;var Le=S.createContext(null);Le.displayName=`RouteError`;var Re=`REACT_ROUTER_ERROR`,ze=`REDIRECT`,Be=`ROUTE_ERROR_RESPONSE`;function Ve(e){if(e.startsWith(`${Re}:${ze}:{`))try{let t=JSON.parse(e.slice(28));if(typeof t==`object`&&t&&typeof t.status==`number`&&typeof t.statusText==`string`&&typeof t.location==`string`&&typeof t.reloadDocument==`boolean`&&typeof t.replace==`boolean`)return t}catch{}}function He(e){if(e.startsWith(`${Re}:${Be}:{`))try{let t=JSON.parse(e.slice(40));if(typeof t==`object`&&t&&typeof t.status==`number`&&typeof t.statusText==`string`)return new Ce(t.status,t.statusText,t.data)}catch{}}function Ue(e,{relative:t}={}){E(We(),`useHref() may be used only in the context of a component.`);let{basename:n,navigator:r}=S.useContext(Pe),{hash:i,pathname:a,search:o}=Qe(e,{relative:t}),s=a;return n!==`/`&&(s=a===`/`?n:ye([n,a])),r.createHref({pathname:s,search:o,hash:i})}function We(){return S.useContext(Fe)!=null}function Ge(){return E(We(),`useLocation() may be used only in the context of a component.`),S.useContext(Fe).location}var Ke=`You should call navigate() in a React.useEffect(), not when your component is first rendered.`;function qe(e){S.useContext(Pe).static||S.useLayoutEffect(e)}function Je(){let{isDataRoute:e}=S.useContext(Ie);return e?_t():Ye()}function Ye(){E(We(),`useNavigate() may be used only in the context of a component.`);let e=S.useContext(Oe),{basename:t,navigator:n}=S.useContext(Pe),{matches:r}=S.useContext(Ie),{pathname:i}=Ge(),a=JSON.stringify(_e(r)),o=S.useRef(!1);return qe(()=>{o.current=!0}),S.useCallback((r,s={})=>{if(D(o.current,Ke),!o.current)return;if(typeof r==`number`){n.go(r);return}let c=ve(r,JSON.parse(a),i,s.relative===`path`);e==null&&t!==`/`&&(c.pathname=c.pathname===`/`?t:ye([t,c.pathname])),(s.replace?n.replace:n.push)(c,s.state,s)},[t,n,a,i,e])}var Xe=S.createContext(null);function Ze(e){let t=S.useContext(Ie).outlet;return S.useMemo(()=>t&&S.createElement(Xe.Provider,{value:e},t),[t,e])}function Qe(e,{relative:t}={}){let{matches:n}=S.useContext(Ie),{pathname:r}=Ge(),i=JSON.stringify(_e(n));return S.useMemo(()=>ve(e,JSON.parse(i),r,t===`path`),[e,i,r,t])}function $e(e,t){return et(e,t)}function et(e,t,n){E(We(),`useRoutes() may be used only in the context of a component.`);let{navigator:r}=S.useContext(Pe),{matches:i}=S.useContext(Ie),a=i[i.length-1],o=a?a.params:{},s=a?a.pathname:`/`,c=a?a.pathnameBase:`/`,l=a&&a.route;{let e=l&&l.path||``;yt(s,!l||e.endsWith(`*`)||e.endsWith(`*?`),`You rendered descendant (or called \`useRoutes()\`) at "${s}" (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 u=Ge(),d;if(t){let e=typeof t==`string`?M(t):t;E(c===`/`||e.pathname?.startsWith(c),`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 "${c}" but pathname "${e.pathname}" was given in the \`location\` prop.`),d=e}else d=u;let f=d.pathname||`/`,p=f;if(c!==`/`){let e=c.replace(/^\//,``).split(`/`);p=`/`+f.replace(/^\//,``).split(`/`).slice(e.length).join(`/`)}let m=ee(e,{pathname:p});D(l||m!=null,`No routes matched location "${d.pathname}${d.search}${d.hash}" `),D(m==null||m[m.length-1].route.element!==void 0||m[m.length-1].route.Component!==void 0||m[m.length-1].route.lazy!==void 0,`Matched leaf route at location "${d.pathname}${d.search}${d.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 h=st(m&&m.map(e=>Object.assign({},e,{params:Object.assign({},o,e.params),pathname:ye([c,r.encodeLocation?r.encodeLocation(e.pathname.replace(/\?/g,`%3F`).replace(/#/g,`%23`)).pathname:e.pathname]),pathnameBase:e.pathnameBase===`/`?c:ye([c,r.encodeLocation?r.encodeLocation(e.pathnameBase.replace(/\?/g,`%3F`).replace(/#/g,`%23`)).pathname:e.pathnameBase])})),i,n);return t&&h?S.createElement(Fe.Provider,{value:{location:{pathname:`/`,search:``,hash:``,state:null,key:`default`,unstable_mask:void 0,...d},navigationType:`POP`}},h):h}function tt(){let e=gt(),t=we(e)?`${e.status} ${e.statusText}`:e instanceof Error?e.message:JSON.stringify(e),n=e instanceof Error?e.stack:null,r=`rgba(200,200,200, 0.5)`,i={padding:`0.5rem`,backgroundColor:r},a={padding:`2px 4px`,backgroundColor:r},o=null;return console.error(`Error handled by React Router default ErrorBoundary:`,e),o=S.createElement(S.Fragment,null,S.createElement(`p`,null,`💿 Hey developer 👋`),S.createElement(`p`,null,`You can provide a way better UX than this when your app throws errors by providing your own `,S.createElement(`code`,{style:a},`ErrorBoundary`),` or`,` `,S.createElement(`code`,{style:a},`errorElement`),` prop on your route.`)),S.createElement(S.Fragment,null,S.createElement(`h2`,null,`Unexpected Application Error!`),S.createElement(`h3`,{style:{fontStyle:`italic`}},t),n?S.createElement(`pre`,{style:i},n):null,o)}var nt=S.createElement(tt,null),rt=class extends S.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?t.error:e.error,location:t.location,revalidation:e.revalidation||t.revalidation}}componentDidCatch(e,t){this.props.onError?this.props.onError(e,t):console.error(`React Router caught the following error during render`,e)}render(){let e=this.state.error;if(this.context&&typeof e==`object`&&e&&`digest`in e&&typeof e.digest==`string`){let t=He(e.digest);t&&(e=t)}let t=e===void 0?this.props.children:S.createElement(Ie.Provider,{value:this.props.routeContext},S.createElement(Le.Provider,{value:e,children:this.props.component}));return this.context?S.createElement(at,{error:e},t):t}};rt.contextType=Ae;var it=new WeakMap;function at({children:e,error:t}){let{basename:n}=S.useContext(Pe);if(typeof t==`object`&&t&&`digest`in t&&typeof t.digest==`string`){let e=Ve(t.digest);if(e){let r=it.get(t);if(r)throw r;let i=De(e.location,n);if(Ee&&!it.get(t))if(i.isExternal||e.reloadDocument)window.location.href=i.absoluteURL||i.to;else{let n=Promise.resolve().then(()=>window.__reactRouterDataRouter.navigate(i.to,{replace:e.replace}));throw it.set(t,n),n}return S.createElement(`meta`,{httpEquiv:`refresh`,content:`0;url=${i.absoluteURL||i.to}`})}}return e}function ot({routeContext:e,match:t,children:n}){let r=S.useContext(Oe);return r&&r.static&&r.staticContext&&(t.route.errorElement||t.route.ErrorBoundary)&&(r.staticContext._deepestRenderedBoundaryId=t.route.id),S.createElement(Ie.Provider,{value:e},n)}function st(e,t=[],n){let r=n?.state;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,a=r?.errors;if(a!=null){let e=i.findIndex(e=>e.route.id&&a?.[e.route.id]!==void 0);E(e>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(a).join(`,`)}`),i=i.slice(0,Math.min(i.length,e+1))}let o=!1,s=-1;if(n&&r){o=r.renderFallback;for(let e=0;e=0?i.slice(0,s+1):[i[0]];break}}}}let c=n?.onError,l=r&&c?(e,t)=>{c(e,{location:r.location,params:r.matches?.[0]?.params??{},unstable_pattern:Te(r.matches),errorInfo:t})}:void 0;return i.reduceRight((e,n,c)=>{let u,d=!1,f=null,p=null;r&&(u=a&&n.route.id?a[n.route.id]:void 0,f=n.route.errorElement||nt,o&&(s<0&&c===0?(yt(`route-fallback`,!1,"No `HydrateFallback` element provided to render during initial hydration"),d=!0,p=null):s===c&&(d=!0,p=n.route.hydrateFallbackElement||null)));let m=t.concat(i.slice(0,c+1)),h=()=>{let t;return t=u?f:d?p:n.route.Component?S.createElement(n.route.Component,null):n.route.element?n.route.element:e,S.createElement(ot,{match:n,routeContext:{outlet:e,matches:m,isDataRoute:r!=null},children:t})};return r&&(n.route.ErrorBoundary||n.route.errorElement||c===0)?S.createElement(rt,{location:r.location,revalidation:r.revalidation,component:f,error:u,children:h(),routeContext:{outlet:null,matches:m,isDataRoute:!0},onError:l}):h()},null)}function ct(e){return`${e} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function lt(e){let t=S.useContext(Oe);return E(t,ct(e)),t}function ut(e){let t=S.useContext(ke);return E(t,ct(e)),t}function dt(e){let t=S.useContext(Ie);return E(t,ct(e)),t}function ft(e){let t=dt(e),n=t.matches[t.matches.length-1];return E(n.route.id,`${e} can only be used on routes that contain a unique "id"`),n.route.id}function pt(){return ft(`useRouteId`)}function mt(){return ut(`useNavigation`).navigation}function ht(){let{matches:e,loaderData:t}=ut(`useMatches`);return S.useMemo(()=>e.map(e=>I(e,t)),[e,t])}function gt(){let e=S.useContext(Le),t=ut(`useRouteError`),n=ft(`useRouteError`);return e===void 0?t.errors?.[n]:e}function _t(){let{router:e}=lt(`useNavigate`),t=ft(`useNavigate`),n=S.useRef(!1);return qe(()=>{n.current=!0}),S.useCallback(async(r,i={})=>{D(n.current,Ke),n.current&&(typeof r==`number`?await e.navigate(r):await e.navigate(r,{fromRouteId:t,...i}))},[e,t])}var vt={};function yt(e,t,n){!t&&!vt[e]&&(vt[e]=!0,D(!1,n))}S.useOptimistic,S.memo(bt);function bt({routes:e,future:t,state:n,isStatic:r,onError:i}){return et(e,void 0,{state:n,isStatic:r,onError:i,future:t})}function xt({to:e,replace:t,state:n,relative:r}){E(We(),` may be used only in the context of a component.`);let{static:i}=S.useContext(Pe);D(!i,` 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:a}=S.useContext(Ie),{pathname:o}=Ge(),s=Je(),c=ve(e,_e(a),o,r===`path`),l=JSON.stringify(c);return S.useEffect(()=>{s(JSON.parse(l),{replace:t,state:n,relative:r})},[s,l,r,t,n]),null}function St(e){return Ze(e.context)}function Ct(e){E(!1,`A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .`)}function wt({basename:e=`/`,children:t=null,location:n,navigationType:r=`POP`,navigator:i,static:a=!1,unstable_useTransitions:o}){E(!We(),`You cannot render a inside another . You should never have more than one in your app.`);let s=e.replace(/^\/*/,`/`),c=S.useMemo(()=>({basename:s,navigator:i,static:a,unstable_useTransitions:o,future:{}}),[s,i,a,o]);typeof n==`string`&&(n=M(n));let{pathname:l=`/`,search:u=``,hash:d=``,state:f=null,key:p=`default`,unstable_mask:m}=n,h=S.useMemo(()=>{let e=de(l,s);return e==null?null:{location:{pathname:e,search:u,hash:d,state:f,key:p,unstable_mask:m},navigationType:r}},[s,l,u,d,f,p,r,m]);return D(h!=null,` is not able to match the URL "${l}${u}${d}" because it does not start with the basename, so the won't render anything.`),h==null?null:S.createElement(Pe.Provider,{value:c},S.createElement(Fe.Provider,{children:t,value:h}))}function Tt({children:e,location:t}){return $e(Et(e),t)}function Et(e,t=[]){let n=[];return S.Children.forEach(e,(e,r)=>{if(!S.isValidElement(e))return;let i=[...t,r];if(e.type===S.Fragment){n.push.apply(n,Et(e.props.children,i));return}E(e.type===Ct,`[${typeof e.type==`string`?e.type:e.type.name}] is not a component. All component children of must be a or `),E(!e.props.index||!e.props.children,`An index route cannot have child routes.`);let a={id:e.props.id||i.join(`-`),caseSensitive:e.props.caseSensitive,element:e.props.element,Component:e.props.Component,index:e.props.index,path:e.props.path,middleware:e.props.middleware,loader:e.props.loader,action:e.props.action,hydrateFallbackElement:e.props.hydrateFallbackElement,HydrateFallback:e.props.HydrateFallback,errorElement:e.props.errorElement,ErrorBoundary:e.props.ErrorBoundary,hasErrorBoundary:e.props.hasErrorBoundary===!0||e.props.ErrorBoundary!=null||e.props.errorElement!=null,shouldRevalidate:e.props.shouldRevalidate,handle:e.props.handle,lazy:e.props.lazy};e.props.children&&(a.children=Et(e.props.children,i)),n.push(a)}),n}var Dt=`get`,Ot=`application/x-www-form-urlencoded`;function kt(e){return typeof HTMLElement<`u`&&e instanceof HTMLElement}function At(e){return kt(e)&&e.tagName.toLowerCase()===`button`}function jt(e){return kt(e)&&e.tagName.toLowerCase()===`form`}function Mt(e){return kt(e)&&e.tagName.toLowerCase()===`input`}function Nt(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function Pt(e,t){return e.button===0&&(!t||t===`_self`)&&!Nt(e)}var Ft=null;function It(){if(Ft===null)try{new FormData(document.createElement(`form`),0),Ft=!1}catch{Ft=!0}return Ft}var Lt=new Set([`application/x-www-form-urlencoded`,`multipart/form-data`,`text/plain`]);function Rt(e){return e!=null&&!Lt.has(e)?(D(!1,`"${e}" is not a valid \`encType\` for \`
\`/\`\` and will default to "${Ot}"`),null):e}function zt(e,t){let n,r,i,a,o;if(jt(e)){let o=e.getAttribute(`action`);r=o?de(o,t):null,n=e.getAttribute(`method`)||Dt,i=Rt(e.getAttribute(`enctype`))||Ot,a=new FormData(e)}else if(At(e)||Mt(e)&&(e.type===`submit`||e.type===`image`)){let o=e.form;if(o==null)throw Error(`Cannot submit a +
+
- {t(section.titleKey)} + {t('sidebar.settings')}
- {section.items + {ADMIN_ITEMS .filter((item) => !item.adminOnly || user?.role === 'admin') .map((item) => ( isActive ? { - background: 'var(--acl2)', - color: 'var(--ac)', - boxShadow: 'inset 3px 0 0 var(--ac)', - } : { - color: 'var(--t2)', - }} + className={linkCls()} + style={({ isActive }) => linkStyle(isActive)} > {item.icon} {t(item.labelKey)} ))} -
- ))} + + ) : ( + NAV_ITEMS + .filter((item) => !item.adminOnly || user?.role === 'admin') + .map((item) => ( + linkStyle(isActive)} + > + {item.icon} + {t(item.labelKey)} + + )) + )} {/* Footer */} @@ -142,6 +156,7 @@ export default function Sidebar() { className="p-2.5 rounded-xl transition-all duration-200" style={{ color: 'var(--t3)' }} title={isDark ? t('sidebar.lightMode') : t('sidebar.darkMode')} + aria-label={isDark ? t('sidebar.lightMode') : t('sidebar.darkMode')} onMouseEnter={(e) => { e.currentTarget.style.background = 'var(--bg3)'; e.currentTarget.style.color = 'var(--t1)'; }} onMouseLeave={(e) => { e.currentTarget.style.background = ''; e.currentTarget.style.color = 'var(--t3)'; }} > @@ -152,6 +167,7 @@ export default function Sidebar() { className="p-2.5 rounded-xl transition-all duration-200 flex items-center gap-1" style={{ color: 'var(--t3)' }} title={lang === 'pt' ? 'English' : 'Português'} + aria-label={lang === 'pt' ? 'English' : 'Português'} onMouseEnter={(e) => { e.currentTarget.style.background = 'var(--bg3)'; e.currentTarget.style.color = 'var(--t1)'; }} onMouseLeave={(e) => { e.currentTarget.style.background = ''; e.currentTarget.style.color = 'var(--t3)'; }} > @@ -170,11 +186,23 @@ export default function Sidebar() {

+ @@ -738,6 +726,7 @@ export default function ChatPage() { className="p-1.5 rounded cursor-pointer" style={{ background: settingsOpen ? 'var(--bg3)' : 'transparent', color: 'var(--t2)' }} title={t('chat.settings')} + aria-label={t('chat.settings')} > @@ -748,6 +737,7 @@ export default function ChatPage() { className="p-1.5 rounded cursor-pointer" style={{ color: 'var(--t2)' }} title={t('chat.newChatTitle')} + aria-label={t('chat.newChatTitle')} > @@ -898,6 +888,7 @@ export default function ChatPage() { className="p-2 rounded cursor-pointer flex-shrink-0" style={{ color: 'var(--t3)', background: 'var(--bg2)', border: '1px solid var(--bd)' }} title={t('chat.attachFile')} + aria-label={t('chat.attachFile')} > @@ -934,6 +925,7 @@ export default function ChatPage() { opacity: sending ? 0.6 : 1, }} title={t('chat.send')} + aria-label={t('chat.send')} > {sending ? : } diff --git a/frontend-react/src/pages/DownloadsPage.tsx b/frontend-react/src/pages/DownloadsPage.tsx deleted file mode 100644 index 83d2679..0000000 --- a/frontend-react/src/pages/DownloadsPage.tsx +++ /dev/null @@ -1,453 +0,0 @@ -import { useState, useEffect, useCallback } from 'react'; -import { - FolderDown, ChevronRight, RefreshCw, Download, FileText, Lock, Globe, - Server, Activity, Database, Package, BarChart3, AlertCircle, Loader2, Filter, -} from 'lucide-react'; -import client from '@/api/client'; -import { useI18n } from '@/i18n'; - -/* ── Types ── */ - -interface Report { - id: string; - tenancy_name: string; - status: string; - level: number | null; - created_at: string; - completed_at: string | null; -} - -interface ReportFile { - id: string; - file_name: string; - file_type: string; - file_category: string; - file_size: number; -} - -/* ── Helpers ── */ - -function extractSection(name: string): string { - const m = name.match(/^(?:cis|obp)_([A-Za-z_]+?)_\d/); - if (m) return m[1].replace(/_/g, ' '); - if (name.includes('summary')) return 'Summary'; - if (name.includes('error')) return 'Error'; - return 'Other'; -} - -function formatSize(bytes: number): string { - if (bytes < 1024) return `${bytes} B`; - if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; - return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; -} - -function fileExt(name: string): string { - return (name.split('.').pop() || '').toLowerCase(); -} - -const extColors: Record = { - html: '#e44d26', - csv: '#217346', - json: '#f5a623', - xlsx: '#217346', - txt: '#6b7280', - pdf: '#dc2626', -}; - -const sectionIcons: Record = { - 'Identity and Access Management': Lock, - 'Networking': Globe, - 'Compute': Server, - 'Logging and Monitoring': Activity, - 'Storage Object Storage': Database, - 'Storage Block Volumes': Database, - 'Storage File Storage Service': Database, - 'Asset Management': Package, - 'Summary': BarChart3, - 'Error': AlertCircle, - 'Other': FileText, -}; - -function sectionSort(a: string, b: string): number { - if (a === 'Summary') return -1; - if (b === 'Summary') return 1; - if (a === 'Other') return 1; - if (b === 'Other') return -1; - return a.localeCompare(b); -} - -function groupBySection(files: ReportFile[]): Record { - const map: Record = {}; - for (const f of files) { - const sec = extractSection(f.file_name); - (map[sec] ||= []).push(f); - } - return map; -} - -/* ── Component ── */ - -export default function DownloadsPage() { - const { t } = useI18n(); - const [reports, setReports] = useState([]); - const [expandedRid, setExpandedRid] = useState(null); - const [filesCache, setFilesCache] = useState>({}); - const [loadingFiles, setLoadingFiles] = useState(null); - const [tenancyFilter, setTenancyFilter] = useState(''); - const [sectionFilter, setSectionFilter] = useState(''); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(''); - - const token = localStorage.getItem('t') || ''; - - const loadReports = useCallback(async () => { - setLoading(true); - setError(''); - try { - const data = await client.get('/reports') as unknown as Report[]; - setReports(data.filter((r) => r.status === 'completed')); - } catch (err) { - setError(err instanceof Error ? err.message : t('dl.loadError')); - } finally { - setLoading(false); - } - }, []); - - useEffect(() => { loadReports(); }, [loadReports]); - - const handleRefresh = useCallback(() => { - setFilesCache({}); - setExpandedRid(null); - setSectionFilter(''); - loadReports(); - }, [loadReports]); - - const toggleExpand = useCallback(async (rid: string) => { - if (expandedRid === rid) { - setExpandedRid(null); - return; - } - setExpandedRid(rid); - if (!filesCache[rid]) { - setLoadingFiles(rid); - try { - const files = await client.get(`/reports/${rid}/files`) as unknown as ReportFile[]; - setFilesCache((prev) => ({ ...prev, [rid]: files })); - } catch { - setFilesCache((prev) => ({ ...prev, [rid]: [] })); - } finally { - setLoadingFiles(null); - } - } - }, [expandedRid, filesCache]); - - const handleDownload = useCallback((rid: string, fid: string) => { - window.open(`/api/reports/${rid}/files/${fid}/download?token=${token}`, '_blank'); - }, [token]); - - /* Derived data */ - const tenancies = [...new Set(reports.map((r) => r.tenancy_name))].sort(); - const filtered = tenancyFilter ? reports.filter((r) => r.tenancy_name === tenancyFilter) : reports; - - /* Sections available in expanded report (for filter) */ - const expandedFiles = expandedRid ? filesCache[expandedRid] : null; - const expandedSections = expandedFiles - ? [...new Set(expandedFiles.map((f) => extractSection(f.file_name)))].sort(sectionSort) - : []; - - return ( -
- {/* Header */} -
-
- -
-
-

{t('dl.title')}

-
{t('dl.subtitle')}
-
-
- {filtered.length} {t('dl.reports')} -
- - {/* Toolbar */} -
-
- {/* Tenancy filter */} -
- - -
- - {/* Section filter (only when expanded) */} - {expandedRid && expandedSections.length > 1 && ( - - )} - - - {filtered.length} {t('dl.reports')} - -
- - -
- - {/* Error */} - {error && ( -
- - {error} -
- )} - - {/* Loading */} - {loading && !reports.length && ( -
- - {t('dl.loading')} -
- )} - - {/* Empty state */} - {!loading && !filtered.length && ( -
-
- -

{t('dl.noReports')}

-

{t('dl.noReportsHint')}

-
-
- )} - - {/* Report list */} -
- {filtered.map((report) => { - const isExpanded = expandedRid === report.id; - const files = filesCache[report.id]; - const isLoading = loadingFiles === report.id; - const fileCount = files ? files.length : null; - - return ( -
- {/* Report header */} - - - {/* Expanded file list */} - {isExpanded && ( -
- {isLoading ? ( -
- - {t('dl.loadingFiles')} -
- ) : !files || !files.length ? ( -

- {t('dl.noFiles')} -

- ) : ( - - )} -
- )} -
- ); - })} -
-
- ); -} - -/* ── FileListGrouped sub-component ── */ - -function FileListGrouped({ - files, - reportId, - sectionFilter, - onDownload, -}: { - files: ReportFile[]; - reportId: string; - sectionFilter: string; - onDownload: (rid: string, fid: string) => void; -}) { - const { t } = useI18n(); - const grouped = groupBySection(files); - let sections = Object.keys(grouped).sort(sectionSort); - if (sectionFilter) sections = sections.filter((s) => s === sectionFilter); - - return ( -
- {/* Header */} -
- - {files.length} {t('dl.filesInSections').replace('{0}', String(Object.keys(grouped).length))} - -
- - {sections.map((sec) => { - const secFiles = grouped[sec]; - const Icon = sectionIcons[sec] || FileText; - return ( -
- {/* Section header */} -
- - {sec} - ({secFiles.length}) -
- - {/* File grid */} -
- {secFiles.map((f) => { - const ext = fileExt(f.file_name); - const color = extColors[ext] || 'var(--ac)'; - return ( - - ); - })} -
-
- ); - })} -
- ); -} diff --git a/frontend-react/src/pages/ExplorerPage.tsx b/frontend-react/src/pages/ExplorerPage.tsx index 3438d52..92bed53 100644 --- a/frontend-react/src/pages/ExplorerPage.tsx +++ b/frontend-react/src/pages/ExplorerPage.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useCallback, useRef, useMemo } from 'react'; +import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react'; import { Search, ChevronRight, ChevronDown, FolderTree, RefreshCw, Loader2, Server, HardDrive, Layers, Network, GitBranch, Shield, ShieldCheck, @@ -186,7 +186,7 @@ function KV({ label, value, mono }: { label: string; value: React.ReactNode; mon } /* ── Compartment Tree Node ────────────────────────────────────────────────── */ -function TreeItem({ +const TreeItem = React.memo(function TreeItem({ node, selected, expanded, onSelect, onToggle, }: { node: TreeNode; @@ -236,7 +236,7 @@ function TreeItem({ )}
); -} +}); /* ── Region Multi-Select ──────────────────────────────────────────────────── */ function RegionSelector({ @@ -381,7 +381,7 @@ function AdbCard({ r, configId, regions, onReload }: { r: ResourceItem; configId const resp = await fetch('https://api.ipify.org?format=json'); const data = await resp.json(); const myIp = data.ip + '/32'; - const ip = prompt('IP para liberar acesso (CIDR):', myIp); + const ip = prompt(t('exp.ipPrompt'), myIp); if (!ip) return; setUpdating(true); await explorerApi.adbUpdateNetwork(r.id, ip.trim(), configId, region); @@ -599,7 +599,7 @@ function SecurityListCard({ r, configId, regions, onReload }: { r: ResourceItem; const resp = await fetch('https://api.ipify.org?format=json'); const data = await resp.json(); const myIp = data.ip + '/32'; - setCidr(myIp); setDesc('Acesso temporário - ' + myIp); + setCidr(myIp); setDesc(t('exp.tempAccess') + myIp); setFormMsg({ type: 's', text: t('exp.ipDetected').replace('{0}', myIp) }); } catch { setFormMsg({ type: 'e', text: t('exp.ipDetectFailed') }); } }; @@ -641,7 +641,7 @@ function SecurityListCard({ r, configId, regions, onReload }: { r: ResourceItem; )} - +