diff --git a/README.md b/README.md index 27c2fd7..d902110 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@

- Version + Version Python FastAPI OCI @@ -23,7 +23,7 @@ AI Agent β€” Infrastructure & Security Engineer is a self-hosted web application that automates **CIS Oracle Cloud Infrastructure Foundations Benchmark 3.0** compliance checks, powered by **OCI Generative AI** for intelligent analysis and an **MCP (Model Context Protocol)** server architecture for extensible task execution. -The platform combines security compliance scanning, AI-powered chat with **RAG (Retrieval-Augmented Generation)**, infrastructure exploration, and vector-based knowledge storage into a single, containerized solution with an **Oracle Dark Premium** theme (light/dark modes), **KPI dashboard** with compliance gauge, and **Chart.js** visualizations. +The platform combines security compliance scanning, AI-powered chat with **RAG (Retrieval-Augmented Generation)**, infrastructure exploration, and vector-based knowledge storage into a single, containerized solution with a **dual frontend** (legacy SPA + React SPA at `/app/`), **Oracle Dark Premium** theme (light/dark modes), **KPI dashboard** with compliance gauge, **i18n** (pt/en), and **Chart.js** visualizations. --- @@ -127,11 +127,26 @@ The platform combines security compliance scanning, AI-powered chat with **RAG ( - **Level 2**: Advanced security controls that may restrict functionality or require more effort to implement. Recommended for high-security environments. - **Multiple output formats**: HTML summary, CSV per section/finding, JSON summary, optional XLSX - **Report history**: full execution history with status, tenancy filter, and download actions (HTML/JSON) in the Reports tab +- **Delete reports**: remove completed reports and their associated files from the system - **File browser**: dedicated Downloads tab with expandable cards per completed report, files grouped by category (Summary, CIS Findings, OBP, Raw Data, etc.) +- **Embed individual files**: embed any report file (CSV, TXT, JSON, PDF) directly into the ADB vector store for RAG enrichment - Region filtering with multi-select - Real-time progress tracking with phase-based progress bar - **CIS Engine auto-update**: check for new versions of `cis_reports.py` from Oracle's GitHub repository and update with one click (admin only). Custom patches are automatically reapplied after update +### πŸ“‹ LAD A-Team CIS Compliance Report +- **Professional compliance report** following Oracle Cloud Security Assessment format (cover page, purpose statement, disclaimer, copyright) +- **Table of Contents** with links to each section and recommendation +- **Security Overview**: 7 Oracle security pillars (Customer Isolation, Data Encryption, Data Management, etc.) +- **CIS Assessment Summary**: table with Domains / Total Controls / Failed / Passed per section +- **CIS Benchmark Recommendation Summary**: full table with Item / Description / Status (OK/FAILED) +- **Detailed findings**: per-recommendation cards with compliance percentage bar, result description, and remediation steps +- **RAG-powered remediation**: enriched remediation steps fetched from ADB vector store (`CISRECOM` table) using cosine similarity search β€” filters by exact recommendation number, extracts only remediation section from matched chunks +- **Affected resources CSV links**: non-compliant findings show a clickable link to download the CSV with affected resource details (authenticated via JWT token) +- **Download PDF**: `window.print()` with CSS `@page A4` formatting for professional PDF output +- **Caching**: generated HTML cached to disk, subsequent views are instant; "Regenerar" button clears cache and re-generates +- **Collapsible sections**: HTML report and compliance report start minimized, expandable on demand + ### πŸ›‘οΈ Built-in CIS MCP Server (Granular Per-Section) - **Auto-registered** CIS Compliance Scanner MCP server β€” available out of the box - **12 granular tools** instead of monolithic full-tenancy scan: @@ -418,16 +433,27 @@ Allow group to read buckets in compartment ``` oci-cis-agent/ β”œβ”€β”€ backend/ -β”‚ β”œβ”€β”€ app.py # FastAPI application (~5900 lines) +β”‚ β”œβ”€β”€ app.py # FastAPI application (~6700 lines) β”‚ β”œβ”€β”€ cis_reports.py # Oracle CIS Benchmark checker (6660 lines, report engine) β”‚ β”œβ”€β”€ mcp_cis_server.py # MCP server with 12 granular CIS tools (~700 lines) β”‚ β”œβ”€β”€ gen_tf_reference.py # OCI Terraform provider resource catalog generator β”‚ β”œβ”€β”€ Dockerfile # Python 3.12 + OCI CLI + Terraform CLI β”‚ └── requirements.txt # Dependencies β”œβ”€β”€ frontend/ -β”‚ └── index.html # SPA with Oracle Dark Premium theme (~3200 lines) +β”‚ └── index.html # Legacy SPA with Oracle Dark Premium theme (~2800 lines) +β”œβ”€β”€ frontend-react/ +β”‚ β”œβ”€β”€ src/ # React 19 SPA (TypeScript, 42 source files, ~12500 lines) +β”‚ β”‚ β”œβ”€β”€ pages/ # 8 page components (Dashboard, Explorer, Reports, Chat, Terraform, etc.) +β”‚ β”‚ β”œβ”€β”€ api/ # API client + endpoint modules +β”‚ β”‚ β”œβ”€β”€ stores/ # Zustand auth store +β”‚ β”‚ β”œβ”€β”€ hooks/ # Custom hooks (theme, i18n) +β”‚ β”‚ └── i18n/ # Internationalization (pt/en, 625 keys) +β”‚ └── dist/ # Built SPA served at /app/ +β”œβ”€β”€ scripts/ +β”‚ β”œβ”€β”€ reportgenerator.js # n8n reference script (report layout) +β”‚ └── *.pdf # Oracle CIS report PDF template β”œβ”€β”€ nginx/ -β”‚ └── default.conf # Reverse proxy config +β”‚ └── default.conf # Reverse proxy (legacy / + React /app/ + API /api/) β”œβ”€β”€ docker-compose.yml # Orchestration β”œβ”€β”€ logo.svg # Project logo (Oracle AI Robot) β”œβ”€β”€ .env.example # Environment template @@ -562,6 +588,7 @@ oci-cis-agent/ |--------|----------|-------------| | GET | `/api/embeddings/preview/{rid}` | Preview report chunks before embedding (with tenancy/regions/compartments) | | POST | `/api/embeddings/report/{rid}` | Generate embeddings from CIS report (chunked by section, accepts `table_name`, `report_date`) | +| POST | `/api/embeddings/report/{rid}/file/{fid}` | Embed individual report file (CSV/TXT/JSON/PDF) into vector store | | POST | `/api/embeddings/upload` | Upload file (.txt/.pdf/.csv/.json/.md) and generate embeddings | | POST | `/api/embeddings/upload-url` | Import URL (web page or PDF), extract text, and generate embeddings | | POST | `/api/embeddings/consult` | Query embeddings via vector search + GenAI (natural language Q&A) | @@ -601,11 +628,15 @@ oci-cis-agent/ | DELETE | `/api/chat/{sid}` | Delete chat session and messages | | POST | `/api/reports/run` | Execute CIS report | | GET | `/api/reports` | List reports | +| DELETE | `/api/reports/{rid}` | Delete report and associated files | | GET | `/api/reports/{id}/html` | View HTML report | | GET | `/api/reports/{id}/download` | Download report | | GET | `/api/reports/{rid}/summary` | Report KPI summary (score, pass/fail, sections) | | GET | `/api/reports/{rid}/files` | List report files by category | | GET | `/api/reports/{rid}/files/{fid}/download` | Download individual report file | +| GET | `/api/reports/{rid}/compliance-report` | Serve cached LAD A-Team compliance report HTML | +| POST | `/api/reports/{rid}/compliance-report/generate` | Generate compliance report (background, with RAG) | +| GET | `/api/reports/{rid}/compliance-report/status` | Check if compliance report is ready | ### CIS Engine @@ -680,7 +711,7 @@ All models include OCID mapping for `us-ashburn-1`. For other regions, use the " | Component | Technology | |-----------|-----------| | Backend | Python 3.12, FastAPI 0.115, Uvicorn | -| Frontend | Vanilla JS SPA, Oracle Dark Premium theme, Chart.js | +| Frontend | React 19 SPA (Vite + TypeScript) at `/app/` + legacy Vanilla JS SPA, Oracle Dark Premium theme, Chart.js, i18n (pt/en) | | Auth | JWT + TOTP MFA + RBAC | | Database | SQLite (WAL mode) | | OCI SDK | `oci` 2.133.0, `oci-cli` | @@ -697,6 +728,7 @@ All models include OCID mapping for `us-ashburn-1`. For other regions, use the " | Version | Date | Changes | |---------|------|---------| +| **v2.7** | 2026-03 | **LAD A-Team CIS Compliance Report**: professional Oracle-format compliance report with cover page, TOC, Security Overview (7 pillars), CIS Assessment Summary, detailed findings with compliance % bars, RAG-powered remediation from ADB vector store (`CISRECOM` table with strict recommendation number filtering), affected resources CSV download links per non-compliant finding (JWT auth via query param). **React SPA**: 16-page React 19 frontend at `/app/` (TypeScript, Vite, Zustand, 42 source files), dual-served alongside legacy SPA via nginx. **i18n**: full internationalization with 625 keys (pt/en), language switcher persisted in localStorage. **Report management**: delete reports endpoint, embed individual report files (CSV/TXT/JSON/PDF) into vector store. **Terraform ZIP download**: replaced individual file downloads with client-side ZIP generation (pure JS, no external lib). **Explorer UX**: silent polling during start/stop actions eliminates flickering (resources update in-place without clearing the list). **Report sections collapsed by default**: HTML report and compliance report iframes start minimized. | | **v2.6** | 2026-03 | **Rename**: app renamed to "AI Agent β€” Infrastructure & Security Engineer". **Session Token Auth**: OCI session token authentication support (type selector API Key/Session Token, conditional form fields, collapsible credential form, type tags in credentials table, `SecurityTokenSigner` backend). **Terraform Workspaces submenu**: sidebar sub-tab listing all workspaces grouped by date with accordion UI, filtered by existing chat sessions (INNER JOIN), actions (plan/apply/destroy/delete/open). **Retry on failure**: "Reenviar" button on failed messages across Chat Agent, Terraform Agent, and Prompt Generator. **Terraform fixes**: `f2` undefined variable in dedup logic, `oci_core_services` false positive validation (data sources excluded), `S.tfCode` reconstruction from `S.tfFiles` for Re-plan, auto-fix button visible without pre-selected model, DRG attachment rules in system prompt. **Resilience**: stuck workspace recovery on container restart (planning/applying/destroying β†’ reset + lock file cleanup). | | **v2.5** | 2026-03 | **Explorer Expansion**: KPI stats bar with per-category resource counts (cached in SQLite `explorer_counts_cache`, background refresh on compartment access), enhanced views for VCNs/Subnets/Load Balancers/Buckets with expandable cards, start/stop for DB Systems + MySQL + Container Instances (5 resource types total), dead state filtering (`TERMINATED`/`DELETED`/`DELETING` hidden across all 36 endpoints), wider compartment tree (280px), IAM excluded from compartment totals. **Prompt Generator Intelligence**: same knowledge pipeline as Terraform Agent β€” auto-detects resource types from user message, fetches official docs (Example Usage + Arguments) from GitHub cached in SQLite, uses compact categorized resource reference. **Network Firewall fix**: `NetworkFirewallPolicySummaryCollection` not iterable β€” added `.items` accessor for collection objects. | | **v2.4** | 2026-03 | **Terraform Safety & UX**: region safety guard (plan blocked if model omits `variable "region"` β€” prevents wrong-region provisioning), rollback button on apply failure (manual `terraform destroy` with "ROLLBACK" confirmation), SSH public key field in OCI configs (auto-injected into `terraform.tfvars` for compute instances), reasoning_effort uppercase fix for OCI SDK, split-panel layout (terminal right 40%, files/plan/resources below chat 60%), plan button persists after destroy. **Terraform tfvars**: expanded `oci_var_map` with `ssh_public_key`/`ssh_authorized_keys` auto-mapping from OCI config | diff --git a/backend/app.py b/backend/app.py index 29d16cc..915f517 100644 --- a/backend/app.py +++ b/backend/app.py @@ -17,7 +17,7 @@ from fastapi import ( Query, Body, BackgroundTasks ) from fastapi.middleware.cors import CORSMiddleware -from fastapi.responses import JSONResponse, FileResponse, StreamingResponse +from fastapi.responses import JSONResponse, FileResponse, StreamingResponse, HTMLResponse from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from pydantic import BaseModel import jwt as pyjwt @@ -3566,6 +3566,38 @@ async def embed_report(rid: str, req: dict, bg: BackgroundTasks, u=Depends(requi _audit(u["id"], u["username"], "embed_report", rid, f"{label}, tenancy={tenancy}") return {"ok": True, "message": f"Embedding de {len(documents)} seΓ§Γ£o(Γ΅es) iniciado (tenancy: {tenancy})", "sections": len(documents), "tenancy": tenancy} +@app.post("/api/embeddings/report/{rid}/file/{fid}") +async def embed_report_file(rid: str, fid: str, req: dict, bg: BackgroundTasks, u=Depends(require("admin","user"))): + """Embed a specific report file (CSV, JSON, TXT, etc.) into ADB vector store.""" + vid = req.get("adb_config_id") + if not vid: raise HTTPException(400, "adb_config_id is required") + with db() as c: + r = c.execute("SELECT tenancy_name, config_id FROM reports WHERE id=? AND status='completed'", (rid,)).fetchone() + if not r: raise HTTPException(404, "Report not found or not completed") + f = c.execute("SELECT * FROM report_files WHERE id=? AND report_id=?", (fid, rid)).fetchone() + if not f: raise HTTPException(404, "File not found") + p = Path(f["file_path"]) + if not p.exists(): raise HTTPException(404, "File not found on disk") + fname = f["file_name"].lower() + allowed = ('.txt', '.csv', '.json', '.md', '.pdf') + if not any(fname.endswith(ext) for ext in allowed): + raise HTTPException(400, f"Formatos aceitos para embedding: {', '.join(allowed)}") + raw = p.read_bytes() + if fname.endswith('.pdf'): + content = _extract_pdf_text(raw) + else: + content = raw.decode("utf-8", errors="replace") + if not content.strip(): raise HTTPException(400, "File is empty") + documents = _chunk_text_file(content, f["file_name"]) + if not documents: raise HTTPException(400, "No content chunks found") + cfg, gc = _get_adb_and_genai(vid, oci_config_id=r.get("config_id")) + target_table = req.get("table_name") or None + tenancy = r["tenancy_name"] or "unknown" + bg.add_task(_ingest_documents_task, cfg, gc, documents, u["id"], u["username"], + table_name=target_table, tenancy=tenancy) + _audit(u["id"], u["username"], "embed_report_file", f"{rid}/{fid}", f"{f['file_name']}, {len(documents)} chunks, tenancy={tenancy}") + return {"ok": True, "message": f"Embedding de {f['file_name']} iniciado ({len(documents)} chunks)", "chunks": len(documents)} + def _extract_pdf_text(file_bytes: bytes) -> str: """Extract text from a PDF file using PyPDF2 or pdfplumber.""" try: @@ -3983,6 +4015,23 @@ async def get_report(rid, u=Depends(current_user)): if u["role"]!="admin" and r["user_id"]!=u["id"]: raise HTTPException(403) return dict(r) +@app.delete("/api/reports/{rid}") +async def delete_report(rid: str, u=Depends(current_user)): + with db() as c: + r = c.execute("SELECT id, user_id, status, json_path, html_path 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) + if r["status"] == "running": raise HTTPException(400, "Cannot delete a running report") + # Remove associated files + for path_col in ("json_path", "html_path"): + p = r[path_col] + if p and Path(p).exists(): + try: Path(p).unlink() + except OSError: pass + c.execute("DELETE FROM reports WHERE id=?", (rid,)) + _audit(u["id"], u["username"], "delete_report", rid) + return {"ok": True} + @app.get("/api/reports/{rid}/summary") async def report_summary(rid: str, u=Depends(current_user)): with db() as c: @@ -4064,6 +4113,689 @@ async def report_html(rid): 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") + +# ═══════════════════════════════════════════════════════════════════════ +# LAD A-Team CIS Compliance Report +# ═══════════════════════════════════════════════════════════════════════ + +def _extract_section(text: str, section_name: str) -> str: + """Extract a named section from a CIS Benchmark chunk (Description, Remediation, etc.).""" + import re as _re + pattern = _re.compile(r'\n' + _re.escape(section_name) + r'[:\s]*\n', _re.IGNORECASE) + match = pattern.search(text) + if not match: + return "" + start = match.end() + end_match = _re.search( + r'\n(?:Description|Rationale|Audit|Remediation|Additional Information|CIS Controls|Controls\s*\nVersion|References|Default Value|Impact|Recommendation|Profile Applicability)[:\s]*\n', + text[start:], _re.IGNORECASE + ) + if end_match: + return text[start:start + end_match.start()].strip() + return text[start:].strip() + + +def _extract_remediation_section(text: str) -> str: + """Extract only the Remediation section.""" + return _extract_section(text, "Remediation") + + +def _extract_description_section(text: str) -> str: + """Extract the Description section (what this CIS finding is about).""" + return _extract_section(text, "Description") + + +def _format_remediation_html(text: str) -> str: + """Format remediation text with code snippets and proper indentation.""" + import re as _re + if not text: + return "" + + def esc(v): + return (v or "").replace("&", "&").replace("<", "<").replace(">", ">").replace('"', """) + + lines = text.split('\n') + html_parts = [] + in_code = False + code_buf = [] + + for line in lines: + stripped = line.strip() + # Detect CLI commands and policy statements + is_code = ( + stripped.startswith('oci ') or + stripped.startswith('Allow ') or + stripped.startswith('Define ') or + stripped.startswith('Endorse ') or + stripped.startswith('Admit ') or + stripped.startswith('terraform ') or + stripped.startswith('curl ') or + stripped.startswith('openssl ') or + stripped.startswith('ssh ') or + stripped.startswith('python') or + stripped.startswith('$ ') or + stripped.startswith('# ') and not stripped.startswith('# ') and len(stripped) < 5 or + (in_code and stripped and not stripped[0].isupper() and not stripped.startswith('http')) + ) + + if is_code: + if not in_code: + in_code = True + code_buf = [] + code_buf.append(stripped) + else: + if in_code and code_buf: + html_parts.append('

' + esc('\n'.join(code_buf)) + '
') + code_buf = [] + in_code = False + if stripped: + # Numbered steps + step_match = _re.match(r'^(\d+)\.\s+(.+)', stripped) + if step_match: + html_parts.append(f'
{step_match.group(1)}. {esc(step_match.group(2))}
') + else: + html_parts.append(f'

{esc(stripped)}

') + + # Flush remaining code + if code_buf: + html_parts.append('
' + esc('\n'.join(code_buf)) + '
') + + return '\n'.join(html_parts) + + +def _search_cis_remediation(title: str, rec_num: str, user_id: str) -> dict: + """Search CIS Recommendations in vector DB. Returns {'description': str, 'remediation': str}.""" + try: + with db() as c: + adb_cfgs = c.execute( + "SELECT * FROM adb_vector_configs WHERE user_id=? AND is_active=1", (user_id,) + ).fetchall() + if not adb_cfgs: + return {"description": "", "remediation": ""} + query = f"Recommendation: {rec_num} {title} Remediation Audit From Console From Command Line" + for adb_cfg in adb_cfgs: + adb_cfg = dict(adb_cfg) + try: + # Resolve embedding config: use the ADB's own embedding model + emb_model = adb_cfg.get("embedding_model_id", "cohere.embed-v4.0") + + tables = _get_tables_for_config(adb_cfg["id"], active_only=True) + if not tables: + continue + + # Resolve GenAI config for embedding generation + genai_linked = None + if adb_cfg.get("genai_config_id"): + with db() as c: + row = c.execute("SELECT * FROM genai_configs WHERE id=?", (adb_cfg["genai_config_id"],)).fetchone() + if row: genai_linked = dict(row) + emb_genai = _resolve_embed_config(genai_cfg=genai_linked) + + # Search across cisrecom + section-specific tables + all_docs = [] + search_tables = [t["table_name"] for t in tables if "cisrecom" in t["table_name"].lower()] + if not search_tables: + search_tables = [t["table_name"] for t in tables] + + for tbl_name in search_tables: + try: + # Detect dimension per table + tbl_model = emb_model + try: + 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 + 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 + + if all_docs: + import re as _re + all_docs.sort(key=lambda d: d.get("distance", 999)) + # STRICT FILTER: only keep chunks that mention THIS exact recommendation number + exact_pattern = _re.compile( + r'(?:Recommendation[:\s]*' + _re.escape(rec_num) + r'|^' + _re.escape(rec_num) + r')\s', + _re.MULTILINE | _re.IGNORECASE + ) + matched = [d["content"] for d in all_docs + if d.get("content") and len(d["content"]) > 500 + and exact_pattern.search(d["content"])] + if matched: + best = matched[0] + desc = _extract_description_section(best) + rem = _extract_remediation_section(best) + # Try second chunk if first has no remediation + if not rem and len(matched) > 1: + rem = _extract_remediation_section(matched[1]) + return {"description": desc, "remediation": rem or best} + except Exception as e: + log.warning(f"CIS remediation search failed for {adb_cfg.get('config_name','?')}: {e}") + return {"description": "", "remediation": ""} + except Exception as e: + log.warning(f"CIS remediation search error: {e}") + return {"description": "", "remediation": ""} + + +def _build_compliance_html(filtered_rows: list, tenancy_name: str, extract_date: str, sections_data: dict, rid: str = "", file_id_map: dict = None) -> str: + """Build the full LAD A-Team CIS Compliance Report HTML.""" + import re as _re + from datetime import datetime as _dt + + now = _dt.utcnow() + months_pt = ["Janeiro","Fevereiro","MarΓ§o","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"] + month_year = f"{months_pt[now.month - 1]}, {now.year}" + + def esc(v): return (v or "").replace("&","&").replace("<","<").replace(">",">").replace('"',""") + def slug(s): return _re.sub(r"[^\w-]","", _re.sub(r"\s+","-",(s or "").strip().lower())) + + total_all = len(filtered_rows) + total_passed = sum(1 for r in filtered_rows if r["__compliant"]) + total_failed = total_all - total_passed + pct_global = round(total_passed / total_all * 100) if total_all else 0 + + # ── Domains table rows ── + domains_rows = "" + for sec_name, sd in sections_data.items(): + domains_rows += f'{esc(sec_name)}{sd["total"]}{sd["failed"]}{sd["passed"]}' + + # ── CIS Benchmark Summary table ── + summary_rows = "" + current_section = "" + for r in filtered_rows: + sec = r["Section"] + rec = r.get("Recommendation #", "") + title = r.get("Title", "") + is_compliant = r["__compliant"] + if sec != current_section: + current_section = sec + summary_rows += f'{esc(sec)}' + status_cls = "pass" if is_compliant else "fail" + status_txt = "OK" if is_compliant else "FAILED" + summary_rows += f'{esc(rec)}{esc(title)}{status_txt}' + + # ── Detailed sections ── + sections_html = "" + for sec_name, sd in sections_data.items(): + sec_id = f"section-{slug(sec_name)}" + sections_html += f'
' + sections_html += f'

{esc(sec_name)}

' + + for r in sd["items"]: + rec = r.get("Recommendation #", "") + title = r.get("Title", "") + rec_id = f"rec-{slug(sec_name)}-{slug(rec)}" + is_compliant = r["__compliant"] + findings = r.get("Findings", "").strip() + findings_num = r["__findings"] + total_items = r.get("Total", "").strip() + compliant_items = r.get("Compliant Items", "").strip() + pct = r["__pct"] + csv_remediation = (r.get("Remediation", "") or "").strip() + rag_data = r.get("__rag_remediation", {}) + if isinstance(rag_data, str): + rag_data = {"description": "", "remediation": rag_data} + rag_desc = (rag_data.get("description", "") or "").strip() + rag_rem = (rag_data.get("remediation", "") or "").strip() + + # Status line + if is_compliant: + result_html = f'
Result: Tenancy {esc(tenancy_name)}: Compliant
' + else: + detail = "" + if findings_num is not None and total_items: + detail = f" ({findings_num} of {esc(total_items)} items)" + result_html = f'
Result: Tenancy {esc(tenancy_name)}: Non-Compliant{detail}
' + + # Description (what this CIS finding is about) + description_html = "" + if not is_compliant and rag_desc: + description_html = f'

Description:

{esc(rag_desc)}

' + + # Remediation (only for non-compliant) β€” formatted with code snippets + remediation_html = "" + if not is_compliant: + rem_text = rag_rem or csv_remediation + if rem_text: + formatted = _format_remediation_html(rem_text) + remediation_html = f'

Remediation:

{formatted}
' + + # Compliance bar + bar_html = "" + if pct is not None: + cls = "bad" if pct <= 20 else "warn" if pct <= 60 else "ok" + bar_html = f'
{pct:.0f}% Compliant
' + + # Findings file link + findings_file_html = "" + csv_filename = (r.get("Filename", "") or "").strip() + if csv_filename and not is_compliant and file_id_map: + fid = file_id_map.get(csv_filename, "") + if fid and rid: + dl_url = f"/api/reports/{rid}/files/{fid}/download" + findings_file_html = f'
πŸ“„ {esc(csv_filename)}Affected resources details
' + + sections_html += f''' +
+

{esc(rec)} {esc(title)}

+ {bar_html} + {result_html} + {findings_file_html} + {description_html} + {remediation_html} +
''' + + sections_html += '
' + + # ── TOC ── + toc_lines = '
CIS Security Assessment Summary
' + toc_lines += '
CIS Benchmark Recommendation Summary
' + for sec_name, sd in sections_data.items(): + sec_id = f"section-{slug(sec_name)}" + toc_lines += f'
{esc(sec_name)}{sd["total"]}
' + for r in sd["items"]: + rec = r.get("Recommendation #", "-") + title = r.get("Title", "-") + rec_id = f"rec-{slug(sec_name)}-{slug(rec)}" + toc_lines += f'
{esc(rec)} {esc(title)}
' + + PURPOSE_TEXT = "The purpose of the cloud security assessment is to comprehensively evaluate the organization's security posture in its cloud environment, identifying vulnerabilities and gaps that could compromise data integrity, confidentiality, and availability. This assessment aims to enhance information security maturity, strengthen defenses against cyber threats, ensure compliance with regulations, and foster a culture of security awareness. Through periodic assessments, the organization proactively mitigates risks, ensuring continuous security for cloud-configured assets and maintaining stakeholders' trust in data protection capabilities." + DISCLAIMER_TEXT = "This document in any form, software or printed matter, contains proprietary information that is the exclusive property of Oracle. Your access to and use of this confidential material is subject to the terms and conditions of your Oracle software license and service agreement, which has been executed and with which you agree to comply. This document and information contained herein may not be disclosed, copied, reproduced or distributed to anyone outside Oracle without prior written consent of Oracle. This document is not part of your license agreement, nor can it be incorporated into any contractual agreement with Oracle or its subsidiaries or affiliates.\n\nThis document is for informational purposes only and is intended solely to assist you in planning for the implementation and upgrade of the product features described. It is not a commitment to deliver any material, code, or functionality, and should not be relied upon in making purchasing decisions." + SECURITY_OVERVIEW = """Oracle's mission is to build cloud infrastructure and platform services for your business to have effective and manageable security to run your mission-critical workloads and store your data with confidence. Oracle Cloud Infrastructure's security approach is based on seven core pillars: + +Customer Isolation β€” Allow customers to deploy their application and data assets in an environment that commits full isolation from other tenants and Oracle's staff. + +Data Encryption β€” Protect customer data at-rest and in-transit in a way that allows customers to meet their security and compliance requirements for cryptographic algorithms and key management. + +Security Controls β€” Offer customers effective and easy-to-use security management solutions that allow them to constrain access to their services and segregate operational responsibilities. + +Visibility β€” Offer customers comprehensive log data and security analytics that they can use to audit and monitor actions on their resources. + +Secure Hybrid Cloud β€” Enable customers to use their existing security assets, such as user accounts and policies, as well as third-party security solutions when accessing their cloud resources. + +High Availability β€” Offer fault-independent data centers that enable high availability scale-out architectures and are resilient against network attacks. + +Verifiably Secure Infrastructure β€” Follow rigorous processes and use effective security controls in all phases of cloud service development and operation.""" + + return f''' + + + +LAD A-Team CIS Compliance Report β€” {esc(tenancy_name)} + + + +
+ + +
+
ORACLE
+
+
Oracle Cloud Security
Assessment
+
{esc(tenancy_name)} – Oracle Cloud Infrastructure
+
+
Tenancy: {esc(tenancy_name)}
+
{esc(month_year)}, Version [1.0]
+ {f'
Extract date: {esc(extract_date)}
' if extract_date else ''} +
Copyright © {now.year}, Oracle and/or its affiliates
+
Confidential – Oracle Restricted
+
+
+ + +
+

Purpose Statement

+

{esc(PURPOSE_TEXT)}

+
+
+

Disclaimer

+

{esc(DISCLAIMER_TEXT)}

+
+ + +
+
ORACLE
+
Table of contents
+
+
{toc_lines}
+
+ + +
+

Security Overview

+

{SECURITY_OVERVIEW}

+
+ + +
+

CIS Security Assessment

+
CIS Oracle Cloud Infrastructure Foundations Benchmark v3.0.0
+

Number of controls per domain and non-compliant controls on tenancy {esc(tenancy_name)}:

+ + + {domains_rows} +
DOMAINSTOTAL CONTROLSFAILEDPASSED
+
Total: {total_all} • Passed: {total_passed} • Failed: {total_failed} • Compliance: {pct_global}%
+
+ + +
+

CIS Benchmark Recommendation Summary

+
Status per recommendation
+ + + {summary_rows} +
ItemDescriptionStatus
+
+ + +{sections_html} + +
+ + + + + + +''' + + +def _generate_compliance_report(rid: str, user_id: str, force_rag: bool = True) -> str: + """Generate and cache the LAD A-Team CIS Compliance Report HTML.""" + import csv as csvmod + import re as _re + + with db() as c: + report = c.execute("SELECT * FROM reports WHERE id=?", (rid,)).fetchone() + if not report: + raise HTTPException(404, "Report not found") + + rdir = REPORTS / rid + csv_path = rdir / "cis_summary_report.csv" + if not csv_path.exists(): + raise HTTPException(404, "cis_summary_report.csv not found") + + rows = [] + with open(csv_path, "r", encoding="utf-8") as f: + for row in csvmod.DictReader(f): + rows.append(row) + if not rows: + raise HTTPException(404, "No data in summary CSV") + + tenancy_name = report["tenancy_name"] or "Unknown" + extract_date = rows[0].get("extract_date", "") if rows else "" + + # Build filename β†’ file_id map for download links + file_id_map = {} + with db() as c: + rf = c.execute("SELECT id, file_name FROM report_files WHERE report_id=?", (rid,)).fetchall() + for f in rf: + file_id_map[f["file_name"]] = f["id"] + + ALLOWED_SECTIONS = { + "Asset Management", "Compute", "Identity and Access Management", + "Logging and Monitoring", "Networking", + "Storage - Block Volumes", "Storage - File Storage Service", "Storage - Object Storage", + } + + def norm_section(raw): + s = _re.sub(r"\s+", " ", (raw or "").strip()) + if _re.match(r"^Logging and Monitoring\s*\d*$", s, _re.I): return "Logging and Monitoring" + if _re.match(r"^Storage$", s, _re.I): return None + if _re.match(r"^Storage\s*-\s*Block Volumes$", s, _re.I): return "Storage - Block Volumes" + if _re.match(r"^Storage\s*-\s*File Storage Service$", s, _re.I): return "Storage - File Storage Service" + if _re.match(r"^Storage\s*-\s*Object Storage$", s, _re.I): return "Storage - Object Storage" + return s or None + + seen = set() + filtered = [] + for r in rows: + sec = norm_section(r.get("Section", "")) + if not sec or sec not in ALLOWED_SECTIONS: continue + key = f"{sec}||{r.get('Recommendation #', '')}||{r.get('Title', '')}".lower() + if key in seen: continue + seen.add(key) + r["Section"] = sec + status_raw = (r.get("Compliant", "") or "").strip().lower() + r["__compliant"] = status_raw in ("yes", "true", "compliant", "ok") + pct_str = (r.get("Compliance Percentage Per Recommendation", "") or "").replace(",", ".") + m = _re.search(r"(\d+(\.\d+)?)", pct_str) + r["__pct"] = max(0, min(100, float(m.group(1)))) if m else None + f_str = (r.get("Findings", "") or "").replace(",", "") + fm = _re.search(r"\d+", f_str) + r["__findings"] = int(fm.group(0)) if fm else None + filtered.append(r) + + # RAG remediations (with per-item timeout) + for r in filtered: + r["__rag_remediation"] = {"description": "", "remediation": ""} + if force_rag: + from concurrent.futures import ThreadPoolExecutor, TimeoutError as FuturesTimeout + non_compliant = [r for r in filtered if not r["__compliant"]] + log.info(f"Compliance report: searching RAG for {len(non_compliant)} non-compliant items") + with ThreadPoolExecutor(max_workers=2) as pool: + for r in non_compliant: + try: + fut = pool.submit(_search_cis_remediation, + r.get("Title", ""), r.get("Recommendation #", ""), user_id) + r["__rag_remediation"] = fut.result(timeout=30) + log.info(f" RAG OK: {r.get('Recommendation #', '?')}") + except FuturesTimeout: + log.warning(f" RAG timeout: {r.get('Recommendation #', '?')}") + except Exception as e: + log.warning(f" RAG error {r.get('Recommendation #', '?')}: {e}") + + SECTION_ORDER = [ + "Identity and Access Management", "Networking", "Compute", "Logging and Monitoring", + "Storage - Object Storage", "Storage - Block Volumes", "Storage - File Storage Service", + "Asset Management", + ] + from collections import OrderedDict + sections = OrderedDict() + for sec in SECTION_ORDER: + items = [r for r in filtered if r["Section"] == sec] + if items: + items.sort(key=lambda x: [int(p) if p.isdigit() else 0 for p in (x.get("Recommendation #", "0") or "0").split(".")]) + passed = sum(1 for x in items if x["__compliant"]) + sections[sec] = {"items": items, "total": len(items), "passed": passed, "failed": len(items) - passed} + + html = _build_compliance_html(filtered, tenancy_name, extract_date, sections, rid, file_id_map) + + # Cache to disk + cache_path = rdir / "compliance_report.html" + cache_path.write_text(html, encoding="utf-8") + log.info(f"Compliance report cached: {cache_path} ({len(html)} bytes)") + return html + + +@app.get("/api/reports/{rid}/compliance-report") +async def compliance_report(rid: str, regen: int = Query(0)): + """Serve cached LAD A-Team CIS Compliance Report, or return 404 if not generated yet.""" + rdir = REPORTS / rid + cache_path = rdir / "compliance_report.html" + + if regen == 1 and cache_path.exists(): + cache_path.unlink() + + if cache_path.exists(): + return FileResponse(cache_path, media_type="text/html") + + raise HTTPException(404, "Compliance report not generated yet") + + +@app.post("/api/reports/{rid}/compliance-report/generate") +async def generate_compliance_report(rid: str, bg: BackgroundTasks, u=Depends(current_user)): + """Start generating the compliance report in background.""" + with db() as c: + report = c.execute("SELECT * FROM reports WHERE id=?", (rid,)).fetchone() + if not report: raise HTTPException(404, "Report not found") + + rdir = REPORTS / rid + csv_path = rdir / "cis_summary_report.csv" + if not csv_path.exists(): + raise HTTPException(404, "cis_summary_report.csv not found") + + # Delete existing cache to signal "generating" + cache_path = rdir / "compliance_report.html" + if cache_path.exists(): + cache_path.unlink() + + has_adb = False + with db() as c: + adb_cfgs = c.execute("SELECT id FROM adb_vector_configs WHERE user_id=? AND is_active=1", (u["id"],)).fetchall() + if adb_cfgs: + has_adb = True + + user_id = u["id"] + log.info(f"Compliance report generation started for {rid} (has_adb={has_adb})") + + def _bg_generate(): + try: + log.info(f"Compliance report thread running for {rid}") + _generate_compliance_report(rid, user_id, force_rag=has_adb) + log.info(f"Compliance report generation completed for {rid}") + except Exception as e: + log.error(f"Compliance report generation failed for {rid}: {e}", exc_info=True) + + _chat_executor.submit(_bg_generate) + return {"status": "generating", "has_rag": has_adb} + + +@app.get("/api/reports/{rid}/compliance-report/status") +async def compliance_report_status(rid: str): + """Check if the compliance report exists.""" + cache_path = REPORTS / rid / "compliance_report.html" + return {"ready": cache_path.exists()} + @app.get("/api/reports/{rid}/download") async def report_dl(rid, fmt: str = Query("json"), 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) @@ -4261,11 +4993,11 @@ async def _chat_background(mid: str, sid: str, msg: ChatMsg, user: dict, genai_c base_prompt = cfg_dict.get("system_prompt", "") cfg_dict["system_prompt"] = f"{base_prompt}\n\n{config_hint}" if base_prompt else config_hint - # ── Terraform agent: boost max_tokens to model limit + force reasoning_effort=high ── + # ── Terraform agent: boost max_tokens (capped at 65K to avoid context overflow) + force reasoning_effort=high ── if agent_type == "terraform": tf_model_info = GENAI_MODELS.get(cfg_dict.get("model_id", ""), {}) tf_model_max = tf_model_info.get("max_tokens", 32768) - cfg_dict["max_tokens"] = tf_model_max + cfg_dict["max_tokens"] = min(tf_model_max, 65000) if tf_model_info.get("reasoning") and not cfg_dict.get("reasoning_effort"): cfg_dict["reasoning_effort"] = "HIGH" diff --git a/docker-compose.yml b/docker-compose.yml index bf60e80..0bda682 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -26,6 +26,7 @@ services: restart: unless-stopped volumes: - ./frontend/index.html:/usr/share/nginx/html/index.html:ro + - ./frontend-react/dist:/usr/share/nginx/html/app:ro - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro ports: - "${PORT:-8080}:80" diff --git a/frontend-react/README.md b/frontend-react/README.md new file mode 100644 index 0000000..7dbf7eb --- /dev/null +++ b/frontend-react/README.md @@ -0,0 +1,73 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs) +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) + +## React Compiler + +The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: + +```js +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + + // Remove tseslint.configs.recommended and replace with this + tseslint.configs.recommendedTypeChecked, + // Alternatively, use this for stricter rules + tseslint.configs.strictTypeChecked, + // Optionally, add this for stylistic rules + tseslint.configs.stylisticTypeChecked, + + // Other configs... + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` + +You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: + +```js +// eslint.config.js +import reactX from 'eslint-plugin-react-x' +import reactDom from 'eslint-plugin-react-dom' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + // Enable lint rules for React + reactX.configs['recommended-typescript'], + // Enable lint rules for React DOM + reactDom.configs.recommended, + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` diff --git a/frontend-react/eslint.config.js b/frontend-react/eslint.config.js new file mode 100644 index 0000000..5e6b472 --- /dev/null +++ b/frontend-react/eslint.config.js @@ -0,0 +1,23 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + }, +]) diff --git a/frontend-react/index.html b/frontend-react/index.html new file mode 100644 index 0000000..94290aa --- /dev/null +++ b/frontend-react/index.html @@ -0,0 +1,21 @@ + + + + + + + AI Agent - Infrastructure & Security Engineer + + + +
+ + + + diff --git a/frontend-react/package-lock.json b/frontend-react/package-lock.json new file mode 100644 index 0000000..cbb11ea --- /dev/null +++ b/frontend-react/package-lock.json @@ -0,0 +1,5920 @@ +{ + "name": "frontend-react", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend-react", + "version": "0.0.0", + "dependencies": { + "axios": "^1.13.6", + "lucide-react": "^0.577.0", + "react": "^19.2.4", + "react-dom": "^19.2.4", + "react-markdown": "^10.1.0", + "react-router-dom": "^7.13.1", + "recharts": "^3.8.0", + "rehype-highlight": "^7.0.2", + "remark-gfm": "^4.0.1", + "zustand": "^5.0.11" + }, + "devDependencies": { + "@eslint/js": "^9.39.4", + "@tailwindcss/postcss": "^4.2.1", + "@types/node": "^24.12.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.0", + "eslint": "^9.39.4", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.4.0", + "postcss": "^8.5.8", + "tailwindcss": "^4.2.1", + "typescript": "~5.9.3", + "typescript-eslint": "^8.56.1", + "vite": "^8.0.0" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.0.tgz", + "integrity": "sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.0.tgz", + "integrity": "sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", + "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", + "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@oxc-project/runtime": { + "version": "0.115.0", + "resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.115.0.tgz", + "integrity": "sha512-Rg8Wlt5dCbXhQnsXPrkOjL1DTSvXLgb2R/KYfnf1/K+R0k6UMLEmbQXPM+kwrWqSmWA2t0B1EtHy2/3zikQpvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.115.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.115.0.tgz", + "integrity": "sha512-4n91DKnebUS4yjUHl2g3/b2T+IUdCfmoZGhmwsovZCDaJSs+QkVAM+0AqqTxHSsHfeiMuueT75cZaZcT/m0pSw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@reduxjs/toolkit": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz", + "integrity": "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@standard-schema/utils": "^0.3.0", + "immer": "^11.0.0", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", + "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, + "node_modules/@reduxjs/toolkit/node_modules/immer": { + "version": "11.1.4", + "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.4.tgz", + "integrity": "sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.9.tgz", + "integrity": "sha512-lcJL0bN5hpgJfSIz/8PIf02irmyL43P+j1pTCfbD1DbLkmGRuFIA4DD3B3ZOvGqG0XiVvRznbKtN0COQVaKUTg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.9.tgz", + "integrity": "sha512-J7Zk3kLYFsLtuH6U+F4pS2sYVzac0qkjcO5QxHS7OS7yZu2LRs+IXo+uvJ/mvpyUljDJ3LROZPoQfgBIpCMhdQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.9.tgz", + "integrity": "sha512-iwtmmghy8nhfRGeNAIltcNXzD0QMNaaA5U/NyZc1Ia4bxrzFByNMDoppoC+hl7cDiUq5/1CnFthpT9n+UtfFyg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.9.tgz", + "integrity": "sha512-DLFYI78SCiZr5VvdEplsVC2Vx53lnA4/Ga5C65iyldMVaErr86aiqCoNBLl92PXPfDtUYjUh+xFFor40ueNs4Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.9.tgz", + "integrity": "sha512-CsjTmTwd0Hri6iTw/DRMK7kOZ7FwAkrO4h8YWKoX/kcj833e4coqo2wzIFywtch/8Eb5enQ/lwLM7w6JX1W5RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-2x9O2JbSPxpxMDhP9Z74mahAStibTlrBMW0520+epJH5sac7/LwZW5Bmg/E6CXuEF53JJFW509uP+lSedaUNxg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.9.tgz", + "integrity": "sha512-JA1QRW31ogheAIRhIg9tjMfsYbglXXYGNPLdPEYrwFxdbkQCAzvpSCSHCDWNl4hTtrol8WeboCSEpjdZK8qrCg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-aOKU9dJheda8Kj8Y3w9gnt9QFOO+qKPAl8SWd7JPHP+Cu0EuDAE5wokQubLzIDQWg2myXq2XhTpOVS07qqvT+w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-OalO94fqj7IWRn3VdXWty75jC5dk4C197AWEuMhIpvVv2lw9fiPhud0+bW2ctCxb3YoBZor71QHbY+9/WToadA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-cVEl1vZtBsBZna3YMjGXNvnYYrOJ7RzuWvZU0ffvJUexWkukMaDuGhUXn0rjnV0ptzGVkvc+vW9Yqy6h8YX4pg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.9.tgz", + "integrity": "sha512-UzYnKCIIc4heAKgI4PZ3dfBGUZefGCJ1TPDuLHoCzgrMYPb5Rv6TLFuYtyM4rWyHM7hymNdsg5ik2C+UD9VDbA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.9.tgz", + "integrity": "sha512-+6zoiF+RRyf5cdlFQP7nm58mq7+/2PFaY2DNQeD4B87N36JzfF/l9mdBkkmTvSYcYPE8tMh/o3cRlsx1ldLfog==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.9.tgz", + "integrity": "sha512-rgFN6sA/dyebil3YTlL2evvi/M+ivhfnyxec7AccTpRPccno/rPoNlqybEZQBkcbZu8Hy+eqNJCqfBR8P7Pg8g==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.9.tgz", + "integrity": "sha512-lHVNUG/8nlF1IQk1C0Ci574qKYyty2goMiPlRqkC5R+3LkXDkL5Dhx8ytbxq35m+pkHVIvIxviD+TWLdfeuadA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.9.tgz", + "integrity": "sha512-G0oA4+w1iY5AGi5HcDTxWsoxF509hrFIPB2rduV5aDqS9FtDg1CAfa7V34qImbjfhIcA8C+RekocJZA96EarwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.7", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz", + "integrity": "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, + "node_modules/@tailwindcss/node": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.1.tgz", + "integrity": "sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.19.0", + "jiti": "^2.6.1", + "lightningcss": "1.31.1", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.2.1" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss": { + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.31.1.tgz", + "integrity": "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.31.1", + "lightningcss-darwin-arm64": "1.31.1", + "lightningcss-darwin-x64": "1.31.1", + "lightningcss-freebsd-x64": "1.31.1", + "lightningcss-linux-arm-gnueabihf": "1.31.1", + "lightningcss-linux-arm64-gnu": "1.31.1", + "lightningcss-linux-arm64-musl": "1.31.1", + "lightningcss-linux-x64-gnu": "1.31.1", + "lightningcss-linux-x64-musl": "1.31.1", + "lightningcss-win32-arm64-msvc": "1.31.1", + "lightningcss-win32-x64-msvc": "1.31.1" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-android-arm64": { + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.31.1.tgz", + "integrity": "sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-darwin-arm64": { + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.31.1.tgz", + "integrity": "sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-darwin-x64": { + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.31.1.tgz", + "integrity": "sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-freebsd-x64": { + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.31.1.tgz", + "integrity": "sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.31.1.tgz", + "integrity": "sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.31.1.tgz", + "integrity": "sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-arm64-musl": { + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.31.1.tgz", + "integrity": "sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-x64-gnu": { + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.31.1.tgz", + "integrity": "sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-x64-musl": { + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.31.1.tgz", + "integrity": "sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.31.1.tgz", + "integrity": "sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-win32-x64-msvc": { + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.31.1.tgz", + "integrity": "sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.1.tgz", + "integrity": "sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.2.1", + "@tailwindcss/oxide-darwin-arm64": "4.2.1", + "@tailwindcss/oxide-darwin-x64": "4.2.1", + "@tailwindcss/oxide-freebsd-x64": "4.2.1", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.1", + "@tailwindcss/oxide-linux-arm64-gnu": "4.2.1", + "@tailwindcss/oxide-linux-arm64-musl": "4.2.1", + "@tailwindcss/oxide-linux-x64-gnu": "4.2.1", + "@tailwindcss/oxide-linux-x64-musl": "4.2.1", + "@tailwindcss/oxide-wasm32-wasi": "4.2.1", + "@tailwindcss/oxide-win32-arm64-msvc": "4.2.1", + "@tailwindcss/oxide-win32-x64-msvc": "4.2.1" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.1.tgz", + "integrity": "sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.1.tgz", + "integrity": "sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.1.tgz", + "integrity": "sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.1.tgz", + "integrity": "sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.1.tgz", + "integrity": "sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.1.tgz", + "integrity": "sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.1.tgz", + "integrity": "sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.1.tgz", + "integrity": "sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.1.tgz", + "integrity": "sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.1.tgz", + "integrity": "sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.8.1", + "@emnapi/runtime": "^1.8.1", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.1.1", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.1.tgz", + "integrity": "sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.1.tgz", + "integrity": "sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.2.1.tgz", + "integrity": "sha512-OEwGIBnXnj7zJeonOh6ZG9woofIjGrd2BORfvE5p9USYKDCZoQmfqLcfNiRWoJlRWLdNPn2IgVZuWAOM4iTYMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.2.1", + "@tailwindcss/oxide": "4.2.1", + "postcss": "^8.5.6", + "tailwindcss": "4.2.1" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.12.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz", + "integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.0.tgz", + "integrity": "sha512-qeu4rTHR3/IaFORbD16gmjq9+rEs9fGKdX0kF6BKSfi+gCuG3RCKLlSBYzn/bGsY9Tj7KE/DAQStbp8AHJGHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.57.0", + "@typescript-eslint/type-utils": "8.57.0", + "@typescript-eslint/utils": "8.57.0", + "@typescript-eslint/visitor-keys": "8.57.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.57.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.0.tgz", + "integrity": "sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.57.0", + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/typescript-estree": "8.57.0", + "@typescript-eslint/visitor-keys": "8.57.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.0.tgz", + "integrity": "sha512-pR+dK0BlxCLxtWfaKQWtYr7MhKmzqZxuii+ZjuFlZlIGRZm22HnXFqa2eY+90MUz8/i80YJmzFGDUsi8dMOV5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.57.0", + "@typescript-eslint/types": "^8.57.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.0.tgz", + "integrity": "sha512-nvExQqAHF01lUM66MskSaZulpPL5pgy5hI5RfrxviLgzZVffB5yYzw27uK/ft8QnKXI2X0LBrHJFr1TaZtAibw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/visitor-keys": "8.57.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.0.tgz", + "integrity": "sha512-LtXRihc5ytjJIQEH+xqjB0+YgsV4/tW35XKX3GTZHpWtcC8SPkT/d4tqdf1cKtesryHm2bgp6l555NYcT2NLvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.0.tgz", + "integrity": "sha512-yjgh7gmDcJ1+TcEg8x3uWQmn8ifvSupnPfjP21twPKrDP/pTHlEQgmKcitzF/rzPSmv7QjJ90vRpN4U+zoUjwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/typescript-estree": "8.57.0", + "@typescript-eslint/utils": "8.57.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.0.tgz", + "integrity": "sha512-dTLI8PEXhjUC7B9Kre+u0XznO696BhXcTlOn0/6kf1fHaQW8+VjJAVHJ3eTI14ZapTxdkOmc80HblPQLaEeJdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.0.tgz", + "integrity": "sha512-m7faHcyVg0BT3VdYTlX8GdJEM7COexXxS6KqGopxdtkQRvBanK377QDHr4W/vIPAR+ah9+B/RclSW5ldVniO1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.57.0", + "@typescript-eslint/tsconfig-utils": "8.57.0", + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/visitor-keys": "8.57.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.0.tgz", + "integrity": "sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.57.0", + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/typescript-estree": "8.57.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.0.tgz", + "integrity": "sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.57.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "license": "ISC" + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz", + "integrity": "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-rc.7" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + } + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz", + "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.7", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.7.tgz", + "integrity": "sha512-1ghYO3HnxGec0TCGBXiDLVns4eCSx4zJpxnHrlqFQajmhfKMQBzUGDdkMK7fUW7PTHTeLf+j87aTuKuuwWzMGw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001778", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001778.tgz", + "integrity": "sha512-PN7uxFL+ExFJO61aVmP1aIEG4i9whQd4eoSCebav62UwDyp5OHh06zN4jqKSMePVgxHifCw1QJxdRkA1Pisekg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", + "license": "MIT" + }, + "node_modules/decode-named-character-reference": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", + "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.313", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.313.tgz", + "integrity": "sha512-QBMrTWEf00GXZmJyx2lbYD45jpI3TUFnNIzJ5BBc8piGUDwMPa1GV6HJWTZVvY/eiN3fSopl7NRbgGp9sZ9LTA==", + "dev": true, + "license": "ISC" + }, + "node_modules/enhanced-resolve": { + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.0.tgz", + "integrity": "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-toolkit": { + "version": "1.45.1", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.45.1.tgz", + "integrity": "sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw==", + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks" + ] + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", + "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.2.tgz", + "integrity": "sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": "^9 || ^10" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.1.tgz", + "integrity": "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.4.0.tgz", + "integrity": "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-text": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", + "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "unist-util-find-after": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/highlight.js": { + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", + "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/html-url-attributes": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", + "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immer": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.2.0.tgz", + "integrity": "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inline-style-parser": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", + "license": "MIT" + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/lowlight": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-3.3.0.tgz", + "integrity": "sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.0.0", + "highlight.js": "~11.11.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "0.577.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.577.0.tgz", + "integrity": "sha512-4LjoFv2eEPwYDPg/CUdBJQSDfPyzXCRrVW1X7jrx/trgxnxkHFjnVZINbzvzxjN70dxychOfg+FTYwBiS3pQ5A==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", + "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.36", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", + "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, + "node_modules/react-is": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.4.tgz", + "integrity": "sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==", + "license": "MIT", + "peer": true + }, + "node_modules/react-markdown": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", + "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, + "node_modules/react-redux": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", + "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", + "license": "MIT", + "dependencies": { + "@types/use-sync-external-store": "^0.0.6", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "@types/react": "^18.2.25 || ^19", + "react": "^18.0 || ^19", + "redux": "^5.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "node_modules/react-router": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.13.1.tgz", + "integrity": "sha512-td+xP4X2/6BJvZoX6xw++A2DdEi++YypA69bJUV5oVvqf6/9/9nNlD70YO1e9d3MyamJEBQFEzk6mbfDYbqrSA==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.13.1.tgz", + "integrity": "sha512-UJnV3Rxc5TgUPJt2KJpo1Jpy0OKQr0AjgbZzBFjaPJcFOb2Y8jA5H3LT8HUJAiRLlWrEXWHbF1Z4SCZaQjWDHw==", + "license": "MIT", + "dependencies": { + "react-router": "7.13.1" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/recharts": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.8.0.tgz", + "integrity": "sha512-Z/m38DX3L73ExO4Tpc9/iZWHmHnlzWG4njQbxsF5aSjwqmHNDDIm0rdEBArkwsBvR8U6EirlEHiQNYWCVh9sGQ==", + "license": "MIT", + "workspaces": [ + "www" + ], + "dependencies": { + "@reduxjs/toolkit": "^1.9.0 || 2.x.x", + "clsx": "^2.1.1", + "decimal.js-light": "^2.5.1", + "es-toolkit": "^1.39.3", + "eventemitter3": "^5.0.1", + "immer": "^10.1.1", + "react-redux": "8.x.x || 9.x.x", + "reselect": "5.1.1", + "tiny-invariant": "^1.3.3", + "use-sync-external-store": "^1.2.2", + "victory-vendor": "^37.0.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", + "license": "MIT" + }, + "node_modules/redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "license": "MIT", + "peerDependencies": { + "redux": "^5.0.0" + } + }, + "node_modules/rehype-highlight": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/rehype-highlight/-/rehype-highlight-7.0.2.tgz", + "integrity": "sha512-k158pK7wdC2qL3M5NcZROZ2tR/l7zOzjxXd5VGdcfIyoijjQqpHd3JKtYSBDpDZ38UI2WJWuFAtkMDxmx5kstA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-to-text": "^4.0.0", + "lowlight": "^3.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/reselect": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", + "license": "MIT" + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/rolldown": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.9.tgz", + "integrity": "sha512-9EbgWge7ZH+yqb4d2EnELAntgPTWbfL8ajiTW+SyhJEC4qhBbkCKbqFV4Ge4zmu5ziQuVbWxb/XwLZ+RIO7E8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.115.0", + "@rolldown/pluginutils": "1.0.0-rc.9" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.9", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.9", + "@rolldown/binding-darwin-x64": "1.0.0-rc.9", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.9", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.9", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.9", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.9", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.9", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.9", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.9", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.9", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.9", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.9", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.9", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.9" + } + }, + "node_modules/rolldown/node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.9.tgz", + "integrity": "sha512-w6oiRWgEBl04QkFZgmW+jnU1EC9b57Oihi2ot3HNWIQRqgHp5PnYDia5iZ5FF7rpa4EQdiqMDXjlqKGXBhsoXw==", + "dev": true, + "license": "MIT" + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-to-js": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", + "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.14" + } + }, + "node_modules/style-to-object": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.7" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tailwindcss": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.1.tgz", + "integrity": "sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-api-utils": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.0.tgz", + "integrity": "sha512-W8GcigEMEeB07xEZol8oJ26rigm3+bfPHxHvwbYUlu1fUDsGuQ7Hiskx5xGW/xM4USc9Ephe3jtv7ZYPQntHeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.57.0", + "@typescript-eslint/parser": "8.57.0", + "@typescript-eslint/typescript-estree": "8.57.0", + "@typescript-eslint/utils": "8.57.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-find-after": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", + "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/victory-vendor": { + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz", + "integrity": "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, + "node_modules/vite": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.0.tgz", + "integrity": "sha512-fPGaRNj9Zytaf8LEiBhY7Z6ijnFKdzU/+mL8EFBaKr7Vw1/FWcTBAMW0wLPJAGMPX38ZPVCVgLceWiEqeoqL2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/runtime": "0.115.0", + "lightningcss": "^1.32.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.8", + "rolldown": "1.0.0-rc.9", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.0.0-alpha.31", + "esbuild": "^0.27.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + }, + "node_modules/zustand": { + "version": "5.0.11", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.11.tgz", + "integrity": "sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/frontend-react/package.json b/frontend-react/package.json new file mode 100644 index 0000000..d5d9d33 --- /dev/null +++ b/frontend-react/package.json @@ -0,0 +1,41 @@ +{ + "name": "frontend-react", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "axios": "^1.13.6", + "lucide-react": "^0.577.0", + "react": "^19.2.4", + "react-dom": "^19.2.4", + "react-markdown": "^10.1.0", + "react-router-dom": "^7.13.1", + "recharts": "^3.8.0", + "rehype-highlight": "^7.0.2", + "remark-gfm": "^4.0.1", + "zustand": "^5.0.11" + }, + "devDependencies": { + "@eslint/js": "^9.39.4", + "@tailwindcss/postcss": "^4.2.1", + "@types/node": "^24.12.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.0", + "eslint": "^9.39.4", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.4.0", + "postcss": "^8.5.8", + "tailwindcss": "^4.2.1", + "typescript": "~5.9.3", + "typescript-eslint": "^8.56.1", + "vite": "^8.0.0" + } +} diff --git a/frontend-react/postcss.config.js b/frontend-react/postcss.config.js new file mode 100644 index 0000000..a34a3d5 --- /dev/null +++ b/frontend-react/postcss.config.js @@ -0,0 +1,5 @@ +export default { + plugins: { + '@tailwindcss/postcss': {}, + }, +}; diff --git a/frontend-react/public/logo.svg b/frontend-react/public/logo.svg new file mode 100644 index 0000000..0b7b905 --- /dev/null +++ b/frontend-react/public/logo.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend-react/src/App.tsx b/frontend-react/src/App.tsx new file mode 100644 index 0000000..9b339f5 --- /dev/null +++ b/frontend-react/src/App.tsx @@ -0,0 +1,63 @@ +import { useEffect } from 'react'; +import { Routes, Route, Navigate } from 'react-router-dom'; +import { useAuthStore } from '@/stores/auth'; +import { useAppStore } from '@/stores/app'; +import AppShell from '@/components/layout/AppShell'; +import LoginPage from '@/pages/LoginPage'; +import ChatPage from '@/pages/ChatPage'; +import TerraformPage from '@/pages/TerraformPage'; +import PromptGeneratorPage from '@/pages/PromptGeneratorPage'; +import WorkspacesPage from '@/pages/WorkspacesPage'; +import ExplorerPage from '@/pages/ExplorerPage'; +import ReportsPage from '@/pages/ReportsPage'; +import OciConfigPage from '@/pages/config/OciConfigPage'; +import GenAiConfigPage from '@/pages/config/GenAiConfigPage'; +import McpServersPage from '@/pages/config/McpServersPage'; +import AdbConfigPage from '@/pages/config/AdbConfigPage'; +import EmbeddingsPage from '@/pages/config/EmbeddingsPage'; +import EmbConsultPage from '@/pages/config/EmbConsultPage'; +import UsersPage from '@/pages/admin/UsersPage'; +import MfaPage from '@/pages/admin/MfaPage'; +import AuditPage from '@/pages/admin/AuditPage'; + +export default function App() { + const checkAuth = useAuthStore((s) => s.checkAuth); + const loadData = useAppStore((s) => s.loadData); + const user = useAuthStore((s) => s.user); + + useEffect(() => { + checkAuth().then(() => { + if (useAuthStore.getState().user) loadData(); + }); + }, [checkAuth, loadData]); + + // Reload data when user changes (login) + useEffect(() => { + if (user) loadData(); + }, [user, loadData]); + + return ( + + } /> + }> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + } /> + + ); +} diff --git a/frontend-react/src/api/client.ts b/frontend-react/src/api/client.ts new file mode 100644 index 0000000..41afac2 --- /dev/null +++ b/frontend-react/src/api/client.ts @@ -0,0 +1,30 @@ +import axios from 'axios'; + +const client = axios.create({ + baseURL: '/api', + headers: { 'Content-Type': 'application/json' }, +}); + +// Request: attach JWT +client.interceptors.request.use((config) => { + const token = localStorage.getItem('t'); + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + return config; +}); + +// Response: handle 401 + extract data +client.interceptors.response.use( + (res) => res.data, + (err) => { + if (err.response?.status === 401) { + localStorage.removeItem('t'); + window.location.href = '/app/login'; + } + const detail = err.response?.data?.detail || err.response?.data?.message || err.message || 'Erro'; + return Promise.reject(new Error(detail)); + } +); + +export default client; diff --git a/frontend-react/src/api/endpoints/adb.ts b/frontend-react/src/api/endpoints/adb.ts new file mode 100644 index 0000000..dc654f2 --- /dev/null +++ b/frontend-react/src/api/endpoints/adb.ts @@ -0,0 +1,71 @@ +import client from '../client'; + +export interface AdbTableDef { + id: string; + table_name: string; + description?: string; + is_active: boolean; +} + +export interface AdbConfigFull { + id: string; + config_name: string; + dsn: string; + username: string; + table_name?: string; + use_mtls: boolean; + is_active: boolean; + wallet_dir?: string; + genai_config_id?: string; + embedding_model_id?: string; + tables?: AdbTableDef[]; + created_at?: string; +} + +export interface AdbTestResult { + status: 'success' | 'error'; + message: string; +} + +export interface WalletParseResult { + dsn_names: string[]; + files: string[]; +} + +export const adbApi = { + list: () => + client.get('/adb/configs') as unknown as Promise, + + create: (fd: FormData) => + client.post('/adb/config', fd, { headers: { 'Content-Type': 'multipart/form-data' } }) as unknown as Promise<{ id: string; config_name: string }>, + + update: (id: string, fd: FormData) => + client.put(`/adb/configs/${id}`, fd, { headers: { 'Content-Type': 'multipart/form-data' } }) as unknown as Promise<{ id: string; config_name: string }>, + + remove: (id: string) => + client.delete(`/adb/configs/${id}`) as unknown as Promise<{ ok: boolean }>, + + test: (id: string) => + client.post(`/adb/test/${id}`) as unknown as Promise, + + parseWallet: (file: File) => { + const fd = new FormData(); + fd.append('wallet', file); + return client.post('/adb/parse-wallet', fd, { headers: { 'Content-Type': 'multipart/form-data' } }) as unknown as Promise; + }, + + uploadWallet: (id: string, file: File) => { + const fd = new FormData(); + fd.append('wallet', file); + return client.post(`/adb/${id}/upload-wallet`, fd, { headers: { 'Content-Type': 'multipart/form-data' } }) as unknown as Promise<{ ok: boolean; files: string[] }>; + }, + + addTable: (vid: string, tableName: string, description: string) => + client.post(`/adb/${vid}/tables`, { table_name: tableName, description }) as unknown as Promise<{ id: string }>, + + updateTable: (vid: string, tid: string, body: { table_name?: string; description?: string; is_active?: boolean }) => + client.put(`/adb/${vid}/tables/${tid}`, body) as unknown as Promise<{ ok: boolean }>, + + removeTable: (vid: string, tid: string) => + client.delete(`/adb/${vid}/tables/${tid}`) as unknown as Promise<{ ok: boolean }>, +}; diff --git a/frontend-react/src/api/endpoints/admin.ts b/frontend-react/src/api/endpoints/admin.ts new file mode 100644 index 0000000..1e2e871 --- /dev/null +++ b/frontend-react/src/api/endpoints/admin.ts @@ -0,0 +1,72 @@ +import client from '../client'; + +/* ── Types ── */ + +export interface AuditEntry { + id: string; + user_id: string | null; + username: string | null; + action: string; + resource: string | null; + details: string | null; + ip: string | null; + created_at: string; +} + +export interface ConfigLogEntry { + id: string; + config_type: string; + config_id: string; + config_name: string | null; + severity: string; + action: string; + message: string; + user_id: string | null; + username: string | null; + created_at: string; +} + +export interface ChatLogEntry { + id: string; + session_id: string | null; + message_id: string | null; + user_id: string | null; + severity: string; + source: string; + action: string; + message: string; + created_at: string; +} + +/* ── API calls ── */ + +export const adminApi = { + getAuditLog: (limit = 100) => + client.get(`/audit-log?limit=${limit}`) as unknown as Promise, + + getConfigLogs: (params: { + config_type?: string; + config_id?: string; + severity?: string; + limit?: number; + } = {}) => { + const qs = new URLSearchParams(); + if (params.config_type) qs.set('config_type', params.config_type); + if (params.config_id) qs.set('config_id', params.config_id); + if (params.severity) qs.set('severity', params.severity); + qs.set('limit', String(params.limit ?? 50)); + return client.get(`/config-logs?${qs}`) as unknown as Promise; + }, + + getChatLogs: (params: { + severity?: string; + session_id?: string; + limit?: number; + } = {}) => { + const qs = new URLSearchParams(); + if (params.severity) qs.set('severity', params.severity); + if (params.session_id) qs.set('session_id', params.session_id); + qs.set('limit', String(params.limit ?? 50)); + return client.get(`/chat-logs?${qs}`) as unknown as Promise; + }, +}; diff --git a/frontend-react/src/api/endpoints/auth.ts b/frontend-react/src/api/endpoints/auth.ts new file mode 100644 index 0000000..4e058fd --- /dev/null +++ b/frontend-react/src/api/endpoints/auth.ts @@ -0,0 +1,31 @@ +import client from '../client'; + +export interface User { + id: string; + username: string; + role: string; + first_name?: string; + last_name?: string; + email?: string; + mfa_enabled?: boolean; +} + +export interface LoginResponse { + token?: string; + user?: User; + mfa_required?: boolean; + mfa_setup?: boolean; + totp_uri?: string; +} + +export const authApi = { + login: (username: string, password: string, totp_code?: string) => + client.post('/auth/login', { username, password, ...(totp_code && { totp_code }) }), + + logout: () => client.post('/auth/logout'), + + me: () => client.get('/users/me'), + + changePassword: (current_password: string, new_password: string) => + client.post('/auth/change-password', { current_password, new_password }), +}; diff --git a/frontend-react/src/api/endpoints/chat.ts b/frontend-react/src/api/endpoints/chat.ts new file mode 100644 index 0000000..97fc77f --- /dev/null +++ b/frontend-react/src/api/endpoints/chat.ts @@ -0,0 +1,94 @@ +import client from '../client'; + +/* ── Types ── */ + +export interface ChatSession { + id: string; + title: string; + agent_type: string; + created_at: string; + updated_at: string; + message_count?: number; +} + +export interface ChatMessage { + id: string; + session_id?: string; + role: 'user' | 'assistant'; + content: string; + model_id?: string; + status?: 'processing' | 'done' | 'failed'; + created_at?: string; +} + +export interface ChatSendBody { + message: string; + session_id?: string; + genai_config_id?: string; + model_id?: string; + oci_config_id?: string; + genai_region?: string; + compartment_id?: string; + temperature?: number; + max_tokens?: number; + top_p?: number; + top_k?: number; + frequency_penalty?: number; + presence_penalty?: number; + reasoning_effort?: string; + use_tools?: boolean; +} + +export interface ChatSendResponse { + message_id: string; + session_id: string; + status: 'processing' | 'done'; +} + +export interface MessageStatusResponse { + id: string; + session_id: string; + role: string; + content: string; + model_id?: string; + status: 'processing' | 'done' | 'failed'; +} + +export interface SessionMessagesResponse { + session: ChatSession; + messages: ChatMessage[]; +} + +/* ── API ── */ + +export const chatApi = { + /** Send a chat message (text only) */ + send: (body: ChatSendBody) => + client.post('/chat', body) as unknown as Promise, + + /** Send a chat message with file attachments */ + upload: (formData: FormData) => + client.post('/chat/upload', formData, { + headers: { 'Content-Type': 'multipart/form-data' }, + }) as unknown as Promise, + + /** Poll message processing status */ + pollStatus: (messageId: string) => + client.get(`/chat/${messageId}/status`) as unknown as Promise, + + /** List chat sessions */ + listSessions: (agentType = 'chat', limit = 50) => + client.get(`/chat/sessions?agent_type=${agentType}&limit=${limit}`) as unknown as Promise, + + /** Load session with all messages */ + loadSession: (sessionId: string) => + client.get(`/chat/sessions/${sessionId}/messages`) as unknown as Promise, + + /** Delete a session */ + deleteSession: (sessionId: string) => + client.delete(`/chat/${sessionId}`) as unknown as Promise<{ ok: boolean }>, + + /** Rename a session */ + renameSession: (sessionId: string, title: string) => + client.put(`/chat/sessions/${sessionId}/title`, { title }) as unknown as Promise<{ ok: boolean }>, +}; diff --git a/frontend-react/src/api/endpoints/embeddings.ts b/frontend-react/src/api/endpoints/embeddings.ts new file mode 100644 index 0000000..1f51d75 --- /dev/null +++ b/frontend-react/src/api/endpoints/embeddings.ts @@ -0,0 +1,97 @@ +import client from '../client'; + +/* ── Types ── */ + +export interface EmbeddingDoc { + id: string; + metadata: string; +} + +export interface EmbeddingListResult { + total: number; + offset: number; + limit: number; + documents: EmbeddingDoc[]; +} + +export interface EmbedUploadResult { + ok: boolean; + message: string; + chunks: number; + filename?: string; + url?: string; +} + +export interface PurgeResult { + ok: boolean; + deleted: number; + table: string; + tenancy: string; +} + +export interface ConsultResult { + answer: string; + documents: { + content: string; + source: string; + distance: number; + metadata: string; + }[]; + total: number; +} + +/* ── API ── */ + +export const embeddingsApi = { + /** List embeddings for a given ADB config + optional table */ + list: (vid: string, tableName?: string, limit = 50, offset = 0) => { + const params = new URLSearchParams(); + if (tableName) params.set('table_name', tableName); + params.set('limit', String(limit)); + params.set('offset', String(offset)); + return client.get(`/embeddings/${vid}/list?${params}`) as unknown as Promise; + }, + + /** Delete a single embedding document */ + remove: (vid: string, docId: string, tableName?: string) => { + const qs = tableName ? `?table_name=${encodeURIComponent(tableName)}` : ''; + return client.delete(`/embeddings/${vid}/${docId}${qs}`) as unknown as Promise<{ ok: boolean }>; + }, + + /** Upload a file to generate embeddings */ + uploadFile: (adbConfigId: string, tableName: string, file: File) => { + const fd = new FormData(); + fd.append('adb_config_id', adbConfigId); + fd.append('table_name', tableName); + fd.append('file', file); + return client.post('/embeddings/upload', fd, { + headers: { 'Content-Type': 'multipart/form-data' }, + }) as unknown as Promise; + }, + + /** Import a URL to generate embeddings */ + uploadUrl: (adbConfigId: string, tableName: string, url: string) => { + const fd = new FormData(); + fd.append('adb_config_id', adbConfigId); + fd.append('table_name', tableName); + fd.append('url', url); + return client.post('/embeddings/upload-url', fd, { + headers: { 'Content-Type': 'multipart/form-data' }, + }) as unknown as Promise; + }, + + /** Purge embeddings from a table (optionally filtered by tenancy) */ + purge: (vid: string, tableName: string, tenancy?: string) => + client.post(`/embeddings/${vid}/purge`, { + table_name: tableName, + tenancy: tenancy || '', + }) as unknown as Promise, + + /** Consult embeddings with a question */ + consult: (query: string, tableName?: string, topK = 10) => + client.post('/embeddings/consult', { + query, + table_name: tableName || '', + top_k: topK, + }) as unknown as Promise, +}; diff --git a/frontend-react/src/api/endpoints/explorer.ts b/frontend-react/src/api/endpoints/explorer.ts new file mode 100644 index 0000000..5247ce2 --- /dev/null +++ b/frontend-react/src/api/endpoints/explorer.ts @@ -0,0 +1,129 @@ +import client from '../client'; + +/* ── Types ─────────────────────────────────────────────────────────────────── */ + +export interface CompartmentNode { + id: string; + name: string; + parent_id: string | null; + lifecycle_state?: string; +} + +export interface Region { + name: string; + key: string; + status: string; + is_home: boolean; +} + +export interface CountEntry { + count: number; + updated_at: string; +} + +export type CountsMap = Record; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type ResourceItem = Record; + +export interface ActionResult { + ok: boolean; + action: string; + [key: string]: unknown; +} + +export interface MyIpResult { + ip: string; +} + +/* ── API ───────────────────────────────────────────────────────────────────── */ + +export const explorerApi = { + /* Compartment tree (flat list with parent_id) */ + getCompartmentTree: (cid: string) => + client.get(`/oci/explore/${cid}/compartment-tree`) as unknown as Promise, + + /* Regions subscribed */ + getRegions: (cid: string) => + client.get(`/oci/explore/${cid}/regions`) as unknown as Promise, + + /* Cached counts */ + getCounts: (cid: string, compartmentId: string, regions: string = '') => + client.get(`/oci/explore/${cid}/counts`, { + params: { compartment_id: compartmentId, regions }, + }) as unknown as Promise, + + /* Trigger background counts refresh */ + refreshCounts: (cid: string, compartmentId: string, regions: string[]) => + client.post(`/oci/explore/${cid}/counts/refresh`, { + compartment_id: compartmentId, + regions, + }) as unknown as Promise<{ status: string; types: number }>, + + /* Fetch resources of a given type */ + getResources: (cid: string, resourceType: string, compartmentId: string, region?: string) => + client.get(`/oci/explore/${cid}/${resourceType}`, { + params: { compartment_id: compartmentId, region: region || undefined }, + }) as unknown as Promise, + + /* ── Actions ─────────────────────────────────────────────────────────────── */ + + instanceAction: (id: string, action: 'START' | 'STOP', ociConfigId: string, region?: string) => + client.post(`/oci/instances/${id}/action`, { + action, oci_config_id: ociConfigId, region, + }) as unknown as Promise, + + adbAction: (id: string, action: 'start' | 'stop', ociConfigId: string, region?: string) => + client.post(`/oci/autonomous-databases/${id}/action`, { + action, oci_config_id: ociConfigId, region, + }) as unknown as Promise, + + dbSystemAction: (id: string, action: 'START' | 'STOP', ociConfigId: string, region?: string) => + client.post(`/oci/db-systems/${id}/action`, { + action, oci_config_id: ociConfigId, region, + }) as unknown as Promise, + + mysqlAction: (id: string, action: 'start' | 'stop', ociConfigId: string, region?: string) => + client.post(`/oci/mysql-db-systems/${id}/action`, { + action, oci_config_id: ociConfigId, region, + }) as unknown as Promise, + + containerAction: (id: string, action: 'start' | 'stop', ociConfigId: string, region?: string) => + client.post(`/oci/container-instances/${id}/action`, { + action, oci_config_id: ociConfigId, region, + }) as unknown as Promise, + + getMyIp: () => + client.get('/oci/my-ip') as unknown as Promise, + + adbUpdateNetwork: (id: string, ip: string, ociConfigId: string, region?: string) => + client.post(`/oci/autonomous-databases/${id}/update-network-access`, { + ip, oci_config_id: ociConfigId, region, + }) as unknown as Promise, + + /* ── Security List Rules ─────────────────────────────────────────────────── */ + + addSlRule: (cid: string, slId: string, body: { + direction: string; protocol: string; source_dest: string; + port_min?: number; port_max?: number; description?: string; + is_stateless?: boolean; region?: string; + }) => + client.post(`/oci/explore/${cid}/security_lists/${slId}/rules`, body) as unknown as Promise, + + removeSlRule: (cid: string, slId: string, body: { + direction: string; rule_index: number; region?: string; + }) => + client.delete(`/oci/explore/${cid}/security_lists/${slId}/rules`, { data: body }) as unknown as Promise, + + /* ── NSG Rules ───────────────────────────────────────────────────────────── */ + + addNsgRule: (cid: string, nsgId: string, body: { + direction: string; protocol: string; source_dest: string; + source_dest_type?: string; port_min?: number; port_max?: number; + description?: string; is_stateless?: boolean; region?: string; + }) => + client.post(`/oci/explore/${cid}/nsgs/${nsgId}/rules`, body) as unknown as Promise, + + removeNsgRule: (cid: string, nsgId: string, ruleId: string, region?: string) => + client.delete(`/oci/explore/${cid}/nsgs/${nsgId}/rules/${ruleId}${region ? `?region=${region}` : ''}`) as unknown as Promise, +}; diff --git a/frontend-react/src/api/endpoints/genai.ts b/frontend-react/src/api/endpoints/genai.ts new file mode 100644 index 0000000..a2fe0f2 --- /dev/null +++ b/frontend-react/src/api/endpoints/genai.ts @@ -0,0 +1,66 @@ +import client from '../client'; + +export interface GenAiConfigFull { + id: string; + name: string; + oci_config_id: string; + model_id: string; + model_ocid?: string; + compartment_id: string; + genai_region: string; + endpoint?: string; + serving_type: string; + dedicated_endpoint_id?: string; + temperature: number; + max_tokens: number; + top_p: number; + top_k: number; + frequency_penalty: number; + presence_penalty: number; + reasoning_effort?: string; + is_default: boolean; + created_at?: string; +} + +export interface GenAiConfigPayload { + name: string; + oci_config_id: string; + model_id: string; + model_ocid?: string | null; + compartment_id: string; + genai_region: string; + endpoint?: string | null; + serving_type?: string; + dedicated_endpoint_id?: string | null; + temperature?: number; + max_tokens?: number; + top_p?: number; + top_k?: number; + frequency_penalty?: number; + presence_penalty?: number; + reasoning_effort?: string | null; + is_default?: boolean; +} + +export interface GenAiTestResult { + status: 'success' | 'error'; + message: string; + response?: string; +} + +export const genaiApi = { + list: () => + client.get('/genai/configs') as unknown as Promise, + + create: (body: GenAiConfigPayload) => + client.post('/genai/config', body) as unknown as Promise<{ id: string; model_id: string; endpoint: string }>, + + update: (id: string, body: GenAiConfigPayload) => + client.put(`/genai/configs/${id}`, body) as unknown as Promise<{ id: string; model_id: string; endpoint: string }>, + + remove: (id: string) => + client.delete(`/genai/configs/${id}`) as unknown as Promise<{ ok: boolean }>, + + test: (id: string) => + client.post(`/genai/test/${id}`) as unknown as Promise, +}; diff --git a/frontend-react/src/api/endpoints/mcp.ts b/frontend-react/src/api/endpoints/mcp.ts new file mode 100644 index 0000000..899ac4d --- /dev/null +++ b/frontend-react/src/api/endpoints/mcp.ts @@ -0,0 +1,68 @@ +import client from '../client'; + +export interface McpServerFull { + id: string; + name: string; + description?: string; + server_type: string; + command?: string; + args?: string[]; + env_vars?: Record; + url?: string; + module_path?: string; + is_active: boolean; + tools?: McpToolDef[]; + linked_adb_id?: string; + created_at?: string; +} + +export interface McpToolDef { + name: string; + description?: string; + input_schema?: Record; +} + +export interface McpServerPayload { + name: string; + description?: string | null; + server_type: string; + command?: string | null; + args?: string[] | null; + env_vars?: Record | null; + url?: string | null; + module_path?: string | null; + tools?: McpToolDef[] | null; + linked_adb_id?: string | null; +} + +export const mcpApi = { + list: () => + client.get('/mcp/servers') as unknown as Promise, + + create: (body: McpServerPayload) => + client.post('/mcp/servers', body) as unknown as Promise<{ id: string; name: string; server_type: string }>, + + update: (id: string, body: McpServerPayload) => + client.put(`/mcp/servers/${id}`, body) as unknown as Promise<{ id: string; name: string; server_type: string }>, + + remove: (id: string) => + client.delete(`/mcp/servers/${id}`) as unknown as Promise<{ ok: boolean }>, + + toggle: (id: string) => + client.put(`/mcp/servers/${id}/toggle`) as unknown as Promise<{ ok: boolean; is_active: boolean }>, + + discoverTools: (id: string) => + client.post(`/mcp/servers/${id}/discover-tools`) as unknown as Promise<{ ok: boolean; discovered: number; total: number; tools: McpToolDef[] }>, + + updateTools: (id: string, tools: McpToolDef[]) => + client.put(`/mcp/servers/${id}/tools`, { tools }) as unknown as Promise<{ ok: boolean }>, + + upload: (id: string, file: File) => { + const fd = new FormData(); + fd.append('file', file); + return client.post(`/mcp/servers/${id}/upload`, fd, { headers: { 'Content-Type': 'multipart/form-data' } }) as unknown as Promise<{ ok: boolean; path: string }>; + }, + + linkAdb: (id: string, adbId: string) => + client.put(`/mcp/servers/${id}/link-adb?adb_id=${adbId}`) as unknown as Promise<{ ok: boolean }>, +}; diff --git a/frontend-react/src/api/endpoints/oci.ts b/frontend-react/src/api/endpoints/oci.ts new file mode 100644 index 0000000..6fe4679 --- /dev/null +++ b/frontend-react/src/api/endpoints/oci.ts @@ -0,0 +1,39 @@ +import client from '../client'; + +export interface OciConfigFull { + id: string; + user_id: string; + tenancy_name: string; + tenancy_ocid: string; + user_ocid: string; + fingerprint: string; + region: string; + compartment_id?: string; + ssh_public_key?: string; + ssh_public_key_preview?: string; + auth_type: 'api_key' | 'session_token'; + created_at: string; +} + +export interface OciTestResult { + status: 'success' | 'error'; + message: string; + data?: unknown; +} + +export const ociApi = { + list: () => + client.get('/oci/configs') as unknown as Promise, + + create: (fd: FormData) => + client.post('/oci/config', fd, { headers: { 'Content-Type': 'multipart/form-data' } }) as unknown as Promise<{ id: string; tenancy_name: string; region: string }>, + + update: (id: string, fd: FormData) => + client.put(`/oci/configs/${id}`, fd, { headers: { 'Content-Type': 'multipart/form-data' } }) as unknown as Promise<{ id: string; tenancy_name: string; region: string }>, + + remove: (id: string) => + client.delete(`/oci/configs/${id}`) as unknown as Promise<{ ok: boolean }>, + + test: (id: string) => + client.post(`/oci/test/${id}`) as unknown as Promise, +}; diff --git a/frontend-react/src/api/endpoints/reports.ts b/frontend-react/src/api/endpoints/reports.ts new file mode 100644 index 0000000..b89f8a2 --- /dev/null +++ b/frontend-react/src/api/endpoints/reports.ts @@ -0,0 +1,142 @@ +import client from '../client'; + +/* ── Types ── */ + +export interface Report { + id: string; + tenancy_name: string; + status: string; + level: number | null; + regions?: string[]; + obp?: boolean; + raw?: boolean; + redact_output?: boolean; + created_at: string; + completed_at: string | null; + error_msg?: string | null; +} + +export interface RunReportBody { + config_id: string; + regions?: string[]; + level: 1 | 2; + obp: boolean; + raw: boolean; + redact_output: boolean; +} + +export interface RunReportResult { + report_id: string; + status: string; +} + +export interface ReportProgress { + status: string; + progress: string[]; + created_at: string; + completed_at: string | null; + error_msg: string | null; +} + +export interface ReportSummary { + tenancy: string; + level: number; + regions: string[]; + generated_at: string; + total: number; + passed: number; + failed: number; + total_findings: number; + total_compliant: number; + score: number; + sections: Record; +} + +export interface ReportFile { + id: string; + file_name: string; + file_type: string; + file_category: string; + file_size: number; +} + +export interface CisEngineVersion { + version: string; + updated_date: string; + file_path: string; +} + +export interface CisEngineUpdate { + local_version: string; + local_date: string; + remote_version: string; + remote_date: string; + update_available: boolean; +} + +/* ── API ── */ + +export const reportsApi = { + run: (body: RunReportBody) => + client.post('/reports/run', body) as unknown as Promise, + + list: () => + client.get('/reports') as unknown as Promise, + + progress: (rid: string) => + client.get(`/reports/${rid}/progress`) as unknown as Promise, + + cancel: (rid: string) => + client.post(`/reports/${rid}/cancel`) as unknown as Promise<{ ok: boolean }>, + + delete: (rid: string) => + client.delete(`/reports/${rid}`) as unknown as Promise<{ ok: boolean }>, + + summary: (rid: string) => + client.get(`/reports/${rid}/summary`) as unknown as Promise, + + htmlUrl: (rid: string) => { + const token = localStorage.getItem('t') || ''; + return `/api/reports/${rid}/html?token=${encodeURIComponent(token)}`; + }, + + complianceReportUrl: (rid: string) => { + const token = localStorage.getItem('t') || ''; + return `/api/reports/${rid}/compliance-report?token=${encodeURIComponent(token)}`; + }, + + generateComplianceReport: (rid: string) => + client.post(`/reports/${rid}/compliance-report/generate`) as unknown as Promise<{ status: string; has_rag: boolean }>, + + complianceReportStatus: (rid: string) => + client.get(`/reports/${rid}/compliance-report/status`) as unknown as Promise<{ ready: boolean }>, + + files: (rid: string) => + client.get(`/reports/${rid}/files`) as unknown as Promise, + + downloadUrl: (rid: string, fid: string) => { + const token = localStorage.getItem('t') || ''; + return `/api/reports/${rid}/files/${fid}/download?token=${encodeURIComponent(token)}`; + }, + + engineVersion: () => + client.get('/cis-engine/version') as unknown as Promise, + + engineCheckUpdate: () => + client.get('/cis-engine/check-update') as unknown as Promise, + + engineUpdate: () => + client.post('/cis-engine/update') as unknown as Promise<{ ok: boolean }>, + + embedReport: (rid: string, adbConfigId: string, tableName?: string) => + client.post(`/embeddings/report/${rid}`, { + adb_config_id: adbConfigId, + ...(tableName ? { table_name: tableName } : {}), + }) as unknown as Promise<{ ok: boolean; message: string; sections: number; tenancy: string }>, + + embedFile: (rid: string, fid: string, adbConfigId: string, tableName?: string) => + client.post(`/embeddings/report/${rid}/file/${fid}`, { + adb_config_id: adbConfigId, + ...(tableName ? { table_name: tableName } : {}), + }) as unknown as Promise<{ ok: boolean; message: string; chunks: number }>, +}; diff --git a/frontend-react/src/api/endpoints/terraform.ts b/frontend-react/src/api/endpoints/terraform.ts new file mode 100644 index 0000000..8e84f7b --- /dev/null +++ b/frontend-react/src/api/endpoints/terraform.ts @@ -0,0 +1,136 @@ +import client from '../client'; + +/* ── Types ── */ + +export type WsStatus = + | 'draft' | 'planning' | 'planned' + | 'applying' | 'applied' + | 'destroying' | 'destroyed' + | 'failed'; + +export interface Workspace { + id: string; + name: string; + session_id: string; + session_title?: string; + oci_config_id: string; + compartment_id?: string; + status: WsStatus; + plan_output?: string; + apply_output?: string; + destroy_output?: string; + error?: string; + tf_code?: string; + created_at: string; + updated_at: string; +} + +export interface WsStatusResult { + status: WsStatus; + plan_output?: string; + apply_output?: string; + destroy_output?: string; + error?: string; + updated_at: string; +} + +export interface ChatSession { + id: string; + title: string; + agent_type: string; + created_at: string; + updated_at: string; +} + +export interface ChatMessage { + id: string; + role: 'user' | 'assistant'; + content: string; + created_at?: string; +} + +export interface PromptResponse { + message_id: string; + session_id: string; + status: 'processing' | 'done'; + prompt?: string; +} + +export interface MessageStatus { + status: 'processing' | 'done' | 'failed'; + content: string; +} + +/* ── Workspace API ── */ + +export const terraformApi = { + /* Workspaces */ + listWorkspaces: () => + client.get('/terraform/workspaces') as unknown as Promise, + + getWorkspace: (wid: string) => + client.get(`/terraform/workspaces/${wid}`) as unknown as Promise, + + deleteWorkspace: (wid: string) => + client.delete(`/terraform/workspaces/${wid}`) as unknown as Promise<{ ok: boolean }>, + + plan: (wid: string) => + client.post(`/terraform/workspaces/${wid}/plan`) as unknown as Promise<{ status: string }>, + + apply: (wid: string) => + client.post(`/terraform/workspaces/${wid}/apply`) as unknown as Promise<{ status: string }>, + + destroy: (wid: string) => + client.post(`/terraform/workspaces/${wid}/destroy`, { confirm: 'DESTROY' }) as unknown as Promise<{ status: string }>, + + cancel: (wid: string) => + client.post(`/terraform/workspaces/${wid}/cancel`) as unknown as Promise<{ ok: boolean }>, + + status: (wid: string) => + client.get(`/terraform/workspaces/${wid}/status`) as unknown as Promise, + + downloadUrl: (wid: string) => { + const token = localStorage.getItem('t') || ''; + return `/api/terraform/workspaces/${wid}/download?token=${encodeURIComponent(token)}`; + }, + + /* Prompt Generator */ + generatePrompt: (body: { + message: string; + session_id?: string; + genai_config: Record; + history?: { role: string; content: string }[] | null; + }) => + client.post('/terraform/generate-prompt', body) as unknown as Promise, + + /* Chat sessions / history */ + listSessions: (agentType: string, limit = 50) => + client.get(`/chat/sessions?agent_type=${agentType}&limit=${limit}`) as unknown as Promise, + + loadSession: (sid: string) => + client.get(`/chat/sessions/${sid}/messages`) as unknown as Promise<{ messages: ChatMessage[] }>, + + deleteSession: (sid: string) => + client.delete(`/chat/sessions/${sid}`) as unknown as Promise<{ ok: boolean }>, + + pollMessageStatus: (mid: string) => + client.get(`/chat/${mid}/status`) as unknown as Promise, + + createWorkspace: (body: { session_id?: string; oci_config_id: string; compartment_id?: string; name: string; tf_code: string }) => + client.post('/terraform/workspaces', body) as unknown as Promise, + + updateCode: (wid: string, tf_code: string) => + client.put(`/terraform/workspaces/${wid}/code`, { tf_code }) as unknown as Promise<{ ok: boolean }>, + + chat: (body: { message: string; session_id?: string; genai_config_id?: string; model_id?: string; oci_config_id?: string; genai_region?: string; compartment_id?: string; temperature?: number; max_tokens?: number; use_tools?: boolean }) => + client.post('/terraform/chat', body) as unknown as Promise<{ message_id: string; session_id: string; status: string }>, + + loadResources: (ociConfigId: string, compartmentId: string, region?: string) => { + const params = new URLSearchParams({ oci_config_id: ociConfigId, compartment_id: compartmentId }); + if (region) params.set('region', region); + return client.get(`/terraform/resources?${params}`) as unknown as Promise>; + }, + + getCompartmentTree: (cid: string) => + client.get(`/oci/explore/${cid}/compartment-tree`) as unknown as Promise, +}; diff --git a/frontend-react/src/components/layout/AppShell.tsx b/frontend-react/src/components/layout/AppShell.tsx new file mode 100644 index 0000000..dd3bccb --- /dev/null +++ b/frontend-react/src/components/layout/AppShell.tsx @@ -0,0 +1,27 @@ +import { Outlet, Navigate } from 'react-router-dom'; +import { useAuthStore } from '@/stores/auth'; +import Sidebar from './Sidebar'; + +export default function AppShell() { + const { user, loading } = useAuthStore(); + + if (loading) { + return ( +
+
+
+ ); + } + + if (!user) return ; + + return ( +
+ +
+ +
+
+ ); +} diff --git a/frontend-react/src/components/layout/Sidebar.tsx b/frontend-react/src/components/layout/Sidebar.tsx new file mode 100644 index 0000000..9379f84 --- /dev/null +++ b/frontend-react/src/components/layout/Sidebar.tsx @@ -0,0 +1,186 @@ +import { NavLink } from 'react-router-dom'; +import { + MessageSquare, Search, BarChart3, Cloud, + Brain, Plug, Database, Dna, Users, Lock, FileText, + Sparkles, Sun, Moon, LogOut, FolderOpen, Languages +} from 'lucide-react'; +import { useTheme } from '@/hooks/useTheme'; +import { useAuthStore } from '@/stores/auth'; +import { useI18n } from '@/i18n'; + +const LOGO = ( + + + + + + + + + + + +); + +const SZ = 18; + +const TerraformIcon = ({ size = 18 }: { size?: number }) => ( + + + + + + +); + +interface NavItem { + to: string; + icon: React.ReactNode; + labelKey: string; + adminOnly?: boolean; + sub?: boolean; +} + +const NAV_SECTIONS: { titleKey: string; items: NavItem[] }[] = [ + { + titleKey: 'nav.principal', + items: [ + { to: '/chat', icon: , labelKey: 'nav.chat' }, + { to: '/terraform', icon: , labelKey: 'nav.terraform' }, + { to: '/terraform/prompt', icon: , labelKey: 'nav.promptGen', sub: true }, + { to: '/terraform/workspaces', icon: , labelKey: 'nav.workspaces', sub: true }, + { to: '/explorer', icon: , labelKey: 'nav.explorer' }, + { to: '/reports', icon: , labelKey: 'nav.reports' }, + ], + }, + { + titleKey: 'nav.config', + items: [ + { to: '/config/oci', icon: , labelKey: 'nav.ociCreds' }, + { to: '/config/genai', icon: , labelKey: 'nav.genai' }, + { to: '/config/mcp', icon: , labelKey: 'nav.mcp' }, + { to: '/config/adb', icon: , labelKey: 'nav.adb' }, + { to: '/config/embeddings', icon: , labelKey: 'nav.embeddings' }, + { to: '/config/embeddings/consult', icon: , labelKey: 'nav.embConsult', sub: true }, + ], + }, + { + titleKey: 'nav.admin', + items: [ + { to: '/admin/users', icon: , labelKey: 'nav.users', adminOnly: true }, + { to: '/admin/mfa', icon: , labelKey: 'nav.mfa' }, + { to: '/admin/audit', icon: , labelKey: 'nav.audit', adminOnly: true }, + ], + }, +]; + +export default function Sidebar() { + const { toggle, isDark } = useTheme(); + const { user, logout } = useAuthStore(); + const { t, lang, setLang } = useI18n(); + + return ( + + ); +} diff --git a/frontend-react/src/components/shared/StubPage.tsx b/frontend-react/src/components/shared/StubPage.tsx new file mode 100644 index 0000000..9f38607 --- /dev/null +++ b/frontend-react/src/components/shared/StubPage.tsx @@ -0,0 +1,68 @@ +import type { ReactNode } from 'react'; +import { ArrowRight, Wrench } from 'lucide-react'; + +interface StubPageProps { + icon: ReactNode; + title: string; + description: string; + legacyTab: string; + color?: string; + features?: string[]; +} + +export default function StubPage({ icon, title, description, legacyTab, color = 'var(--ac)', features }: StubPageProps) { + return ( +
+
+ {/* Icon with glow */} +
+
+
+
{icon}
+
+
+ + {/* Title */} +

{title}

+

{description}

+ + {/* Features preview */} + {features && ( +
+
+ + + Em desenvolvimento + +
+ {features.map((f, i) => ( +
+
+ {f} +
+ ))} +
+ )} + + {/* CTA */} + { e.currentTarget.style.transform = 'translateY(-1px)'; e.currentTarget.style.boxShadow = 'var(--sh2)'; }} + onMouseLeave={(e) => { e.currentTarget.style.transform = ''; e.currentTarget.style.boxShadow = ''; }} + > + Abrir versao atual + + + +

+ Migrando para React β€” em breve nesta interface +

+
+
+ ); +} diff --git a/frontend-react/src/hooks/usePolling.ts b/frontend-react/src/hooks/usePolling.ts new file mode 100644 index 0000000..029840f --- /dev/null +++ b/frontend-react/src/hooks/usePolling.ts @@ -0,0 +1,25 @@ +import { useEffect, useRef } from 'react'; + +export function usePolling( + callback: () => Promise, + intervalMs: number, + enabled: boolean +) { + const savedCallback = useRef(callback); + savedCallback.current = callback; + + useEffect(() => { + if (!enabled) return; + + let active = true; + const poll = async () => { + if (!active) return; + const shouldStop = await savedCallback.current(); + if (shouldStop || !active) return; + setTimeout(poll, intervalMs); + }; + poll(); + + return () => { active = false; }; + }, [intervalMs, enabled]); +} diff --git a/frontend-react/src/hooks/useTheme.ts b/frontend-react/src/hooks/useTheme.ts new file mode 100644 index 0000000..89d0c07 --- /dev/null +++ b/frontend-react/src/hooks/useTheme.ts @@ -0,0 +1,20 @@ +import { useState, useCallback, useEffect } from 'react'; + +type Theme = 'light' | 'dark'; + +export function useTheme() { + const [theme, setTheme] = useState(() => { + return (localStorage.getItem('theme') as Theme) || 'light'; + }); + + useEffect(() => { + document.documentElement.className = theme; + localStorage.setItem('theme', theme); + }, [theme]); + + const toggle = useCallback(() => { + setTheme((t) => (t === 'dark' ? 'light' : 'dark')); + }, []); + + return { theme, toggle, isDark: theme === 'dark' }; +} diff --git a/frontend-react/src/i18n/en.ts b/frontend-react/src/i18n/en.ts new file mode 100644 index 0000000..9b8142c --- /dev/null +++ b/frontend-react/src/i18n/en.ts @@ -0,0 +1,666 @@ +const en: Record = { + // ── Sidebar ── + 'nav.principal': 'Main', + 'nav.config': 'Configuration', + 'nav.admin': 'Administration', + 'nav.chat': 'Chat Agent', + 'nav.terraform': 'Terraform', + 'nav.promptGen': 'Prompt Generator', + 'nav.workspaces': 'Workspaces', + 'nav.explorer': 'OCI Explorer', + 'nav.reports': 'Reports', + 'nav.ociCreds': 'OCI Credentials', + 'nav.genai': 'GenAI Config', + 'nav.mcp': 'MCP Servers', + 'nav.adb': 'ADB Vector', + 'nav.embeddings': 'Embeddings', + 'nav.embConsult': 'Query Embeddings', + 'nav.users': 'Users', + 'nav.mfa': 'MFA', + 'nav.audit': 'Audit Log', + 'sidebar.lightMode': 'Light mode', + 'sidebar.darkMode': 'Dark mode', + 'sidebar.logout': 'Logout', + 'sidebar.roleAdmin': 'Administrator', + 'sidebar.roleUser': 'User', + 'sidebar.subtitle': 'Infrastructure & Security', + + // ── Login ── + 'login.subtitle': 'Infrastructure & Security Engineer', + 'login.userPlaceholder': 'Username', + 'login.passPlaceholder': 'Password', + 'login.mfaScanQr': 'Scan the QR code with your authenticator app:', + 'login.totpPlaceholder': 'TOTP Code', + 'login.loading': 'Signing in...', + 'login.verify': 'Verify', + 'login.submit': 'Sign in', + 'login.error': 'Login failed', + + // ── Chat ── + 'chat.timeNow': 'now', + 'chat.today': 'Today', + 'chat.yesterday': 'Yesterday', + 'chat.last7': 'Last 7 days', + 'chat.last30': 'Last 30 days', + 'chat.older': 'Older', + 'chat.other': 'Other', + 'chat.history': 'History', + 'chat.newChat': 'New Chat', + 'chat.untitled': 'Untitled', + 'chat.noConversations': 'No conversations yet', + 'chat.selectModel': 'Select a model...', + 'chat.savedConfigs': 'Saved Configs', + 'chat.searchModel': 'Search model...', + 'chat.noModels': 'No models found', + 'chat.ociCred': 'OCI Credential...', + 'chat.settings': 'Settings', + 'chat.newChatTitle': 'New chat', + 'chat.emptyTitle': 'Start a conversation with the agent.', + 'chat.emptySubtitle': 'Select a model to get started.', + 'chat.thinking': 'Thinking...', + 'chat.retry': 'Retry', + 'chat.attachFile': 'Attach file', + 'chat.placeholder': 'Type your message... (Shift+Enter for new line)', + 'chat.send': 'Send', + 'chat.selectOciCred': 'Select an OCI credential to use direct models.', + 'chat.sendError': 'Failed to send message', + 'chat.requestFailed': 'The request failed.', + 'chat.timeout': 'Timeout: the response is taking too long. Try again.', + 'chat.toolsAvailable': 'tools available', + 'chat.ociCredLabel': 'OCI Credential', + 'chat.select': 'Select...', + 'chat.analyzeFiles': 'Analyze the attached files.', + + // ── Terraform ── + 'tf.history': 'History', + 'tf.newChat': 'New', + 'tf.noConversations': 'No conversations', + 'tf.searchModel': 'Search model...', + 'tf.selectModel': 'Select model...', + 'tf.savedConfigs': 'Saved Configs', + 'tf.ociConfig': 'OCI Config...', + 'tf.settings': 'Settings', + 'tf.newConversation': 'New conversation', + 'tf.emptyTitle': 'Describe the OCI infrastructure', + 'tf.emptySubtitle': 'The agent will generate Terraform code based on your description.', + 'tf.thinking': 'Processing...', + 'tf.retry': 'Retry', + 'tf.placeholder': 'Describe the desired infrastructure... (Shift+Enter for new line)', + 'tf.send': 'Send', + 'tf.workspace': 'Workspace', + 'tf.files': 'Files', + 'tf.terminal': 'Terminal', + 'tf.resources': 'Resources', + 'tf.plan': 'Plan', + 'tf.apply': 'Apply', + 'tf.destroy': 'Destroy', + 'tf.cancel': 'Cancel', + 'tf.download': 'Download ZIP', + 'tf.createWs': 'Create Workspace', + 'tf.noFiles': 'No files generated', + 'tf.timeout': 'Timeout: generation is taking too long.', + 'tf.selectOci': 'Select an OCI credential', + 'tf.compartment': 'Compartment', + 'tf.region': 'Region', + 'tf.rollback': 'Rollback', + 'tf.copy': 'Copy', + 'tf.copied': 'Copied', + 'tf.noWs': 'No active workspace', + 'tf.deleteWs': 'Delete', + + // ── Prompt Generator ── + 'pg.history': 'History', + 'pg.new': 'New', + 'pg.noConversations': 'No conversations', + 'pg.emptyTitle': 'Describe the OCI infrastructure you need', + 'pg.emptySubtitle': 'The agent will generate a structured, optimized prompt for the Terraform Agent.', + 'pg.loading': 'Generating prompt...', + 'pg.placeholder': 'Describe the desired infrastructure... (Shift+Enter for new line)', + 'pg.copy': 'Copy', + 'pg.copied': 'Copied', + 'pg.retry': 'Retry', + 'pg.timeout': 'Timeout: generation is taking too long.', + 'pg.search': 'Search...', + 'pg.selectModel': 'Select model...', + + // ── Workspaces ── + 'ws.title': 'Terraform Workspaces', + 'ws.all': 'All', + 'ws.active': 'Active', + 'ws.draft': 'Draft', + 'ws.failed': 'Failed', + 'ws.destroyed': 'Destroyed', + 'ws.loading': 'Loading...', + 'ws.refresh': 'Refresh', + 'ws.noWs': 'No workspaces found', + 'ws.noWsHint': 'Create workspaces via the Terraform Agent', + 'ws.loadingWs': 'Loading workspaces...', + 'ws.open': 'Open', + 'ws.plan': 'Plan', + 'ws.apply': 'Apply', + 'ws.destroy': 'Destroy', + 'ws.delete': 'Delete', + 'ws.cancel': 'Cancel', + 'ws.hideOutput': 'hide', + 'ws.viewOutput': 'view output', + 'ws.confirmDestroy': 'Confirm Destroy', + 'ws.confirmDestroyMsg': 'This action will destroy all resources provisioned by this workspace.', + 'ws.typeDestroy': 'Type DESTROY to confirm:', + 'ws.deleteWs': 'Delete Workspace', + 'ws.deleteWsMsg': 'Delete this workspace permanently? Local files will be removed.', + 'ws.noDate': 'No date', + + // ── Explorer ── + 'exp.title': 'OCI Explorer', + 'exp.subtitle': 'Explore your Oracle Cloud Infrastructure resources', + 'exp.search': 'Search resource...', + 'exp.refresh': 'Refresh', + 'exp.loading': 'Loading...', + 'exp.noResources': 'No resources found', + 'exp.selectResource': 'Select a resource type', + 'exp.compartment': 'Compartment', + 'exp.region': 'Region', + 'exp.allRegions': 'All regions', + 'exp.start': 'Start', + 'exp.stop': 'Stop', + 'exp.details': 'Details', + 'exp.total': 'Total', + 'exp.network': 'Network', + 'exp.observability': 'Observability', + 'exp.security': 'Security', + 'exp.selectRegions': 'Select regions', + 'exp.nRegions': '{0} regions', + 'exp.all': 'All', + 'exp.none': 'None', + 'exp.noCompartment': 'No compartments found', + 'exp.selectConfig': 'Select an OCI config', + 'exp.loadingResources': 'Loading resources...', + 'exp.clearFilter': 'Try clearing the search filter', + 'exp.resources': 'resources', + 'exp.updating': 'updating...', + 'exp.regions': 'region(s)', + 'exp.filter': 'Filter...', + 'exp.startAction': 'Start', + 'exp.stopAction': 'Stop', + 'exp.updatingState': 'Updating...', + 'exp.createdAt': 'Created at', + 'exp.public': 'Public', + 'exp.yesPublic': 'Yes', + 'exp.noPrivate': 'No (private)', + 'exp.freeTier': 'Free Tier', + 'exp.yes': 'Yes', + 'exp.no': 'No', + 'exp.noRestrictions': 'No restrictions (open)', + 'exp.updateNetworkAccess': 'Update Network Access', + 'exp.accessUpdated': 'Access updated: ', + 'exp.objects': 'Objects', + 'exp.size': 'Size', + 'exp.visibility': 'Visibility', + 'exp.private': 'Private', + 'exp.rules': 'Rules', + 'exp.routes': 'Routes', + 'exp.hideRules': 'Hide rules', + 'exp.viewRules': 'View rules', + 'exp.add': 'Add', + 'exp.direction': 'Direction', + 'exp.protocol': 'Protocol', + 'exp.originDest': 'Source/Dest', + 'exp.ports': 'Ports', + 'exp.description': 'Description', + 'exp.portMin': 'Port Min', + 'exp.portMax': 'Port Max', + 'exp.myIp': 'My IP', + 'exp.save': 'Save', + 'exp.cancel': 'Cancel', + 'exp.ruleAdded': 'Rule added.', + 'exp.enterCidr': 'Enter the CIDR.', + 'exp.removeRule': 'Remove this rule?', + 'exp.removeNsgRule': 'Remove this NSG rule?', + 'exp.ipDetected': 'IP detected: {0} β€” adjust port and click Save', + 'exp.ipDetectFailed': 'Could not detect IP.', + 'exp.destination': 'Destination', + 'exp.destType': 'Dest Type', + 'exp.version': 'Version', + 'exp.ha': 'HA', + + // ── Reports ── + 'rpt.title': 'CIS Reports', + 'rpt.subtitle': 'Run CIS OCI Benchmark compliance scans', + 'rpt.startScan': 'Start Scan', + 'rpt.running': 'Running...', + 'rpt.cancel': 'Cancel', + 'rpt.tenancy': 'Tenancy', + 'rpt.level': 'Level', + 'rpt.region': 'Region', + 'rpt.allRegions': 'All regions', + 'rpt.noReports': 'No reports found', + 'rpt.noReportsHint': 'Run a scan to generate compliance reports.', + 'rpt.progress': 'Progress', + 'rpt.completed': 'Completed', + 'rpt.failed': 'Failed', + 'rpt.viewReport': 'View Report', + 'rpt.download': 'Download', + 'rpt.delete': 'Delete', + 'rpt.summary': 'Summary', + 'rpt.passed': 'Passed', + 'rpt.findings': 'Findings', + 'rpt.recommendations': 'Recommendations', + 'rpt.selectTenancy': 'Select a tenancy', + 'rpt.selectCredential': 'Select a credential', + 'rpt.loadingReports': 'Loading reports...', + 'rpt.refresh': 'Refresh', + 'rpt.pageTitle': 'CIS Reports', + 'rpt.pageSubtitle': 'CIS Benchmark 3.0 β€” Compliance Scan & Dashboard', + 'rpt.runScanCis': 'Run CIS Scan', + 'rpt.inExecution': 'Running', + 'rpt.configParams': 'Configure the parameters and run the CIS compliance scan.', + 'rpt.ociCredential': 'OCI Credential', + 'rpt.selectOciCred': 'Select OCI credential...', + 'rpt.cisLevel': 'CIS Level', + 'rpt.level1Desc': 'Essential security checks', + 'rpt.level2Desc': 'Complete checks (includes Level 1)', + 'rpt.regions': 'Regions', + 'rpt.leaveEmpty': 'Leave empty to scan all subscribed regions', + 'rpt.executeCompliance': 'Run CIS Compliance', + 'rpt.scanProgress': 'Scan Progress', + 'rpt.hideLog': 'Hide log', + 'rpt.showLog': 'View full log', + 'rpt.lines': 'lines', + 'rpt.cancelScan': 'Cancel Scan', + 'rpt.selectReport': 'Select Report', + 'rpt.selectCompletedReport': 'Select a completed report...', + 'rpt.searchTenancy': 'Search tenancy...', + 'rpt.searchByTenancy': 'Search by tenancy...', + 'rpt.noCredentials': 'No credentials found', + 'rpt.allRegionsDefault': 'All regions (default)', + 'rpt.searchRegion': 'Search region...', + 'rpt.loadingDashboard': 'Loading dashboard...', + 'rpt.complianceScore': 'Compliance Score', + 'rpt.compliant': 'Compliant', + 'rpt.nonCompliant': 'Non-Compliant', + 'rpt.ofTotal': 'of total', + 'rpt.compliantItems': 'compliant items', + 'rpt.totalFindings': 'total findings', + 'rpt.evaluated': 'Evaluated', + 'rpt.controlsVerified': 'Controls verified', + 'rpt.regionsCount': 'region(s)', + 'rpt.complianceDist': 'Compliance Distribution', + 'rpt.findingsBySection': 'Findings by Section', + 'rpt.generatedAt': 'Generated at', + 'rpt.htmlReport': 'HTML Report', + 'rpt.files': 'Files', + 'rpt.inSections': 'sections', + 'rpt.fullEmbedding': 'Full Embedding', + 'rpt.noActiveTables': 'No active tables', + 'rpt.executionHistory': 'Execution History', + 'rpt.reports': 'report(s)', + 'rpt.loading': 'Loading...', + 'rpt.firstScanHint': 'Run the first CIS scan above.', + 'rpt.selected': 'Selected', + 'rpt.deleteConfirm': 'Delete this report?', + 'rpt.deleteFailed': 'Failed to delete', + 'rpt.selectOciCredError': 'Select an OCI credential', + 'rpt.alreadyRunning': 'A scan is already running', + 'rpt.errorLoadReports': 'Error loading reports', + 'rpt.errorStartScan': 'Error starting scan', + 'rpt.selectAdb': 'Select an ADB connection.', + 'rpt.selectTable': 'Select a table.', + 'rpt.errorEmbedding': 'Error generating embedding', + 'rpt.cancelled': 'Cancelled', + 'rpt.completedAt': 'Completed:', + + // ── Downloads ── + 'dl.title': 'Downloads', + 'dl.subtitle': 'Download generated CIS reports', + 'dl.allTenancies': 'All Tenancies', + 'dl.allSections': 'All sections', + 'dl.reports': 'report(s)', + 'dl.refresh': 'Refresh', + 'dl.loadError': 'Error loading reports', + 'dl.loading': 'Loading reports...', + 'dl.noReports': 'No completed reports.', + 'dl.noReportsHint': 'Run a scan in the Reports tab to generate reports.', + 'dl.files': 'file(s)', + 'dl.hide': 'Hide', + 'dl.viewFiles': 'View Files', + 'dl.loadingFiles': 'Loading files...', + 'dl.noFiles': 'No files found', + 'dl.filesInSections': 'file(s) in {0} sections', + + // ── OCI Config ── + 'oci.title': 'OCI Credentials', + 'oci.subtitle': 'Manage Oracle Cloud Infrastructure credentials', + 'oci.registered': 'Registered Credentials', + 'oci.credential': 'credential', + 'oci.credentials': 'credentials', + 'oci.noCredentials': 'No credentials registered.', + 'oci.edit': 'Edit', + 'oci.test': 'Test', + 'oci.delete': 'Delete', + 'oci.yes': 'Yes', + 'oci.no': 'No', + 'oci.newCredential': 'New Credential', + 'oci.editCredential': 'Edit Credential', + 'oci.addDesc': 'Add your OCI account credentials to run reports and access services.', + 'oci.editing': 'Editing:', + 'oci.tenancyName': 'Tenancy Name', + 'oci.ocidTenancy': 'OCID Tenancy', + 'oci.region': 'Region', + 'oci.selectRegion': 'Select region...', + 'oci.searchRegion': 'Search region...', + 'oci.noRegion': 'No regions found', + 'oci.compartment': 'Compartment OCID', + 'oci.ocidUser': 'OCID User', + 'oci.fingerprint': 'Fingerprint', + 'oci.sessionToken': 'Session Token', + 'oci.sessionTokenHint': 'Paste the token file content generated by', + 'oci.sessionKeyLabel': 'Session Key (.pem)', + 'oci.privateKey': 'Private Key (.pem)', + 'oci.publicKey': 'Public Key (.pem)', + 'oci.keyPassphrase': 'Key Passphrase', + 'oci.keyPassphraseHint': 'Only if the private key is password protected', + 'oci.sshPublicKey': 'SSH Public Key', + 'oci.sshPublicKeyHint': 'SSH key for compute instance access (e.g. ssh-rsa AAAA...)', + 'oci.saveCredential': 'Save Credential', + 'oci.saveChanges': 'Save Changes', + 'oci.cancel': 'Cancel', + 'oci.saving': 'Saving credential...', + 'oci.saved': 'Credential saved successfully!', + 'oci.updated': 'Credential updated successfully!', + 'oci.deleting': 'Deleting credential...', + 'oci.deleted': 'Credential deleted successfully!', + 'oci.fillField': 'Fill in', + 'oci.fillOcidTenancy': 'Fill in OCID Tenancy', + 'oci.fillUserAndFP': 'Fill in OCID User and Fingerprint', + 'oci.pasteToken': 'Paste the Session Token', + 'oci.selectKey': 'Select the private key (.pem)', + 'oci.selectSessionKey': 'Select the session key (.pem)', + 'oci.changeHint': 'Fill to change. Current:', + 'oci.keepKey': 'Leave empty to keep current key', + 'oci.typeCannotChange': '(type cannot be changed)', + 'oci.sshNotConfigured': 'not configured', + + // ── GenAI Config ── + 'genai.title': 'GenAI Models', + 'genai.subtitle': 'Configure generative AI models for the Chat Agent', + 'genai.configured': 'Configured Models', + 'genai.model': 'model', + 'genai.models': 'models', + 'genai.noModels': 'No models configured.', + 'genai.newModel': 'New GenAI Model', + 'genai.editModel': 'Edit Model', + 'genai.configName': 'Config Name', + 'genai.ociCredential': 'OCI Credential', + 'genai.modelSelect': 'Model', + 'genai.genaiRegion': 'GenAI Region', + 'genai.compartment': 'Compartment OCID', + 'genai.modelOcid': 'Model OCID', + 'genai.modelOcidHint': 'Paste the model OCID from OCI portal (optional)', + 'genai.customOcid': 'Custom (use OCID)', + 'genai.default': 'Default Model', + 'genai.setDefault': 'Set as Default', + 'genai.saveModel': 'Save Model', + 'genai.saveChanges': 'Save Changes', + 'genai.cancel': 'Cancel', + 'genai.saving': 'Saving model...', + 'genai.deleting': 'Deleting model...', + 'genai.addDesc': 'Add custom models or create presets with specific credentials.', + 'genai.fillName': 'Fill in config name', + 'genai.selectOci': 'Select an OCI credential', + 'genai.fillOcid': 'Enter Model OCID for custom model', + + // ── MCP Servers ── + 'mcp.title': 'MCP Servers', + 'mcp.subtitle': 'MCP servers for tool integration and task execution', + 'mcp.registered': 'Registered Servers', + 'mcp.noServers': 'No MCP servers registered.', + 'mcp.edit': 'Edit', + 'mcp.activate': 'Activate', + 'mcp.deactivate': 'Deactivate', + 'mcp.active': 'Active', + 'mcp.inactive': 'Inactive', + 'mcp.delete': 'Delete', + 'mcp.yes': 'Yes', + 'mcp.no': 'No', + 'mcp.command': 'Command:', + 'mcp.url': 'URL:', + 'mcp.module': 'Module:', + 'mcp.adb': 'ADB:', + 'mcp.noAdb': 'None', + 'mcp.tools': 'Tools', + 'mcp.noTools': 'No tools registered', + 'mcp.discoverTools': 'Discover Tools', + 'mcp.discovering': 'Connecting to MCP server to discover tools...', + 'mcp.newServer': 'Register New Server', + 'mcp.editServer': 'Edit Server', + 'mcp.name': 'Name', + 'mcp.description': 'Description', + 'mcp.serverType': 'Server Type', + 'mcp.commandLabel': 'Command', + 'mcp.commandHint': 'Command to start the server', + 'mcp.argsLabel': 'Arguments (JSON)', + 'mcp.argsHint': 'Array of arguments passed to the command', + 'mcp.urlLabel': 'Server URL', + 'mcp.urlHint': 'MCP server SSE endpoint', + 'mcp.linkAdb': 'Link ADB Vector', + 'mcp.linkAdbHint': 'Optional β€” the MCP server can use this database as a tool.', + 'mcp.saveServer': 'Register Server', + 'mcp.saveChanges': 'Save Changes', + 'mcp.cancel': 'Cancel', + 'mcp.saving': 'Registering server...', + 'mcp.updating': 'Updating server...', + 'mcp.fillName': 'Fill in server name', + 'mcp.invalidArgs': 'Arguments must be a valid JSON array', + 'mcp.configDesc': 'Configure a new MCP server. Fields adjust based on the selected type.', + + // ── ADB Config ── + 'adb.title': 'ADB Connections', + 'adb.subtitle': 'Oracle Autonomous Database connections via Wallet (mTLS)', + 'adb.registered': 'Registered Connections', + 'adb.connection': 'connection', + 'adb.connections': 'connections', + 'adb.noConnections': 'No ADB connections registered.', + 'adb.edit': 'Edit', + 'adb.test': 'Test', + 'adb.delete': 'Delete', + 'adb.yes': 'Yes', + 'adb.no': 'No', + 'adb.newConnection': 'New Connection', + 'adb.editConnection': 'Edit Connection', + 'adb.connName': 'Connection Name', + 'adb.walletZip': 'Wallet ZIP', + 'adb.walletHint': 'Upload wallet to extract DSNs from tnsnames.ora', + 'adb.walletEditHint': 'Upload new wallet or leave empty to keep current', + 'adb.analyze': 'Analyze', + 'adb.dsn': 'DSN (TNS Name)', + 'adb.dsnHint': 'Select or type the DSN. E.g. myatp_high', + 'adb.username': 'Username', + 'adb.password': 'Password', + 'adb.walletPassword': 'Wallet Password', + 'adb.genaiConfig': 'GenAI Config (Embeddings)', + 'adb.genaiConfigHint': 'OCI credentials used to generate embeddings via GenAI.', + 'adb.noRag': 'None (no RAG)', + 'adb.embeddingModel': 'Embedding Model', + 'adb.embeddingModelHint': 'OCI GenAI model to generate vectors.', + 'adb.saveConnection': 'Save Connection', + 'adb.saveChanges': 'Save Changes', + 'adb.cancel': 'Cancel', + 'adb.saving': 'Saving connection...', + 'adb.deleting': 'Deleting connection...', + 'adb.updateWallet': 'Update Wallet', + 'adb.adbConnection': 'ADB Connection', + 'adb.uploadWallet': 'Upload Wallet', + 'adb.connDesc': 'Connection via python-oracledb Thin mode with Wallet (mTLS) to Oracle Autonomous Database.', + 'adb.selectWallet': 'Select a wallet ZIP file.', + 'adb.selectConnection': 'Select an ADB connection.', + 'adb.fillName': 'Fill in connection name', + 'adb.fillDsn': 'Fill in DSN', + 'adb.fillUsername': 'Fill in username', + 'adb.fillPassword': 'Fill in password', + + // ── Embeddings ── + 'emb.title': 'Embeddings', + 'emb.subtitle': 'Vector knowledge base β€” upload, import and management', + 'emb.noAdb': 'No ADB connection configured.', + 'emb.noAdbHint': 'Configure in ADB Vector.', + 'emb.noOci': 'No OCI credential configured.', + 'emb.noOciHint': 'Configure in Config β†’ OCI.', + 'emb.existing': 'Existing Embeddings', + 'emb.existingDesc': 'Query documents stored in ADB embedding tables.', + 'emb.adbConnection': 'ADB Connection', + 'emb.table': 'Table', + 'emb.load': 'Load', + 'emb.noEmbeddings': 'No embeddings found.', + 'emb.total': 'Total: {0} documents', + 'emb.cisTitle': 'CIS Recommendations', + 'emb.cisDesc': 'Upload the latest PDF version to keep CIS Oracle Cloud recommendations updated in the vector database.', + 'emb.pdfFile': 'PDF File', + 'emb.uploadCis': 'Upload CIS Recommendations', + 'emb.kbTitle': 'Knowledge Base', + 'emb.kbDesc': 'Upload documents to feed the team knowledge base. Files will be vectorized and available for chat queries.', + 'emb.fileLabel': 'File (.txt, .pdf, .csv, .json, .md)', + 'emb.uploadFile': 'Upload File', + 'emb.urlLabel': 'URL (web page or PDF)', + 'emb.importUrl': 'Import URL', + 'emb.purgeTitle': 'Purge & Re-embed', + 'emb.purgeDesc': 'Clear embeddings from a table to free space or prepare for re-ingestion. This action is irreversible.', + 'emb.tenancyOptional': 'Tenancy (optional)', + 'emb.purge': 'Purge', + 'emb.confirm': 'Confirm', + 'emb.selectTable': 'Select a table.', + 'emb.selectAdbAndFile': 'Select ADB connection and file.', + 'emb.selectAdbAndUrl': 'Select ADB connection and enter URL.', + 'emb.processing': 'Processing PDF and generating embeddings...', + 'emb.uploading': 'Uploading and generating embeddings...', + 'emb.fetchingUrl': 'Fetching content and generating embeddings...', + 'emb.purging': 'Clearing embeddings...', + + // ── EmbConsult ── + 'ec.title': 'Query Embeddings', + 'ec.subtitle': 'Ask about data stored in vector databases', + 'ec.clear': 'Clear', + 'ec.tableOptional': 'Table (optional)', + 'ec.allTables': 'All tables', + 'ec.emptyTitle': 'Make a query to search embeddings.', + 'ec.emptyHint': 'E.g. "findings for CIS 1.1", "networking recommendations", "how to fix MFA"', + 'ec.searching': 'Querying vector databases...', + 'ec.placeholder': 'Type your query...', + 'ec.submit': 'Query', + 'ec.sources': 'Consulted sources', + 'ec.noAnswer': 'No model response.', + + // ── Users ── + 'usr.title': 'Users', + 'usr.activeCount': 'active', + 'usr.activesCount': 'active', + 'usr.inactiveCount': 'inactive', + 'usr.inactivesCount': 'inactive', + 'usr.total': 'total', + 'usr.newUser': 'New User', + 'usr.cancel': 'Cancel', + 'usr.createUser': 'Create New User', + 'usr.firstName': 'First Name', + 'usr.lastName': 'Last Name', + 'usr.username': 'Username', + 'usr.email': 'Email', + 'usr.password': 'Password', + 'usr.role': 'Role', + 'usr.creating': 'Creating...', + 'usr.create': 'Create User', + 'usr.noUsers': 'No users registered', + 'usr.noUsersHint': 'Click "New User" to create the first user.', + 'usr.name': 'Name', + 'usr.mfa': 'MFA', + 'usr.status': 'Status', + 'usr.lastLogin': 'Last Login', + 'usr.actions': 'Actions', + 'usr.active': 'Active', + 'usr.inactive': 'Inactive', + 'usr.never': 'Never', + 'usr.confirm': 'Confirm', + 'usr.deactivate': 'Deactivate user', + 'usr.created': 'User created successfully!', + 'usr.roleUpdated': 'Role updated', + 'usr.deactivated': 'User deactivated', + 'usr.requiredName': 'First and last name are required', + 'usr.requiredUsername': 'Username is required', + 'usr.requiredPassword': 'Password is required', + + // ── MFA ── + 'mfa.title': 'MFA Authentication (TOTP)', + 'mfa.subtitle': 'Protect your account with two-factor authentication via Google Authenticator or Authy.', + 'mfa.enabled': 'MFA is enabled', + 'mfa.disabled': 'MFA is disabled', + 'mfa.enabledDesc': 'Your account is protected with two-factor authentication.', + 'mfa.disabledDesc': 'Enable MFA to add an extra layer of security.', + 'mfa.disableHint': 'To disable MFA, click the button below. This will reduce your account security.', + 'mfa.disableBtn': 'Disable MFA', + 'mfa.confirmDisable': 'Are you sure you want to disable MFA for your account?', + 'mfa.scanQr': 'Scan the QR Code or copy the secret', + 'mfa.openApp': 'Open your authenticator app (Google Authenticator, Authy, etc.)', + 'mfa.enterCode': 'Enter the code from your app', + 'mfa.activateMfa': 'Activate MFA', + 'mfa.generateSecret': 'Generate Secret', + 'mfa.generateHint': 'Click the button below to generate a TOTP secret. You will need an authenticator app to scan the QR Code.', + 'mfa.activated': 'MFA activated successfully!', + 'mfa.deactivated': 'MFA deactivated.', + 'mfa.invalidCode': 'Enter a 6-digit code', + + // ── Audit ── + 'audit.title': 'Audit Log', + 'audit.subtitle': 'System activity monitoring, configurations and chat sessions.', + 'audit.tabAudit': 'Audit Log', + 'audit.tabConfig': 'Config Logs', + 'audit.tabChat': 'Chat Logs', + 'audit.records': 'records', + 'audit.refresh': 'Refresh', + 'audit.all': 'All', + 'audit.errors': 'Errors', + 'audit.success': 'Success', + 'audit.info': 'Info', + 'audit.loadingLogs': 'Loading logs...', + 'audit.noAudit': 'No audit records.', + 'audit.noConfig': 'No configuration logs recorded.', + 'audit.noChat': 'No chat logs recorded.', + 'audit.date': 'Date', + 'audit.user': 'User', + 'audit.action': 'Action', + 'audit.resource': 'Resource', + 'audit.details': 'Details', + 'audit.ip': 'IP', + 'audit.config': 'Config', + 'audit.status': 'Status', + 'audit.message': 'Message', + 'audit.session': 'Session', + 'audit.source': 'Source', + + // ── Common ── + 'common.save': 'Save', + 'common.cancel': 'Cancel', + 'common.delete': 'Delete', + 'common.edit': 'Edit', + 'common.create': 'Create', + 'common.update': 'Refresh', + 'common.close': 'Close', + 'common.confirm': 'Confirm', + 'common.back': 'Back', + 'common.search': 'Search', + 'common.refresh': 'Refresh', + 'common.download': 'Download', + 'common.upload': 'Upload', + 'common.copy': 'Copy', + 'common.retry': 'Retry', + 'common.send': 'Send', + 'common.clear': 'Clear', + 'common.test': 'Test', + 'common.yes': 'Yes', + 'common.no': 'No', + 'common.loading': 'Loading...', + 'common.saving': 'Saving...', + 'common.select': 'Select...', + 'common.noData': 'No data found', + 'common.error': 'Error', + 'common.success': 'Success', +}; + +export default en; diff --git a/frontend-react/src/i18n/index.ts b/frontend-react/src/i18n/index.ts new file mode 100644 index 0000000..1e8d188 --- /dev/null +++ b/frontend-react/src/i18n/index.ts @@ -0,0 +1,31 @@ +import { create } from 'zustand'; +import pt from './pt'; +import en from './en'; + +type Lang = 'pt' | 'en'; + +const LANGS: Record> = { pt, en }; + +function detectLang(): Lang { + const saved = localStorage.getItem('lang') as Lang | null; + if (saved && LANGS[saved]) return saved; + return navigator.language.startsWith('pt') ? 'pt' : 'en'; +} + +interface I18nState { + lang: Lang; + setLang: (lang: Lang) => void; + t: (key: string) => string; +} + +export const useI18n = create((set, get) => ({ + lang: detectLang(), + setLang: (lang: Lang) => { + localStorage.setItem('lang', lang); + set({ lang }); + }, + t: (key: string) => { + const { lang } = get(); + return LANGS[lang]?.[key] ?? LANGS.pt[key] ?? key; + }, +})); diff --git a/frontend-react/src/i18n/pt.ts b/frontend-react/src/i18n/pt.ts new file mode 100644 index 0000000..086fa8b --- /dev/null +++ b/frontend-react/src/i18n/pt.ts @@ -0,0 +1,666 @@ +const pt: Record = { + // ── Sidebar ── + 'nav.principal': 'Principal', + 'nav.config': 'ConfiguraΓ§Γ£o', + 'nav.admin': 'AdministraΓ§Γ£o', + 'nav.chat': 'Chat Agent', + 'nav.terraform': 'Terraform', + 'nav.promptGen': 'Prompt Generator', + 'nav.workspaces': 'Workspaces', + 'nav.explorer': 'OCI Explorer', + 'nav.reports': 'RelatΓ³rios', + 'nav.ociCreds': 'Credenciais OCI', + 'nav.genai': 'GenAI Config', + 'nav.mcp': 'MCP Servers', + 'nav.adb': 'ADB Vector', + 'nav.embeddings': 'Embeddings', + 'nav.embConsult': 'Consultar Embeddings', + 'nav.users': 'UsuΓ‘rios', + 'nav.mfa': 'MFA', + 'nav.audit': 'Audit Log', + 'sidebar.lightMode': 'Modo claro', + 'sidebar.darkMode': 'Modo escuro', + 'sidebar.logout': 'Sair', + 'sidebar.roleAdmin': 'Administrador', + 'sidebar.roleUser': 'UsuΓ‘rio', + 'sidebar.subtitle': 'Infrastructure & Security', + + // ── Login ── + 'login.subtitle': 'Infrastructure & Security Engineer', + 'login.userPlaceholder': 'Usuario', + 'login.passPlaceholder': 'Senha', + 'login.mfaScanQr': 'Escaneie o QR code no seu app autenticador:', + 'login.totpPlaceholder': 'Codigo TOTP', + 'login.loading': 'Entrando...', + 'login.verify': 'Verificar', + 'login.submit': 'Entrar', + 'login.error': 'Erro ao fazer login', + + // ── Chat ── + 'chat.timeNow': 'agora', + 'chat.today': 'Hoje', + 'chat.yesterday': 'Ontem', + 'chat.last7': 'Ultimos 7 dias', + 'chat.last30': 'Ultimos 30 dias', + 'chat.older': 'Anteriores', + 'chat.other': 'Outras', + 'chat.history': 'Historico', + 'chat.newChat': 'Nova Conversa', + 'chat.untitled': 'Sem titulo', + 'chat.noConversations': 'Nenhuma conversa ainda', + 'chat.selectModel': 'Selecione um modelo...', + 'chat.savedConfigs': 'Configs Salvas', + 'chat.searchModel': 'Buscar modelo...', + 'chat.noModels': 'Nenhum modelo encontrado', + 'chat.ociCred': 'Credencial OCI...', + 'chat.settings': 'Configuracoes', + 'chat.newChatTitle': 'Nova conversa', + 'chat.emptyTitle': 'Inicie uma conversa com o agente.', + 'chat.emptySubtitle': 'Selecione um modelo para comecar.', + 'chat.thinking': 'Pensando...', + 'chat.retry': 'Reenviar', + 'chat.attachFile': 'Anexar arquivo', + 'chat.placeholder': 'Digite sua mensagem... (Shift+Enter para nova linha)', + 'chat.send': 'Enviar', + 'chat.selectOciCred': 'Selecione uma credencial OCI para usar modelos diretos.', + 'chat.sendError': 'Falha ao enviar mensagem', + 'chat.requestFailed': 'A requisicao falhou.', + 'chat.timeout': 'Timeout: a resposta esta demorando muito. Tente novamente.', + 'chat.toolsAvailable': 'tools disponiveis', + 'chat.ociCredLabel': 'Credencial OCI', + 'chat.select': 'Selecionar...', + 'chat.analyzeFiles': 'Analise os arquivos anexados.', + + // ── Terraform ── + 'tf.history': 'Historico', + 'tf.newChat': 'Nova', + 'tf.noConversations': 'Nenhuma conversa', + 'tf.searchModel': 'Buscar modelo...', + 'tf.selectModel': 'Selecione modelo...', + 'tf.savedConfigs': 'Configs Salvas', + 'tf.ociConfig': 'OCI Config...', + 'tf.settings': 'Configuracoes', + 'tf.newConversation': 'Nova conversa', + 'tf.emptyTitle': 'Descreva a infraestrutura OCI', + 'tf.emptySubtitle': 'O agente vai gerar codigo Terraform baseado na sua descricao.', + 'tf.thinking': 'Processando...', + 'tf.retry': 'Reenviar', + 'tf.placeholder': 'Descreva a infraestrutura desejada... (Shift+Enter para nova linha)', + 'tf.send': 'Enviar', + 'tf.workspace': 'Workspace', + 'tf.files': 'Arquivos', + 'tf.terminal': 'Terminal', + 'tf.resources': 'Recursos', + 'tf.plan': 'Plan', + 'tf.apply': 'Apply', + 'tf.destroy': 'Destroy', + 'tf.cancel': 'Cancelar', + 'tf.download': 'Download ZIP', + 'tf.createWs': 'Criar Workspace', + 'tf.noFiles': 'Nenhum arquivo gerado', + 'tf.timeout': 'Timeout: a geracao esta demorando muito.', + 'tf.selectOci': 'Selecione uma credencial OCI', + 'tf.compartment': 'Compartimento', + 'tf.region': 'Regiao', + 'tf.rollback': 'Rollback', + 'tf.copy': 'Copiar', + 'tf.copied': 'Copiado', + 'tf.noWs': 'Nenhum workspace ativo', + 'tf.deleteWs': 'Excluir', + + // ── Prompt Generator ── + 'pg.history': 'Historico', + 'pg.new': 'Nova', + 'pg.noConversations': 'Nenhuma conversa', + 'pg.emptyTitle': 'Descreva a infraestrutura OCI que voce precisa', + 'pg.emptySubtitle': 'O agente vai gerar um prompt estruturado e otimizado para o Terraform Agent.', + 'pg.loading': 'Gerando prompt...', + 'pg.placeholder': 'Descreva a infraestrutura desejada... (Shift+Enter para nova linha)', + 'pg.copy': 'Copiar', + 'pg.copied': 'Copiado', + 'pg.retry': 'Reenviar', + 'pg.timeout': 'Timeout: a geracao esta demorando muito.', + 'pg.search': 'Buscar...', + 'pg.selectModel': 'Selecione modelo...', + + // ── Workspaces ── + 'ws.title': 'Terraform Workspaces', + 'ws.all': 'Todos', + 'ws.active': 'Ativos', + 'ws.draft': 'Draft', + 'ws.failed': 'Falha', + 'ws.destroyed': 'DestruΓ­dos', + 'ws.loading': 'Carregando...', + 'ws.refresh': 'Atualizar', + 'ws.noWs': 'Nenhum workspace encontrado', + 'ws.noWsHint': 'Crie workspaces pelo Terraform Agent', + 'ws.loadingWs': 'Carregando workspaces...', + 'ws.open': 'Abrir', + 'ws.plan': 'Plan', + 'ws.apply': 'Apply', + 'ws.destroy': 'Destroy', + 'ws.delete': 'Excluir', + 'ws.cancel': 'Cancelar', + 'ws.hideOutput': 'ocultar', + 'ws.viewOutput': 'ver output', + 'ws.confirmDestroy': 'Confirmar Destroy', + 'ws.confirmDestroyMsg': 'Esta acao ira destruir todos os recursos provisionados por este workspace.', + 'ws.typeDestroy': 'Digite DESTROY para confirmar:', + 'ws.deleteWs': 'Excluir Workspace', + 'ws.deleteWsMsg': 'Deseja excluir este workspace permanentemente? Arquivos locais serao removidos.', + 'ws.noDate': 'Sem data', + + // ── Explorer ── + 'exp.title': 'OCI Explorer', + 'exp.subtitle': 'Explore recursos da sua infraestrutura Oracle Cloud', + 'exp.search': 'Buscar recurso...', + 'exp.refresh': 'Atualizar', + 'exp.loading': 'Carregando...', + 'exp.noResources': 'Nenhum recurso encontrado', + 'exp.selectResource': 'Selecione um tipo de recurso', + 'exp.compartment': 'Compartimento', + 'exp.region': 'Regiao', + 'exp.allRegions': 'Todas as regioes', + 'exp.start': 'Start', + 'exp.stop': 'Stop', + 'exp.details': 'Detalhes', + 'exp.total': 'Total', + 'exp.network': 'Rede', + 'exp.observability': 'Observabilidade', + 'exp.security': 'Seguranca', + 'exp.selectRegions': 'Selecionar regioes', + 'exp.nRegions': '{0} regioes', + 'exp.all': 'Todas', + 'exp.none': 'Nenhuma', + 'exp.noCompartment': 'Nenhum compartment encontrado', + 'exp.selectConfig': 'Selecione uma config OCI', + 'exp.loadingResources': 'Carregando recursos...', + 'exp.clearFilter': 'Tente limpar o filtro de busca', + 'exp.resources': 'recursos', + 'exp.updating': 'atualizando...', + 'exp.regions': 'regiao(oes)', + 'exp.filter': 'Filtrar...', + 'exp.startAction': 'Iniciar', + 'exp.stopAction': 'Parar', + 'exp.updatingState': 'Atualizando...', + 'exp.createdAt': 'Criado em', + 'exp.public': 'Publico', + 'exp.yesPublic': 'Sim', + 'exp.noPrivate': 'Nao (privada)', + 'exp.freeTier': 'Free Tier', + 'exp.yes': 'Sim', + 'exp.no': 'Nao', + 'exp.noRestrictions': 'Sem restricoes (aberto)', + 'exp.updateNetworkAccess': 'Atualizar Acesso de Rede', + 'exp.accessUpdated': 'Acesso atualizado: ', + 'exp.objects': 'Objetos', + 'exp.size': 'Tamanho', + 'exp.visibility': 'Visibilidade', + 'exp.private': 'Privado', + 'exp.rules': 'Regras', + 'exp.routes': 'Rotas', + 'exp.hideRules': 'Ocultar regras', + 'exp.viewRules': 'Ver regras', + 'exp.add': 'Adicionar', + 'exp.direction': 'Direcao', + 'exp.protocol': 'Protocolo', + 'exp.originDest': 'Origem/Destino', + 'exp.ports': 'Portas', + 'exp.description': 'Descricao', + 'exp.portMin': 'Porta Min', + 'exp.portMax': 'Porta Max', + 'exp.myIp': 'Meu IP', + 'exp.save': 'Salvar', + 'exp.cancel': 'Cancelar', + 'exp.ruleAdded': 'Regra adicionada.', + 'exp.enterCidr': 'Informe o CIDR.', + 'exp.removeRule': 'Remover esta regra?', + 'exp.removeNsgRule': 'Remover esta regra do NSG?', + 'exp.ipDetected': 'IP detectado: {0} β€” ajuste porta e clique Salvar', + 'exp.ipDetectFailed': 'Nao foi possivel detectar o IP.', + 'exp.destination': 'Destino', + 'exp.destType': 'Tipo Destino', + 'exp.version': 'Versao', + 'exp.ha': 'HA', + + // ── Reports ── + 'rpt.title': 'CIS Reports', + 'rpt.subtitle': 'Execute scans de conformidade CIS OCI Benchmark', + 'rpt.startScan': 'Iniciar Scan', + 'rpt.running': 'Executando...', + 'rpt.cancel': 'Cancelar', + 'rpt.tenancy': 'Tenancy', + 'rpt.level': 'Level', + 'rpt.region': 'Regiao', + 'rpt.allRegions': 'Todas as regioes', + 'rpt.noReports': 'Nenhum relatorio encontrado', + 'rpt.noReportsHint': 'Execute um scan para gerar relatorios de conformidade.', + 'rpt.progress': 'Progresso', + 'rpt.completed': 'Concluido', + 'rpt.failed': 'Falhou', + 'rpt.viewReport': 'Ver Relatorio', + 'rpt.download': 'Download', + 'rpt.delete': 'Excluir', + 'rpt.summary': 'Resumo', + 'rpt.passed': 'Passou', + 'rpt.findings': 'Findings', + 'rpt.recommendations': 'Recomendacoes', + 'rpt.selectTenancy': 'Selecione uma tenancy', + 'rpt.selectCredential': 'Selecione uma credencial', + 'rpt.loadingReports': 'Carregando relatorios...', + 'rpt.refresh': 'Atualizar', + 'rpt.pageTitle': 'Relatorios CIS', + 'rpt.pageSubtitle': 'CIS Benchmark 3.0 β€” Compliance Scan & Dashboard', + 'rpt.runScanCis': 'Executar Scan CIS', + 'rpt.inExecution': 'Em execucao', + 'rpt.configParams': 'Configure os parametros e execute o scan de compliance CIS.', + 'rpt.ociCredential': 'Credencial OCI', + 'rpt.selectOciCred': 'Selecione a credencial OCI...', + 'rpt.cisLevel': 'CIS Level', + 'rpt.level1Desc': 'Verificacoes essenciais de seguranca', + 'rpt.level2Desc': 'Verificacoes completas (inclui Level 1)', + 'rpt.regions': 'Regioes', + 'rpt.leaveEmpty': 'Deixe vazio para escanear todas as regioes subscritas', + 'rpt.executeCompliance': 'Executar CIS Compliance', + 'rpt.scanProgress': 'Progresso do Scan', + 'rpt.hideLog': 'Ocultar log', + 'rpt.showLog': 'Ver log completo', + 'rpt.lines': 'linhas', + 'rpt.cancelScan': 'Cancelar Scan', + 'rpt.selectReport': 'Selecionar Relatorio', + 'rpt.selectCompletedReport': 'Selecione um relatorio concluido...', + 'rpt.searchTenancy': 'Buscar tenancy...', + 'rpt.searchByTenancy': 'Buscar por tenancy...', + 'rpt.noCredentials': 'Nenhuma credencial encontrada', + 'rpt.allRegionsDefault': 'Todas as regioes (padrao)', + 'rpt.searchRegion': 'Buscar regiao...', + 'rpt.loadingDashboard': 'Carregando dashboard...', + 'rpt.complianceScore': 'Score de Compliance', + 'rpt.compliant': 'Conforme', + 'rpt.nonCompliant': 'Nao Conforme', + 'rpt.ofTotal': 'do total', + 'rpt.compliantItems': 'itens conformes', + 'rpt.totalFindings': 'achados totais', + 'rpt.evaluated': 'Avaliados', + 'rpt.controlsVerified': 'Controles verificados', + 'rpt.regionsCount': 'regiao(oes)', + 'rpt.complianceDist': 'Distribuicao de Compliance', + 'rpt.findingsBySection': 'Achados por Secao', + 'rpt.generatedAt': 'Gerado em', + 'rpt.htmlReport': 'Relatorio HTML', + 'rpt.files': 'Arquivos', + 'rpt.inSections': 'secoes', + 'rpt.fullEmbedding': 'Embedding Completo', + 'rpt.noActiveTables': 'Sem tabelas ativas', + 'rpt.executionHistory': 'Historico de Execucoes', + 'rpt.reports': 'relatorio(s)', + 'rpt.loading': 'Carregando...', + 'rpt.firstScanHint': 'Execute o primeiro scan CIS acima.', + 'rpt.selected': 'Selecionado', + 'rpt.deleteConfirm': 'Excluir este relatorio?', + 'rpt.deleteFailed': 'Falha ao excluir', + 'rpt.selectOciCredError': 'Selecione uma credencial OCI', + 'rpt.alreadyRunning': 'Ja existe um scan em execucao', + 'rpt.errorLoadReports': 'Erro ao carregar relatorios', + 'rpt.errorStartScan': 'Erro ao iniciar scan', + 'rpt.selectAdb': 'Selecione uma conexao ADB.', + 'rpt.selectTable': 'Selecione uma tabela.', + 'rpt.errorEmbedding': 'Erro ao gerar embedding', + 'rpt.cancelled': 'Cancelado', + 'rpt.completedAt': 'Concluido:', + + // ── Downloads ── + 'dl.title': 'Downloads', + 'dl.subtitle': 'Baixe relatorios CIS gerados', + 'dl.allTenancies': 'Todas as Tenancies', + 'dl.allSections': 'Todas as secoes', + 'dl.reports': 'relatorio(s)', + 'dl.refresh': 'Atualizar', + 'dl.loadError': 'Erro ao carregar relatorios', + 'dl.loading': 'Carregando relatorios...', + 'dl.noReports': 'Nenhum relatorio concluido.', + 'dl.noReportsHint': 'Execute um scan na aba Reports para gerar relatorios.', + 'dl.files': 'arquivo(s)', + 'dl.hide': 'Ocultar', + 'dl.viewFiles': 'Ver Arquivos', + 'dl.loadingFiles': 'Carregando arquivos...', + 'dl.noFiles': 'Nenhum arquivo encontrado', + 'dl.filesInSections': 'arquivo(s) em {0} secoes', + + // ── OCI Config ── + 'oci.title': 'Credenciais OCI', + 'oci.subtitle': 'Gerencie credenciais Oracle Cloud Infrastructure', + 'oci.registered': 'Credenciais Registradas', + 'oci.credential': 'credencial', + 'oci.credentials': 'credenciais', + 'oci.noCredentials': 'Nenhuma credencial registrada.', + 'oci.edit': 'Editar', + 'oci.test': 'Testar', + 'oci.delete': 'Excluir', + 'oci.yes': 'Sim', + 'oci.no': 'Nao', + 'oci.newCredential': 'Nova Credencial', + 'oci.editCredential': 'Editar Credencial', + 'oci.addDesc': 'Adicione as credenciais da sua conta OCI para executar reports e acessar servicos.', + 'oci.editing': 'Editando:', + 'oci.tenancyName': 'Tenancy Name', + 'oci.ocidTenancy': 'OCID Tenancy', + 'oci.region': 'Region', + 'oci.selectRegion': 'Selecione a regiao...', + 'oci.searchRegion': 'Buscar regiao...', + 'oci.noRegion': 'Nenhuma regiao encontrada', + 'oci.compartment': 'Compartment OCID', + 'oci.ocidUser': 'OCID User', + 'oci.fingerprint': 'Fingerprint', + 'oci.sessionToken': 'Session Token', + 'oci.sessionTokenHint': 'Cole o conteudo do arquivo token gerado por', + 'oci.sessionKeyLabel': 'Chave de Sessao (.pem)', + 'oci.privateKey': 'Private Key (.pem)', + 'oci.publicKey': 'Public Key (.pem)', + 'oci.keyPassphrase': 'Key Passphrase', + 'oci.keyPassphraseHint': 'Apenas se a chave privada for protegida por senha', + 'oci.sshPublicKey': 'SSH Public Key', + 'oci.sshPublicKeyHint': 'Chave SSH para acesso a instancias compute (ex: ssh-rsa AAAA...)', + 'oci.saveCredential': 'Salvar Credencial', + 'oci.saveChanges': 'Salvar Alteracoes', + 'oci.cancel': 'Cancelar', + 'oci.saving': 'Salvando credencial...', + 'oci.saved': 'Credencial salva com sucesso!', + 'oci.updated': 'Credencial atualizada com sucesso!', + 'oci.deleting': 'Excluindo credencial...', + 'oci.deleted': 'Credencial excluida com sucesso!', + 'oci.fillField': 'Preencha o', + 'oci.fillOcidTenancy': 'Preencha o OCID Tenancy', + 'oci.fillUserAndFP': 'Preencha OCID User e Fingerprint', + 'oci.pasteToken': 'Cole o Session Token', + 'oci.selectKey': 'Selecione a chave privada (.pem)', + 'oci.selectSessionKey': 'Selecione a chave de sessao (.pem)', + 'oci.changeHint': 'Preencha para alterar. Atual:', + 'oci.keepKey': 'Deixe vazio para manter a chave atual', + 'oci.typeCannotChange': '(tipo nao pode ser alterado)', + 'oci.sshNotConfigured': 'nao configurada', + + // ── GenAI Config ── + 'genai.title': 'Modelos GenAI', + 'genai.subtitle': 'Configure modelos de IA generativa para o Chat Agent', + 'genai.configured': 'Modelos Configurados', + 'genai.model': 'modelo', + 'genai.models': 'modelos', + 'genai.noModels': 'Nenhum modelo configurado.', + 'genai.newModel': 'Novo Modelo GenAI', + 'genai.editModel': 'Editar Modelo', + 'genai.configName': 'Nome da Config', + 'genai.ociCredential': 'Credencial OCI', + 'genai.modelSelect': 'Modelo', + 'genai.genaiRegion': 'Regiao GenAI', + 'genai.compartment': 'Compartment OCID', + 'genai.modelOcid': 'Model OCID', + 'genai.modelOcidHint': 'Cole o OCID do modelo do portal OCI (opcional)', + 'genai.customOcid': 'Personalizado (usar OCID)', + 'genai.default': 'Modelo Padrao', + 'genai.setDefault': 'Definir como Padrao', + 'genai.saveModel': 'Salvar Modelo', + 'genai.saveChanges': 'Salvar Alteracoes', + 'genai.cancel': 'Cancelar', + 'genai.saving': 'Salvando modelo...', + 'genai.deleting': 'Excluindo modelo...', + 'genai.addDesc': 'Adicione modelos personalizados ou crie presets com credenciais especificas.', + 'genai.fillName': 'Preencha o nome da config', + 'genai.selectOci': 'Selecione uma credencial OCI', + 'genai.fillOcid': 'Informe o Model OCID para modelo personalizado', + + // ── MCP Servers ── + 'mcp.title': 'MCP Servers', + 'mcp.subtitle': 'Servidores MCP para integracao com ferramentas e execucao de tarefas', + 'mcp.registered': 'Servidores Registrados', + 'mcp.noServers': 'Nenhum MCP server registrado.', + 'mcp.edit': 'Editar', + 'mcp.activate': 'Ativar', + 'mcp.deactivate': 'Desativar', + 'mcp.active': 'Ativo', + 'mcp.inactive': 'Inativo', + 'mcp.delete': 'Excluir', + 'mcp.yes': 'Sim', + 'mcp.no': 'Nao', + 'mcp.command': 'Comando:', + 'mcp.url': 'URL:', + 'mcp.module': 'Modulo:', + 'mcp.adb': 'ADB:', + 'mcp.noAdb': 'Nenhum', + 'mcp.tools': 'Tools', + 'mcp.noTools': 'Nenhuma tool registrada', + 'mcp.discoverTools': 'Descobrir Tools', + 'mcp.discovering': 'Conectando ao MCP server para descobrir tools...', + 'mcp.newServer': 'Registrar Novo Server', + 'mcp.editServer': 'Editar Server', + 'mcp.name': 'Nome', + 'mcp.description': 'Descricao', + 'mcp.serverType': 'Tipo de Servidor', + 'mcp.commandLabel': 'Comando', + 'mcp.commandHint': 'Comando para iniciar o servidor', + 'mcp.argsLabel': 'Argumentos (JSON)', + 'mcp.argsHint': 'Array de argumentos passados ao comando', + 'mcp.urlLabel': 'URL do Servidor', + 'mcp.urlHint': 'Endpoint SSE do servidor MCP', + 'mcp.linkAdb': 'Vincular ADB Vector', + 'mcp.linkAdbHint': 'Opcional β€” o MCP server podera usar este banco como ferramenta.', + 'mcp.saveServer': 'Registrar Server', + 'mcp.saveChanges': 'Salvar Alteracoes', + 'mcp.cancel': 'Cancelar', + 'mcp.saving': 'Registrando servidor...', + 'mcp.updating': 'Atualizando servidor...', + 'mcp.fillName': 'Preencha o nome do server', + 'mcp.invalidArgs': 'Argumentos deve ser um JSON array valido', + 'mcp.configDesc': 'Configure um novo servidor MCP. Os campos se ajustam ao tipo selecionado.', + + // ── ADB Config ── + 'adb.title': 'ADB Connections', + 'adb.subtitle': 'Conexoes com Oracle Autonomous Database via Wallet (mTLS)', + 'adb.registered': 'Conexoes Registradas', + 'adb.connection': 'conexao', + 'adb.connections': 'conexoes', + 'adb.noConnections': 'Nenhuma conexao ADB registrada.', + 'adb.edit': 'Editar', + 'adb.test': 'Testar', + 'adb.delete': 'Excluir', + 'adb.yes': 'Sim', + 'adb.no': 'Nao', + 'adb.newConnection': 'Nova Conexao', + 'adb.editConnection': 'Editar Conexao', + 'adb.connName': 'Nome da Conexao', + 'adb.walletZip': 'Wallet ZIP', + 'adb.walletHint': 'Envie o wallet para extrair os DSNs do tnsnames.ora', + 'adb.walletEditHint': 'Envie novo wallet ou deixe vazio para manter o atual', + 'adb.analyze': 'Analisar', + 'adb.dsn': 'DSN (TNS Name)', + 'adb.dsnHint': 'Selecione ou digite o DSN. Ex: myatp_high', + 'adb.username': 'Username', + 'adb.password': 'Password', + 'adb.walletPassword': 'Wallet Password', + 'adb.genaiConfig': 'Config GenAI (Embeddings)', + 'adb.genaiConfigHint': 'Credenciais OCI usadas para gerar embeddings via GenAI.', + 'adb.noRag': 'Nenhum (sem RAG)', + 'adb.embeddingModel': 'Modelo de Embedding', + 'adb.embeddingModelHint': 'Modelo OCI GenAI para gerar vetores.', + 'adb.saveConnection': 'Salvar Conexao', + 'adb.saveChanges': 'Salvar Alteracoes', + 'adb.cancel': 'Cancelar', + 'adb.saving': 'Salvando conexao...', + 'adb.deleting': 'Excluindo conexao...', + 'adb.updateWallet': 'Atualizar Wallet', + 'adb.adbConnection': 'Conexao ADB', + 'adb.uploadWallet': 'Upload Wallet', + 'adb.connDesc': 'Conexao via python-oracledb Thin mode com Wallet (mTLS) para Oracle Autonomous Database.', + 'adb.selectWallet': 'Selecione um arquivo wallet ZIP.', + 'adb.selectConnection': 'Selecione uma conexao ADB.', + 'adb.fillName': 'Preencha o nome da conexao', + 'adb.fillDsn': 'Preencha o DSN', + 'adb.fillUsername': 'Preencha o username', + 'adb.fillPassword': 'Preencha a senha', + + // ── Embeddings ── + 'emb.title': 'Embeddings', + 'emb.subtitle': 'Base de conhecimento vetorial β€” upload, importacao e gerenciamento', + 'emb.noAdb': 'Nenhuma conexao ADB configurada.', + 'emb.noAdbHint': 'Configure em ADB Vector.', + 'emb.noOci': 'Nenhuma credencial OCI configurada.', + 'emb.noOciHint': 'Configure em Config β†’ OCI.', + 'emb.existing': 'Embeddings Existentes', + 'emb.existingDesc': 'Consulte os documentos armazenados nas tabelas de embeddings do ADB.', + 'emb.adbConnection': 'Conexao ADB', + 'emb.table': 'Tabela', + 'emb.load': 'Carregar', + 'emb.noEmbeddings': 'Nenhum embedding encontrado.', + 'emb.total': 'Total: {0} documentos', + 'emb.cisTitle': 'CIS Recommendations', + 'emb.cisDesc': 'Envie a versao mais recente do PDF para manter as recomendacoes CIS Oracle Cloud atualizadas na base vetorial.', + 'emb.pdfFile': 'Arquivo PDF', + 'emb.uploadCis': 'Upload CIS Recommendations', + 'emb.kbTitle': 'Base de Conhecimento', + 'emb.kbDesc': 'Envie documentos para alimentar a base de conhecimento da equipe. Os arquivos serao vetorizados e disponibilizados para consulta via chat.', + 'emb.fileLabel': 'Arquivo (.txt, .pdf, .csv, .json, .md)', + 'emb.uploadFile': 'Upload Arquivo', + 'emb.urlLabel': 'URL (pagina web ou PDF)', + 'emb.importUrl': 'Importar URL', + 'emb.purgeTitle': 'Purge & Re-embed', + 'emb.purgeDesc': 'Limpe os embeddings de uma tabela para liberar espaco ou preparar para re-ingestao. Esta acao e irreversivel.', + 'emb.tenancyOptional': 'Tenancy (opcional)', + 'emb.purge': 'Purge', + 'emb.confirm': 'Confirmar', + 'emb.selectTable': 'Selecione uma tabela.', + 'emb.selectAdbAndFile': 'Selecione conexao ADB e arquivo.', + 'emb.selectAdbAndUrl': 'Selecione conexao ADB e informe a URL.', + 'emb.processing': 'Processando PDF e gerando embeddings...', + 'emb.uploading': 'Enviando e gerando embeddings...', + 'emb.fetchingUrl': 'Buscando conteudo e gerando embeddings...', + 'emb.purging': 'Limpando embeddings...', + + // ── EmbConsult ── + 'ec.title': 'Consultar Embeddings', + 'ec.subtitle': 'Pergunte sobre os dados armazenados nas bases vetoriais', + 'ec.clear': 'Limpar', + 'ec.tableOptional': 'Tabela (opcional)', + 'ec.allTables': 'Todas as tabelas', + 'ec.emptyTitle': 'Faca uma consulta para pesquisar nos embeddings.', + 'ec.emptyHint': 'Ex: "findings para CIS 1.1", "recomendacoes de networking", "como corrigir MFA"', + 'ec.searching': 'Consultando bases vetoriais...', + 'ec.placeholder': 'Digite sua consulta...', + 'ec.submit': 'Consultar', + 'ec.sources': 'Fontes consultadas', + 'ec.noAnswer': 'Sem resposta do modelo.', + + // ── Users ── + 'usr.title': 'Usuarios', + 'usr.activeCount': 'ativo', + 'usr.activesCount': 'ativos', + 'usr.inactiveCount': 'inativo', + 'usr.inactivesCount': 'inativos', + 'usr.total': 'total', + 'usr.newUser': 'Novo Usuario', + 'usr.cancel': 'Cancelar', + 'usr.createUser': 'Criar Novo Usuario', + 'usr.firstName': 'Nome', + 'usr.lastName': 'Sobrenome', + 'usr.username': 'Usuario (login)', + 'usr.email': 'Email', + 'usr.password': 'Senha', + 'usr.role': 'Role', + 'usr.creating': 'Criando...', + 'usr.create': 'Criar Usuario', + 'usr.noUsers': 'Nenhum usuario cadastrado', + 'usr.noUsersHint': 'Clique em "Novo Usuario" para criar o primeiro usuario.', + 'usr.name': 'Nome', + 'usr.mfa': 'MFA', + 'usr.status': 'Status', + 'usr.lastLogin': 'Ultimo Login', + 'usr.actions': 'Acoes', + 'usr.active': 'Ativo', + 'usr.inactive': 'Inativo', + 'usr.never': 'Nunca', + 'usr.confirm': 'Confirmar', + 'usr.deactivate': 'Desativar usuario', + 'usr.created': 'Usuario criado com sucesso!', + 'usr.roleUpdated': 'Role atualizada', + 'usr.deactivated': 'Usuario desativado', + 'usr.requiredName': 'Nome e sobrenome sao obrigatorios', + 'usr.requiredUsername': 'Usuario (login) e obrigatorio', + 'usr.requiredPassword': 'Senha e obrigatoria', + + // ── MFA ── + 'mfa.title': 'Autenticacao MFA (TOTP)', + 'mfa.subtitle': 'Proteja sua conta com autenticacao em dois fatores via Google Authenticator ou Authy.', + 'mfa.enabled': 'MFA esta ativado', + 'mfa.disabled': 'MFA esta desativado', + 'mfa.enabledDesc': 'Sua conta esta protegida com autenticacao de dois fatores.', + 'mfa.disabledDesc': 'Ative o MFA para adicionar uma camada extra de seguranca.', + 'mfa.disableHint': 'Para desativar o MFA, clique no botao abaixo. Isso reduzira a seguranca da sua conta.', + 'mfa.disableBtn': 'Desativar MFA', + 'mfa.confirmDisable': 'Deseja realmente desativar o MFA da sua conta?', + 'mfa.scanQr': 'Escaneie o QR Code ou copie o secret', + 'mfa.openApp': 'Abra seu app autenticador (Google Authenticator, Authy, etc.)', + 'mfa.enterCode': 'Insira o codigo gerado pelo app', + 'mfa.activateMfa': 'Ativar MFA', + 'mfa.generateSecret': 'Gerar Secret', + 'mfa.generateHint': 'Clique no botao abaixo para gerar um secret TOTP. Voce precisara de um app autenticador para escanear o QR Code.', + 'mfa.activated': 'MFA ativado com sucesso!', + 'mfa.deactivated': 'MFA desativado.', + 'mfa.invalidCode': 'Informe um codigo de 6 digitos', + + // ── Audit ── + 'audit.title': 'Log de Auditoria', + 'audit.subtitle': 'Monitoramento de atividades do sistema, configuracoes e sessoes de chat.', + 'audit.tabAudit': 'Audit Log', + 'audit.tabConfig': 'Config Logs', + 'audit.tabChat': 'Chat Logs', + 'audit.records': 'registros', + 'audit.refresh': 'Atualizar', + 'audit.all': 'Todos', + 'audit.errors': 'Erros', + 'audit.success': 'Sucesso', + 'audit.info': 'Info', + 'audit.loadingLogs': 'Carregando logs...', + 'audit.noAudit': 'Nenhum registro de auditoria.', + 'audit.noConfig': 'Nenhum log de configuracao registrado.', + 'audit.noChat': 'Nenhum log de chat registrado.', + 'audit.date': 'Data', + 'audit.user': 'Usuario', + 'audit.action': 'Acao', + 'audit.resource': 'Recurso', + 'audit.details': 'Detalhes', + 'audit.ip': 'IP', + 'audit.config': 'Config', + 'audit.status': 'Status', + 'audit.message': 'Mensagem', + 'audit.session': 'Sessao', + 'audit.source': 'Origem', + + // ── Common ── + 'common.save': 'Salvar', + 'common.cancel': 'Cancelar', + 'common.delete': 'Excluir', + 'common.edit': 'Editar', + 'common.create': 'Criar', + 'common.update': 'Atualizar', + 'common.close': 'Fechar', + 'common.confirm': 'Confirmar', + 'common.back': 'Voltar', + 'common.search': 'Buscar', + 'common.refresh': 'Atualizar', + 'common.download': 'Download', + 'common.upload': 'Upload', + 'common.copy': 'Copiar', + 'common.retry': 'Tentar novamente', + 'common.send': 'Enviar', + 'common.clear': 'Limpar', + 'common.test': 'Testar', + 'common.yes': 'Sim', + 'common.no': 'Nao', + 'common.loading': 'Carregando...', + 'common.saving': 'Salvando...', + 'common.select': 'Selecione...', + 'common.noData': 'Nenhum dado encontrado', + 'common.error': 'Erro', + 'common.success': 'Sucesso', +}; + +export default pt; diff --git a/frontend-react/src/index.css b/frontend-react/src/index.css new file mode 100644 index 0000000..7019aa0 --- /dev/null +++ b/frontend-react/src/index.css @@ -0,0 +1,437 @@ +@import 'tailwindcss/theme' layer(theme); +@import 'tailwindcss/preflight' layer(base); +@import 'tailwindcss/utilities' layer(utilities); + +/* ── Oracle Dark Premium Theme (in theme layer) ── */ +@layer theme { + :root, html.light { + --bg: #f4f2ef; --bg1: #ffffff; --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: rgba(199,70,52,.06); --acl2: rgba(199,70,52,.1); + --gn: #16a34a; --gnl: rgba(22,163,74,.08); --rd: #dc2626; --rdl: rgba(220,38,38,.06); + --bl: #2563eb; --bll: rgba(37,99,235,.06); --yl: #ca8a04; --yll: rgba(202,138,4,.06); + --pp: #7c3aed; --ppl: rgba(124,58,237,.06); + --ok: #16a34a; --w: #ca8a04; --err: #dc2626; + --r: 12px; --rl: 16px; --rr: 20px; + --sh1: 0 1px 2px rgba(28,25,23,.04), 0 1px 3px rgba(28,25,23,.06); + --sh2: 0 4px 6px -1px rgba(28,25,23,.05), 0 2px 4px -2px rgba(28,25,23,.04); + --sh3: 0 10px 15px -3px rgba(28,25,23,.06), 0 4px 6px -4px rgba(28,25,23,.04); + --shh: 0 20px 25px -5px rgba(28,25,23,.08), 0 8px 10px -6px rgba(28,25,23,.04); + --tb-bg: rgba(255,255,255,.8); + --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: rgba(199,70,52,.1); --acl2: rgba(199,70,52,.16); + --gn: #34c759; --gnl: rgba(52,199,89,.1); --rd: #ff453a; --rdl: rgba(255,69,58,.1); + --bl: #0a84ff; --bll: rgba(10,132,255,.1); --yl: #ffd60a; --yll: rgba(255,214,10,.1); + --pp: #bf5af2; --ppl: rgba(191,90,242,.1); + --ok: #34c759; --w: #ffd60a; --err: #ff453a; + --sh1: 0 1px 3px rgba(0,0,0,.3), 0 1px 2px rgba(0,0,0,.2); + --sh2: 0 4px 8px rgba(0,0,0,.3), 0 2px 4px rgba(0,0,0,.2); + --sh3: 0 10px 20px rgba(0,0,0,.35), 0 4px 8px rgba(0,0,0,.2); + --shh: 0 20px 30px rgba(0,0,0,.4), 0 8px 12px rgba(0,0,0,.25); + --tb-bg: rgba(21,24,32,.85); + } +} + +/* ── Base resets (in base layer) ── */ +@layer base { + *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; } + body { + font-family: var(--fs); + background: var(--bg); + color: var(--t1); + height: 100vh; + overflow: hidden; + -webkit-font-smoothing: antialiased; + } + ::selection { background: rgba(199,70,52,.2); color: var(--t1); } + + 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: transparent; } + ::-webkit-scrollbar-thumb { background: var(--bd); border-radius: 3px; } + ::-webkit-scrollbar-thumb:hover { background: var(--bdf); } + + @keyframes fadeIn { + from { opacity: 0; transform: translateY(12px); } + to { opacity: 1; transform: translateY(0); } + } + + /* Form elements */ + 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); + padding: 9px 36px 9px 14px; + font-size: .82rem; + font-family: var(--fs); + color: var(--t1); + cursor: pointer; + transition: border-color .2s, box-shadow .2s; + min-width: 0; + } + select:focus { + outline: none; + border-color: var(--ac); + box-shadow: 0 0 0 3px var(--acl); + } + 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); + padding: 9px 14px; + font-size: .82rem; + font-family: var(--fs); + color: var(--t1); + transition: border-color .2s, box-shadow .2s; + width: 100%; + } + input[type="text"]:focus, input[type="email"]:focus, input[type="password"]:focus, + input[type="number"]:focus, input[type="search"]:focus, textarea:focus { + outline: none; + border-color: var(--ac); + box-shadow: 0 0 0 3px var(--acl); + } + + input[type="checkbox"] { + appearance: none; + width: 18px; + height: 18px; + border: 2px solid var(--bd); + border-radius: 5px; + background: var(--bg1); + cursor: pointer; + transition: all .15s; + flex-shrink: 0; + position: relative; + } + input[type="checkbox"]:checked { + background: var(--ac); + border-color: var(--ac); + } + input[type="checkbox"]:checked::after { + content: ''; + position: absolute; + left: 4px; + top: 1px; + width: 6px; + height: 10px; + border: solid white; + border-width: 0 2px 2px 0; + transform: rotate(45deg); + } +} + +/* ── Component styles β€” OUTSIDE layers so they override Tailwind utilities ── */ + +/* Nav item hover */ +nav a:hover { background: var(--bg3); color: var(--t1) !important; } + +/* Page wrapper */ +.page { + padding: 28px 32px; + max-width: 1280px; + animation: fadeIn .35s ease; +} +.page-full { + height: 100%; + animation: fadeIn .35s ease; +} + +/* Page header */ +.page-header { + display: flex; + align-items: center; + gap: 14px; + margin-bottom: 24px; +} +.page-header h1 { + font-size: 1.35rem; + font-weight: 700; + color: var(--t1); + letter-spacing: -.01em; +} +.page-header .subtitle { + font-size: .8rem; + color: var(--t3); + margin-top: 2px; +} +.page-header .spacer { flex: 1; } +.page-header .count { + font-size: .75rem; + font-weight: 600; + color: var(--t3); + background: var(--bg3); + padding: 4px 12px; + border-radius: 20px; +} + +/* Cards */ +.card { + background: var(--bg1); + border: 1px solid var(--bd); + border-radius: var(--rl); + box-shadow: var(--sh1); + padding: 20px 24px; + margin-bottom: 16px; +} +.card-header { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 16px; + padding-bottom: 12px; + border-bottom: 1px solid var(--bd); +} +.card-header h2 { + font-size: .92rem; + font-weight: 700; + color: var(--t1); +} +.card-header .icon { + width: 36px; + height: 36px; + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +/* Tables */ +.data-table { + width: 100%; + border-collapse: separate; + border-spacing: 0; + font-size: .82rem; +} +.data-table thead th { + text-align: left; + font-size: .7rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: .06em; + color: var(--t3); + padding: 10px 14px; + background: var(--bg2); + border-bottom: 1px solid var(--bd); +} +.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 { + padding: 12px 14px; + color: var(--t2); + border-bottom: 1px solid var(--bg3); + vertical-align: middle; +} +.data-table tbody tr:last-child td { border-bottom: none; } +.data-table tbody tr:hover td { background: var(--bg2); } + +/* Buttons */ +.btn { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 9px 18px; + border-radius: var(--r); + font-size: .82rem; + font-weight: 600; + font-family: var(--fs); + cursor: pointer; + border: 1px solid transparent; + transition: all .2s var(--trans); + white-space: nowrap; +} +.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 { + background: transparent; + color: var(--rd); + border-color: var(--rd); +} +.btn-danger:hover:not(:disabled) { background: var(--rdl); } +.btn-success { + background: var(--gn); + color: #fff; + border-color: var(--gn); +} +.btn-sm { + padding: 6px 12px; + font-size: .75rem; + border-radius: 8px; +} + +/* Badges */ +.badge { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 3px 10px; + border-radius: 20px; + font-size: .68rem; + font-weight: 600; + white-space: nowrap; +} +.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 */ +.empty-state { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 60px 20px; + text-align: center; + color: var(--t4); +} +.empty-state svg { opacity: .35; margin-bottom: 12px; } +.empty-state h3 { font-size: .92rem; font-weight: 600; color: var(--t3); margin-bottom: 4px; } +.empty-state p { font-size: .78rem; } + +/* KPI cards */ +.kpi-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); + gap: 16px; + margin-bottom: 24px; +} +.kpi-card { + background: var(--bg1); + border: 1px solid var(--bd); + border-radius: var(--rl); + padding: 18px 20px; + box-shadow: var(--sh1); +} +.kpi-card .label { + font-size: .7rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: .06em; + color: var(--t3); + margin-bottom: 8px; +} +.kpi-card .value { + font-size: 1.5rem; + font-weight: 800; + color: var(--t1); + letter-spacing: -.02em; +} +.kpi-card .sub { + font-size: .72rem; + color: var(--t4); + margin-top: 4px; +} + +/* Form groups */ +.form-group { + margin-bottom: 16px; +} +.form-group label { + display: block; + font-size: .75rem; + font-weight: 600; + color: var(--t2); + margin-bottom: 6px; +} +.form-row { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 16px; +} + +/* Tags/pills */ +.tag { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 4px 10px; + border-radius: 20px; + font-size: .72rem; + font-weight: 500; + background: var(--bg3); + color: var(--t2); + border: 1px solid var(--bd); +} +.tag button { + background: none; + border: none; + cursor: pointer; + color: var(--t3); + padding: 0; + font-size: .82rem; + line-height: 1; +} +.tag button:hover { color: var(--rd); } + +/* Section divider */ +.section-title { + font-size: .72rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: .08em; + color: var(--t4); + margin: 24px 0 12px; + padding-bottom: 8px; + border-bottom: 1px solid var(--bg3); +} + +/* Prevent text overflow on resize */ +.prose pre, .chat-markdown pre { + overflow-x: auto; + max-width: 100%; +} +.prose table, .chat-markdown table { + overflow-x: auto; + display: block; + max-width: 100%; +} +.prose code, .chat-markdown code { + word-break: break-all; +} +.prose p, .chat-markdown p { + overflow-wrap: break-word; + word-break: break-word; +} + +/* Tooltip-like labels */ +.field-label { + font-size: .7rem; + font-weight: 600; + color: var(--t3); + text-transform: uppercase; + letter-spacing: .04em; +} diff --git a/frontend-react/src/main.tsx b/frontend-react/src/main.tsx new file mode 100644 index 0000000..6c1e1f3 --- /dev/null +++ b/frontend-react/src/main.tsx @@ -0,0 +1,13 @@ +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; +import { BrowserRouter } from 'react-router-dom'; +import './index.css'; +import App from './App.tsx'; + +createRoot(document.getElementById('root')!).render( + + + + + , +); diff --git a/frontend-react/src/pages/ChatPage.tsx b/frontend-react/src/pages/ChatPage.tsx new file mode 100644 index 0000000..01b62e0 --- /dev/null +++ b/frontend-react/src/pages/ChatPage.tsx @@ -0,0 +1,1138 @@ +import { useState, useEffect, useRef, useCallback, useMemo } from 'react'; +import { + Menu, Settings, Plus, Trash2, Send, Paperclip, X, RefreshCw, + Bot, ChevronDown, Database, Wrench, Loader2, Search, +} from 'lucide-react'; +import ReactMarkdown from 'react-markdown'; +import remarkGfm from 'remark-gfm'; +import { useAppStore } from '@/stores/app'; +import { useI18n } from '@/i18n'; +import { + chatApi, + type ChatSession, + type ChatSendBody, +} from '@/api/endpoints/chat'; + +/* ── Local types ── */ + +interface UIMessage { + role: 'user' | 'assistant'; + content: string; + timestamp: string; + failed?: boolean; + thinking?: boolean; +} + +interface AttachedFile { + file: File; + name: string; + type: string; + preview?: string; +} + +interface ChatParams { + temperature: number; + max_tokens: number; + top_p: number; + top_k: number; + frequency_penalty: number; + presence_penalty: number; + reasoning_effort: string; +} + +const ACCEPT = + 'image/*,.pdf,.txt,.csv,.json,.log,.xml,.yaml,.yml,.md,.py,.js,.ts,.html,.css,.sql,.tf,.hcl'; + +const DEFAULT_PARAMS: ChatParams = { + temperature: 1, + max_tokens: 6000, + top_p: 0.95, + top_k: 1, + frequency_penalty: 0, + presence_penalty: 0, + reasoning_effort: 'medium', +}; + +/* ── Helpers ── */ + +function nowHHmm(): string { + return new Date().toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' }); +} + +function timeAgo(dateStr: string): string { + const tFn = useI18n.getState().t; + const d = new Date(dateStr.endsWith('Z') ? dateStr : dateStr + 'Z'); + const diff = (Date.now() - d.getTime()) / 1000; + if (diff < 60) return tFn('chat.timeNow'); + if (diff < 3600) return `${Math.floor(diff / 60)}min`; + if (diff < 86400) return `${Math.floor(diff / 3600)}h`; + return `${Math.floor(diff / 86400)}d`; +} + +function dateGroup(dateStr: string): string { + const tFn = useI18n.getState().t; + if (!dateStr) return tFn('chat.other'); + const d = new Date(dateStr.endsWith('Z') ? dateStr : dateStr + 'Z'); + const diff = (Date.now() - d.getTime()) / 86400000; + if (diff < 1) return tFn('chat.today'); + if (diff < 2) return tFn('chat.yesterday'); + if (diff < 7) return tFn('chat.last7'); + if (diff < 30) return tFn('chat.last30'); + return tFn('chat.older'); +} + +function groupSessions(sessions: ChatSession[]): Record { + const groups: Record = {}; + for (const s of sessions) { + const g = dateGroup(s.updated_at || s.created_at); + (groups[g] ??= []).push(s); + } + return groups; +} + +/* ── Component ── */ + +export default function ChatPage() { + /* ── Store ── */ + const { ociCfg, genaiCfg, models, mcpSvr, adbCfg } = useAppStore(); + const { t } = useI18n(); + + /* ── State ── */ + const [messages, setMessages] = useState([]); + const [sessionId, setSessionId] = useState(null); + const [sessions, setSessions] = useState([]); + const [model, setModel] = useState(''); + const [ociConfig, setOciConfig] = useState(''); + const [files, setFiles] = useState([]); + const [chatParams, setChatParams] = useState({ ...DEFAULT_PARAMS }); + const [useTools, setUseTools] = useState(true); + const [historyOpen, setHistoryOpen] = useState(false); + const [settingsOpen, setSettingsOpen] = useState(false); + const [sending, setSending] = useState(false); + const [modelDropOpen, setModelDropOpen] = useState(false); + const [modelSearch, setModelSearch] = useState(''); + + const inputRef = useRef(null); + const messagesEndRef = useRef(null); + const fileInputRef = useRef(null); + const modelDropRef = useRef(null); + const pollRef = useRef | null>(null); + + /* ── Derived ── */ + const isDirect = !!model && !model.startsWith('cfg:'); + + const modelInfo = useMemo(() => { + let mid = ''; + if (model.startsWith('cfg:')) { + const g = genaiCfg.find((x) => x.id === model.slice(4)); + if (g) mid = g.model_id; + } else { + mid = model; + } + return models[mid] || ({} as { provider?: string; reasoning?: boolean; max_tokens?: number; penalties?: boolean }); + }, [model, models, genaiCfg]); + + const isReasoning = !!(modelInfo as any).reasoning; + const maxTokenLimit = (modelInfo as any).max_tokens || 128000; + const hasTopK = ['meta', 'google', ''].includes((modelInfo as any).provider || ''); + const hasPenalties = !!(modelInfo as any).penalties; + + const toolCount = useMemo( + () => + mcpSvr + .filter((m) => m.is_active && Array.isArray(m.tools) && m.tools.length) + .reduce((a, m) => a + (m.tools?.length || 0), 0), + [mcpSvr], + ); + + const hasRag = useMemo( + () => adbCfg.some((c) => c.is_active) && (genaiCfg.length > 0 || ociCfg.length > 0), + [adbCfg, genaiCfg, ociCfg], + ); + + const modelLabel = useMemo(() => { + if (!model) return t('chat.selectModel'); + if (model.startsWith('cfg:')) { + const g = genaiCfg.find((x) => x.id === model.slice(4)); + if (!g) return 'Config...'; + const mi = models[g.model_id]; + return `${g.name || mi?.name || g.model_id} (${g.genai_region})`; + } + const mi = models[model]; + return mi?.name || model; + }, [model, genaiCfg, models, t]); + + /* Model dropdown items grouped by provider */ + const modelGroups = useMemo(() => { + const groups: { label: string; items: { value: string; name: string }[] }[] = []; + const q = modelSearch.toLowerCase(); + + // GenAI configs + if (genaiCfg.length) { + const items = genaiCfg + .map((g) => { + const mi = models[g.model_id]; + const name = `${g.name || mi?.name || g.model_id} (${g.genai_region})`; + return { value: `cfg:${g.id}`, name }; + }) + .filter((i) => !q || i.name.toLowerCase().includes(q)); + if (items.length) groups.push({ label: t('chat.savedConfigs'), items }); + } + + // Direct models grouped by provider + const provs: Record = {}; + for (const [mid, info] of Object.entries(models)) { + const p = info.provider || 'other'; + (provs[p] ??= []).push({ value: mid, name: info.name }); + } + const order = ['openai', 'google', 'meta', 'xai', 'cohere']; + const seen = new Set(); + for (const p of [...order, ...Object.keys(provs)]) { + if (seen.has(p) || !provs[p]) continue; + seen.add(p); + const items = provs[p].filter((i) => !q || i.name.toLowerCase().includes(q)); + if (items.length) { + groups.push({ label: p.charAt(0).toUpperCase() + p.slice(1), items }); + } + } + return groups; + }, [models, genaiCfg, modelSearch, t]); + + const sessionGroups = useMemo(() => groupSessions(sessions), [sessions]); + + /* ── Effects ── */ + + // Load sessions on mount + useEffect(() => { + chatApi.listSessions('chat', 50).then(setSessions).catch(() => {}); + }, []); + + // Close model dropdown on outside click + useEffect(() => { + function handleClick(e: MouseEvent) { + if (modelDropRef.current && !modelDropRef.current.contains(e.target as Node)) { + setModelDropOpen(false); + } + } + document.addEventListener('mousedown', handleClick); + return () => document.removeEventListener('mousedown', handleClick); + }, []); + + // Keyboard shortcuts + useEffect(() => { + function handleKey(e: KeyboardEvent) { + if (e.key === 'Escape') { + setHistoryOpen(false); + setSettingsOpen(false); + setModelDropOpen(false); + } + } + document.addEventListener('keydown', handleKey); + return () => document.removeEventListener('keydown', handleKey); + }, []); + + // Auto-scroll + useEffect(() => { + messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); + }, [messages]); + + // Cleanup poll on unmount + useEffect(() => { + return () => { + if (pollRef.current) clearTimeout(pollRef.current); + }; + }, []); + + /* ── Actions ── */ + + const refreshSessions = useCallback(() => { + chatApi.listSessions('chat', 50).then(setSessions).catch(() => {}); + }, []); + + const loadSession = useCallback(async (sid: string) => { + try { + const data = await chatApi.loadSession(sid); + setSessionId(sid); + setMessages( + data.messages.map((m) => ({ + role: m.role, + content: m.content, + timestamp: m.created_at?.slice(11, 16) || '', + })), + ); + } catch { + /* ignore */ + } + }, []); + + const deleteSession = useCallback( + async (sid: string) => { + try { + await chatApi.deleteSession(sid); + } catch { + /* ignore */ + } + if (sessionId === sid) { + setSessionId(null); + setMessages([]); + } + setSessions((prev) => prev.filter((s) => s.id !== sid)); + }, + [sessionId], + ); + + const newChat = useCallback(() => { + setSessionId(null); + setMessages([]); + setFiles([]); + if (inputRef.current) inputRef.current.value = ''; + }, []); + + const pickModel = useCallback( + (value: string) => { + setModel(value); + setModelDropOpen(false); + // Auto-select first OCI config for direct models + if (value && !value.startsWith('cfg:') && !ociConfig && ociCfg.length) { + setOciConfig(ociCfg[0].id); + } + }, + [ociConfig, ociCfg], + ); + + const handleFiles = useCallback( + (fileList: FileList | null) => { + if (!fileList) return; + const newFiles: AttachedFile[] = []; + for (const f of Array.from(fileList)) { + if (files.length + newFiles.length >= 5) break; + const entry: AttachedFile = { file: f, name: f.name, type: f.type.startsWith('image/') ? 'image' : 'file' }; + if (entry.type === 'image') { + entry.preview = URL.createObjectURL(f); + } + newFiles.push(entry); + } + setFiles((prev) => [...prev, ...newFiles].slice(0, 5)); + if (fileInputRef.current) fileInputRef.current.value = ''; + }, + [files.length], + ); + + const removeFile = useCallback((idx: number) => { + setFiles((prev) => { + const f = prev[idx]; + if (f.preview) URL.revokeObjectURL(f.preview); + return prev.filter((_, i) => i !== idx); + }); + }, []); + + /* ── Send ── */ + + const sendMessage = useCallback(async () => { + const text = inputRef.current?.value.trim() || ''; + const hasFiles = files.length > 0; + if (!text && !hasFiles) return; + if (!model) return; + + // Validate OCI config for direct models + if (isDirect && !ociConfig) { + setMessages((prev) => [ + ...prev, + { role: 'assistant', content: t('chat.selectOciCred'), timestamp: nowHHmm(), failed: true }, + ]); + return; + } + + setSending(true); + if (inputRef.current) inputRef.current.value = ''; + + // User message display + let userDisplay = text; + if (hasFiles) { + const fileList = files.map((f) => f.name).join(', '); + userDisplay += (userDisplay ? '\n' : '') + '\u{1F4CE} ' + fileList; + } + + setMessages((prev) => [ + ...prev, + { role: 'user', content: userDisplay, timestamp: nowHHmm() }, + { role: 'assistant', content: '', timestamp: '', thinking: true }, + ]); + + try { + let data: { message_id: string; session_id: string; status: string }; + + if (hasFiles) { + const fd = new FormData(); + fd.append('message', text || t('chat.analyzeFiles')); + if (sessionId) fd.append('session_id', sessionId); + fd.append('use_tools', String(useTools)); + + if (model.startsWith('cfg:')) { + fd.append('genai_config_id', model.slice(4)); + } else { + fd.append('model_id', model); + fd.append('oci_config_id', ociConfig); + const oc = ociCfg.find((c) => c.id === ociConfig); + if (oc) { + fd.append('genai_region', oc.region); + fd.append('compartment_id', oc.compartment_id || ''); + } + } + + fd.append('temperature', String(chatParams.temperature)); + fd.append('max_tokens', String(chatParams.max_tokens)); + fd.append('top_p', String(chatParams.top_p)); + fd.append('top_k', String(chatParams.top_k)); + fd.append('frequency_penalty', String(chatParams.frequency_penalty)); + fd.append('presence_penalty', String(chatParams.presence_penalty)); + if (isReasoning && chatParams.reasoning_effort) { + fd.append('reasoning_effort', chatParams.reasoning_effort); + } + files.forEach((f) => fd.append('files', f.file)); + + data = await chatApi.upload(fd); + setFiles([]); + } else { + const body: ChatSendBody = { + message: text, + session_id: sessionId || undefined, + use_tools: useTools, + }; + + if (model.startsWith('cfg:')) { + body.genai_config_id = model.slice(4); + } else { + body.model_id = model; + body.oci_config_id = ociConfig; + const oc = ociCfg.find((c) => c.id === ociConfig); + if (oc) { + body.genai_region = oc.region; + body.compartment_id = oc.compartment_id || ''; + } + } + + body.temperature = chatParams.temperature; + body.max_tokens = chatParams.max_tokens; + body.top_p = chatParams.top_p; + body.top_k = chatParams.top_k; + body.frequency_penalty = chatParams.frequency_penalty; + body.presence_penalty = chatParams.presence_penalty; + if (isReasoning && chatParams.reasoning_effort) { + body.reasoning_effort = chatParams.reasoning_effort; + } + + data = await chatApi.send(body); + } + + setSessionId(data.session_id); + + if (data.status === 'processing' && data.message_id) { + await pollResult(data.message_id); + } else { + setMessages((prev) => prev.filter((m) => !m.thinking)); + } + + refreshSessions(); + } catch (err: any) { + setMessages((prev) => [ + ...prev.filter((m) => !m.thinking), + { role: 'assistant', content: `Erro: ${err.message || t('chat.sendError')}`, timestamp: nowHHmm(), failed: true }, + ]); + } finally { + setSending(false); + inputRef.current?.focus(); + } + }, [model, sessionId, ociConfig, files, chatParams, useTools, isDirect, isReasoning, ociCfg, refreshSessions, t]); + + /* ── Poll ── */ + + const pollResult = useCallback( + async (mid: string) => { + for (let i = 0; i < 1200; i++) { + await new Promise((resolve) => { + pollRef.current = setTimeout(resolve, i < 10 ? 1000 : 3000); + }); + try { + const r = await chatApi.pollStatus(mid); + if (r.status === 'done') { + setMessages((prev) => [ + ...prev.filter((m) => !m.thinking), + { role: 'assistant', content: r.content, timestamp: nowHHmm() }, + ]); + return; + } + if (r.status === 'failed') { + setMessages((prev) => [ + ...prev.filter((m) => !m.thinking), + { role: 'assistant', content: r.content || t('chat.requestFailed'), timestamp: nowHHmm(), failed: true }, + ]); + return; + } + } catch { + /* continue polling */ + } + } + // Timeout + setMessages((prev) => [ + ...prev.filter((m) => !m.thinking), + { role: 'assistant', content: t('chat.timeout'), timestamp: nowHHmm(), failed: true }, + ]); + }, + [t], + ); + + /* ── Retry ── */ + + const retryMessage = useCallback( + (idx: number) => { + // Find last user message before this failed one + let userMsg = ''; + for (let i = idx - 1; i >= 0; i--) { + if (messages[i].role === 'user') { + userMsg = messages[i].content; + break; + } + } + if (!userMsg) return; + // Remove failed message + setMessages((prev) => prev.filter((_, i) => i !== idx)); + // Put text back in input and send + if (inputRef.current) inputRef.current.value = userMsg; + // Auto-send after state update + setTimeout(() => sendMessage(), 50); + }, + [messages, sendMessage], + ); + + /* ── Auto-grow textarea ── */ + + const autoGrow = useCallback((el: HTMLTextAreaElement) => { + el.style.height = 'auto'; + el.style.height = Math.min(el.scrollHeight, 200) + 'px'; + }, []); + + /* ── Render ── */ + + return ( +
+ {/* ── Left Panel: History ── */} +
+
+ + {t('chat.history')} + + +
+
+ {Object.entries(sessionGroups).map(([group, items]) => ( +
+
+ {group} +
+ {items.map((s) => ( +
loadSession(s.id)} + className="group flex items-center justify-between px-2 py-1.5 rounded cursor-pointer" + style={{ + background: sessionId === s.id ? 'var(--bg3)' : 'transparent', + color: sessionId === s.id ? 'var(--t1)' : 'var(--t3)', + }} + onMouseEnter={(e) => (e.currentTarget.style.background = sessionId === s.id ? 'var(--bg3)' : 'var(--bg2)')} + onMouseLeave={(e) => (e.currentTarget.style.background = sessionId === s.id ? 'var(--bg3)' : 'transparent')} + > +
+
{s.title || t('chat.untitled')}
+
+ {timeAgo(s.updated_at || s.created_at)} +
+
+ +
+ ))} +
+ ))} + {sessions.length === 0 && ( +
+ {t('chat.noConversations')} +
+ )} +
+
+ + {/* ── Center Panel: Chat ── */} +
+ {/* Toolbar */} +
+ {/* History toggle */} + + + {/* Model dropdown */} +
+ + {modelDropOpen && ( +
+
+
+ + setModelSearch(e.target.value)} + className="flex-1 bg-transparent text-xs outline-none" + style={{ color: 'var(--t2)', border: 'none' }} + autoFocus + /> +
+
+
+ {modelGroups.map((group) => ( +
+
+ {group.label} +
+ {group.items.map((item) => ( +
pickModel(item.value)} + className="px-3 py-1.5 text-xs cursor-pointer" + style={{ + color: model === item.value ? 'var(--ac)' : 'var(--t2)', + background: model === item.value ? 'var(--bg2)' : 'transparent', + }} + onMouseEnter={(e) => { + if (model !== item.value) e.currentTarget.style.background = 'var(--bg2)'; + }} + onMouseLeave={(e) => { + if (model !== item.value) e.currentTarget.style.background = 'transparent'; + }} + > + {item.name} +
+ ))} +
+ ))} + {modelGroups.length === 0 && ( +
+ {t('chat.noModels')} +
+ )} +
+
+ )} +
+ + {/* OCI config selector for direct models */} + {isDirect && ( + + )} + + {/* RAG badge */} + {hasRag && ( + + + RAG + + )} + +
+ + {/* Settings toggle */} + + + {/* New chat */} + +
+ + {/* Messages area */} +
+ {messages.length === 0 ? ( +
+ +

+ {t('chat.emptyTitle')} +

+

+ {t('chat.emptySubtitle')} +

+
+ ) : ( +
+ {messages.map((msg, idx) => { + if (msg.thinking) { + return ( +
+ + + {t('chat.thinking')} + +
+ ); + } + + const isUser = msg.role === 'user'; + + return ( +
+
+ {isUser ? ( +
{msg.content}
+ ) : ( +
+ {msg.content} +
+ )} +
+
+ {msg.timestamp && ( + + {msg.timestamp} + + )} + {msg.failed && ( + + )} +
+
+ ); + })} +
+
+ )} +
+ + {/* File preview chips */} + {files.length > 0 && ( +
+ {files.map((f, i) => + f.type === 'image' && f.preview ? ( +
+ {f.name} + +
+ ) : ( +
+ + + {f.name} + + +
+ ), + )} +
+ )} + + {/* Input area */} +
+ handleFiles(e.target.files)} + /> + +