commit 23621306a1c5a21323e0734596d6ee9ee39da8ec Author: haikdc Date: Fri Mar 13 12:09:25 2026 -0700 [Haik]: and so it begins diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..4da81b08 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.DS_Store +.worktrees +.env +backend/data/** +!backend/data/outputs/ +!backend/data/outputs/*.json \ No newline at end of file diff --git a/GETTING_STARTED.md b/GETTING_STARTED.md new file mode 100644 index 00000000..70227318 --- /dev/null +++ b/GETTING_STARTED.md @@ -0,0 +1,222 @@ +# Getting Started + +A step-by-step guide to get Open Swarm running locally — from clone to launch. + +--- + +## Prerequisites + +Make sure the following are installed on your machine before proceeding: + +| Tool | Version | Check | +|------|---------|-------| +| **Git** | Any recent | `git --version` | +| **Python** | 3.11+ | `python --version` | +| **Node.js** | 18+ | `node --version` | +| **npm** | 9+ (ships with Node) | `npm --version` | + +### Installing prerequisites + +
+Node.js (via nvm) + +```bash +curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash +source "$HOME/.nvm/nvm.sh" +nvm install 22 +nvm use 22 +``` + +
+ +--- + +## 1. Clone the repository + +```bash +git clone https://github.com//self-swarm.git +cd self-swarm +``` + +--- + +## 2. Backend setup (Configure environment variables) + +Copy the example environment file and fill in your values: + +```bash +cp backend/.env.example backend/.env +``` + +Edit `backend/.env` with your values: + +```env +# Backend server port +BACKEND_PORT=8324 + +# Google OAuth (optional — needed for Google Workspace tools) +GOOGLE_OAUTH_CLIENT_ID= +GOOGLE_OAUTH_CLIENT_SECRET= +``` + +--- + +## 3. Run the application + +You have two options: use the provided **run scripts** (recommended) or start each service manually. + +### Option A: Run scripts (recommended) + +These scripts handle virtual environments, dependency installation, and startup automatically. + +**Backend** (starts FastAPI server): + +```bash +./backend/run/dev.sh +``` + +**Frontend** (in a separate terminal): + +```bash +./frontend/run/dev.sh +``` + +### Option B: Manual startup + +**Terminal 1 — Backend server:** + +```bash +cd backend +source .venv/bin/activate +cd .. +python -m uvicorn backend.main:app --host 0.0.0.0 --port 8324 --reload --reload-dir backend +``` + +**Terminal 2 — Frontend dev server:** + +```bash +cd frontend +npm run dev +``` + +--- + +## 4. Open the app + +Once everything is running: + +| Service | URL | +|---------|-----| +| **Frontend (UI)** | [http://localhost:3000](http://localhost:3000) | +| **Backend API** | [http://localhost:8324](http://localhost:8324) | +| **API Docs (Swagger)** | [http://localhost:8324/docs](http://localhost:8324/docs) | + +--- + +## Google Workspace integration (optional) + +To use Google Calendar, Gmail, Drive, and other Google tools from your agents, you need to set up OAuth credentials. This is a one-time setup. + +### a. Create a Google Cloud project + +1. Go to the [Google Cloud Console](https://console.cloud.google.com/) +2. Create a new project (or select an existing one) +3. From the left sidebar, go to **APIs & Services → Library** +4. Enable the APIs you want to use: + - **Google Calendar API** + - **Gmail API** + - **Google Drive API** + - **Google Contacts API** (People API) + +### b. Create OAuth credentials + +1. Go to **APIs & Services → Credentials** +2. Click **Create Credentials → OAuth client ID** +3. If prompted, configure the **OAuth consent screen** first: + - Choose **External** (or Internal if you're on a Workspace org) + - Fill in the required app name and email fields + - Add the scopes you enabled above + - Add your Google account as a test user (required while the app is in "Testing" status) +4. Back on the credentials page, create an **OAuth client ID**: + - Application type: **Web application** + - Authorized redirect URIs: `http://localhost:8324/api/tools/oauth/callback` +5. Copy the **Client ID** and **Client Secret** + +### c. Add credentials to your `.env` + +Paste the values into `backend/.env`: + +```env +GOOGLE_OAUTH_CLIENT_ID=123456789-abc.apps.googleusercontent.com +GOOGLE_OAUTH_CLIENT_SECRET=GOCSPX-... +``` + +### d. Connect from the UI + +1. Open the **Tools** page in the sidebar +2. Add or select a Google Workspace tool +3. Click **Connect** — a Google sign-in popup will appear +4. Authorize the requested scopes +5. The popup closes and the tool status changes to **Connected** + +Your agents can now use Google Calendar, Gmail, Drive, etc. through MCP tools. + +--- + +## Project structure + +``` +self-swarm/ +├── backend/ +│ ├── apps/ # FastAPI route modules +│ │ ├── agents/ # Agent lifecycle, WebSocket, worktree management +│ │ ├── templates/ # Prompt template CRUD +│ │ ├── skills/ # Skills CRUD (synced to ~/.claude/skills/) +│ │ ├── tools_lib/ # Tool definitions CRUD +│ │ ├── modes/ # Agent mode configurations +│ │ ├── settings/ # App settings (API keys, preferences) +│ │ ├── outputs/ # Output management +│ │ └── dashboards/ # Dashboard layout persistence +│ ├── config/ # FastAPI app configuration +│ ├── data/ # Persistent JSON file storage +│ ├── run/ # Shell scripts for starting the backend +│ ├── main.py # FastAPI entrypoint +│ ├── requirements.txt # Python dependencies +│ └── .env # Environment variables (not committed) +├── frontend/ +│ ├── src/ +│ │ ├── app/ +│ │ │ ├── components/ # AppShell, CommandPicker, modals +│ │ │ └── pages/ # Dashboard, AgentChat, Templates, Skills, Tools, etc. +│ │ └── shared/ +│ │ ├── state/ # Redux slices +│ │ ├── ws/ # WebSocket manager +│ │ └── hooks/ # Custom React hooks +│ ├── public/ # Static assets +│ ├── webpack.config.js # Webpack bundler config +│ └── package.json # Node dependencies +├── debugger/ # Optional debugging tool +└── README.md +``` + +--- + +## Troubleshooting + +### Backend won't start — `ModuleNotFoundError` +Make sure you're running from the **project root** (not from `backend/`): +```bash +cd self-swarm +python -m uvicorn backend.main:app --host 0.0.0.0 --port 8324 --reload +``` + +### Frontend proxy errors / API calls failing +The frontend dev server proxies `/api` requests to `http://localhost:8324`. Make sure the backend is running first. + +### Mock mode vs real mode +If you see mock responses, either: +- `claude-agent-sdk` is not installed — run `pip install claude-agent-sdk` +- No Anthropic API key is configured — set `ANTHROPIC_API_KEY` env var or configure it in the Settings page + +### `playwright install` errors +Playwright requires browser binaries. Run `playwright install` after pip install to download them. diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..6eb33b2d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 ClusterLabs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 00000000..0904df32 --- /dev/null +++ b/README.md @@ -0,0 +1,104 @@ +# Open Swarm — Agent Orchestrator + +A locally-running React + FastAPI application for managing multiple Claude Code instances in parallel. Designed for power users who run multiple agents simultaneously and need a unified interface to monitor, control, and coordinate them. + +## Features + +- **Multi-agent management** — Launch and monitor multiple Claude Code instances side by side +- **Git worktree isolation** — Each agent works on its own git worktree/branch to avoid conflicts +- **Real-time streaming** — WebSocket-based streaming of agent messages and status updates +- **HITL approvals** — Approve or deny tool usage requests from the dashboard or within each chat +- **Message branching** — Edit prior messages to fork conversations, navigate between branches +- **Prompt template library** — Reusable prompt templates with structured input fields, invoked via `/` commands +- **Skills library** — Manage skills synced to the native `~/.claude/skills/` directory +- **Tools library** — Define custom tool configurations (bash, MCP, Python) +- **Keyboard shortcuts** — Navigate between agents and approve/deny requests without a mouse +- **Diff viewer** — View uncommitted changes in each agent's worktree + +## Architecture + +``` +Frontend (React/TypeScript :3000) Backend (FastAPI/Python :8324) +┌─────────────────────────────┐ ┌──────────────────────────────┐ +│ Dashboard │◄────►│ REST API (/api/*) │ +│ Agent Chat (per session) │ │ WebSocket (/ws/*) │ +│ Templates / Skills / Tools │ │ Agent Manager │ +│ Slash Command Picker │ │ └─ Claude Agent SDK │ +│ Keyboard Shortcuts │ │ Worktree Manager │ +│ Diff Viewer │ │ Library Storage (JSON/files)│ +└─────────────────────────────┘ └──────────────────────────────┘ +``` + +## Quick Start + +### Prerequisites + +- Python 3.11+ +- Node.js 18+ +- Git +- An Anthropic API key (for real agent usage) — `export ANTHROPIC_API_KEY=...` + +### Backend + +```bash +bash backend/run/dev.sh +# API runs at http://localhost:8324 +# Docs at http://localhost:8324/docs +``` + +### Frontend + +```bash +bash frontend/run/dev.sh +# App runs at http://localhost:3000 +``` + +### Mock Mode + +If `claude-agent-sdk` is not installed, the backend runs in **mock mode** — agents simulate tool calls and responses so you can develop and test the UI without an API key. + +## Keyboard Shortcuts + +| Key | Action | +|-----|--------| +| `d` | Go to Dashboard | +| `t` | Go to Templates | +| `1`–`9` | Open agent by position | +| `Shift+A` | Approve all pending requests | +| `Shift+D` | Deny all pending requests | +| `?` | Show shortcuts help | + +## Slash Commands + +In the chat input, type `/` to invoke templates and skills: +- `/template-name` — Opens the template's input modal +- `/skill-name` — Inserts the skill content into the message + +## Project Structure + +``` +backend/ + apps/ + agents/ — Agent lifecycle, WebSocket, worktree management + templates/ — Prompt template CRUD (JSON file storage) + skills/ — Skills CRUD (synced to ~/.claude/skills/) + tools_lib/ — Tool definitions CRUD (JSON file storage) + health/ — Health check endpoint + config/ — FastAPI app configuration + data/ — Persistent JSON file storage (sessions, dashboards, settings, templates, tools, etc.) + +frontend/ + src/ + app/ + components/ — AppShell, NewAgentModal, SlashCommandPicker, KeyboardShortcutsHelp + pages/ + Dashboard/ — Agent overview grid with live status + AgentChat/ — Full chat UI with streaming, HITL, branching, diff viewer + Templates/ — Template library with editor + Skills/ — Skills library with editor + Tools/ — Tools library with editor + shared/ + state/ — Redux slices (agents, templates, skills, tools) + ws/ — WebSocket manager + hooks/ — Custom hooks (keyboard shortcuts) +``` diff --git a/backend/.env.example b/backend/.env.example new file mode 100644 index 00000000..4d293e35 --- /dev/null +++ b/backend/.env.example @@ -0,0 +1,8 @@ +# ============================================================================= +# Backend Server +# ============================================================================= +BACKEND_PORT=8324 + + +GOOGLE_OAUTH_CLIENT_ID=your-google-oauth-client-id.apps.googleusercontent.com +GOOGLE_OAUTH_CLIENT_SECRET=your-google-oauth-client-secret diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 00000000..6e519548 --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1,18 @@ +# ignore all py cache files +__pycache__/ +*.pyc +*.pyo +*.pyd +*.pyw +*.pyz +*.pywz +*.pyzw +*.pyzwz +*.pyzwzw + +# ignore everything in apps/db/snips (except .gitignore) +apps/db/snips/* +!apps/db/snips/.gitkeep + +# ignore everything in data/ +data/** \ No newline at end of file diff --git a/backend/__init__.py b/backend/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/apps/__init__.py b/backend/apps/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/apps/agents/__init__.py b/backend/apps/agents/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/apps/agents/agent_manager.py b/backend/apps/agents/agent_manager.py new file mode 100644 index 00000000..388db807 --- /dev/null +++ b/backend/apps/agents/agent_manager.py @@ -0,0 +1,1379 @@ +import asyncio +import json +import logging +import os +import time +from datetime import datetime +from uuid import uuid4 +from typing import Optional + +from backend.apps.agents.models import ( + AgentConfig, AgentSession, Message, MessageBranch, ApprovalRequest, ToolGroupMeta, +) +from backend.apps.agents.worktree_manager import WorktreeManager +from backend.apps.agents.ws_manager import ws_manager +from backend.apps.modes.modes import load_mode +from backend.apps.outputs.outputs import _load_all as load_all_outputs +from backend.apps.settings.settings import load_settings +from backend.apps.tools_lib.tools_lib import ( + _load_all as load_all_tools, + _sanitize_server_name, + derive_mcp_config, + load_builtin_permissions, + refresh_google_token, +) + +logger = logging.getLogger(__name__) + +os.environ.setdefault("CLAUDE_CODE_STREAM_CLOSE_TIMEOUT", "3600000") + +SESSIONS_DIR = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), + "data", "sessions", +) + + +def _save_session(session_id: str, doc_data: dict): + os.makedirs(SESSIONS_DIR, exist_ok=True) + with open(os.path.join(SESSIONS_DIR, f"{session_id}.json"), "w") as f: + json.dump(doc_data, f, indent=2) + + +def _load_session_data(session_id: str) -> dict | None: + path = os.path.join(SESSIONS_DIR, f"{session_id}.json") + if not os.path.exists(path): + return None + with open(path) as f: + return json.load(f) + + +def _delete_session_file(session_id: str): + path = os.path.join(SESSIONS_DIR, f"{session_id}.json") + if os.path.exists(path): + os.remove(path) + + +def _load_all_session_data() -> list[tuple[str, dict]]: + results = [] + if not os.path.exists(SESSIONS_DIR): + return results + for fname in os.listdir(SESSIONS_DIR): + if fname.endswith(".json"): + with open(os.path.join(SESSIONS_DIR, fname)) as f: + results.append((fname[:-5], json.load(f))) + return results + +FULL_TOOLS = [ + "Read", "Edit", "Write", "Bash", "Glob", "Grep", "AskUserQuestion", + "WebSearch", "WebFetch", "NotebookEdit", "TodoWrite", + "EnterPlanMode", "ExitPlanMode", "EnterWorktree", + "TaskOutput", "TaskStop", + "CronCreate", "CronList", "CronDelete", + "RenderOutput", +] + +REPO_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) + + +def _get_denied_tool_names(tool) -> set[str]: + """Return the set of MCP sub-tool names whose permission is 'deny'.""" + return { + key for key, value in tool.tool_permissions.items() + if not key.startswith("_") and value == "deny" + } + + +def _get_all_known_tool_names(tool) -> set[str]: + """Return all known sub-tool names for an MCP tool (from _tool_descriptions).""" + return set(tool.tool_permissions.get("_tool_descriptions", {}).keys()) + + +def _is_fully_denied(tool) -> bool: + """True when every known sub-tool on this MCP server is set to 'deny'.""" + known = _get_all_known_tool_names(tool) + if not known: + return False + return known <= _get_denied_tool_names(tool) + + +def get_all_tool_names() -> list[str]: + """FULL_TOOLS + installed MCP tool identifiers (mcp:). + + Builtin tools set to 'deny' and MCP servers whose every sub-tool + is denied are excluded. + """ + builtin_perms = load_builtin_permissions() + builtin_tools = [ + t for t in FULL_TOOLS + if builtin_perms.get(t, "always_allow") != "deny" + ] + mcp_names = [ + f"mcp:{t.name}" + for t in load_all_tools() + if t.mcp_config + and t.enabled + and t.auth_status in ("configured", "connected") + and not _is_fully_denied(t) + ] + return builtin_tools + mcp_names + + +class AgentManager: + def __init__(self): + self.sessions: dict[str, AgentSession] = {} + self.tasks: dict[str, asyncio.Task] = {} + self.worktree_mgr = WorktreeManager(REPO_ROOT) + + def _resolve_mode(self, mode_id: str) -> tuple[list[str], str | None, str | None]: + """Return (tools, system_prompt, default_folder) resolved from the mode store.""" + mode_def = load_mode(mode_id) + if mode_def: + tools = mode_def.tools if mode_def.tools is not None else get_all_tool_names() + return tools, mode_def.system_prompt, mode_def.default_folder + return get_all_tool_names(), None, None + + async def _build_mcp_servers(self, allowed_tools: list[str]) -> dict: + """Build the mcp_servers dict for ClaudeAgentOptions from installed MCP tools. + + Servers whose every sub-tool is denied are skipped entirely. + """ + mcp_servers: dict = {} + all_tools = load_all_tools() + mcp_tools = [t for t in all_tools if t.mcp_config and t.enabled and t.auth_status in ("configured", "connected")] + + for tool in mcp_tools: + tool_ref = f"mcp:{tool.name}" + if tool_ref not in allowed_tools and allowed_tools != get_all_tool_names(): + if not any(tool_ref == at for at in allowed_tools): + continue + + if _is_fully_denied(tool): + continue + + if tool.auth_type == "oauth2" and tool.auth_status == "connected": + await refresh_google_token(tool) + + config = derive_mcp_config(tool) + if config: + server_name = _sanitize_server_name(tool.name) + mcp_servers[server_name] = config + + return mcp_servers + + def _build_connected_tools_context(self, allowed_tools: list[str]) -> str | None: + """Build a context block describing connected MCP tools and their accounts. + + Tools set to 'deny' and fully-denied servers are excluded. + """ + all_tools = load_all_tools() + mcp_tools = [t for t in all_tools if t.mcp_config and t.enabled and t.auth_status in ("configured", "connected")] + + sections = [] + for tool in mcp_tools: + tool_ref = f"mcp:{tool.name}" + if tool_ref not in allowed_tools and allowed_tools != get_all_tool_names(): + continue + + if _is_fully_denied(tool): + continue + + server_name = _sanitize_server_name(tool.name) + denied = _get_denied_tool_names(tool) + tool_descs = { + k: v for k, v in tool.tool_permissions.get("_tool_descriptions", {}).items() + if k not in denied + } + if not tool_descs: + continue + + lines = [f"MCP Server: {server_name}"] + lines.append(f" Status: {tool.auth_status}") + + if tool.connected_account_email: + lines.append(f" Connected account: {tool.connected_account_email}") + lines.append( + f" IMPORTANT: When calling tools from this server that require an email " + f"parameter (e.g. user_google_email, user_email), always use " + f"\"{tool.connected_account_email}\" automatically — do NOT ask the user." + ) + + tool_names = list(tool_descs.keys()) + if tool_names: + lines.append(f" Available tools ({len(tool_names)}): {', '.join(tool_names[:15])}") + if len(tool_names) > 15: + lines.append(f" ... and {len(tool_names) - 15} more") + + sections.append("\n".join(lines)) + + if not sections: + return None + return ( + "\n" + "The following MCP tool servers are connected and available. " + "Use them directly when relevant to the user's request.\n\n" + + "\n\n".join(sections) + + "\n" + ) + + def _build_outputs_context(self) -> str | None: + """Build a context block describing available Outputs the agent can render.""" + import json as _json + all_outputs = load_all_outputs() + if not all_outputs: + return None + + sections = [] + for out in all_outputs: + lines = [f"- **{out.name}** (id: `{out.id}`)"] + if out.description: + lines.append(f" Description: {out.description}") + schema_str = _json.dumps(out.input_schema, indent=2) + lines.append(f" Input schema:\n```json\n{schema_str}\n```") + sections.append("\n".join(lines)) + + return ( + "\n" + "The following reusable View artifacts are available. " + "Use the RenderOutput tool to invoke one by providing its output_id " + "and the required input_data matching its schema.\n\n" + + "\n\n".join(sections) + + "\n" + ) + + def _compose_system_prompt(self, default_prompt: str | None, mode_prompt: str | None, session_prompt: str | None, connected_tools_ctx: str | None = None, outputs_ctx: str | None = None) -> str | None: + parts = [p for p in (default_prompt, mode_prompt, session_prompt, connected_tools_ctx, outputs_ctx) if p] + return "\n\n".join(parts) if parts else None + + async def launch_agent(self, config: AgentConfig) -> AgentSession: + session_id = uuid4().hex + branch_name = f"agent-{session_id[:8]}" + + worktree_path = await self.worktree_mgr.create_worktree(branch_name) + + mode_tools, _, mode_folder = self._resolve_mode(config.mode) + tools = mode_tools + + global_settings = load_settings() + effective_cwd = ( + config.target_directory + or mode_folder + or global_settings.default_folder + or str(REPO_ROOT) + ) + + if config.mode in ("view-builder", "skill-builder") and not config.target_directory: + effective_cwd = os.path.join(effective_cwd, session_id) + + os.makedirs(effective_cwd, exist_ok=True) + + session = AgentSession( + id=session_id, + name=config.name, + model=config.model, + mode=config.mode, + worktree_path=worktree_path, + branch_name=branch_name, + system_prompt=config.system_prompt, + allowed_tools=tools, + max_turns=config.max_turns, + cwd=effective_cwd, + dashboard_id=config.dashboard_id, + ) + self.sessions[session_id] = session + + await ws_manager.send_to_session(session_id, "agent:status", { + "session_id": session_id, + "status": "running", + "session": session.model_dump(mode="json"), + }) + + return session + + def _resolve_context_paths(self, context_paths: list | None) -> str: + """Read file contents / directory trees for attached context paths.""" + if not context_paths: + return "" + sections = [] + for cp in context_paths: + path = cp.get("path", "") + cp_type = cp.get("type", "file") + if not path or not os.path.exists(path): + sections.append(f"[Context: {path} — not found]") + continue + if cp_type == "file" and os.path.isfile(path): + try: + with open(path, "r", errors="replace") as f: + content = f.read(512_000) # ~500KB cap per file + sections.append( + f"\n{content}\n" + ) + except Exception as e: + sections.append(f"[Context: {path} — error reading: {e}]") + elif cp_type == "directory" and os.path.isdir(path): + tree_lines = self._build_dir_tree(path, max_depth=4) + sections.append( + f"\n{chr(10).join(tree_lines)}\n" + ) + else: + sections.append(f"[Context: {path} — type mismatch]") + return "\n\n".join(sections) + + def _build_dir_tree(self, root: str, max_depth: int = 4, prefix: str = "") -> list[str]: + """Build a recursive directory tree listing.""" + lines = [] + try: + entries = sorted(os.listdir(root)) + except PermissionError: + return [f"{prefix}[permission denied]"] + dirs = [e for e in entries if not e.startswith(".") and os.path.isdir(os.path.join(root, e))] + files = [e for e in entries if not e.startswith(".") and os.path.isfile(os.path.join(root, e))] + for f in files: + lines.append(f"{prefix}{f}") + for d in dirs: + lines.append(f"{prefix}{d}/") + if max_depth > 1: + sub = self._build_dir_tree(os.path.join(root, d), max_depth - 1, prefix + " ") + lines.extend(sub) + return lines + + def _resolve_forced_tools(self, forced_tools: list[str] | None) -> str: + """Build a context block describing explicitly requested tools.""" + if not forced_tools: + return "" + from backend.apps.tools_lib.models import BUILTIN_TOOLS + desc_map: dict[str, str] = {t.name: t.description for t in BUILTIN_TOOLS} + tool_to_server: dict[str, str] = {} + tool_to_email: dict[str, str] = {} + for t in load_all_tools(): + if not t.enabled or not t.tool_permissions: + continue + tool_descs = t.tool_permissions.get("_tool_descriptions", {}) + server_name = _sanitize_server_name(t.name) + for tn, td in tool_descs.items(): + desc_map[tn] = td + tool_to_server[tn] = server_name + if t.connected_account_email: + tool_to_email[tn] = t.connected_account_email + + lines = [] + for name in forced_tools: + desc = desc_map.get(name, "") + line = f"- {name}: {desc}" if desc else f"- {name}" + server = tool_to_server.get(name) + if server: + line += f"\n (MCP server: {server})" + email = tool_to_email.get(name) + if email: + line += f"\n (connected account: {email} — use this for any email parameter)" + lines.append(line) + + return ( + "\n" + "The user explicitly requested these tools be used. " + "Prioritize using them to address the user's request.\n" + + "\n".join(lines) + + "\n" + ) + + def _resolve_attached_skills(self, attached_skills: list | None) -> str: + """Build a context block injecting attached skill content into the prompt.""" + if not attached_skills: + return "" + sections = [] + for skill in attached_skills: + name = skill.get("name", "Unknown") + content = skill.get("content", "") + if content: + sections.append(f"[Using skill: {name}]\n\n{content}") + return "\n\n".join(sections) + + def _build_prompt_content(self, prompt: str, images: list | None = None, context_paths: list | None = None, forced_tools: list[str] | None = None, attached_skills: list | None = None): + """Build message content with optional image blocks, context, and forced tools for the Claude API.""" + context_text = self._resolve_context_paths(context_paths) + forced_tools_text = self._resolve_forced_tools(forced_tools) + skills_text = self._resolve_attached_skills(attached_skills) + + parts = [p for p in (forced_tools_text, context_text, skills_text, prompt) if p] + full_prompt = "\n\n".join(parts) + + if not images: + return full_prompt + content = [{"type": "text", "text": full_prompt}] + for img in images: + content.append({ + "type": "image", + "source": { + "type": "base64", + "media_type": img.get("media_type", "image/png"), + "data": img["data"], + }, + }) + return content + + async def _run_agent_loop(self, session_id: str, prompt: str, images: list | None = None, context_paths: list | None = None, forced_tools: list[str] | None = None, attached_skills: list | None = None): + """Run the Claude Agent SDK query loop for a session.""" + session = self.sessions.get(session_id) + if not session: + return + + prompt_content = self._build_prompt_content(prompt, images, context_paths, forced_tools, attached_skills) + + try: + from claude_agent_sdk import ( + query, ClaudeAgentOptions, AssistantMessage, ResultMessage, + ) + from claude_agent_sdk.types import ( + HookMatcher, PermissionResultAllow, PermissionResultDeny, + TextBlock, ToolUseBlock, StreamEvent, + ) + except ImportError: + logger.warning("claude_agent_sdk not installed, running in mock mode") + await self._run_mock_agent(session_id, prompt) + return + + session.status = "running" + + _builtin_perms = load_builtin_permissions() + + def _check_tool_permission(tool_name: str) -> str | None: + """Check tool permissions for both builtin and MCP tools. + Returns 'always_allow', 'deny', or None (ask).""" + if tool_name in _builtin_perms: + policy = _builtin_perms[tool_name] + if policy in ("always_allow", "deny"): + return policy + return None + + import re as _re + m = _re.match(r"mcp__([^_]+(?:-[^_]+)*)__(.+)", tool_name) + if not m: + return None + server_slug, mcp_tool_name = m.group(1), m.group(2) + for t in load_all_tools(): + if not t.mcp_config or not t.enabled: + continue + if _sanitize_server_name(t.name) == server_slug: + policy = t.tool_permissions.get(mcp_tool_name, "ask") + if policy in ("always_allow", "deny"): + return policy + return None + return None + + async def can_use_tool(tool_name, input_data, context): + if tool_name != "AskUserQuestion": + policy = _check_tool_permission(tool_name) + if policy == "always_allow": + return PermissionResultAllow(updated_input=input_data) + if policy == "deny": + return PermissionResultDeny(message="Tool denied by permission policy") + + request_id = uuid4().hex + approval_req = ApprovalRequest( + id=request_id, + session_id=session_id, + tool_name=tool_name, + tool_input=input_data if isinstance(input_data, dict) else {}, + ) + session.pending_approvals.append(approval_req) + session.status = "waiting_approval" + + await ws_manager.send_to_session(session_id, "agent:status", { + "session_id": session_id, + "status": "waiting_approval", + }) + + decision = await ws_manager.send_approval_request( + session_id, request_id, tool_name, + input_data if isinstance(input_data, dict) else {} + ) + + session.pending_approvals = [ + a for a in session.pending_approvals if a.id != request_id + ] + session.status = "running" + await ws_manager.send_to_session(session_id, "agent:status", { + "session_id": session_id, + "status": "running", + }) + + if decision.get("behavior") == "allow": + return PermissionResultAllow( + updated_input=decision.get("updated_input", input_data) + ) + else: + return PermissionResultDeny( + message=decision.get("message", "User denied this action") + ) + + tool_start_times: dict[str, float] = {} + + async def pre_tool_hook(input_data, tool_use_id, context): + if tool_use_id: + tool_start_times[tool_use_id] = time.time() + return {"continue_": True} + + async def post_tool_hook(input_data, tool_use_id, context): + elapsed_ms = None + if tool_use_id and tool_use_id in tool_start_times: + elapsed_ms = int((time.time() - tool_start_times.pop(tool_use_id)) * 1000) + + raw_response = input_data.get("tool_response", "") + + if isinstance(raw_response, list) and raw_response: + text_parts = [ + block.get("text", "") + for block in raw_response + if isinstance(block, dict) and block.get("type") == "text" + ] + if text_parts: + raw_response = "\n".join(text_parts) if len(text_parts) > 1 else text_parts[0] + + if isinstance(raw_response, str): + content = raw_response + else: + try: + import json as _json + content = _json.dumps(raw_response, indent=2, default=str) + except Exception: + content = str(raw_response) + + result_payload = {"text": content} + hook_tool_name = input_data.get("tool_name", "") + if hook_tool_name: + result_payload["tool_name"] = hook_tool_name + if elapsed_ms is not None: + result_payload["elapsed_ms"] = elapsed_ms + + result_msg = Message(role="tool_result", content=result_payload) + session.messages.append(result_msg) + await ws_manager.send_to_session(session_id, "agent:message", { + "session_id": session_id, + "message": result_msg.model_dump(mode="json"), + }) + return {"continue_": True} + + try: + _, mode_sys_prompt, _ = self._resolve_mode(session.mode) + connected_tools_ctx = self._build_connected_tools_context(session.allowed_tools) + outputs_ctx = self._build_outputs_context() + global_settings = load_settings() + composed_prompt = self._compose_system_prompt(global_settings.default_system_prompt, mode_sys_prompt, session.system_prompt, connected_tools_ctx, outputs_ctx) + + mcp_servers = await self._build_mcp_servers(session.allowed_tools) + + effective_allowed = [ + t for t in session.allowed_tools + if _builtin_perms.get(t, "always_allow") == "always_allow" + ] + if mcp_servers: + all_tools_list = load_all_tools() + for name in mcp_servers: + tool_def = next( + (t for t in all_tools_list + if t.mcp_config and t.enabled and _sanitize_server_name(t.name) == name), + None, + ) + if tool_def: + denied = _get_denied_tool_names(tool_def) + known = _get_all_known_tool_names(tool_def) + for tn in known - denied: + policy = tool_def.tool_permissions.get(tn, "ask") + if policy == "always_allow": + effective_allowed.append(f"mcp__{name}__{tn}") + else: + effective_allowed.append(f"mcp__{name}__*") + + options_kwargs = { + "model": session.model, + "can_use_tool": can_use_tool, + "hooks": { + "PreToolUse": [HookMatcher(matcher=None, hooks=[pre_tool_hook])], + "PostToolUse": [HookMatcher(matcher=None, hooks=[post_tool_hook])], + }, + "allowed_tools": effective_allowed, + "include_partial_messages": True, + } + if global_settings.anthropic_api_key: + options_kwargs["env"] = {"ANTHROPIC_API_KEY": global_settings.anthropic_api_key} + if mcp_servers: + options_kwargs["mcp_servers"] = mcp_servers + if composed_prompt: + options_kwargs["system_prompt"] = composed_prompt + if session.max_turns: + options_kwargs["max_turns"] = session.max_turns + + if session.cwd: + options_kwargs["cwd"] = session.cwd + + if session.sdk_session_id: + options_kwargs["resume"] = session.sdk_session_id + + options = ClaudeAgentOptions(**options_kwargs) + + async def prompt_stream(): + yield { + "type": "user", + "message": {"role": "user", "content": prompt_content}, + } + + stream_text_msg_id = None + stream_tool_msg_ids_ordered = [] + stream_block_index_map = {} + + async for message in query( + prompt=prompt_stream(), + options=options, + ): + if isinstance(message, StreamEvent): + event = message.event + event_type = event.get("type") + + if event_type == "content_block_start": + block = event.get("content_block", {}) + index = event.get("index") + block_type = block.get("type") + + if block_type == "text": + if stream_text_msg_id is None: + stream_text_msg_id = uuid4().hex + await ws_manager.send_to_session(session_id, "agent:stream_start", { + "session_id": session_id, + "message_id": stream_text_msg_id, + "role": "assistant", + }) + stream_block_index_map[index] = stream_text_msg_id + + elif block_type == "tool_use": + tool_msg_id = uuid4().hex + stream_tool_msg_ids_ordered.append(tool_msg_id) + stream_block_index_map[index] = tool_msg_id + await ws_manager.send_to_session(session_id, "agent:stream_start", { + "session_id": session_id, + "message_id": tool_msg_id, + "role": "tool_call", + "tool_name": block.get("name", ""), + }) + + elif event_type == "content_block_delta": + index = event.get("index") + delta = event.get("delta", {}) + delta_type = delta.get("type") + msg_id = stream_block_index_map.get(index) + + if msg_id and delta_type == "text_delta": + await ws_manager.send_to_session(session_id, "agent:stream_delta", { + "session_id": session_id, + "message_id": msg_id, + "delta": delta.get("text", ""), + }) + elif msg_id and delta_type == "input_json_delta": + await ws_manager.send_to_session(session_id, "agent:stream_delta", { + "session_id": session_id, + "message_id": msg_id, + "delta": delta.get("partial_json", ""), + }) + + elif event_type == "content_block_stop": + index = event.get("index") + msg_id = stream_block_index_map.get(index) + if msg_id and msg_id != stream_text_msg_id: + await ws_manager.send_to_session(session_id, "agent:stream_end", { + "session_id": session_id, + "message_id": msg_id, + }) + + elif event_type == "message_stop": + if stream_text_msg_id: + await ws_manager.send_to_session(session_id, "agent:stream_end", { + "session_id": session_id, + "message_id": stream_text_msg_id, + }) + + elif isinstance(message, AssistantMessage): + content_parts = [] + tool_uses = [] + for block in message.content: + if isinstance(block, TextBlock): + content_parts.append(block.text) + elif isinstance(block, ToolUseBlock): + tool_uses.append({ + "id": block.id, + "tool": block.name, + "input": block.input, + }) + + if content_parts: + asst_msg = Message( + id=stream_text_msg_id or uuid4().hex, + role="assistant", + content="\n".join(content_parts), + ) + session.messages.append(asst_msg) + await ws_manager.send_to_session(session_id, "agent:message", { + "session_id": session_id, + "message": asst_msg.model_dump(mode="json"), + }) + + for i, tu in enumerate(tool_uses): + msg_id = stream_tool_msg_ids_ordered[i] if i < len(stream_tool_msg_ids_ordered) else uuid4().hex + tool_msg = Message(id=msg_id, role="tool_call", content=tu) + session.messages.append(tool_msg) + await ws_manager.send_to_session(session_id, "agent:message", { + "session_id": session_id, + "message": tool_msg.model_dump(mode="json"), + }) + + stream_text_msg_id = None + stream_tool_msg_ids_ordered = [] + stream_block_index_map = {} + + elif isinstance(message, ResultMessage): + session.sdk_session_id = getattr(message, "session_id", None) + cost = getattr(message, "total_cost_usd", None) + if cost is not None: + session.cost_usd = cost + await ws_manager.send_to_session(session_id, "agent:cost_update", { + "session_id": session_id, + "cost_usd": session.cost_usd, + }) + + session.status = "completed" + except asyncio.CancelledError: + session.status = "stopped" + except Exception as e: + logger.exception(f"Agent {session_id} error: {e}") + session.status = "error" + error_msg = Message(role="system", content=f"Error: {str(e)}") + session.messages.append(error_msg) + await ws_manager.send_to_session(session_id, "agent:message", { + "session_id": session_id, + "message": error_msg.model_dump(mode="json"), + }) + finally: + if session_id in self.sessions: + await ws_manager.send_to_session(session_id, "agent:status", { + "session_id": session_id, + "status": session.status, + "session": session.model_dump(mode="json"), + }) + try: + _save_session(session_id, session.model_dump(mode="json")) + except Exception as e: + logger.warning(f"Failed to snapshot session {session_id}: {e}") + + async def _stream_text(self, session_id: str, msg_id: str, text: str, delay: float = 0.03): + """Emit stream_start, word-by-word deltas, and stream_end for a text message.""" + await ws_manager.send_to_session(session_id, "agent:stream_start", { + "session_id": session_id, + "message_id": msg_id, + "role": "assistant", + }) + words = text.split(" ") + for i, word in enumerate(words): + chunk = word if i == 0 else " " + word + await ws_manager.send_to_session(session_id, "agent:stream_delta", { + "session_id": session_id, + "message_id": msg_id, + "delta": chunk, + }) + await asyncio.sleep(delay) + await ws_manager.send_to_session(session_id, "agent:stream_end", { + "session_id": session_id, + "message_id": msg_id, + }) + + async def _stream_tool_input(self, session_id: str, msg_id: str, tool_name: str, input_json: str, delay: float = 0.02): + """Emit stream_start, chunked deltas, and stream_end for a tool_call input.""" + await ws_manager.send_to_session(session_id, "agent:stream_start", { + "session_id": session_id, + "message_id": msg_id, + "role": "tool_call", + "tool_name": tool_name, + }) + chunk_size = 12 + for i in range(0, len(input_json), chunk_size): + await ws_manager.send_to_session(session_id, "agent:stream_delta", { + "session_id": session_id, + "message_id": msg_id, + "delta": input_json[i:i + chunk_size], + }) + await asyncio.sleep(delay) + await ws_manager.send_to_session(session_id, "agent:stream_end", { + "session_id": session_id, + "message_id": msg_id, + }) + + async def _run_mock_agent(self, session_id: str, prompt: str): + """Mock agent loop for development without claude_agent_sdk installed.""" + session = self.sessions.get(session_id) + if not session: + return + + await asyncio.sleep(1) + + request_id = uuid4().hex + approval_req = ApprovalRequest( + id=request_id, + session_id=session_id, + tool_name="Bash", + tool_input={"command": f"echo 'Processing: {prompt}'", "description": "Echo the user prompt"}, + ) + session.pending_approvals.append(approval_req) + session.status = "waiting_approval" + await ws_manager.send_to_session(session_id, "agent:status", { + "session_id": session_id, + "status": "waiting_approval", + }) + + decision = await ws_manager.send_approval_request( + session_id, request_id, "Bash", + {"command": f"echo 'Processing: {prompt}'", "description": "Echo the user prompt"} + ) + + session.pending_approvals = [a for a in session.pending_approvals if a.id != request_id] + session.status = "running" + await ws_manager.send_to_session(session_id, "agent:status", { + "session_id": session_id, + "status": "running", + }) + + import json as _json + tool_input_content = {"tool": "Bash", "input": {"command": f"echo 'Processing: {prompt}'"}, "approved": decision.get("behavior") == "allow"} + tool_msg_id = uuid4().hex + await self._stream_tool_input( + session_id, tool_msg_id, "Bash", + _json.dumps(tool_input_content["input"], indent=2), + ) + tool_msg = Message(id=tool_msg_id, role="tool_call", content=tool_input_content) + session.messages.append(tool_msg) + await ws_manager.send_to_session(session_id, "agent:message", { + "session_id": session_id, + "message": tool_msg.model_dump(mode="json"), + }) + + await asyncio.sleep(1) + + if decision.get("behavior") == "allow": + tool_result = Message(role="tool_result", content=f"Processing: {prompt}") + session.messages.append(tool_result) + await ws_manager.send_to_session(session_id, "agent:message", { + "session_id": session_id, + "message": tool_result.model_dump(mode="json"), + }) + + await asyncio.sleep(1) + + asst_text = ( + f"I've processed your request: \"{prompt}\"\n\n" + "This is a mock response because `claude-agent-sdk` is not installed. " + "Install it with `pip install claude-agent-sdk` to use real Claude Code instances.\n\n" + f"The agent was configured with:\n- Model: {session.model}\n- Mode: {session.mode}\n- Worktree: {session.branch_name}" + ) + asst_msg_id = uuid4().hex + await self._stream_text(session_id, asst_msg_id, asst_text) + + asst_msg = Message(id=asst_msg_id, role="assistant", content=asst_text) + session.messages.append(asst_msg) + await ws_manager.send_to_session(session_id, "agent:message", { + "session_id": session_id, + "message": asst_msg.model_dump(mode="json"), + }) + + session.status = "completed" + session.cost_usd = 0.001 + await ws_manager.send_to_session(session_id, "agent:status", { + "session_id": session_id, + "status": "completed", + "session": session.model_dump(mode="json"), + }) + await ws_manager.send_to_session(session_id, "agent:cost_update", { + "session_id": session_id, + "cost_usd": session.cost_usd, + }) + + async def send_message( + self, + session_id: str, + prompt: str, + mode: str | None = None, + model: str | None = None, + images: list | None = None, + context_paths: list | None = None, + forced_tools: list[str] | None = None, + attached_skills: list | None = None, + ): + """Send a follow-up message to an existing session.""" + session = self.sessions.get(session_id) + if not session: + raise ValueError(f"Session {session_id} not found") + + existing = self.tasks.get(session_id) + if existing and not existing.done(): + return + + session_changed = False + if model and model != session.model: + session.model = model + session_changed = True + if mode and mode != session.mode: + session.mode = mode + mode_tools, _, _ = self._resolve_mode(mode) + session.allowed_tools = mode_tools + session_changed = True + if session_changed: + await ws_manager.send_to_session(session_id, "agent:status", { + "session_id": session_id, + "status": session.status, + "session": session.model_dump(mode="json"), + }) + + skill_meta = [{"id": s["id"], "name": s["name"]} for s in (attached_skills or [])] or None + image_meta = [{"data": img["data"], "media_type": img.get("media_type", "image/png")} for img in (images or [])] or None + user_msg = Message( + role="user", + content=prompt, + context_paths=context_paths if context_paths else None, + attached_skills=skill_meta, + forced_tools=forced_tools if forced_tools else None, + images=image_meta, + ) + session.messages.append(user_msg) + await ws_manager.send_to_session(session_id, "agent:message", { + "session_id": session_id, + "message": user_msg.model_dump(mode="json"), + }) + + task = asyncio.create_task(self._run_agent_loop(session_id, prompt, images=images, context_paths=context_paths, forced_tools=forced_tools, attached_skills=attached_skills)) + self.tasks[session_id] = task + + async def stop_agent(self, session_id: str, remove_worktree: bool = False): + """Stop a running agent.""" + task = self.tasks.get(session_id) + if task and not task.done(): + task.cancel() + try: + await task + except asyncio.CancelledError: + pass + + session = self.sessions.get(session_id) + if session: + session.status = "stopped" + await ws_manager.send_to_session(session_id, "agent:status", { + "session_id": session_id, + "status": "stopped", + "session": session.model_dump(mode="json"), + }) + + if remove_worktree and session and session.branch_name: + await self.worktree_mgr.remove_worktree(session.branch_name) + + def handle_approval(self, request_id: str, decision: dict): + """Resolve a pending HITL approval.""" + ws_manager.resolve_approval(request_id, decision) + + async def edit_message(self, session_id: str, message_id: str, new_content: str): + """Edit a prior user message, creating a new branch (fork).""" + session = self.sessions.get(session_id) + if not session: + raise ValueError(f"Session {session_id} not found") + + target_msg = None + for i, msg in enumerate(session.messages): + if msg.id == message_id: + target_msg = msg + break + + if not target_msg or target_msg.role != "user": + raise ValueError("Can only edit user messages") + + new_branch_id = uuid4().hex[:8] + new_branch = MessageBranch( + id=new_branch_id, + parent_branch_id=target_msg.branch_id, + fork_point_message_id=message_id, + ) + session.branches[new_branch_id] = new_branch + session.active_branch_id = new_branch_id + + edited_msg = Message( + role="user", + content=new_content, + branch_id=new_branch_id, + parent_id=target_msg.parent_id, + ) + session.messages.append(edited_msg) + + await ws_manager.send_to_session(session_id, "agent:message", { + "session_id": session_id, + "message": edited_msg.model_dump(mode="json"), + }) + await ws_manager.send_to_session(session_id, "agent:branch_created", { + "session_id": session_id, + "branch": new_branch.model_dump(mode="json"), + "active_branch_id": new_branch_id, + }) + + task = asyncio.create_task(self._run_agent_loop(session_id, new_content)) + self.tasks[session_id] = task + + async def switch_branch(self, session_id: str, branch_id: str): + session = self.sessions.get(session_id) + if not session: + raise ValueError(f"Session {session_id} not found") + if branch_id not in session.branches: + raise ValueError(f"Branch {branch_id} not found") + session.active_branch_id = branch_id + await ws_manager.send_to_session(session_id, "agent:branch_switched", { + "session_id": session_id, + "active_branch_id": branch_id, + }) + + async def generate_title(self, session_id: str, first_prompt: str) -> str: + """Use a cheap LLM call to generate a short chat title from the first user message.""" + session = self.sessions.get(session_id) + if not session: + raise ValueError(f"Session {session_id} not found") + + title = first_prompt[:40].strip() + try: + import anthropic + global_settings = load_settings() + client_kwargs = {} + if global_settings.anthropic_api_key: + client_kwargs["api_key"] = global_settings.anthropic_api_key + client = anthropic.AsyncAnthropic(**client_kwargs) + resp = await client.messages.create( + model="claude-sonnet-4-20250514", + max_tokens=30, + system="Generate a concise 3-6 word title for a chat that starts with this message. Return only the title, nothing else.", + messages=[{"role": "user", "content": first_prompt}], + ) + generated = resp.content[0].text.strip().strip('"\'') + if generated: + title = generated + except Exception as e: + logger.warning(f"Title generation failed, using fallback: {e}") + + session.name = title + await ws_manager.send_to_session(session_id, "agent:name_updated", { + "session_id": session_id, + "name": title, + }) + return title + + async def generate_group_meta( + self, + session_id: str, + group_id: str, + tool_calls: list[dict], + results_summary: list[str] | None = None, + is_refinement: bool = False, + ) -> dict: + """Use a cheap LLM call to generate a name + SVG icon for a tool group.""" + session = self.sessions.get(session_id) + if not session: + raise ValueError(f"Session {session_id} not found") + + fallback_name = tool_calls[0].get("tool", "Tool calls") if tool_calls else "Tool calls" + fallback_name = fallback_name.split("__")[-1].replace("_", " ").title() if "__" in fallback_name else fallback_name + + name = fallback_name + svg = "" + + try: + import anthropic, json as _json + global_settings = load_settings() + client_kwargs = {} + if global_settings.anthropic_api_key: + client_kwargs["api_key"] = global_settings.anthropic_api_key + client = anthropic.AsyncAnthropic(**client_kwargs) + + tool_desc = "\n".join( + f"- {tc.get('tool', '?')}: {tc.get('input_summary', '')}" for tc in tool_calls + ) + user_content = f"Tool actions:\n{tool_desc}" + if results_summary: + user_content += f"\n\nResults:\n" + "\n".join(f"- {r}" for r in results_summary) + + system = ( + "Generate a concise 2-5 word name and a minimal SVG icon for a group of tool actions.\n\n" + "Return ONLY valid JSON: {\"name\": \"...\", \"svg\": \"...\"}\n\n" + "Name rules:\n" + "- 2-5 words, title case, describes the action (e.g. \"Email Inbox Search\", \"Reading Project Files\")\n\n" + "SVG rules:\n" + "- 24x24 viewBox\n" + "- Use currentColor for all stroke/fill values\n" + "- Simple geometric shapes only (line, circle, rect, path, polyline)\n" + "- No text elements, no embedded images, no gradients, no filters\n" + "- Minimal: 1-3 shapes, stroke-width=\"1.5\", fill=\"none\" unless intentional\n" + "- Return ONLY the inner SVG elements (no outer tag)\n" + "- Max 400 characters for the svg string" + ) + + resp = await client.messages.create( + model="claude-sonnet-4-20250514", + max_tokens=300, + system=system, + messages=[{"role": "user", "content": user_content}], + ) + + raw = resp.content[0].text.strip() + if raw.startswith("```"): + raw = raw.split("\n", 1)[-1].rsplit("```", 1)[0].strip() + parsed = _json.loads(raw) + if parsed.get("name"): + name = parsed["name"].strip().strip("\"'") + if parsed.get("svg"): + svg = parsed["svg"].strip() + except Exception as e: + logger.warning(f"Group meta generation failed, using fallback: {e}") + + meta = ToolGroupMeta(id=group_id, name=name, svg=svg, is_refined=is_refinement) + session.tool_group_meta[group_id] = meta + + await ws_manager.send_to_session(session_id, "agent:group_meta_updated", { + "session_id": session_id, + "group_id": group_id, + "name": name, + "svg": svg, + "is_refined": is_refinement, + }) + + return {"name": name, "svg": svg, "is_refined": is_refinement} + + async def update_session(self, session_id: str, **fields): + """Update mutable session fields (system_prompt, name).""" + session = self.sessions.get(session_id) + if not session: + raise ValueError(f"Session {session_id} not found") + + allowed = {"system_prompt", "name"} + for key, value in fields.items(): + if key in allowed: + setattr(session, key, value) + + await ws_manager.send_to_session(session_id, "agent:status", { + "session_id": session_id, + "status": session.status, + "session": session.model_dump(mode="json"), + }) + + @staticmethod + def _build_search_text(session: AgentSession, max_len: int = 5000) -> str: + """Build a search-indexing string from the session name and message content.""" + parts = [session.name or ""] + for msg in session.messages: + if msg.role in ("user", "assistant") and isinstance(msg.content, str): + parts.append(msg.content) + text = " ".join(parts) + return text[:max_len] + + async def close_session(self, session_id: str) -> None: + """Close a session: pause the agent if running, persist to JSON file, + and remove from in-memory state. Worktree is kept on disk for resume.""" + task = self.tasks.get(session_id) + if task and not task.done(): + task.cancel() + try: + await task + except asyncio.CancelledError: + pass + + session = self.sessions.get(session_id) + if not session: + raise ValueError(f"Session {session_id} not found") + + if session.status in ("running", "waiting_approval"): + session.status = "stopped" + session.closed_at = datetime.now() + session.pending_approvals = [] + + doc_data = session.model_dump(mode="json") + doc_data["search_text"] = self._build_search_text(session) + + _save_session(session_id, doc_data) + + await ws_manager.send_to_session(session_id, "agent:closed", { + "session_id": session_id, + "status": session.status, + "name": session.name, + "model": session.model, + "mode": session.mode, + "created_at": session.created_at.isoformat() if session.created_at else None, + "closed_at": session.closed_at.isoformat() if session.closed_at else None, + "cost_usd": session.cost_usd, + "dashboard_id": session.dashboard_id, + }) + + self.sessions.pop(session_id, None) + self.tasks.pop(session_id, None) + logger.info(f"Session {session_id} closed and persisted") + + async def delete_session(self, session_id: str) -> None: + """Permanently delete a session: remove from memory, JSON file, worktree, and branch.""" + task = self.tasks.get(session_id) + if task and not task.done(): + task.cancel() + try: + await task + except asyncio.CancelledError: + pass + + session = self.sessions.pop(session_id, None) + self.tasks.pop(session_id, None) + + branch_name: str | None = None + if session: + branch_name = session.branch_name + else: + data = _load_session_data(session_id) + if data: + branch_name = data.get("branch_name") + + if branch_name: + await self.worktree_mgr.remove_worktree(branch_name) + await self.worktree_mgr.delete_branch(branch_name) + + _delete_session_file(session_id) + logger.info(f"Session {session_id} permanently deleted") + + async def resume_session(self, session_id: str) -> AgentSession: + """Restore a closed session from JSON file back into active memory.""" + if session_id in self.sessions: + return self.sessions[session_id] + + data = _load_session_data(session_id) + if data is None: + raise ValueError(f"Session {session_id} not found in history") + + session = AgentSession(**data) + + if session.branch_name: + worktree_path = os.path.join(self.worktree_mgr.worktrees_dir, session.branch_name) + if not os.path.exists(worktree_path): + try: + worktree_path = await self.worktree_mgr.create_worktree(session.branch_name) + except RuntimeError: + new_branch = f"agent-{session_id[:8]}" + worktree_path = await self.worktree_mgr.create_worktree(new_branch) + session.branch_name = new_branch + session.worktree_path = worktree_path + + session.closed_at = None + self.sessions[session_id] = session + + _delete_session_file(session_id) + + await ws_manager.send_to_session(session_id, "agent:status", { + "session_id": session_id, + "status": session.status, + "session": session.model_dump(mode="json"), + }) + + logger.info(f"Session {session_id} resumed from history") + return session + + def get_history( + self, + q: str = "", + limit: int = 20, + offset: int = 0, + dashboard_id: str | None = None, + ) -> dict: + """Return paginated, optionally filtered summaries of closed sessions.""" + all_data = _load_all_session_data() + all_data.sort(key=lambda pair: pair[1].get("closed_at") or "", reverse=True) + + q_lower = q.strip().lower() + history = [] + for sid, data in all_data: + if dashboard_id and data.get("dashboard_id") != dashboard_id: + continue + if q_lower: + name = (data.get("name") or "").lower() + search_text = (data.get("search_text") or "").lower() + if q_lower not in name and q_lower not in search_text: + continue + history.append({ + "id": data.get("id", sid), + "name": data.get("name", "Untitled"), + "status": data.get("status", "stopped"), + "model": data.get("model", "sonnet"), + "mode": data.get("mode", "agent"), + "created_at": data.get("created_at"), + "closed_at": data.get("closed_at"), + "cost_usd": data.get("cost_usd", 0), + "dashboard_id": data.get("dashboard_id"), + }) + + total = len(history) + page = history[offset : offset + limit] + return { + "sessions": page, + "total": total, + "has_more": offset + limit < total, + } + + async def reconcile_on_startup(self) -> None: + """Mark any stale running sessions as stopped.""" + for sid, data in _load_all_session_data(): + if data.get("status") in ("running", "waiting_approval"): + data["status"] = "stopped" + _save_session(sid, data) + logger.info(f"Marked stale session {sid} as stopped") + + async def persist_all_sessions(self) -> None: + """Flush every in-memory session to JSON files (for graceful shutdown).""" + for session_id, session in list(self.sessions.items()): + if session.status in ("running", "waiting_approval"): + session.status = "stopped" + session.pending_approvals = [] + session.closed_at = session.closed_at or datetime.now() + doc_data = session.model_dump(mode="json") + doc_data["search_text"] = self._build_search_text(session) + _save_session(session_id, doc_data) + logger.info(f"Persisted session {session_id} on shutdown") + self.sessions.clear() + self.tasks.clear() + + async def restore_all_sessions(self) -> None: + """On startup, reload all persisted sessions from JSON files back into memory.""" + for sid, data in _load_all_session_data(): + try: + session = AgentSession(**data) + except Exception as e: + logger.warning(f"Skipping corrupt session file {sid}: {e}") + continue + if session.status in ("running", "waiting_approval"): + session.status = "stopped" + session.closed_at = None + session.pending_approvals = [] + if session.branch_name: + worktree_path = os.path.join( + self.worktree_mgr.worktrees_dir, session.branch_name + ) + if not os.path.exists(worktree_path): + try: + worktree_path = await self.worktree_mgr.create_worktree( + session.branch_name + ) + except RuntimeError: + logger.warning( + f"Could not restore worktree for session {session.id}, skipping" + ) + continue + session.worktree_path = worktree_path + self.sessions[session.id] = session + _delete_session_file(sid) + logger.info(f"Restored session {session.id}") + + def get_all_sessions(self, dashboard_id: str | None = None) -> list[AgentSession]: + if dashboard_id: + return [s for s in self.sessions.values() if s.dashboard_id == dashboard_id] + return list(self.sessions.values()) + + def get_session(self, session_id: str) -> Optional[AgentSession]: + return self.sessions.get(session_id) + +agent_manager = AgentManager() diff --git a/backend/apps/agents/agents.py b/backend/apps/agents/agents.py new file mode 100644 index 00000000..890ab856 --- /dev/null +++ b/backend/apps/agents/agents.py @@ -0,0 +1,177 @@ +from backend.config.Apps import SubApp +from backend.apps.agents.agent_manager import agent_manager +from backend.apps.agents.ws_manager import ws_manager +from backend.apps.agents.models import AgentConfig, ApprovalResponse +from contextlib import asynccontextmanager +from fastapi import WebSocket, WebSocketDisconnect, HTTPException +from fastapi.responses import JSONResponse +import json +import logging + +logger = logging.getLogger(__name__) + +@asynccontextmanager +async def agents_lifespan(): + logger.info("Agents sub-app starting") + await agent_manager.worktree_mgr.cleanup_all_worktrees() + await agent_manager.reconcile_on_startup() + await agent_manager.restore_all_sessions() + yield + logger.info("Agents sub-app shutting down") + for session_id in list(agent_manager.tasks.keys()): + await agent_manager.stop_agent(session_id) + await agent_manager.persist_all_sessions() + +agents = SubApp("agents", agents_lifespan) + +# REST Endpoints + +@agents.router.get("/sessions") +async def list_sessions(dashboard_id: str = ""): + sessions = agent_manager.get_all_sessions(dashboard_id=dashboard_id or None) + return {"sessions": [s.model_dump(mode="json") for s in sessions]} + +@agents.router.get("/sessions/{session_id}") +async def get_session(session_id: str): + session = agent_manager.get_session(session_id) + if not session: + raise HTTPException(status_code=404, detail="Session not found") + return session.model_dump(mode="json") + +@agents.router.post("/launch") +async def launch_agent(config: AgentConfig): + session = await agent_manager.launch_agent(config) + return {"session_id": session.id, "session": session.model_dump(mode="json")} + +@agents.router.post("/sessions/{session_id}/message") +async def send_message(session_id: str, body: dict): + prompt = body.get("prompt", "") + if not prompt: + raise HTTPException(status_code=400, detail="prompt is required") + await agent_manager.send_message( + session_id, + prompt, + mode=body.get("mode"), + model=body.get("model"), + images=body.get("images"), + context_paths=body.get("context_paths"), + forced_tools=body.get("forced_tools"), + attached_skills=body.get("attached_skills"), + ) + return {"ok": True} + +@agents.router.post("/sessions/{session_id}/stop") +async def stop_agent(session_id: str, body: dict = {}): + remove_worktree = body.get("remove_worktree", False) + await agent_manager.stop_agent(session_id, remove_worktree=remove_worktree) + return {"ok": True} + +@agents.router.post("/approval") +async def handle_approval(response: ApprovalResponse): + agent_manager.handle_approval(response.request_id, { + "behavior": response.behavior, + "message": response.message, + "updated_input": response.updated_input, + }) + return {"ok": True} + +@agents.router.post("/sessions/{session_id}/edit_message") +async def edit_message(session_id: str, body: dict): + message_id = body.get("message_id") + new_content = body.get("content", "") + if not message_id or not new_content: + raise HTTPException(status_code=400, detail="message_id and content are required") + await agent_manager.edit_message(session_id, message_id, new_content) + return {"ok": True} + +@agents.router.post("/sessions/{session_id}/switch_branch") +async def switch_branch(session_id: str, body: dict): + branch_id = body.get("branch_id", "") + if not branch_id: + raise HTTPException(status_code=400, detail="branch_id is required") + await agent_manager.switch_branch(session_id, branch_id) + return {"ok": True} + +@agents.router.post("/sessions/{session_id}/generate-title") +async def generate_title(session_id: str, body: dict): + prompt = body.get("prompt", "") + if not prompt: + raise HTTPException(status_code=400, detail="prompt is required") + title = await agent_manager.generate_title(session_id, prompt) + return {"title": title} + +@agents.router.post("/sessions/{session_id}/generate-group-meta") +async def generate_group_meta(session_id: str, body: dict): + group_id = body.get("group_id", "") + tool_calls = body.get("tool_calls", []) + if not group_id or not tool_calls: + raise HTTPException(status_code=400, detail="group_id and tool_calls are required") + result = await agent_manager.generate_group_meta( + session_id, + group_id, + tool_calls, + results_summary=body.get("results_summary"), + is_refinement=body.get("is_refinement", False), + ) + return result + +@agents.router.patch("/sessions/{session_id}") +async def update_session(session_id: str, body: dict): + session = agent_manager.get_session(session_id) + if not session: + raise HTTPException(status_code=404, detail="Session not found") + await agent_manager.update_session(session_id, **body) + return {"ok": True} + +@agents.router.get("/sessions/{session_id}/branches") +async def get_branches(session_id: str): + session = agent_manager.get_session(session_id) + if not session: + raise HTTPException(status_code=404, detail="Session not found") + return { + "branches": {k: v.model_dump(mode="json") for k, v in session.branches.items()}, + "active_branch_id": session.active_branch_id, + } + +@agents.router.post("/sessions/{session_id}/close") +async def close_session(session_id: str): + try: + await agent_manager.close_session(session_id) + except ValueError as e: + raise HTTPException(status_code=404, detail=str(e)) + return {"ok": True} + +@agents.router.delete("/sessions/{session_id}") +async def delete_session(session_id: str): + await agent_manager.delete_session(session_id) + return {"ok": True} + +@agents.router.get("/history") +async def get_history(q: str = "", limit: int = 20, offset: int = 0, dashboard_id: str = ""): + return agent_manager.get_history( + q=q, limit=limit, offset=offset, + dashboard_id=dashboard_id or None, + ) + +@agents.router.post("/sessions/{session_id}/resume") +async def resume_session(session_id: str): + try: + session = await agent_manager.resume_session(session_id) + except ValueError as e: + raise HTTPException(status_code=404, detail=str(e)) + return {"session": session.model_dump(mode="json")} + +@agents.router.get("/worktrees") +async def list_worktrees(): + worktrees = await agent_manager.worktree_mgr.list_worktrees() + return {"worktrees": worktrees} + +@agents.router.get("/sessions/{session_id}/diff") +async def get_session_diff(session_id: str): + session = agent_manager.get_session(session_id) + if not session: + raise HTTPException(status_code=404, detail="Session not found") + if not session.branch_name: + return {"diff": ""} + diff = await agent_manager.worktree_mgr.get_worktree_diff(session.branch_name) + return {"diff": diff} diff --git a/backend/apps/agents/models.py b/backend/apps/agents/models.py new file mode 100644 index 00000000..06805ac0 --- /dev/null +++ b/backend/apps/agents/models.py @@ -0,0 +1,75 @@ +from pydantic import BaseModel, Field +from typing import Optional, Literal, Any +from datetime import datetime +from uuid import uuid4 + +class AgentConfig(BaseModel): + name: str = Field(default_factory=lambda: f"Agent-{uuid4().hex[:6]}") + model: str = "sonnet" + mode: str = "agent" + system_prompt: Optional[str] = None + allowed_tools: list[str] = Field(default_factory=lambda: ["Read", "Edit", "Write", "Bash", "Glob", "Grep", "AskUserQuestion"]) + max_turns: Optional[int] = None + target_directory: Optional[str] = None # if None, uses repo root + dashboard_id: Optional[str] = None + +class ApprovalRequest(BaseModel): + id: str = Field(default_factory=lambda: uuid4().hex) + session_id: str + tool_name: str + tool_input: dict[str, Any] + created_at: datetime = Field(default_factory=datetime.now) + +class ApprovalResponse(BaseModel): + request_id: str + behavior: Literal["allow", "deny"] + message: Optional[str] = None + updated_input: Optional[dict[str, Any]] = None + +class Message(BaseModel): + id: str = Field(default_factory=lambda: uuid4().hex) + role: Literal["user", "assistant", "tool_call", "tool_result", "system"] + content: Any # str or list of content blocks + timestamp: datetime = Field(default_factory=datetime.now) + branch_id: str = "main" + parent_id: Optional[str] = None + context_paths: Optional[list[dict]] = None + attached_skills: Optional[list[dict]] = None + forced_tools: Optional[list[str]] = None + images: Optional[list[dict]] = None + +class MessageBranch(BaseModel): + id: str = Field(default_factory=lambda: uuid4().hex) + parent_branch_id: Optional[str] = None + fork_point_message_id: Optional[str] = None + created_at: datetime = Field(default_factory=datetime.now) + +class ToolGroupMeta(BaseModel): + id: str + name: str + svg: str = "" + is_refined: bool = False + +class AgentSession(BaseModel): + id: str = Field(default_factory=lambda: uuid4().hex) + name: str + status: Literal["running", "waiting_approval", "completed", "error", "stopped"] = "running" + model: str = "sonnet" + mode: str = "agent" + worktree_path: Optional[str] = None + branch_name: Optional[str] = None + sdk_session_id: Optional[str] = None + system_prompt: Optional[str] = None + allowed_tools: list[str] = Field(default_factory=list) + max_turns: Optional[int] = None + cwd: Optional[str] = None + created_at: datetime = Field(default_factory=datetime.now) + closed_at: Optional[datetime] = None + cost_usd: float = 0.0 + tokens: dict[str, int] = Field(default_factory=lambda: {"input": 0, "output": 0}) + messages: list[Message] = Field(default_factory=list) + pending_approvals: list[ApprovalRequest] = Field(default_factory=list) + branches: dict[str, "MessageBranch"] = Field(default_factory=lambda: {"main": MessageBranch(id="main")}) + active_branch_id: str = "main" + tool_group_meta: dict[str, "ToolGroupMeta"] = Field(default_factory=dict) + dashboard_id: Optional[str] = None diff --git a/backend/apps/agents/worktree_manager.py b/backend/apps/agents/worktree_manager.py new file mode 100644 index 00000000..ebddb7e1 --- /dev/null +++ b/backend/apps/agents/worktree_manager.py @@ -0,0 +1,130 @@ +import asyncio +import os +import shutil +import logging + +logger = logging.getLogger(__name__) + +class WorktreeManager: + def __init__(self, repo_root: str): + self.repo_root = repo_root + self.worktrees_dir = os.path.join(repo_root, ".worktrees") + os.makedirs(self.worktrees_dir, exist_ok=True) + + async def create_worktree(self, branch_name: str) -> str: + """Create a new git worktree and return its path.""" + worktree_path = os.path.join(self.worktrees_dir, branch_name) + if os.path.exists(worktree_path): + return worktree_path + + proc = await asyncio.create_subprocess_exec( + "git", "worktree", "add", worktree_path, "-b", branch_name, + cwd=self.repo_root, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + stdout, stderr = await proc.communicate() + if proc.returncode != 0: + proc = await asyncio.create_subprocess_exec( + "git", "worktree", "add", worktree_path, branch_name, + cwd=self.repo_root, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + stdout, stderr = await proc.communicate() + if proc.returncode != 0: + raise RuntimeError(f"Failed to create worktree: {stderr.decode()}") + + logger.info(f"Created worktree at {worktree_path} on branch {branch_name}") + return worktree_path + + async def remove_worktree(self, branch_name: str) -> None: + """Remove a git worktree.""" + worktree_path = os.path.join(self.worktrees_dir, branch_name) + if not os.path.exists(worktree_path): + return + + proc = await asyncio.create_subprocess_exec( + "git", "worktree", "remove", worktree_path, "--force", + cwd=self.repo_root, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + await proc.communicate() + logger.info(f"Removed worktree at {worktree_path}") + + async def list_worktrees(self) -> list[dict]: + """List all worktrees with their branch and path info.""" + proc = await asyncio.create_subprocess_exec( + "git", "worktree", "list", "--porcelain", + cwd=self.repo_root, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + stdout, _ = await proc.communicate() + + worktrees = [] + current = {} + for line in stdout.decode().strip().split("\n"): + if line.startswith("worktree "): + if current: + worktrees.append(current) + current = {"path": line.split(" ", 1)[1]} + elif line.startswith("HEAD "): + current["head"] = line.split(" ", 1)[1] + elif line.startswith("branch "): + current["branch"] = line.split(" ", 1)[1].replace("refs/heads/", "") + elif line == "": + if current: + worktrees.append(current) + current = {} + if current: + worktrees.append(current) + return worktrees + + async def delete_branch(self, branch_name: str) -> None: + """Delete a local git branch.""" + proc = await asyncio.create_subprocess_exec( + "git", "branch", "-D", branch_name, + cwd=self.repo_root, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + stdout, stderr = await proc.communicate() + if proc.returncode == 0: + logger.info(f"Deleted branch {branch_name}") + else: + logger.warning(f"Could not delete branch {branch_name}: {stderr.decode().strip()}") + + async def cleanup_all_worktrees(self) -> None: + """Remove all worktree directories and prune stale git worktree refs.""" + if os.path.exists(self.worktrees_dir): + for entry in os.listdir(self.worktrees_dir): + entry_path = os.path.join(self.worktrees_dir, entry) + if os.path.isdir(entry_path): + shutil.rmtree(entry_path, ignore_errors=True) + logger.info("Removed all worktree directories") + + proc = await asyncio.create_subprocess_exec( + "git", "worktree", "prune", + cwd=self.repo_root, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + await proc.communicate() + logger.info("Pruned stale git worktree refs") + + async def get_worktree_diff(self, branch_name: str) -> str: + """Get the diff of uncommitted changes in a worktree.""" + worktree_path = os.path.join(self.worktrees_dir, branch_name) + if not os.path.exists(worktree_path): + return "" + + proc = await asyncio.create_subprocess_exec( + "git", "diff", "HEAD", + cwd=worktree_path, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + stdout, _ = await proc.communicate() + return stdout.decode() diff --git a/backend/apps/agents/ws_manager.py b/backend/apps/agents/ws_manager.py new file mode 100644 index 00000000..3d17b326 --- /dev/null +++ b/backend/apps/agents/ws_manager.py @@ -0,0 +1,88 @@ +import asyncio +import json +import logging +from fastapi import WebSocket + +logger = logging.getLogger(__name__) + +class ConnectionManager: + """Manages WebSocket connections and bridges HITL approval requests.""" + + def __init__(self): + self.connections: dict[str, list[WebSocket]] = {} + self.global_connections: list[WebSocket] = [] + self.pending_futures: dict[str, asyncio.Future] = {} + + async def connect_session(self, session_id: str, websocket: WebSocket): + await websocket.accept() + if session_id not in self.connections: + self.connections[session_id] = [] + self.connections[session_id].append(websocket) + + async def connect_global(self, websocket: WebSocket): + await websocket.accept() + self.global_connections.append(websocket) + + def disconnect_session(self, session_id: str, websocket: WebSocket): + if session_id in self.connections: + self.connections[session_id] = [ + ws for ws in self.connections[session_id] if ws != websocket + ] + if not self.connections[session_id]: + del self.connections[session_id] + + def disconnect_global(self, websocket: WebSocket): + self.global_connections = [ + ws for ws in self.global_connections if ws != websocket + ] + + async def send_to_session(self, session_id: str, event: str, data: dict): + """Send a message to all connections watching a specific session.""" + payload = json.dumps({"event": event, "session_id": session_id, "data": data}) + for ws in self.connections.get(session_id, []): + try: + await ws.send_text(payload) + except Exception: + pass + for ws in self.global_connections: + try: + await ws.send_text(payload) + except Exception: + pass + + async def broadcast_global(self, event: str, data: dict): + """Send a message to all global (dashboard) connections.""" + payload = json.dumps({"event": event, "data": data}) + for ws in self.global_connections: + try: + await ws.send_text(payload) + except Exception: + pass + + async def send_approval_request( + self, session_id: str, request_id: str, tool_name: str, tool_input: dict + ) -> dict: + """Send an approval request and wait for the user's response. + Returns the approval decision dict.""" + future = asyncio.get_event_loop().create_future() + self.pending_futures[request_id] = future + + await self.send_to_session(session_id, "agent:approval_request", { + "request_id": request_id, + "tool_name": tool_name, + "tool_input": tool_input, + }) + + try: + result = await future + return result + finally: + self.pending_futures.pop(request_id, None) + + def resolve_approval(self, request_id: str, decision: dict): + """Resolve a pending approval Future with the user's decision.""" + future = self.pending_futures.get(request_id) + if future and not future.done(): + future.set_result(decision) + +ws_manager = ConnectionManager() diff --git a/backend/apps/dashboard_layout/__init__.py b/backend/apps/dashboard_layout/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/apps/dashboard_layout/dashboard_layout.py b/backend/apps/dashboard_layout/dashboard_layout.py new file mode 100644 index 00000000..c3f23355 --- /dev/null +++ b/backend/apps/dashboard_layout/dashboard_layout.py @@ -0,0 +1,57 @@ +import json +import os +import logging +from contextlib import asynccontextmanager +from backend.config.Apps import SubApp +from backend.apps.dashboard_layout.models import DashboardLayout, DashboardLayoutUpdate + +logger = logging.getLogger(__name__) + +DATA_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "data", "dashboard_layout") +LAYOUT_FILE = os.path.join(DATA_DIR, "layout.json") + + +@asynccontextmanager +async def dashboard_layout_lifespan(): + os.makedirs(DATA_DIR, exist_ok=True) + yield + + +dashboard_layout = SubApp("dashboard_layout", dashboard_layout_lifespan) + + +def _default_layout() -> DashboardLayout: + return DashboardLayout(cards={}) + + +def _load() -> DashboardLayout: + if not os.path.exists(LAYOUT_FILE): + return _default_layout() + try: + with open(LAYOUT_FILE) as f: + data = json.load(f) + if "columns" in data and "cards" not in data: + logger.info("Detected old column-based layout format, resetting to empty canvas") + return _default_layout() + return DashboardLayout(**data) + except Exception: + logger.exception("Failed to load dashboard layout, returning default") + return _default_layout() + + +def _save(layout: DashboardLayout): + with open(LAYOUT_FILE, "w") as f: + json.dump(layout.model_dump(), f, indent=2) + + +@dashboard_layout.router.get("") +async def get_layout(): + layout = _load() + return layout.model_dump() + + +@dashboard_layout.router.put("") +async def update_layout(body: DashboardLayoutUpdate): + layout = DashboardLayout(cards=body.cards, view_cards=body.view_cards) + _save(layout) + return layout.model_dump() diff --git a/backend/apps/dashboard_layout/models.py b/backend/apps/dashboard_layout/models.py new file mode 100644 index 00000000..65193747 --- /dev/null +++ b/backend/apps/dashboard_layout/models.py @@ -0,0 +1,27 @@ +from pydantic import BaseModel, Field + + +class CardPosition(BaseModel): + session_id: str + x: float = 0 + y: float = 0 + width: float = 420 + height: float = 280 + + +class ViewCardPosition(BaseModel): + output_id: str + x: float = 0 + y: float = 0 + width: float = 480 + height: float = 360 + + +class DashboardLayout(BaseModel): + cards: dict[str, CardPosition] = Field(default_factory=dict) + view_cards: dict[str, ViewCardPosition] = Field(default_factory=dict) + + +class DashboardLayoutUpdate(BaseModel): + cards: dict[str, CardPosition] + view_cards: dict[str, ViewCardPosition] = Field(default_factory=dict) diff --git a/backend/apps/dashboards/dashboards.py b/backend/apps/dashboards/dashboards.py new file mode 100644 index 00000000..1c2d2a45 --- /dev/null +++ b/backend/apps/dashboards/dashboards.py @@ -0,0 +1,199 @@ +import json +import os +import logging +from contextlib import asynccontextmanager +from datetime import datetime +from uuid import uuid4 + +from backend.config.Apps import SubApp +from backend.apps.dashboards.models import ( + Dashboard, + DashboardCreate, + DashboardUpdate, + DashboardLayout, + CardPosition, + ViewCardPosition, +) +from fastapi import HTTPException + +logger = logging.getLogger(__name__) + +BACKEND_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +DATA_DIR = os.path.join(BACKEND_DIR, "data", "dashboards") +SESSIONS_DIR = os.path.join(BACKEND_DIR, "data", "sessions") + +OLD_LAYOUT_DIR = os.path.join(BACKEND_DIR, "data", "dashboard_layout") +OLD_LAYOUT_FILE = os.path.join(OLD_LAYOUT_DIR, "layout.json") + + +def _load_all() -> list[Dashboard]: + result = [] + if not os.path.exists(DATA_DIR): + return result + for fname in os.listdir(DATA_DIR): + if fname.endswith(".json"): + with open(os.path.join(DATA_DIR, fname)) as f: + result.append(Dashboard(**json.load(f))) + return result + + +def _save(dashboard: Dashboard): + with open(os.path.join(DATA_DIR, f"{dashboard.id}.json"), "w") as f: + json.dump(dashboard.model_dump(mode="json"), f, indent=2) + + +def _load(dashboard_id: str) -> Dashboard: + path = os.path.join(DATA_DIR, f"{dashboard_id}.json") + if not os.path.exists(path): + raise HTTPException(status_code=404, detail="Dashboard not found") + with open(path) as f: + return Dashboard(**json.load(f)) + + +def _delete(dashboard_id: str): + path = os.path.join(DATA_DIR, f"{dashboard_id}.json") + if os.path.exists(path): + os.remove(path) + + +def _migrate_if_needed(): + """One-time migration: if no dashboards exist, create 'Dashboard 1' from old layout.""" + existing = _load_all() + if existing: + return + + logger.info("No dashboards found — running one-time migration") + + layout = DashboardLayout() + if os.path.exists(OLD_LAYOUT_FILE): + try: + with open(OLD_LAYOUT_FILE) as f: + data = json.load(f) + if "cards" in data: + layout = DashboardLayout(**data) + logger.info("Migrated layout from old layout.json") + except Exception: + logger.exception("Failed to read old layout.json, using empty layout") + + dashboard = Dashboard(name="Dashboard 1", layout=layout) + _save(dashboard) + logger.info(f"Created default dashboard: {dashboard.id}") + + if os.path.exists(SESSIONS_DIR): + count = 0 + for fname in os.listdir(SESSIONS_DIR): + if not fname.endswith(".json"): + continue + fpath = os.path.join(SESSIONS_DIR, fname) + with open(fpath) as f: + session_data = json.load(f) + session_data["dashboard_id"] = dashboard.id + with open(fpath, "w") as f: + json.dump(session_data, f, indent=2) + count += 1 + if count: + logger.info(f"Tagged {count} existing chat sessions with dashboard_id={dashboard.id}") + + +@asynccontextmanager +async def dashboards_lifespan(): + os.makedirs(DATA_DIR, exist_ok=True) + _migrate_if_needed() + yield + + +dashboards = SubApp("dashboards", dashboards_lifespan) + + +@dashboards.router.get("/list") +async def list_dashboards(): + all_dashboards = _load_all() + all_dashboards.sort(key=lambda d: d.updated_at or d.created_at, reverse=True) + items = [] + for d in all_dashboards: + dumped = d.model_dump(mode="json") + items.append({ + "id": dumped["id"], + "name": dumped.get("name", "Untitled"), + "created_at": dumped.get("created_at"), + "updated_at": dumped.get("updated_at"), + }) + return {"dashboards": items} + + +@dashboards.router.post("/create") +async def create_dashboard(body: DashboardCreate): + dashboard = Dashboard(name=body.name) + _save(dashboard) + return dashboard.model_dump(mode="json") + + +@dashboards.router.get("/{dashboard_id}") +async def get_dashboard(dashboard_id: str): + dashboard = _load(dashboard_id) + return dashboard.model_dump(mode="json") + + +@dashboards.router.put("/{dashboard_id}") +async def update_dashboard(dashboard_id: str, body: DashboardUpdate): + dashboard = _load(dashboard_id) + if body.name is not None: + dashboard.name = body.name + if body.layout is not None: + dashboard.layout = body.layout + dashboard.updated_at = datetime.now() + _save(dashboard) + return dashboard.model_dump(mode="json") + + +@dashboards.router.delete("/{dashboard_id}") +async def delete_dashboard(dashboard_id: str): + _load(dashboard_id) + + if os.path.exists(SESSIONS_DIR): + for fname in os.listdir(SESSIONS_DIR): + if not fname.endswith(".json"): + continue + fpath = os.path.join(SESSIONS_DIR, fname) + try: + with open(fpath) as f: + data = json.load(f) + if data.get("dashboard_id") == dashboard_id: + os.remove(fpath) + except Exception: + logger.warning(f"Failed to read/delete session file {fname}") + + from backend.apps.agents.agent_manager import agent_manager + to_remove = [ + sid for sid, sess in agent_manager.sessions.items() + if getattr(sess, "dashboard_id", None) == dashboard_id + ] + for sid in to_remove: + try: + await agent_manager.delete_session(sid) + except Exception: + logger.warning(f"Failed to delete active session {sid} during dashboard deletion") + + _delete(dashboard_id) + return {"ok": True} + + +@dashboards.router.post("/{dashboard_id}/duplicate") +async def duplicate_dashboard(dashboard_id: str): + source = _load(dashboard_id) + source_data = source.model_dump(mode="json") + new_id = uuid4().hex + now = datetime.now().isoformat() + + new_dashboard = { + **source_data, + "id": new_id, + "name": f"{source_data.get('name', 'Untitled')} (copy)", + "created_at": now, + "updated_at": now, + "layout": {"cards": {}, "view_cards": source_data.get("layout", {}).get("view_cards", {})}, + } + with open(os.path.join(DATA_DIR, f"{new_id}.json"), "w") as f: + json.dump(new_dashboard, f, indent=2) + + return new_dashboard diff --git a/backend/apps/dashboards/models.py b/backend/apps/dashboards/models.py new file mode 100644 index 00000000..149f2069 --- /dev/null +++ b/backend/apps/dashboards/models.py @@ -0,0 +1,42 @@ +from pydantic import BaseModel, Field +from typing import Optional +from datetime import datetime +from uuid import uuid4 + + +class CardPosition(BaseModel): + session_id: str + x: float = 0 + y: float = 0 + width: float = 420 + height: float = 280 + + +class ViewCardPosition(BaseModel): + output_id: str + x: float = 0 + y: float = 0 + width: float = 480 + height: float = 360 + + +class DashboardLayout(BaseModel): + cards: dict[str, CardPosition] = Field(default_factory=dict) + view_cards: dict[str, ViewCardPosition] = Field(default_factory=dict) + + +class Dashboard(BaseModel): + id: str = Field(default_factory=lambda: uuid4().hex) + name: str = "Untitled Dashboard" + created_at: datetime = Field(default_factory=datetime.now) + updated_at: datetime = Field(default_factory=datetime.now) + layout: DashboardLayout = Field(default_factory=DashboardLayout) + + +class DashboardCreate(BaseModel): + name: str = "Untitled Dashboard" + + +class DashboardUpdate(BaseModel): + name: Optional[str] = None + layout: Optional[DashboardLayout] = None diff --git a/backend/apps/health/health.py b/backend/apps/health/health.py new file mode 100644 index 00000000..1ffbddfd --- /dev/null +++ b/backend/apps/health/health.py @@ -0,0 +1,33 @@ +from backend.config.Apps import SubApp +from contextlib import asynccontextmanager +from fastapi.responses import PlainTextResponse +from typeguard import typechecked +import debug +from fastapi import status, HTTPException + +@asynccontextmanager +async def health_lifespan(): + debug("START") + yield + debug("END") + +health = SubApp("health", health_lifespan) + +###################################### +# Health Check Endpoints # +###################################### + +@health.router.get("/check") +@typechecked +async def check() -> PlainTextResponse: + debug("Health check successful") + # Use PlainTextResponse instead of JSONResponse for AWS ALB compatibility + # ALB health checks can be sensitive to JSON responses and Content-Length headers + return PlainTextResponse( + content="OK", + status_code=status.HTTP_200_OK, + headers={ + "Content-Type": "text/plain", + "Content-Length": "2" + } + ) \ No newline at end of file diff --git a/backend/apps/mcp_registry/__init__.py b/backend/apps/mcp_registry/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/apps/mcp_registry/mcp_registry.py b/backend/apps/mcp_registry/mcp_registry.py new file mode 100644 index 00000000..4a8bdeac --- /dev/null +++ b/backend/apps/mcp_registry/mcp_registry.py @@ -0,0 +1,393 @@ +import asyncio +import logging +import os +import re +import time +from contextlib import asynccontextmanager +from typing import Optional + +import httpx +from fastapi import Query +from backend.config.Apps import SubApp + +logger = logging.getLogger(__name__) + +REGISTRY_BASE = "https://registry.modelcontextprotocol.io/v0.1" +PAGE_LIMIT = 100 +REFRESH_INTERVAL_S = 3600 + +GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN", "") +GITHUB_BATCH = 4000 if GITHUB_TOKEN else 50 +GITHUB_CONCURRENT = 10 + +_cache: dict[str, dict] = {} +_cache_updated_at: float = 0 +_refresh_task: Optional[asyncio.Task] = None +_stars_cache: dict[str, int] = {} + + +def _extract_gh_repo(repo_url: str) -> Optional[str]: + """Parse 'owner/repo' from a GitHub URL.""" + if not repo_url or "github.com" not in repo_url: + return None + parts = repo_url.rstrip("/").split("/") + try: + idx = next(i for i, p in enumerate(parts) if "github.com" in p) + if len(parts) > idx + 2: + owner = parts[idx + 1] + repo = parts[idx + 2].removesuffix(".git") + return f"{owner}/{repo}" + except StopIteration: + pass + return None + + +def _extract_server(entry: dict) -> Optional[dict]: + """Extract a flat server record from a registry entry, keeping only latest versions.""" + meta = entry.get("_meta", {}).get("io.modelcontextprotocol.registry/official", {}) + if not meta.get("isLatest"): + return None + + srv = entry.get("server", {}) + name = srv.get("name", "") + if not name: + return None + + remotes = srv.get("remotes", []) + remote_url = "" + remote_type = "" + if remotes: + remote_url = remotes[0].get("url", "") + remote_type = remotes[0].get("type", "") + + repo = srv.get("repository", {}) + + packages = srv.get("packages", []) + env_vars = [] + if packages: + env_vars = packages[0].get("environmentVariables", []) + + pub_meta = srv.get("_meta", {}).get("io.modelcontextprotocol.registry/publisher-provided", {}) + + icons = srv.get("icons", []) + icon_url = icons[0]["src"] if icons else "" + repo_url = repo.get("url", "") if isinstance(repo, dict) else "" + if not icon_url and repo_url and "github.com" in repo_url: + parts = repo_url.rstrip("/").split("/") + gh_idx = next((i for i, p in enumerate(parts) if "github.com" in p), -1) + if gh_idx >= 0 and len(parts) > gh_idx + 1: + icon_url = f"https://github.com/{parts[gh_idx + 1]}.png?size=64" + + return { + "name": name, + "title": srv.get("title", ""), + "description": srv.get("description", ""), + "version": srv.get("version", ""), + "websiteUrl": srv.get("websiteUrl", ""), + "repositoryUrl": repo_url, + "remoteUrl": remote_url, + "remoteType": remote_type, + "iconUrl": icon_url, + "environmentVariables": env_vars, + "keywords": pub_meta.get("keywords", []), + "license": pub_meta.get("license", ""), + "stars": None, + "source": "community", + } + + +async def _fetch_all_servers() -> dict[str, dict]: + """Paginate through the full registry and return a dict keyed by server name.""" + servers: dict[str, dict] = {} + cursor: Optional[str] = None + pages = 0 + + async with httpx.AsyncClient(timeout=30.0) as client: + while True: + params: dict = {"limit": PAGE_LIMIT} + if cursor: + params["cursor"] = cursor + + try: + resp = await client.get(f"{REGISTRY_BASE}/servers", params=params) + resp.raise_for_status() + data = resp.json() + except Exception as e: + logger.warning(f"MCP registry fetch failed on page {pages}: {e}") + break + + entries = data.get("servers", []) + if not entries: + break + + for entry in entries: + record = _extract_server(entry) + if record: + servers[record["name"]] = record + + pages += 1 + next_cursor = data.get("metadata", {}).get("nextCursor") + if not next_cursor: + break + cursor = next_cursor + + logger.info(f"MCP registry cache refreshed: {len(servers)} servers from {pages} pages") + return servers + + +GOOGLE_README_URL = "https://raw.githubusercontent.com/google/mcp/main/README.md" +GOOGLE_ICON_URL = "https://github.com/google.png?size=64" +_ENTRY_RE = re.compile(r"\[\*\*(.+?)\*\*\]\((.+?)\)(?:[,\s]*(.+))?") + + +def _slugify(name: str) -> str: + return re.sub(r"[^a-z0-9]+", "-", name.lower()).strip("-") + + +def _parse_google_readme(text: str) -> dict[str, dict]: + servers: dict[str, dict] = {} + section: Optional[str] = None + + for line in text.splitlines(): + stripped = line.strip() + if "remote mcp servers" in stripped.lower() and stripped.startswith("#"): + section = "remote" + continue + if "open-source mcp servers" in stripped.lower() and stripped.startswith("#"): + section = "open-source" + continue + if stripped.startswith("#") and section is not None: + # Hit a new top-level section (e.g. Examples, Resources), stop parsing + if not stripped.lower().startswith("### **"): + section = None + continue + if section is None: + continue + + m = _ENTRY_RE.search(stripped) + if not m: + continue + + title = m.group(1).strip() + url = m.group(2).strip() + desc_raw = (m.group(3) or "").strip().rstrip(".") + + slug = _slugify(title) + key = f"google/{slug}" + + is_github = "github.com" in url or "go.dev" in url + repo_url = url if is_github else "" + website_url = url if not is_github else "" + + if section == "remote": + remote_type = "google-cloud-remote" + description = desc_raw or f"Google Cloud managed MCP server for {title}" + else: + remote_type = "open-source" + description = desc_raw or f"Google open-source MCP server for {title}" + + servers[key] = { + "name": key, + "title": title, + "description": description, + "version": "", + "websiteUrl": website_url, + "repositoryUrl": repo_url, + "remoteUrl": "", + "remoteType": remote_type, + "iconUrl": GOOGLE_ICON_URL, + "environmentVariables": [], + "keywords": ["google", section], + "license": "Apache-2.0", + "stars": None, + "source": "google", + } + + return servers + + +async def _fetch_google_servers() -> dict[str, dict]: + """Fetch and parse Google's MCP server catalog from their GitHub README.""" + try: + async with httpx.AsyncClient(timeout=15.0) as client: + resp = await client.get(GOOGLE_README_URL) + resp.raise_for_status() + servers = _parse_google_readme(resp.text) + logger.info(f"Google MCP catalog: parsed {len(servers)} servers") + return servers + except Exception as e: + logger.warning(f"Google MCP catalog fetch failed: {e}") + return {} + + +async def _fetch_github_stars(servers: dict[str, dict]): + """Batch-fetch GitHub star counts for servers with GitHub repos. + + Uses an in-memory cache so stars accumulate across refresh cycles even + when rate-limited (60 req/hr unauthenticated, 5 000 with GITHUB_TOKEN). + """ + global _stars_cache + + needed: list[str] = [] + for srv in servers.values(): + gh = _extract_gh_repo(srv.get("repositoryUrl", "")) + if gh and gh not in _stars_cache and gh not in needed: + needed.append(gh) + + if not needed: + logger.info(f"GitHub stars: all {len(_stars_cache)} repos cached, 0 to fetch") + _apply_stars(servers) + return + + to_fetch = needed[: GITHUB_BATCH] + logger.info( + f"GitHub stars: fetching {len(to_fetch)} repos " + f"({len(_stars_cache)} cached, {len(needed)} pending)" + ) + + headers: dict[str, str] = {"Accept": "application/vnd.github.v3+json"} + if GITHUB_TOKEN: + headers["Authorization"] = f"token {GITHUB_TOKEN}" + + sem = asyncio.Semaphore(GITHUB_CONCURRENT) + rate_limited = False + fetched = 0 + + async def _fetch_one(client: httpx.AsyncClient, repo: str): + nonlocal rate_limited, fetched + if rate_limited: + return + async with sem: + if rate_limited: + return + try: + resp = await client.get( + f"https://api.github.com/repos/{repo}", headers=headers + ) + if resp.status_code == 200: + _stars_cache[repo] = resp.json().get("stargazers_count", 0) + fetched += 1 + elif resp.status_code in (403, 429): + rate_limited = True + logger.warning("GitHub API rate-limited, stopping star fetch") + elif resp.status_code == 404: + _stars_cache[repo] = 0 + fetched += 1 + except Exception as exc: + logger.debug(f"GitHub stars fetch failed for {repo}: {exc}") + + async with httpx.AsyncClient(timeout=15.0) as client: + await asyncio.gather(*[_fetch_one(client, r) for r in to_fetch]) + + logger.info(f"GitHub stars: fetched {fetched} new, {len(_stars_cache)} total cached") + _apply_stars(servers) + + +def _apply_stars(servers: dict[str, dict]): + for srv in servers.values(): + gh = _extract_gh_repo(srv.get("repositoryUrl", "")) + srv["stars"] = _stars_cache.get(gh) if gh else None + + +async def _refresh_loop(): + """Background loop that refreshes the cache on startup and then hourly.""" + global _cache, _cache_updated_at + while True: + try: + community, google = await asyncio.gather( + _fetch_all_servers(), + _fetch_google_servers(), + ) + _cache = {**community, **google} + await _fetch_github_stars(_cache) + _cache_updated_at = time.time() + except Exception as e: + logger.exception(f"MCP registry refresh error: {e}") + await asyncio.sleep(REFRESH_INTERVAL_S) + + +@asynccontextmanager +async def mcp_registry_lifespan(): + global _refresh_task + _refresh_task = asyncio.create_task(_refresh_loop()) + yield + if _refresh_task: + _refresh_task.cancel() + try: + await _refresh_task + except asyncio.CancelledError: + pass + + +mcp_registry = SubApp("mcp-registry", mcp_registry_lifespan) + + +@mcp_registry.router.get("/stats") +async def registry_stats(): + google = sum(1 for s in _cache.values() if s.get("source") == "google") + community = sum(1 for s in _cache.values() if s.get("source") == "community") + return { + "total": len(_cache), + "google": google, + "community": community, + "lastUpdated": _cache_updated_at, + } + + +@mcp_registry.router.get("/search") +async def registry_search( + q: str = Query("", description="Search query"), + limit: int = Query(20, ge=1, le=100), + offset: int = Query(0, ge=0), + sort: str = Query("name", description="Sort by: name, stars"), + source: str = Query("", description="Filter by source: google, community, or empty for all"), +): + pool = _cache.values() + if source: + pool = [s for s in pool if s.get("source") == source] + + query_lower = q.lower().strip() + + if not query_lower: + results = list(pool) + else: + results = [] + for srv in pool: + searchable = f"{srv['name']} {srv['title']} {srv['description']} {' '.join(srv.get('keywords', []))}".lower() + if query_lower in searchable: + results.append(srv) + + if sort == "stars": + results.sort(key=lambda s: (s.get("stars") is None, -(s.get("stars") or 0), s["name"])) + else: + results.sort(key=lambda s: s["name"]) + + total = len(results) + page = results[offset : offset + limit] + + summary = [ + { + "name": s["name"], + "title": s["title"], + "description": s["description"], + "version": s["version"], + "remoteUrl": s["remoteUrl"], + "remoteType": s["remoteType"], + "repositoryUrl": s["repositoryUrl"], + "websiteUrl": s["websiteUrl"], + "iconUrl": s.get("iconUrl", ""), + "stars": s.get("stars"), + "source": s.get("source", "community"), + } + for s in page + ] + + return {"servers": summary, "total": total, "offset": offset, "limit": limit} + + +@mcp_registry.router.get("/detail/{server_name:path}") +async def registry_detail(server_name: str): + srv = _cache.get(server_name) + if not srv: + return {"error": "Server not found"}, 404 + return {"server": srv} diff --git a/backend/apps/modes/__init__.py b/backend/apps/modes/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/apps/modes/models.py b/backend/apps/modes/models.py new file mode 100644 index 00000000..3a884199 --- /dev/null +++ b/backend/apps/modes/models.py @@ -0,0 +1,195 @@ +import os +from pydantic import BaseModel, Field +from typing import Optional +from uuid import uuid4 + +OUTPUTS_WORKSPACE = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), + "data", "outputs_workspace", +) + +SKILLS_WORKSPACE = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), + "data", "skills_workspace", +) + + +class Mode(BaseModel): + id: str = Field(default_factory=lambda: uuid4().hex) + name: str + description: str = "" + system_prompt: Optional[str] = None + tools: Optional[list[str]] = None + default_next_mode: Optional[str] = None + is_builtin: bool = False + icon: str = "smart_toy" + color: str = "#818cf8" + default_folder: Optional[str] = None + + +class ModeCreate(BaseModel): + name: str + description: str = "" + system_prompt: Optional[str] = None + tools: Optional[list[str]] = None + default_next_mode: Optional[str] = None + icon: str = "smart_toy" + color: str = "#818cf8" + default_folder: Optional[str] = None + + +class ModeUpdate(BaseModel): + name: Optional[str] = None + description: Optional[str] = None + system_prompt: Optional[str] = None + tools: Optional[list[str]] = None + default_next_mode: Optional[str] = None + icon: Optional[str] = None + color: Optional[str] = None + default_folder: Optional[str] = None + + +BUILTIN_MODES: list[Mode] = [ + Mode( + id="agent", + name="Agent", + description="Full autonomous agent with read and write access to tools.", + system_prompt=None, + tools=None, + default_next_mode=None, + is_builtin=True, + icon="smart_toy", + color="#818cf8", + ), + Mode( + id="ask", + name="Ask", + description="Answer questions about the codebase. Read-only, no edits or changes.", + system_prompt="Answer questions about the codebase. Do not make any edits or changes.", + tools=["Read", "Glob", "Grep", "AskUserQuestion"], + default_next_mode=None, + is_builtin=True, + icon="question_answer", + color="#4ade80", + ), + Mode( + id="plan", + name="Plan", + description="Analyze requests and produce a detailed step-by-step plan without executing.", + system_prompt="Analyze the request and produce a detailed step-by-step plan. Do not execute the plan or make any changes.", + tools=["Read", "Glob", "Grep", "AskUserQuestion"], + default_next_mode="agent", + is_builtin=True, + icon="map", + color="#fbbf24", + ), + Mode( + id="view-builder", + name="View Builder", + description="Create and iterate on reusable View artifacts.", + system_prompt=( + "You are helping the user build a reusable View — a self-contained " + "web app rendered in an iframe.\n\n" + "Your working directory is a dedicated workspace folder for this view. " + "You can create any file structure you need using the Write tool.\n\n" + "## Required files\n\n" + "1. **index.html** — The entry point. A complete HTML document. " + "React 18 is available via esm.sh CDN imports:\n" + ' \n' + " The structured input data is available at `window.OUTPUT_INPUT` (object) " + "and any server-side result at `window.OUTPUT_BACKEND_RESULT`.\n\n" + "2. **schema.json** — A JSON Schema object defining the structured input " + "the view accepts. Example:\n" + ' {"type":"object","properties":{"name":{"type":"string"}},"required":["name"]}\n\n' + "3. **meta.json** — Metadata for this view. Always write this file with " + "a short name and one-sentence description. Example:\n" + ' {"name":"Sales Dashboard","description":"Interactive dashboard showing sales metrics"}\n\n' + "## Optional files\n\n" + "- **backend.py** — Python code that receives `input_data` as " + "a global dict and must assign its result to a global `result` dict.\n" + "- **Any additional files** — You can create subdirectories and split code " + "across multiple files. For example:\n" + " - `components/Chart.js` — Reusable components\n" + " - `utils/helpers.js` — Utility functions\n" + " - `styles/main.css` — Stylesheets\n\n" + "Files are served from the workspace, so relative imports work naturally:\n" + ' ``\n' + ' ``\n' + " `import { helper } from './utils/helpers.js'` (in ES modules)\n\n" + "## Guidelines\n\n" + "Write files immediately when you have code ready. The user can see " + "a live preview that auto-refreshes from these files. Always write the " + "complete file content (do not use Edit for partial patches on first creation). " + "For complex views, split code into separate files to keep things organized." + ), + tools=None, + default_next_mode=None, + is_builtin=True, + icon="view_quilt", + color="#f472b6", + default_folder=OUTPUTS_WORKSPACE, + ), + Mode( + id="skill-builder", + name="Skill Builder", + description="Create and iterate on skills using AI-assisted vibe coding.", + system_prompt=( + "You are a Skill Builder — an AI assistant that helps users create, " + "refine, and iterate on Claude skills (SKILL.md files).\n\n" + "## How Skills Work\n\n" + "A skill is a Markdown file that teaches Claude how to perform a specific task. " + "Skills have YAML frontmatter with `name` and `description` fields, followed by " + "the skill body in Markdown. The description is the primary triggering mechanism — " + "it tells Claude when to use the skill.\n\n" + "## Your Working Directory\n\n" + "Your working directory is a dedicated workspace folder for this skill. " + "Write your output directly to these files using the Write tool:\n\n" + "1. **SKILL.md** — The complete skill file with YAML frontmatter and Markdown body. " + "Example frontmatter:\n" + " ```\n" + " ---\n" + " name: my-skill\n" + " description: When to trigger and what this skill does.\n" + " ---\n" + " ```\n\n" + "2. **meta.json** — Metadata for the skill builder UI. Always write this file. Example:\n" + ' {"name":"My Skill","description":"A short description","command":"my-skill"}\n\n' + "Write these files immediately when you have content ready. The user can see " + "a live preview that auto-refreshes from these files. Always write the " + "complete file content (do not use Edit for partial patches on first creation).\n\n" + "## Skill Creation Process\n\n" + "1. **Understand intent** — Ask what the skill should do, when it should trigger, " + "and what the expected output format is.\n" + "2. **Draft the skill** — Write a SKILL.md with clear instructions, examples, " + "and good progressive disclosure.\n" + "3. **Iterate** — Refine based on user feedback. Update the files each time.\n\n" + "## Skill Writing Best Practices\n\n" + "- Keep SKILL.md under 500 lines; use bundled reference files for large content.\n" + "- The `description` frontmatter is the primary trigger. Make it slightly \"pushy\" — " + "include both what the skill does AND specific contexts for when to use it.\n" + "- Use imperative form in instructions.\n" + "- Include examples with input/output pairs when helpful.\n" + "- Define output formats explicitly with templates.\n" + "- Use theory of mind — explain *why* things matter rather than just MUST directives.\n" + "- Think about edge cases, error handling, and progressive disclosure.\n\n" + "## Skill Anatomy\n\n" + "```\n" + "skill-name/\n" + "├── SKILL.md (required) — YAML frontmatter + Markdown instructions\n" + "└── Bundled Resources (optional)\n" + " ├── scripts/ — Executable code for repetitive tasks\n" + " ├── references/ — Docs loaded into context as needed\n" + " └── assets/ — Files used in output\n" + "```\n\n" + "Be collaborative and flexible. If the user wants to \"just vibe\", skip the formal " + "process and iterate freely. Always write updated files so the preview stays current." + ), + tools=None, + default_next_mode=None, + is_builtin=True, + icon="psychology", + color="#10b981", + default_folder=SKILLS_WORKSPACE, + ), +] diff --git a/backend/apps/modes/modes.py b/backend/apps/modes/modes.py new file mode 100644 index 00000000..a9c134f7 --- /dev/null +++ b/backend/apps/modes/modes.py @@ -0,0 +1,102 @@ +import json +import os +import logging +from contextlib import asynccontextmanager +from fastapi import HTTPException +from backend.config.Apps import SubApp +from backend.apps.modes.models import Mode, ModeCreate, ModeUpdate, BUILTIN_MODES + +logger = logging.getLogger(__name__) + +DATA_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "data", "modes") + + +@asynccontextmanager +async def modes_lifespan(): + os.makedirs(DATA_DIR, exist_ok=True) + for builtin in BUILTIN_MODES: + _save(builtin) + yield + + +modes = SubApp("modes", modes_lifespan) + + +def _load_all() -> list[Mode]: + result = [] + if not os.path.exists(DATA_DIR): + return result + for fname in os.listdir(DATA_DIR): + if fname.endswith(".json"): + with open(os.path.join(DATA_DIR, fname)) as f: + result.append(Mode(**json.load(f))) + return result + + +def _save(mode: Mode): + with open(os.path.join(DATA_DIR, f"{mode.id}.json"), "w") as f: + json.dump(mode.model_dump(), f, indent=2) + + +def _load(mode_id: str) -> Mode: + path = os.path.join(DATA_DIR, f"{mode_id}.json") + if not os.path.exists(path): + raise HTTPException(status_code=404, detail="Mode not found") + with open(path) as f: + return Mode(**json.load(f)) + + +def load_mode(mode_id: str) -> Mode | None: + """Public helper for other modules to resolve a mode by ID.""" + path = os.path.join(DATA_DIR, f"{mode_id}.json") + if not os.path.exists(path): + return None + with open(path) as f: + return Mode(**json.load(f)) + + +@modes.router.get("/list") +async def list_modes(): + return {"modes": [m.model_dump() for m in _load_all()]} + + +@modes.router.get("/{mode_id}") +async def get_mode(mode_id: str): + return _load(mode_id).model_dump() + + +@modes.router.post("/create") +async def create_mode(body: ModeCreate): + mode = Mode( + name=body.name, + description=body.description, + system_prompt=body.system_prompt, + tools=body.tools, + default_next_mode=body.default_next_mode, + icon=body.icon, + color=body.color, + default_folder=body.default_folder, + is_builtin=False, + ) + _save(mode) + return {"ok": True, "mode": mode.model_dump()} + + +@modes.router.put("/{mode_id}") +async def update_mode(mode_id: str, body: ModeUpdate): + mode = _load(mode_id) + for k, v in body.model_dump(exclude_none=True).items(): + setattr(mode, k, v) + _save(mode) + return {"ok": True, "mode": mode.model_dump()} + + +@modes.router.delete("/{mode_id}") +async def delete_mode(mode_id: str): + mode = _load(mode_id) + if mode.is_builtin: + raise HTTPException(status_code=403, detail="Cannot delete built-in modes") + path = os.path.join(DATA_DIR, f"{mode_id}.json") + if os.path.exists(path): + os.remove(path) + return {"ok": True} diff --git a/backend/apps/outputs/__init__.py b/backend/apps/outputs/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/apps/outputs/executor.py b/backend/apps/outputs/executor.py new file mode 100644 index 00000000..2ce9bd87 --- /dev/null +++ b/backend/apps/outputs/executor.py @@ -0,0 +1,74 @@ +import asyncio +import json +import logging +import sys +from dataclasses import dataclass + +logger = logging.getLogger(__name__) + +TIMEOUT_SECONDS = 30 + + +@dataclass +class BackendExecResult: + result: dict + stdout: str + stderr: str + + +async def execute_backend_code(code: str, input_data: dict) -> BackendExecResult: + """Execute user-provided Python code in a subprocess. + + The code receives ``input_data`` as a global dict and must assign its + result to a global ``result`` dict. User print() calls are captured + separately from the result via an in-process StringIO redirect. + """ + + preamble = ( + "import json, sys, io\n" + "_orig_stdout = sys.stdout\n" + "_capture = io.StringIO()\n" + "sys.stdout = _capture\n" + "input_data = json.loads(sys.stdin.read())\n" + "result = {}\n" + ) + postamble = ( + "\nsys.stdout = _orig_stdout\n" + 'json.dump({"__stdout__": _capture.getvalue(), "__result__": result}, sys.stdout)\n' + ) + wrapper = preamble + code + postamble + + proc = await asyncio.create_subprocess_exec( + sys.executable, "-c", wrapper, + stdin=asyncio.subprocess.PIPE, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + + try: + stdout, stderr = await asyncio.wait_for( + proc.communicate(input=json.dumps(input_data).encode()), + timeout=TIMEOUT_SECONDS, + ) + except asyncio.TimeoutError: + proc.kill() + await proc.wait() + raise RuntimeError(f"Backend code execution timed out after {TIMEOUT_SECONDS}s") + + stderr_text = stderr.decode(errors="replace").strip() + + if proc.returncode != 0: + raise RuntimeError(f"Backend code error (exit {proc.returncode}): {stderr_text}") + + try: + parsed = json.loads(stdout.decode()) + return BackendExecResult( + result=parsed.get("__result__", {}), + stdout=parsed.get("__stdout__", ""), + stderr=stderr_text, + ) + except json.JSONDecodeError: + raw = stdout.decode(errors="replace").strip() + raise RuntimeError( + f"Backend code did not produce valid JSON. Raw output: {raw[:500]}" + ) diff --git a/backend/apps/outputs/models.py b/backend/apps/outputs/models.py new file mode 100644 index 00000000..79e1b922 --- /dev/null +++ b/backend/apps/outputs/models.py @@ -0,0 +1,197 @@ +from pydantic import BaseModel, Field, model_validator +from typing import Optional, Any +from uuid import uuid4 +from datetime import datetime + + +class AutoRunConfig(BaseModel): + enabled: bool = False + prompt: str = "" + context_paths: list[dict[str, str]] = Field(default_factory=list) + forced_tools: list[dict[str, Any]] = Field(default_factory=list) + mode: str = "agent" + model: str = "sonnet" + + +class Output(BaseModel): + id: str = Field(default_factory=lambda: uuid4().hex) + name: str + description: str = "" + icon: str = "view_quilt" + input_schema: dict[str, Any] = Field(default_factory=lambda: { + "type": "object", + "properties": {}, + "required": [], + }) + files: dict[str, str] = Field(default_factory=dict) + permission: str = "ask" + auto_run_config: Optional[AutoRunConfig] = None + thumbnail: Optional[str] = None + created_at: str = Field(default_factory=lambda: datetime.now().isoformat()) + updated_at: str = Field(default_factory=lambda: datetime.now().isoformat()) + + @model_validator(mode="before") + @classmethod + def _migrate_flat_fields(cls, data: Any) -> Any: + """Migrate legacy frontend_code/backend_code fields into the files dict.""" + if not isinstance(data, dict): + return data + if "files" not in data or not data["files"]: + files: dict[str, str] = {} + fc = data.pop("frontend_code", None) + bc = data.pop("backend_code", None) + if fc: + files["index.html"] = fc + if bc: + files["backend.py"] = bc + data["files"] = files + else: + data.pop("frontend_code", None) + data.pop("backend_code", None) + return data + + @property + def frontend_code(self) -> str: + return self.files.get("index.html", "") + + @property + def backend_code(self) -> str | None: + return self.files.get("backend.py") + + +class OutputCreate(BaseModel): + name: str + description: str = "" + icon: str = "view_quilt" + input_schema: dict[str, Any] = Field(default_factory=lambda: { + "type": "object", + "properties": {}, + "required": [], + }) + files: dict[str, str] = Field(default_factory=dict) + auto_run_config: Optional[dict[str, Any]] = None + thumbnail: Optional[str] = None + + @model_validator(mode="before") + @classmethod + def _migrate_flat_fields(cls, data: Any) -> Any: + if not isinstance(data, dict): + return data + if "files" not in data or not data["files"]: + files: dict[str, str] = {} + fc = data.pop("frontend_code", None) + bc = data.pop("backend_code", None) + if fc: + files["index.html"] = fc + if bc: + files["backend.py"] = bc + data["files"] = files + else: + data.pop("frontend_code", None) + data.pop("backend_code", None) + return data + + +class OutputUpdate(BaseModel): + name: Optional[str] = None + description: Optional[str] = None + icon: Optional[str] = None + input_schema: Optional[dict[str, Any]] = None + files: Optional[dict[str, str]] = None + permission: Optional[str] = None + auto_run_config: Optional[dict[str, Any]] = None + thumbnail: Optional[str] = None + + @model_validator(mode="before") + @classmethod + def _migrate_flat_fields(cls, data: Any) -> Any: + if not isinstance(data, dict): + return data + if "files" not in data: + files: dict[str, str] = {} + fc = data.pop("frontend_code", None) + bc = data.pop("backend_code", None) + if fc: + files["index.html"] = fc + if bc: + files["backend.py"] = bc + if files: + data["files"] = files + else: + data.pop("frontend_code", None) + data.pop("backend_code", None) + return data + + +class OutputExecute(BaseModel): + output_id: str + input_data: dict[str, Any] = Field(default_factory=dict) + + +class OutputExecuteResult(BaseModel): + output_id: str + output_name: str + frontend_code: str + input_data: dict[str, Any] + backend_result: Optional[dict[str, Any]] = None + stdout: Optional[str] = None + stderr: Optional[str] = None + error: Optional[str] = None + + +class AutoRunRequest(BaseModel): + prompt: str + input_schema: dict[str, Any] = Field(default_factory=dict) + backend_code: Optional[str] = None + context_paths: list[dict[str, str]] = Field(default_factory=list) + forced_tools: list[str] = Field(default_factory=list) + model: str = "sonnet" + + +class AutoRunAgentRequest(BaseModel): + prompt: str + input_schema: dict[str, Any] = Field(default_factory=dict) + output_id: str + model: str = "sonnet" + forced_tools: list[str] = Field(default_factory=list) + context_paths: list[dict[str, str]] = Field(default_factory=list) + + +class WorkspaceSeedRequest(BaseModel): + workspace_id: str + files: Optional[dict[str, str]] = None + meta: Optional[dict[str, Any]] = None + + @model_validator(mode="before") + @classmethod + def _migrate_flat_fields(cls, data: Any) -> Any: + """Accept legacy frontend_code/backend_code/schema_json fields.""" + if not isinstance(data, dict): + return data + if "files" not in data: + files: dict[str, str] = {} + fc = data.pop("frontend_code", None) + bc = data.pop("backend_code", None) + sj = data.pop("schema_json", None) + if fc: + files["index.html"] = fc + if bc: + files["backend.py"] = bc + if sj: + files["schema.json"] = sj + if files: + data["files"] = files + else: + data.pop("frontend_code", None) + data.pop("backend_code", None) + data.pop("schema_json", None) + return data + + +class VibeCodeRequest(BaseModel): + prompt: str + current_frontend_code: str = "" + current_backend_code: str = "" + current_schema: str = "" + name: str = "" + description: str = "" diff --git a/backend/apps/outputs/outputs.py b/backend/apps/outputs/outputs.py new file mode 100644 index 00000000..2e22eac4 --- /dev/null +++ b/backend/apps/outputs/outputs.py @@ -0,0 +1,596 @@ +import json +import os +import logging +import mimetypes +import base64 +from datetime import datetime +from contextlib import asynccontextmanager +from fastapi import HTTPException, Query +from fastapi.responses import Response +from jsonschema import validate as schema_validate, ValidationError as SchemaValidationError +from backend.config.Apps import SubApp +from backend.apps.outputs.models import ( + Output, OutputCreate, OutputUpdate, OutputExecute, OutputExecuteResult, + VibeCodeRequest, AutoRunRequest, AutoRunConfig, AutoRunAgentRequest, + WorkspaceSeedRequest, +) +from backend.apps.outputs.executor import execute_backend_code +from backend.apps.settings.settings import load_settings + +logger = logging.getLogger(__name__) + +MODEL_MAP = { + "sonnet": "claude-sonnet-4-20250514", + "opus": "claude-opus-4-20250514", + "haiku": "claude-haiku-4-20250414", +} + + +def _resolve_model(short_name: str) -> str: + return MODEL_MAP.get(short_name, short_name) + + +def _get_anthropic_client(): + """Create an AsyncAnthropic client, pulling the API key from app settings if + the ANTHROPIC_API_KEY env var isn't set.""" + import anthropic + + if os.environ.get("ANTHROPIC_API_KEY"): + return anthropic.AsyncAnthropic() + + settings = load_settings() + if settings.anthropic_api_key: + return anthropic.AsyncAnthropic(api_key=settings.anthropic_api_key) + + return anthropic.AsyncAnthropic() + + +def _validate_against_schema(data: dict, schema: dict) -> str | None: + """Validate *data* against *schema*. Return an error string or None.""" + try: + schema_validate(instance=data, schema=schema) + return None + except SchemaValidationError as exc: + path = " -> ".join(str(p) for p in exc.absolute_path) if exc.absolute_path else "(root)" + return f"Schema validation failed at {path}: {exc.message}" + +DATA_DIR = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), + "data", "outputs", +) + +WORKSPACE_DIR = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), + "data", "outputs_workspace", +) + + +def _build_data_injection(input_json: str, result_json: str) -> str: + """Build a

Invalid OAuth state

", status_code=400) + + tool = _load(tool_id) + client_id = os.environ.get("GOOGLE_OAUTH_CLIENT_ID", "") + client_secret = os.environ.get("GOOGLE_OAUTH_CLIENT_SECRET", "") + redirect_uri = "http://localhost:8324/api/tools/oauth/callback" + + async with httpx.AsyncClient(timeout=15.0) as client: + resp = await client.post(GOOGLE_TOKEN_URL, data={ + "code": code, + "client_id": client_id, + "client_secret": client_secret, + "redirect_uri": redirect_uri, + "grant_type": "authorization_code", + }) + + if resp.status_code != 200: + logger.warning(f"OAuth token exchange failed: {resp.text}") + return HTMLResponse(f"

Token exchange failed

{resp.text}
", status_code=400) + + tokens = resp.json() + access_token = tokens.get("access_token", "") + tool.oauth_tokens = { + "access_token": access_token, + "refresh_token": tokens.get("refresh_token", ""), + "token_expiry": time.time() + tokens.get("expires_in", 3600), + } + tool.auth_status = "connected" + + if access_token: + try: + async with httpx.AsyncClient(timeout=10.0) as info_client: + info_resp = await info_client.get( + GOOGLE_USERINFO_URL, + headers={"Authorization": f"Bearer {access_token}"}, + ) + if info_resp.status_code == 200: + tool.connected_account_email = info_resp.json().get("email") + except Exception as e: + logger.warning(f"Failed to fetch Google userinfo: {e}") + + _save(tool) + + return HTMLResponse(""" + +

Connected successfully!

+

You can close this window.

+ + + """) + + +@tools_lib.router.get("/{tool_id}") +async def get_tool(tool_id: str): + return _load(tool_id).model_dump() + + +@tools_lib.router.post("/create") +async def create_tool(body: ToolCreate): + tool = ToolDefinition( + name=body.name, + description=body.description, + command=body.command, + mcp_config=body.mcp_config, + credentials=body.credentials, + auth_type=body.auth_type, + auth_status=body.auth_status, + ) + _save(tool) + return {"ok": True, "tool": tool.model_dump()} + + +_XBIRD_CONFIG_DIR = os.path.join(os.path.expanduser("~"), ".config", "xbird") +_XBIRD_CONFIG_PATH = os.path.join(_XBIRD_CONFIG_DIR, "config.json") + + +async def _fetch_twitter_screen_name(auth_token: str, ct0: str) -> str | None: + """Fetch the logged-in Twitter/X screen name using session cookies.""" + try: + async with httpx.AsyncClient(timeout=10.0) as client: + resp = await client.get( + "https://api.twitter.com/1.1/account/verify_credentials.json", + headers={"x-csrf-token": ct0}, + cookies={"auth_token": auth_token, "ct0": ct0}, + ) + if resp.status_code == 200: + screen_name = resp.json().get("screen_name") + return f"@{screen_name}" if screen_name else None + except Exception as e: + logger.warning("Failed to fetch Twitter screen name: %s", e) + return None + + +def _sync_external_config(tool: ToolDefinition): + """Write credentials to external config files for tools that need them. + + xbird reads auth from ~/.config/xbird/config.json rather than env vars, + so we sync credentials there when the user connects via the UI. + """ + if tool.name == "xbird" and tool.credentials: + auth_token = tool.credentials.get("TWITTER_AUTH_TOKEN", "") + ct0 = tool.credentials.get("TWITTER_CT0", "") + if auth_token and ct0: + os.makedirs(_XBIRD_CONFIG_DIR, exist_ok=True) + config = {} + if os.path.exists(_XBIRD_CONFIG_PATH): + try: + with open(_XBIRD_CONFIG_PATH) as f: + config = json.load(f) + except Exception: + pass + config["auth_token"] = auth_token + config["ct0"] = ct0 + with open(_XBIRD_CONFIG_PATH, "w") as f: + json.dump(config, f, indent=2) + os.chmod(_XBIRD_CONFIG_PATH, 0o600) + logger.info("Synced xbird credentials to %s", _XBIRD_CONFIG_PATH) + elif tool.name == "xbird" and not tool.credentials: + if os.path.exists(_XBIRD_CONFIG_PATH): + try: + with open(_XBIRD_CONFIG_PATH) as f: + config = json.load(f) + config.pop("auth_token", None) + config.pop("ct0", None) + with open(_XBIRD_CONFIG_PATH, "w") as f: + json.dump(config, f, indent=2) + logger.info("Cleared xbird credentials from %s", _XBIRD_CONFIG_PATH) + except Exception: + pass + + +@tools_lib.router.put("/{tool_id}") +async def update_tool(tool_id: str, body: ToolUpdate): + tool = _load(tool_id) + for k, v in body.model_dump(exclude_none=True).items(): + setattr(tool, k, v) + _sync_external_config(tool) + + if tool.name == "xbird" and tool.auth_status == "connected" and tool.credentials: + auth_token = tool.credentials.get("TWITTER_AUTH_TOKEN", "") + ct0 = tool.credentials.get("TWITTER_CT0", "") + if auth_token and ct0: + screen_name = await _fetch_twitter_screen_name(auth_token, ct0) + if screen_name: + tool.connected_account_email = screen_name + + _save(tool) + return {"ok": True, "tool": tool.model_dump()} + + +@tools_lib.router.delete("/{tool_id}") +async def delete_tool(tool_id: str): + path = os.path.join(DATA_DIR, f"{tool_id}.json") + if os.path.exists(path): + os.remove(path) + return {"ok": True} + + +# --------------------------------------------------------------------------- +# MCP config derivation +# --------------------------------------------------------------------------- + +def _sanitize_server_name(name: str) -> str: + """Convert a tool name into a valid MCP server identifier (alphanumeric + hyphens).""" + return re.sub(r"[^a-z0-9]+", "-", name.lower()).strip("-") + + +def _resolve_command(command: str) -> str | None: + """Find a command on PATH, falling back to common user-local bin directories.""" + found = shutil.which(command) + if found: + return found + home = os.path.expanduser("~") + extra_dirs = [ + os.path.join(home, ".bun", "bin"), + os.path.join(home, ".cargo", "bin"), + os.path.join(home, ".local", "bin"), + "/opt/homebrew/bin", + ] + for d in extra_dirs: + candidate = os.path.join(d, command) + if os.path.isfile(candidate) and os.access(candidate, os.X_OK): + return candidate + return None + + +def derive_mcp_config(tool: ToolDefinition) -> Optional[dict]: + """Build the claude_agent_sdk mcp_servers config entry for a tool. + + Returns None if the tool cannot be configured (e.g. missing data). + """ + if not tool.mcp_config: + return None + + config: dict = dict(tool.mcp_config) + + if tool.credentials: + if config.get("type") in ("http", "sse"): + headers = config.setdefault("headers", {}) + for key, val in tool.credentials.items(): + if key.lower() in ("authorization", "api_key", "api-key"): + headers.setdefault("Authorization", f"Bearer {val}") + else: + env = config.setdefault("env", {}) + env.update(tool.credentials) + + if tool.auth_type == "oauth2" and tool.oauth_tokens.get("access_token"): + if config.get("type") in ("http", "sse"): + headers = config.setdefault("headers", {}) + headers["Authorization"] = f"Bearer {tool.oauth_tokens['access_token']}" + else: + env = config.setdefault("env", {}) + env["OAUTH_ACCESS_TOKEN"] = tool.oauth_tokens["access_token"] + if tool.oauth_tokens.get("refresh_token"): + env["GOOGLE_WORKSPACE_REFRESH_TOKEN"] = tool.oauth_tokens["refresh_token"] + client_id = os.environ.get("GOOGLE_OAUTH_CLIENT_ID", "") + client_secret = os.environ.get("GOOGLE_OAUTH_CLIENT_SECRET", "") + if client_id: + env["GOOGLE_WORKSPACE_CLIENT_ID"] = client_id + if client_secret: + env["GOOGLE_WORKSPACE_CLIENT_SECRET"] = client_secret + + if config.get("type") == "stdio" and config.get("command"): + resolved = _resolve_command(config["command"]) + if resolved: + config["command"] = resolved + + return config + + +# --------------------------------------------------------------------------- +# OAuth2 flow for Google Workspace (and other OAuth providers) +# --------------------------------------------------------------------------- + + +# --------------------------------------------------------------------------- +# MCP tool discovery +# --------------------------------------------------------------------------- + +_READ_PREFIXES = ("get", "list", "read", "search", "fetch", "find", "query", "count", "check", "describe", "show", "download", "browse", "analy", "explain") +_WRITE_PREFIXES = ("create", "write", "delete", "update", "send", "remove", "modify", "add", "set", "put", "post", "patch", "insert", "move", "copy", "rename", "archive", "trash", "publish", "approve", "reject") + + +_SERVICE_RULES: list[tuple[list[str], str, str]] = [ + # (keywords, service_name, group) + # Google Workspace + (["gmail"], "Gmail", "Google"), + (["drive"], "Drive", "Google"), + (["calendar", "event", "freebusy"], "Calendar", "Google"), + (["spreadsheet", "sheet"], "Sheets", "Google"), + (["doc", "paragraph", "table"], "Docs", "Google"), + (["chat", "space", "reaction", "message"], "Chat", "Google"), + (["form", "publish_settings"], "Forms", "Google"), + (["presentation", "slide", "page"], "Slides", "Google"), + (["task_list", "task"], "Tasks", "Google"), + (["contact"], "Contacts", "Google"), + (["script", "deployment", "version", "trigger"], "Apps Script", "Google"), + (["search_custom", "search_engine"], "Search", "Google"), + # Reddit (before Twitter so "search_reddit" etc. don't mis-match) + (["subreddit"], "Subreddits", "Reddit"), + (["search_reddit"], "Search", "Reddit"), + (["post_detail"], "Posts", "Reddit"), + (["user_analysis"], "Users", "Reddit"), + (["reddit_explain"], "Reference", "Reddit"), + # Twitter / X + (["tweet", "thread", "reply", "replies", "quote", "retweet", "article"], "Tweets", "Twitter"), + (["timeline", "home", "news", "trending"], "Timeline", "Twitter"), + (["follower", "following", "follow", "unfollow"], "Network", "Twitter"), + (["like", "unlike", "bookmark"], "Engagement", "Twitter"), + (["mention"], "Mentions", "Twitter"), + (["user", "profile"], "Users", "Twitter"), + (["media", "upload", "image", "video"], "Media", "Twitter"), + (["search"], "Search", "Twitter"), + (["list", "list_member"], "Lists", "Twitter"), +] + + +def _categorize_tool(name: str) -> str: + lower = name.lower().replace("_", " ").replace("-", " ").strip() + for word in lower.split(): + for prefix in _READ_PREFIXES: + if word.startswith(prefix): + return "read" + for prefix in _WRITE_PREFIXES: + if word.startswith(prefix): + return "write" + return "write" + + +def _extract_service(name: str) -> tuple[str, str]: + """Extract the service and group from a tool name (e.g. 'search_gmail_messages' -> ('Gmail', 'Google')).""" + lower = name.lower() + for keywords, display, group in _SERVICE_RULES: + for kw in keywords: + if kw in lower: + return display, group + return "Other", "" + + +def _parse_sse_json(text: str) -> dict | None: + """Extract JSON from an SSE response body (handles `data: {...}` lines).""" + for line in text.splitlines(): + stripped = line.strip() + if stripped.startswith("data:"): + payload = stripped[len("data:"):].strip() + if payload: + try: + return json.loads(payload) + except json.JSONDecodeError: + continue + try: + return json.loads(text) + except json.JSONDecodeError: + return None + + +async def _discover_mcp_tools_http(url: str, headers: dict | None = None) -> list[dict]: + """Connect to a Streamable HTTP MCP server and call tools/list via JSON-RPC POST.""" + h = { + "Content-Type": "application/json", + "Accept": "application/json, text/event-stream", + **(headers or {}), + } + async with httpx.AsyncClient(timeout=30.0) as client: + init_resp = await client.post(url, headers=h, json={ + "jsonrpc": "2.0", "id": 1, "method": "initialize", + "params": {"protocolVersion": "2025-03-26", "capabilities": {}, + "clientInfo": {"name": "self-swarm", "version": "0.1.0"}}, + }) + if init_resp.status_code not in (200, 201): + raise HTTPException(status_code=502, detail=f"MCP initialize failed: {init_resp.status_code}") + + session_id = init_resp.headers.get("mcp-session-id", "") + if session_id: + h["mcp-session-id"] = session_id + + await client.post(url, headers=h, json={ + "jsonrpc": "2.0", "method": "notifications/initialized", + }) + + list_resp = await client.post(url, headers=h, json={ + "jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {}, + }) + if list_resp.status_code not in (200, 201): + raise HTTPException(status_code=502, detail=f"MCP tools/list failed: {list_resp.status_code}") + + ct = list_resp.headers.get("content-type", "") + if "text/event-stream" in ct: + data = _parse_sse_json(list_resp.text) + else: + data = list_resp.json() + + if not data: + raise HTTPException(status_code=502, detail="Empty response from MCP server") + + tools_list = data.get("result", {}).get("tools", []) + return [{"name": t.get("name", ""), "description": t.get("description", "")} for t in tools_list] + + +async def _discover_mcp_tools_sse(url: str, headers: dict | None = None) -> list[dict]: + """Connect to a legacy SSE MCP server (GET event-stream + POST messages) and call tools/list.""" + from mcp.client.sse import sse_client + from mcp import ClientSession + from mcp.types import Implementation + + try: + async with sse_client( + url=url, + headers=headers, + timeout=30, + sse_read_timeout=30, + ) as (read_stream, write_stream): + async with ClientSession( + read_stream, + write_stream, + client_info=Implementation(name="self-swarm", version="0.1.0"), + ) as session: + await session.initialize() + result = await session.list_tools() + return [{"name": t.name, "description": t.description or ""} for t in result.tools] + except BaseExceptionGroup as eg: + first = eg.exceptions[0] if eg.exceptions else eg + raise HTTPException(status_code=502, detail=f"SSE discovery failed: {first}") from first + + +async def _discover_mcp_tools_stdio(command: str, args: list[str] | None = None, env: dict | None = None) -> list[dict]: + """Spawn a stdio MCP server process and call tools/list via JSON-RPC over stdin/stdout.""" + cmd_path = _resolve_command(command) + if not cmd_path: + raise HTTPException(status_code=400, detail=f"Command '{command}' not found on PATH or common install locations") + + proc_env = {**os.environ, **(env or {})} + + proc = await asyncio.create_subprocess_exec( + cmd_path, *(args or []), + stdin=asyncio.subprocess.PIPE, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + env=proc_env, + ) + + async def _send(msg: dict) -> None: + line = json.dumps(msg) + "\n" + proc.stdin.write(line.encode()) + await proc.stdin.drain() + + async def _recv() -> dict: + """Read JSON-RPC responses, skipping notification lines (no 'id' field).""" + while True: + line = await asyncio.wait_for(proc.stdout.readline(), timeout=30.0) + if not line: + stderr_out = "" + try: + stderr_out = (await asyncio.wait_for(proc.stderr.read(4096), timeout=2.0)).decode(errors="replace") + except (asyncio.TimeoutError, Exception): + pass + raise HTTPException( + status_code=502, + detail=f"MCP stdio process exited unexpectedly{': ' + stderr_out if stderr_out else ''}", + ) + stripped = line.decode(errors="replace").strip() + if not stripped: + continue + try: + data = json.loads(stripped) + except json.JSONDecodeError: + continue + if "id" in data: + return data + + try: + await _send({ + "jsonrpc": "2.0", "id": 1, "method": "initialize", + "params": { + "protocolVersion": "2025-03-26", + "capabilities": {}, + "clientInfo": {"name": "self-swarm", "version": "0.1.0"}, + }, + }) + await _recv() + + await _send({"jsonrpc": "2.0", "method": "notifications/initialized"}) + + await _send({"jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {}}) + data = await _recv() + + tools_list = data.get("result", {}).get("tools", []) + return [{"name": t.get("name", ""), "description": t.get("description", "")} for t in tools_list] + + except HTTPException: + raise + except asyncio.TimeoutError: + raise HTTPException(status_code=504, detail="MCP stdio server timed out during discovery") + finally: + try: + proc.stdin.close() + except Exception: + pass + try: + proc.terminate() + await asyncio.wait_for(proc.wait(), timeout=5.0) + except Exception: + proc.kill() + + +@tools_lib.router.post("/{tool_id}/discover") +async def discover_tools(tool_id: str): + tool = _load(tool_id) + config = derive_mcp_config(tool) + if not config: + raise HTTPException(status_code=400, detail="Cannot derive MCP config for tool") + + transport = config.get("type", "") + + try: + if transport == "stdio": + command = config.get("command", "") + if not command: + raise HTTPException(status_code=400, detail="stdio transport requires a 'command' in MCP config") + raw_tools = await _discover_mcp_tools_stdio( + command=command, + args=config.get("args"), + env=config.get("env"), + ) + elif transport in ("http", "sse") or config.get("url"): + url = config.get("url", "") + if not url: + raise HTTPException(status_code=400, detail="HTTP/SSE transport requires a 'url' in MCP config") + if transport == "sse": + raw_tools = await _discover_mcp_tools_sse(url, config.get("headers")) + else: + try: + raw_tools = await _discover_mcp_tools_http(url, config.get("headers")) + except HTTPException: + logger.info(f"Streamable HTTP failed for {tool.name}, retrying with SSE transport") + raw_tools = await _discover_mcp_tools_sse(url, config.get("headers")) + else: + raise HTTPException(status_code=400, detail=f"Unsupported MCP transport type: '{transport}'. Use 'stdio', 'http', or 'sse'.") + except HTTPException: + raise + except Exception as e: + logger.warning(f"MCP tool discovery failed for {tool.name}: {e}") + raise HTTPException(status_code=502, detail=f"Discovery failed: {e}") + + services: dict[str, dict[str, list[str]]] = {} + service_groups: dict[str, list[str]] = {} + permissions: dict[str, Any] = {} + + for t in raw_tools: + name = t["name"] + cat = _categorize_tool(name) + svc, group = _extract_service(name) + if svc not in services: + services[svc] = {"read": [], "write": []} + services[svc][cat].append(name) + permissions[name] = tool.tool_permissions.get(name, "ask") + if group: + service_groups.setdefault(group, []) + if svc not in service_groups[group]: + service_groups[group].append(svc) + + all_read = [n for s in services.values() for n in s["read"]] + all_write = [n for s in services.values() for n in s["write"]] + permissions["_categories"] = {"read": all_read, "write": all_write} + permissions["_services"] = services + permissions["_service_groups"] = service_groups + permissions["_tool_descriptions"] = {t["name"]: t["description"] for t in raw_tools} + + tool.tool_permissions = permissions + _save(tool) + + return {"ok": True, "tool": tool.model_dump()} + + +@tools_lib.router.post("/{tool_id}/oauth/start") +async def oauth_start(tool_id: str): + _load(tool_id) + client_id = os.environ.get("GOOGLE_OAUTH_CLIENT_ID", "") + if not client_id: + raise HTTPException(status_code=400, detail="GOOGLE_OAUTH_CLIENT_ID not set in backend .env") + + redirect_uri = "http://localhost:8324/api/tools/oauth/callback" + state = tool_id + + _pending_oauth[state] = tool_id + + params = { + "client_id": client_id, + "redirect_uri": redirect_uri, + "response_type": "code", + "scope": " ".join(GOOGLE_SCOPES), + "access_type": "offline", + "prompt": "consent", + "state": state, + } + auth_url = f"{GOOGLE_AUTH_URL}?{urlencode(params)}" + return {"auth_url": auth_url} + + + + +async def refresh_google_token(tool: ToolDefinition) -> Optional[str]: + """Refresh an expired Google OAuth token. Returns the fresh access_token or None.""" + if tool.auth_type != "oauth2": + return None + refresh_token = tool.oauth_tokens.get("refresh_token") + if not refresh_token: + return None + expiry = tool.oauth_tokens.get("token_expiry", 0) + if time.time() < expiry - 60: + return tool.oauth_tokens.get("access_token") + + client_id = os.environ.get("GOOGLE_OAUTH_CLIENT_ID", "") + client_secret = os.environ.get("GOOGLE_OAUTH_CLIENT_SECRET", "") + if not client_id or not client_secret: + return None + + try: + async with httpx.AsyncClient(timeout=15.0) as client: + resp = await client.post(GOOGLE_TOKEN_URL, data={ + "client_id": client_id, + "client_secret": client_secret, + "refresh_token": refresh_token, + "grant_type": "refresh_token", + }) + if resp.status_code == 200: + data = resp.json() + new_token = data["access_token"] + tool.oauth_tokens["access_token"] = new_token + tool.oauth_tokens["token_expiry"] = time.time() + data.get("expires_in", 3600) + + if not tool.connected_account_email: + try: + async with httpx.AsyncClient(timeout=10.0) as info_client: + info_resp = await info_client.get( + GOOGLE_USERINFO_URL, + headers={"Authorization": f"Bearer {new_token}"}, + ) + if info_resp.status_code == 200: + tool.connected_account_email = info_resp.json().get("email") + except Exception: + pass + + _save(tool) + return new_token + except Exception as e: + logger.warning(f"Google token refresh failed for tool {tool.id}: {e}") + return None diff --git a/backend/config/Apps.py b/backend/config/Apps.py new file mode 100644 index 00000000..ce8ffb3a --- /dev/null +++ b/backend/config/Apps.py @@ -0,0 +1,45 @@ +from fastapi import FastAPI, APIRouter +import debug +from uuid import uuid4 +from typing import List +from contextlib import asynccontextmanager +from contextlib import AsyncExitStack +from typing import Callable + + +class SubApp: + def __init__(self, name:str, lifespan:Callable): + debug("START", name) + self.id = uuid4() + self.name = name + self.prefix = f"/api/{name}" + self.lifespan = lifespan + self.router = APIRouter() + debug("END") + + def __str__(self): + return f"SubApp(name={self.name}, prefix={self.prefix}, id={self.id})" + +class MainApp: + def __init__(self, sub_apps: List[SubApp]): + debug("START") + + @asynccontextmanager + async def lifespan(app: FastAPI): + async with AsyncExitStack() as stack: + for sub_app in sub_apps: + debug(sub_app.name) + await stack.enter_async_context(sub_app.lifespan()) + print("\nCheck out the API docs at: http://127.0.0.1:8324/docs\n") + yield + + self.app = FastAPI(lifespan=lifespan) + + # Include all sub-app routers in the main app with their prefixes + for sub_app in sub_apps: + self.app.include_router( + sub_app.router, + prefix=sub_app.prefix, + tags=[sub_app.name] + ) + debug("END") \ No newline at end of file diff --git a/backend/config/__init__.py b/backend/config/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/main.py b/backend/main.py new file mode 100644 index 00000000..d3708a9f --- /dev/null +++ b/backend/main.py @@ -0,0 +1,91 @@ +from fastapi.responses import JSONResponse +from backend.config.Apps import MainApp +from backend.apps.health.health import health +from backend.apps.agents.agents import agents +from backend.apps.agents.ws_manager import ws_manager +from backend.apps.templates.templates import templates +from backend.apps.skills.skills import skills +from backend.apps.tools_lib.tools_lib import tools_lib +from backend.apps.modes.modes import modes +from backend.apps.settings.settings import settings +from backend.apps.mcp_registry.mcp_registry import mcp_registry +from backend.apps.skill_registry.skill_registry import skill_registry +from backend.apps.outputs.outputs import outputs +from backend.apps.dashboards.dashboards import dashboards +from fastapi.middleware.cors import CORSMiddleware +from fastapi import WebSocket, WebSocketDisconnect +import json + +main_app = MainApp([health, agents, templates, skills, tools_lib, modes, settings, mcp_registry, skill_registry, outputs, dashboards]) +app = main_app.app + +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +@app.websocket("/ws/agents/{session_id}") +async def websocket_session(websocket: WebSocket, session_id: str): + await ws_manager.connect_session(session_id, websocket) + try: + while True: + data = await websocket.receive_text() + msg = json.loads(data) + event = msg.get("event") + payload = msg.get("data", {}) + + if event == "agent:send_message": + from backend.apps.agents.agent_manager import agent_manager + await agent_manager.send_message( + session_id, + payload.get("prompt", ""), + mode=payload.get("mode"), + model=payload.get("model"), + images=payload.get("images"), + ) + elif event == "agent:approval_response": + from backend.apps.agents.agent_manager import agent_manager + agent_manager.handle_approval(payload.get("request_id"), { + "behavior": payload.get("behavior", "deny"), + "message": payload.get("message"), + "updated_input": payload.get("updated_input"), + }) + elif event == "agent:edit_message": + from backend.apps.agents.agent_manager import agent_manager + await agent_manager.edit_message( + session_id, + payload.get("message_id", ""), + payload.get("content", ""), + ) + elif event == "agent:stop": + from backend.apps.agents.agent_manager import agent_manager + await agent_manager.stop_agent(session_id) + except WebSocketDisconnect: + ws_manager.disconnect_session(session_id, websocket) + +@app.websocket("/ws/dashboard") +async def websocket_dashboard(websocket: WebSocket): + await ws_manager.connect_global(websocket) + try: + while True: + data = await websocket.receive_text() + msg = json.loads(data) + event = msg.get("event") + payload = msg.get("data", {}) + + if event == "agent:approval_response": + from backend.apps.agents.agent_manager import agent_manager + agent_manager.handle_approval(payload.get("request_id"), { + "behavior": payload.get("behavior", "deny"), + "message": payload.get("message"), + "updated_input": payload.get("updated_input"), + }) + except WebSocketDisconnect: + ws_manager.disconnect_global(websocket) + +if __name__ == "__main__": + import uvicorn + uvicorn.run("backend.main:app", host="0.0.0.0", port=8324, reload=True) diff --git a/backend/requirements.txt b/backend/requirements.txt new file mode 100644 index 00000000..813faf0e --- /dev/null +++ b/backend/requirements.txt @@ -0,0 +1,11 @@ +anthropic +claude-agent-sdk +jsonschema +fastapi[standard] +pydantic==2.10.5 +langchain-core==0.3.51 +langchain-openai==0.3.12 +pytest==8.3.4 +pytest-asyncio==0.25.2 +typeguard==4.4.2 +python-dotenv==1.1.1 \ No newline at end of file diff --git a/backend/run/_utils.sh b/backend/run/_utils.sh new file mode 100755 index 00000000..ea052bb5 --- /dev/null +++ b/backend/run/_utils.sh @@ -0,0 +1,202 @@ +#!/bin/bash +# Flag processing function with namespacing and global variable declaration +UTILS_FILE_ABSPATH="$(readlink -f "${BASH_SOURCE[0]}")" +if [[ "$OSTYPE" == "darwin"* ]]; then + # echo "In macOS utils sed START" + # echo "UTILS_ABSPATH: $UTILS_FILE_ABSPATH" + sed -i '' 's/\r//g' "$UTILS_FILE_ABSPATH" + # echo "In macOS utils sed END" +else + # echo "NOT in macOS utils START" + # echo "UTILS_ABSPATH: $UTILS_FILE_ABSPATH" + sed -i 's/\r//g' "$UTILS_FILE_ABSPATH" + # echo "NOT in macOS utils END" +fi +chmod +x "$UTILS_FILE_ABSPATH" + +RUN_DIR_ABSPATH="$(dirname "$UTILS_FILE_ABSPATH")" +BACKEND_DIR_ABSPATH="$(dirname "$RUN_DIR_ABSPATH")" + +formatted_error() { + # Arguments: error message and array of conflicting flags + local initial_message="$1" + shift + local conflicting_flags=("$@") + + # Red color for the error message box + local COLOR_CODE='\033[0;31m' + local NC='\033[0m' # No Color + + # Start the error message with the initial message + local error_message="$initial_message" + + # Add each conflicting flag on a new line with indentation + for conflict in "${conflicting_flags[@]}"; do + error_message+="\n $conflict" # Replacing `\t` with four spaces + done + + # Prepare for printing by finding max length of each line in the message + local lines=() + local max_length=0 + + # Use printf to interpret new lines and calculate max length with spaces instead of tabs + while IFS= read -r line; do + # Substitute tabs with spaces for consistent width measurement + local line_with_spaces="${line//$'\t'/ }" + lines+=("$line_with_spaces") + if (( ${#line_with_spaces} > max_length )); then + max_length=${#line_with_spaces} + fi + done <<< "$(printf "$error_message")" + + # Create the top and bottom borders based on the maximum line length + local border=$(printf '%*s' "$((max_length + 4))" '' | tr ' ' '-') + + # Print the formatted error message with a red box + printf "\n${COLOR_CODE}%s${NC}\n" "$border" + for line in "${lines[@]}"; do + printf "${COLOR_CODE}| %-*s |${NC}\n" "$max_length" "$line" + done + printf "${COLOR_CODE}| %-*s |${NC}\n" "$max_length" "" + printf "${COLOR_CODE}%s${NC}\n" "$border" +} + + +function process_flags() { + local -n flags_to_commands="$1" # Reference to the dictionary of flags and commands + local -n exclusives="$2" # Reference to the list of exclusive flag groups + local namespace="$3" # Unique prefix for variables + local calling_script_name="$(basename "$(readlink -f "${BASH_SOURCE[1]}")")" + local caller_id="${FUNCNAME[1]}" + local should_exit=false + if [ "$caller_id" == "main" ]; then + caller_id="$calling_script_name" + fi + # echo "caller_id: $caller_id" + # echo "Initializing flags with namespace: $namespace" + + # Initialize flag variables with namespacing in the sourcing script context + for flag in "${!flags_to_commands[@]}"; do + # Convert flag to uppercase variable name and apply namespace, e.g., MYAPP_FLAG1 + local flag_var="${namespace}_${flag^^}" # Prefix and uppercase + flag_var="${flag_var//-}" # Remove dashes + eval "declare -g $flag_var=false" # Initialize as global false + + # Diagnostic output for each initialized variable + # echo "Initialized $flag_var as false" + done + + # echo "Parsing command line arguments: $@" + + # Parse command line arguments and set flags + local unsupported_flags=() + # local potential_typos=() + for arg in "$@"; do + # echo "Processing argument: $arg" + if [[ -n "${flags_to_commands[$arg]}" ]]; then + # echo "Flag found: $arg" + local flag_var="${namespace}_${arg^^}" # Prefix and uppercase + flag_var="${flag_var//-}" # Remove dashes + eval "declare -g $flag_var=true" # Set as global true + # echo "Set $flag_var to true" + # echo "Executing command for $arg: ${flags_to_commands[$arg]}" + eval "${flags_to_commands[$arg]}" + else + if [[ "$arg" == "-"* ]]; then + # echo "Flag not found: $arg" + unsupported_flags+=("$arg") + if [[ -n "${flags_to_commands[-$arg]}" ]]; then + # echo "Potential typo: $arg" + unsupported_flags+=("\t*Note: Potential typo detected.") + unsupported_flags+=("\tDid you mean: -$arg") + fi + fi + fi + done + # Check exclusive flag groups for conflicts + # echo "Checking exclusive flag groups" + for group in "${exclusives[@]}"; do + local count=0 + local conflicting_flags=() + for flag in $group; do + local flag_var="${namespace}_${flag^^}" + flag_var="${flag_var//-}" + if [[ "$(eval echo "\$$flag_var")" == "true" ]]; then + conflicting_flags+=("$flag") + # count=$((count + 1)) + # ec "$flag_var is true in exclusive group" + fi + done + if (( ${#conflicting_flags[@]} > 1 )); then + # echo "found conflicting flags" + formatted_error "Error: $caller_id\n-----------------------------------------\nIncompatible flags:\nThe flags below cannot be used together\n-----------------------------------------\n" "${conflicting_flags[@]}" + should_exit=true + fi + done + # echo "should_exit: $should_exit" + # echo "unsupported_flags: ${#unsupported_flags[@]}" + if (( ${#unsupported_flags[@]} > 0 )); then + # echo "found unsupported flags 2" + formatted_error "Error: $caller_id\n-------------------------------------------------\nUnsupported flags:\nThe flags below are not supported by this script\n-------------------------------------------------\n" "${unsupported_flags[@]}" + should_exit=true + fi + if [[ $should_exit == true ]]; then + exit 1 + fi +} + + + +formatted_echo() { + local COLOR_CODE='\033[0m' # No Color + local NC='\033[0m' # No Color variable + # local message="$2" + # If the second argument is empty, then theres no color specified, so we use the first argument as the message + if [[ -z "$2" ]]; then + message="$1" + else + message="$2" + fi + + declare -A format_flags + format_flags=( + [--red]="COLOR_CODE='\033[0;31m'" + [--green]="COLOR_CODE='\033[0;32m'" + [--yellow]="COLOR_CODE='\033[0;33m'" + [--blue]="COLOR_CODE='\033[0;34m'" + [--purple]="COLOR_CODE='\033[0;35m'" + [--cyan]="COLOR_CODE='\033[0;36m'" + ) + exclusive_format_flags=( + "--red --green --yellow --blue --purple --cyan" + ) + + # Pass the arguments with the namespace "FORMAT" + process_flags format_flags exclusive_format_flags "FORMAT" "$@" + + # Process the message + local text + text=$(printf "%b" "$message") + + # Expand any escaped characters in the input (e.g., \n) + local lines=() + local max_length=0 + + # Read the text line by line and find the maximum length + while IFS= read -r line; do + lines+=("$line") + if (( ${#line} > max_length )); then + max_length=${#line} + fi + done <<< "$text" + + # Create the top and bottom borders based on the maximum line length + local border=$(printf '%*s' "$((max_length + 4))" '' | tr ' ' '-') + + # Print the formatted box with selected color + printf "\n${COLOR_CODE}%s${NC}\n" "$border" + for line in "${lines[@]}"; do + printf "${COLOR_CODE}| %-*s |${NC}\n" "$max_length" "$line" + done + printf "${COLOR_CODE}%s${NC}\n" "$border" +} \ No newline at end of file diff --git a/backend/run/dev.sh b/backend/run/dev.sh new file mode 100755 index 00000000..92eede71 --- /dev/null +++ b/backend/run/dev.sh @@ -0,0 +1,62 @@ +#!/bin/bash +# The comment above is shebang, DO NOT REMOVE +DEV_ABSPATH="$(readlink -f "${BASH_SOURCE[0]}")" +if [[ "$OSTYPE" == "darwin"* ]]; then + # echo "In macOS server sed START" + # echo "SERVER_ABSPATH: $SERVER_ABSPATH" + sed -i '' 's/\r//g' "$DEV_ABSPATH" + # echo "In macOS server sed END" +else + # echo "NOT in macOS server START" + # echo "SERVER_ABSPATH: $SERVER_ABSPATH" + sed -i 's/\r//g' "$DEV_ABSPATH" + # echo "NOT in macOS server START" +fi +chmod +x "$DEV_ABSPATH" +source "$(dirname "$DEV_ABSPATH")/_utils.sh" + + +PROJECT_ROOT_ABSPATH="$(dirname "$BACKEND_DIR_ABSPATH")" + +# Cleanup function on exit +cleanup() { + formatted_echo --yellow "Shutting down..." + cd - > /dev/null 2>&1 +} +trap cleanup EXIT INT TERM + +# --- Create virtual environment if it doesn't exist --- +VENV_DIR="$BACKEND_DIR_ABSPATH/.venv" +if [[ ! -d "$VENV_DIR" ]]; then + formatted_echo --green "Creating virtual environment..." + python -m venv "$VENV_DIR" +fi +source "$VENV_DIR/bin/activate" + +# --- Install custom debugger module if not already installed --- +DEBUGGER_DIR_ABSPATH="$PROJECT_ROOT_ABSPATH/debugger" +if ! pip show debug > /dev/null 2>&1; then + formatted_echo --green "Installing debugger module..." + cd "$DEBUGGER_DIR_ABSPATH" + pip install -e . + if [[ $? -ne 0 ]]; then + formatted_error "Failed to install debugger module." + exit 1 + fi +fi + +# --- Install Python dependencies --- +formatted_echo --green "Installing dependencies..." +cd "$BACKEND_DIR_ABSPATH" +pip install -r requirements.txt +if [[ $? -ne 0 ]]; then + formatted_error "Failed to install Python dependencies." + exit 1 +fi + +# --- Start the backend server --- +formatted_echo --green "Starting backend server on http://0.0.0.0:8324 ..." +cd "$PROJECT_ROOT_ABSPATH" +python -m uvicorn backend.main:app --host 0.0.0.0 --port 8324 --reload \ + --reload-dir "$BACKEND_DIR_ABSPATH" \ + --reload-exclude '*.pyc' diff --git a/debugger/.gitignore b/debugger/.gitignore new file mode 100644 index 00000000..22eaa101 --- /dev/null +++ b/debugger/.gitignore @@ -0,0 +1,5 @@ +*.egg-info/ +__pycache__ +.venv/ +debug_toggles.json +needs_resync.txt \ No newline at end of file diff --git a/debugger/README.md b/debugger/README.md new file mode 100644 index 00000000..2e1a088b --- /dev/null +++ b/debugger/README.md @@ -0,0 +1,9 @@ +# Installation + +1. Navigate to this directory: + + +2. Install the package in development mode: + ```bash + pip install -e . + ``` \ No newline at end of file diff --git a/debugger/debug.py b/debugger/debug.py new file mode 100644 index 00000000..bef85d4a --- /dev/null +++ b/debugger/debug.py @@ -0,0 +1,57 @@ +import re +import inspect +import os +from debugger_backend.log_config import log_config +from debugger_backend.Debugleton import Debugleton +from debugger_backend.color_adjuster import rgb_to_ansi, bold_and_italicize_text, hex_to_rgb +from debugger_backend.debug_arg_parser import is_text, is_error + +def debug(*args, mode:str='debug', override_max_chars:bool=False): + frame = inspect.currentframe().f_back + code = frame.f_code + line_no = frame.f_lineno + calling_function_name = frame.f_code.co_name + calling_file_name = os.path.basename(code.co_filename) + if calling_function_name == "": + calling_function_name = calling_file_name + # Retrieve the file path of the calling function + file_path = os.path.abspath(code.co_filename) + # print(f"FILE PATH: {file_path}") + t_color, t_is_on, t_emoji = Debugleton().find_file_info(file_path) + # print(f"DEBUGGING: {t_color}, {t_is_on}") + max_chars = 3000 + + with open(code.co_filename, 'r', encoding='utf-8') as f: + lines = f.readlines() + line = lines[line_no - 1] + leading_spaces = len(line) - len(line.lstrip(' ')) + indent = leading_spaces // 4 + arg_names = re.findall(r'debug\((.*?)\)', line)[0].split(', ') + for arg_name, arg_value in zip(arg_names, args): + indent_str = ' |\t' * indent + if indent > 0: + indent_str = indent_str[:-3] + ' |-- ' + arg_is_error = is_error(arg_value, arg_name) + arg_is_text = is_text(arg_value, arg_name) + if arg_is_error: + t_color = "#FE3F3F" + t_emoji = "❌" + t_is_on = True + + arg_len = len(str(arg_value)) + if arg_len > max_chars and not override_max_chars: + if not arg_is_text: arg_value = str(arg_value) + arg_value = arg_value[:int(max_chars/2)] + "...\n..." + arg_value[arg_len-int(max_chars/2):] + + function_print_str = calling_function_name if 'self' not in frame.f_locals else f'{frame.f_locals["self"].__class__.__name__}.{calling_function_name}' + # color = COLORS.get(function_print_str, white) + color = hex_to_rgb(t_color) + if arg_is_text: + print_str = f"{t_emoji}{rgb_to_ansi(color)}{indent_str}[{function_print_str}] : {bold_and_italicize_text(arg_value)}\033[0m" + else: + print_str = f"{t_emoji}{rgb_to_ansi(color)}{indent_str}[{function_print_str}] : {arg_name} = {arg_value}\033[0m" + if t_is_on: log_config.debug_custom(print_str, mode) + +# Assign the function to the module's __call__ attribute +import sys +sys.modules[__name__] = debug diff --git a/debugger/debugger_backend/DEFAULTS.py b/debugger/debugger_backend/DEFAULTS.py new file mode 100644 index 00000000..129962ab --- /dev/null +++ b/debugger/debugger_backend/DEFAULTS.py @@ -0,0 +1,7 @@ +import os +TOGGLE_FILE = os.path.join(os.path.dirname(__file__), 'debug_toggles.json') +DEFAULT_COLOR = '#ffffff' +DEFAULT_TOGGLED = False +DEFAULT_SET_MANUALLY = False +DEFAULT_EMOJI = '⚫' +ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) \ No newline at end of file diff --git a/debugger/debugger_backend/DebugFile.py b/debugger/debugger_backend/DebugFile.py new file mode 100644 index 00000000..870566cf --- /dev/null +++ b/debugger/debugger_backend/DebugFile.py @@ -0,0 +1,39 @@ +import os +from debugger_backend.File import File +from debugger_backend.DEFAULTS import DEFAULT_COLOR, DEFAULT_TOGGLED, DEFAULT_SET_MANUALLY, DEFAULT_EMOJI + +class DebugFile(File): + def __init__(self, filename, path, color=DEFAULT_COLOR, is_toggled=DEFAULT_TOGGLED, + set_manually=DEFAULT_SET_MANUALLY, emoji=DEFAULT_EMOJI, directory=None): + super().__init__(filename, path) + self.color = color + self.is_toggled = is_toggled + self.set_manually = set_manually + self.emoji = emoji + self.directory = directory # Reference to parent directory + + def to_dict(self): + """ + Converts the DebugFile object to a dictionary format. + """ + return { + "name": os.path.basename(self.filename), + "color": self.color, + "is_toggled": self.is_toggled, + "set_manually": self.set_manually, + "emoji": self.emoji + } + + @classmethod + def from_dict(cls, file_dict, directory): + """ + Creates a DebugFile object from a dictionary loaded from JSON. + """ + filename = os.path.join(directory.path, file_dict["name"]) + return cls( + filename=filename, + color=file_dict.get("color", DEFAULT_COLOR), + is_toggled=file_dict.get("is_toggled", DEFAULT_TOGGLED), + set_manually=file_dict.get("set_manually", DEFAULT_SET_MANUALLY), + directory=directory + ) diff --git a/debugger/debugger_backend/Debugleton.py b/debugger/debugger_backend/Debugleton.py new file mode 100644 index 00000000..7c97ae2e --- /dev/null +++ b/debugger/debugger_backend/Debugleton.py @@ -0,0 +1,88 @@ +# Haik: sorry bout the filename + +import threading +from debugger_backend.project_scanner import update_debug_toggles +from debugger_backend.Directory import Directory +from debugger_backend.DebugFile import DebugFile +from debugger_backend.DEFAULTS import DEFAULT_COLOR, DEFAULT_TOGGLED, DEFAULT_EMOJI +import os +import time + +NEEDS_RESYNC_FILE = os.path.join(os.path.dirname(__file__), 'needs_resync.txt') + +class Debugleton: + _instance = None + _lock = threading.Lock() # Lock for thread-safe singleton creation + sync_lock: threading.Lock + + def __new__(cls, *args, **kwargs): + # Double-checked locking for thread-safe singleton creation + if cls._instance is None: + with cls._lock: + if cls._instance is None: + cls._instance = super(Debugleton, cls).__new__(cls) + print("\033[38;5;120m\n---------------------------------\033[0m") + print("\033[38;5;120m|\tDEBUGLETON INIT \t|\033[0m") + cls._instance.dir = None + print("\033[38;5;120m|\tScanning Project...\t|\033[0m") + cls._instance.sync_lock = threading.Lock() + cls._instance.sync_lock.acquire(blocking=False) + cls._instance.sync_to_saved(is_first_sync=True) + cls._instance.sync_lock.release() + print("\033[38;5;120m|\t...Project Scanned\t|\033[0m") + print("\033[38;5;120m|\tDEBUGLETON INIT DONE\t|\033[0m") + print("\033[38;5;120m---------------------------------\n\033[0m") + # else: print("DEBUGLETON Already initialized INNER") + # else: print("DEBUGLETON Already initialized OUTER") + return cls._instance + + + def sync_to_saved(self, is_first_sync=False): + # print(f"[sync_to_saved]: START") + if not is_first_sync: self.sync_lock.acquire() + # print(f"[sync_to_saved]: Acquired sync lock") + self.dir = update_debug_toggles(save_to_file=False) + # print(f"Synced to saved dir: {self.dir}") + self.abspaths, self.instances = self.dir.get_ordered_abspaths_and_instances() + # print(f"Synced to abspaths: {self.abspaths}") + with open(NEEDS_RESYNC_FILE, 'w') as f: + f.write('0') + if not is_first_sync: self.sync_lock.release() + # print(f"[sync_to_saved]: Released sync lock") + # print(f"[sync_to_saved]: END") + + def needs_resync(self): + # print(f"[needs_resync]: START") + num_tries = 0 + while self.is_syncing(): + print(f"Waiting for Debugleton to sync... ({num_tries})") + time.sleep(5) + num_tries += 1 + if num_tries > 10: + print(f""" + NOTE: Debugleton is taking a long time, there's one scenario where it breaks: + \n\t- If running in docker, and you deleted one of the root dirs in the volumes of docker compose, + \n\t then the debugger will not be able to find the project and will get stuck in an infinite loop. + \n\t- In this case, you can restart the docker container and delete the volume in the docker compose file and it will resync. + """) + with open(NEEDS_RESYNC_FILE, 'r') as f: + does_need_resync = True if f.read().strip() == '1' else False + # if does_need_resync: print("Resyncing Debugleton...") + # print(f"[needs_resync]: END") + return does_need_resync + + def is_syncing(self): + return self.sync_lock.locked() + + def find_file_info(self, filepath: str): + filepath = filepath.lower() + # print(f"Finding file info for {filepath}") + if self.needs_resync(): + self.sync_to_saved() + try: + filepath_id = self.abspaths.index(filepath) + match = self.instances[filepath_id] + return match.color, match.is_toggled, match.emoji + except ValueError: + print(f"Filepath not found: {filepath}") + return DEFAULT_COLOR, DEFAULT_TOGGLED, DEFAULT_EMOJI \ No newline at end of file diff --git a/debugger/debugger_backend/Directory.py b/debugger/debugger_backend/Directory.py new file mode 100644 index 00000000..412e0ab3 --- /dev/null +++ b/debugger/debugger_backend/Directory.py @@ -0,0 +1,200 @@ +import os +import json +import colorsys +from pathlib import Path +from debugger_backend.DebugFile import DebugFile +from debugger_backend.DEFAULTS import DEFAULT_COLOR, DEFAULT_TOGGLED, DEFAULT_SET_MANUALLY, DEFAULT_EMOJI +from debugger_backend.path_mngr import get_abspath, get_root_rel_path + +class Directory: + def __init__(self, path, color=DEFAULT_COLOR, is_toggled=DEFAULT_TOGGLED, + set_manually=DEFAULT_SET_MANUALLY, emoji=DEFAULT_EMOJI): + self.path = path + # print(f"Directory init: {self.path}") + self.children = [] # Can contain DebugFile or other Directory objects + self.color = color + self.is_toggled = is_toggled + self.set_manually = set_manually + self.emoji = emoji + + def __str__(self): + return f"Directory: {self.path}\nNum Children: {len(self.children)}\nColor: {self.color}\nToggled: {self.is_toggled}\nSet Manually: {self.set_manually}" + + def get_abspath(self): + return get_abspath(self.path) + + def add_child(self, child): + """ + Adds a child to the directory (either a DebugFile or another Directory). + """ + self.children.append(child) + + def get_ordered_abspaths_and_instances(self): + # print("[get_ordered_abspaths]: START") + curr_file_path = os.path.abspath(__file__) + root_dir = os.path.dirname(os.path.dirname(os.path.dirname(curr_file_path))) + # print(f"[get_ordered_abspaths]: Curr path: {curr_file_path}") + # print(f"[get_ordered_abspaths]: Dir path: {root_dir}") + def construct_ordered_abspaths(dir: Directory, ordered_abspaths: list): + dir_path = dir.path + full_path = os.path.join(root_dir, dir_path) + ordered_abspaths.append({"abspath": full_path, "instance": dir}) + # print(f"\t[construct_ordered_abspaths]: Full path: {full_path}") + for child in dir.children: + child_abspath = os.path.join(root_dir, child.path).lower() + if os.path.isdir(child_abspath): + construct_ordered_abspaths(child, ordered_abspaths) + elif os.path.isfile(child_abspath): + # print(f"\t[construct_ordered_abspaths]: Child is file: {child_abspath}") + ordered_abspaths.append({"abspath": child_abspath, "instance": child}) + else: + print(f"\033[38;5;120mEntry is non existent: {child_abspath}\033[0m") + # print(f"\t[construct_ordered_abspaths]: Finished for dir: {full_path}") + # print(f"\t[construct_ordered_abspaths]: RETURNING FROM DIR: {full_path}") + return ordered_abspaths + ordered_abspaths_and_instances = construct_ordered_abspaths(self, []) + # print("[get_ordered_abspaths]: Finished getting ordered abspaths and instances") + # for abspath_and_instance in ordered_abspaths_and_instances: + # abspath = abspath_and_instance["abspath"] + # print(f"\t[get_ordered_abspaths]: Abspath: {abspath}") + ordered_abspaths = [abspath_and_instance["abspath"] for abspath_and_instance in ordered_abspaths_and_instances] + ordered_instances = [abspath_and_instance["instance"] for abspath_and_instance in ordered_abspaths_and_instances] + return ordered_abspaths, ordered_instances + + + def build_structure(self): + print("[build_structure]: START") + root_dir = self.get_abspath() + # print(f"[build_structure]: Root dir: {root_dir}") + excluded_dirs = [".venv", "debugger", "node_modules", ".git", "__pycache__"] + project_structure = [] + + def construct_project_structure(dir_path: str, parent_dir: Directory): + # print(f"[build_structure]: Scanning dir: {dir_path}") + with os.scandir(dir_path) as it: + for entry in it: + # print(f"[build_structure]: Entry: {entry.path}") + if any(excluded_dir in entry.path for excluded_dir in excluded_dirs): + # print(f"[build_structure]: Excluding {entry.path}") + continue + root_rel_path = get_root_rel_path(entry.path) + if entry.is_dir(): + subdir = Directory(root_rel_path) + construct_project_structure(entry.path, subdir) + parent_dir.add_child(subdir) + elif entry.is_file(): + debug_file = DebugFile(filename=entry.name, path=root_rel_path) + if debug_file.calls_debug_function(): + parent_dir.add_child(debug_file) + else: + raise Exception(f"[build_structure]: Entry is not dir or file: {entry.path}") + + construct_project_structure(root_dir, self) + # [print(f"[build_structure]: {file}") for file in project_structure] + # print(f"[build_structure]: END") + return + + def to_dict(self): + """ + Converts the Directory object to a dictionary format, recursively. + """ + return { + "name": os.path.basename(self.path), + "color": self.color, + "is_toggled": self.is_toggled, + "set_manually": self.set_manually, + "emoji": self.emoji, + "children": [child.to_dict() if isinstance(child, DebugFile) else child.to_dict() for child in self.children] + } + + def prune_empty(self): + # Recursively prune empty directories + # Base case) if the current directory has no children, return + # Recursive case) for each of the directories in the current directory, call prune_empty + # then remove the directory from the children of the current directory if it has no children + for child in self.children[:]: + if isinstance(child, Directory): + # Recursively prune empty subdirectories + child.prune_empty() + # If the subdirectory is empty after pruning, remove it + if len(child.children) == 0: + self.children.remove(child) + + def propagate_toggled_state(self): + """ + Propagates the toggled state down the hierarchy. + """ + for child in self.children: + if isinstance(child, DebugFile) and not child.set_manually: + child.is_toggled = self.is_toggled + elif isinstance(child, Directory) and not child.set_manually: + child.is_toggled = self.is_toggled + child.propagate_toggled_state() + + def propagate_color(self, parent_color=DEFAULT_COLOR): + """ + Propagates the color from parent to children. + """ + if self.color == DEFAULT_COLOR: + self.color = lighten_color(parent_color) + for child in self.children: + if isinstance(child, DebugFile) and child.color == DEFAULT_COLOR: + child.color = lighten_color(self.color) + elif isinstance(child, Directory): + child.propagate_color(self.color) + + def load_from_json(self, json_data): + """ + Loads a directory structure from a JSON file into this Directory instance. + """ + for item in json_data: + if 'children' in item: + subdir = Directory( + path=os.path.join(self.path, item['name']), + color=item.get('color', DEFAULT_COLOR), + is_toggled=item.get('is_toggled', DEFAULT_TOGGLED), + set_manually=item.get('set_manually', DEFAULT_SET_MANUALLY), + emoji=item.get('emoji', DEFAULT_EMOJI) + ) + + subdir.load_from_json(item['children']) + self.add_child(subdir) + else: + # debug_file = DebugFile.from_dict(item, self) + debug_file = DebugFile( + filename=item['name'], + path=os.path.join(self.path, item['name']), + color=item.get('color', DEFAULT_COLOR), + is_toggled=item.get('is_toggled', DEFAULT_TOGGLED), + set_manually=item.get('set_manually', DEFAULT_SET_MANUALLY), + emoji=item.get('emoji', DEFAULT_EMOJI), + directory=self + ) + self.add_child(debug_file) + + def reset_colors(self): + """ + Resets the color of all DebugFile and Directory objects in this directory structure to the default color. + """ + self.color = DEFAULT_COLOR + for child in self.children: + if isinstance(child, DebugFile): + child.color = DEFAULT_COLOR + elif isinstance(child, Directory): + child.reset_colors() + + +def lighten_color(color, amount=0.1): + """ + Lightens the given color by the specified amount. + """ + try: + color = color.lstrip('#') + r, g, b = int(color[:2], 16), int(color[2:4], 16), int(color[4:6], 16) + h, l, s = colorsys.rgb_to_hls(r / 255.0, g / 255.0, b / 255.0) + l = min(1, l + amount) + r, g, b = colorsys.hls_to_rgb(h, l, s) + return '#{:02x}{:02x}{:02x}'.format(int(r * 255), int(g * 255), int(b * 255)) + except Exception as e: + print(f"Error lightening color {color}: {e}") + return color diff --git a/debugger/debugger_backend/File.py b/debugger/debugger_backend/File.py new file mode 100644 index 00000000..f785a9eb --- /dev/null +++ b/debugger/debugger_backend/File.py @@ -0,0 +1,29 @@ +import os +from debugger_backend.path_mngr import get_abspath + +class File: + def __init__(self, filename, path): + self.filename = filename + self.path = path + + def get_abspath(self): + return get_abspath(self.path) + + def calls_debug_function(self): + """ + Checks if the file calls the debug function. + """ + full_path = self.get_abspath() + + if not full_path.endswith('.py') or full_path.endswith('.pyc'): + result = False + else: + try: + with open(full_path, 'r', encoding='utf-8') as file: + content = file.read() + result = 'debug(' in content + except (UnicodeDecodeError, FileNotFoundError) as e: + print(f"Error reading file {full_path}") + result = False + # print(f"??calls_debug_function?? {result}") + return result \ No newline at end of file diff --git a/debugger/debugger_backend/__init__.py b/debugger/debugger_backend/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/debugger/debugger_backend/color_adjuster.py b/debugger/debugger_backend/color_adjuster.py new file mode 100644 index 00000000..de7abf22 --- /dev/null +++ b/debugger/debugger_backend/color_adjuster.py @@ -0,0 +1,21 @@ +import colorsys + +def adjust_brightness(color, brightness_factor): + hls = colorsys.rgb_to_hls(*[x/255.0 for x in color]) # Convert RGB to HLS + hls = (hls[0], max(0, min(1, hls[1] + brightness_factor)), hls[2]) # Adjust lightness + rgb = [int(x*255.0) for x in colorsys.hls_to_rgb(*hls)] # Convert back to RGB + return rgb + + +def rgb_to_ansi(rgb): + return '\033[38;2;{};{};{}m'.format(*rgb) + +def bold_and_italicize_text(text): + return f"\033[1m\033[3m{text}\033[0m" + +def hex_to_rgb(hex_code): + # Remove the '#' symbol if it exists + hex_code = hex_code.lstrip('#') + + # Convert the hex code to RGB + return tuple(int(hex_code[i:i+2], 16) for i in (0, 2, 4)) \ No newline at end of file diff --git a/debugger/debugger_backend/debug_arg_parser.py b/debugger/debugger_backend/debug_arg_parser.py new file mode 100644 index 00000000..fcabb1f0 --- /dev/null +++ b/debugger/debugger_backend/debug_arg_parser.py @@ -0,0 +1,21 @@ + +def is_fstring(arg_name): + if not isinstance(arg_name, str): + return False + # print(f"arg_name: {arg_name}") + fstring_start_values = ["f'", "f\""] + num_start_matches = sum(arg_name.startswith(start_value) for start_value in fstring_start_values) + conditions = [num_start_matches == 1] + return all(conditions) + +def is_text(arg_value, arg_name): + arg_is_text = isinstance(arg_value, str) and len(arg_name) > 2 and arg_name[1:len(arg_name)-1] == arg_value and not arg_name.endswith(")") + if not arg_is_text: + arg_is_text = is_fstring(arg_name) + # print(f"is_text: {arg_is_text}") + return arg_is_text + +def is_error(arg_value, arg_name): + arg_is_error = isinstance(arg_value, Exception) or "error" in str(arg_value).lower() or "error" in str(arg_name).lower() + return arg_is_error + diff --git a/debugger/debugger_backend/debugger_server.py b/debugger/debugger_backend/debugger_server.py new file mode 100644 index 00000000..12277dc5 --- /dev/null +++ b/debugger/debugger_backend/debugger_server.py @@ -0,0 +1,49 @@ +from flask import Flask, request, jsonify, Response +from flask_cors import CORS +from debugger_backend.project_scanner import update_debug_toggles, dir_to_output_format +import json +import os +NEEDS_RESYNC_FILE = os.path.join(os.path.dirname(__file__), 'needs_resync.txt') +DEBUG_TOGGLE_FILE = os.path.join(os.path.dirname(__file__), 'debug_toggles.json') +app = Flask(__name__) +CORS(app) + +@app.route('/pull_structure', methods=['GET']) +def api_get_structure(): + print("GET /get_structure") + scanned_dir=update_debug_toggles(save_to_file=True) + # print("\n\nPS scanned_dir: ", scanned_dir) + output = dir_to_output_format(scanned_dir) + output = json.dumps(output, ensure_ascii=False, indent=4) + # print("output: ", output) + return Response(output, mimetype='application/json') + +@app.route('/push_structure', methods=['POST']) +def api_push_structure(): + print("POST /push_structure") + data = request.get_json() + data = data['projectStructure'] + # print(data) + with open(DEBUG_TOGGLE_FILE, 'w', encoding='utf-8') as file: + json.dump(data, file, indent=4) + with open(NEEDS_RESYNC_FILE, 'w') as f: + f.write('1') + return jsonify({"status": "success"}) + +@app.route('/reset_color', methods=['POST']) +def api_reset_color(): + print("POST /reset_color") + scanned_dir=update_debug_toggles(save_to_file=False) + scanned_dir.reset_colors() + # print("RS: scanned_dir: ", scanned_dir) + output = dir_to_output_format(scanned_dir) + output = json.dumps(output, ensure_ascii=False, indent=4) + # print("RS: output: ", output) + return Response(output, mimetype='application/json') + + + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=6969, debug=False) + + diff --git a/debugger/debugger_backend/log_config.py b/debugger/debugger_backend/log_config.py new file mode 100644 index 00000000..19a3b14a --- /dev/null +++ b/debugger/debugger_backend/log_config.py @@ -0,0 +1,50 @@ +import logging +from debugger_backend.log_mode import get_log_mode, set_log_mode + +class LogConfig: + _instance = None + MODES = { + "all": 1, + "debug": 10, + "test": 20, + } + + def __new__(cls): + if cls._instance is None: + cls._instance = super(LogConfig, cls).__new__(cls) + cls._instance._initialize_logger() + return cls._instance + + def _initialize_logger(self): + for name, level in self.MODES.items(): + logging.addLevelName(level, name.upper()) + self.logger = logging.getLogger('custom_logger') + self.logger.propagate = False # Prevent log propagation + handler = logging.StreamHandler() + formatter = logging.Formatter('%(message)s') + handler.setFormatter(formatter) + + # Remove existing handlers to prevent duplicate logging + if self.logger.hasHandlers(): + self.logger.handlers.clear() + + self.logger.addHandler(handler) + self.set_debug_mode(get_log_mode()) + + def debug_custom(self, message, mode = None, *args, **kwargs): + if mode is None: + mode = get_log_mode() + if self.logger.isEnabledFor(self.MODES[mode]): + self.logger._log(self.MODES[mode], message, args, **kwargs) + + def set_debug_mode(self, mode): + current_mode = get_log_mode() + # print(f"Setting debug mode from {current_mode} -> to {mode}") + if mode not in self.MODES: raise ValueError(f"Invalid mode: {mode}") + set_log_mode(mode) + self.logger.setLevel(self.MODES[mode]) + + def get_debug_mode(self): + return get_log_mode() + +log_config = LogConfig() diff --git a/debugger/debugger_backend/log_mode.py b/debugger/debugger_backend/log_mode.py new file mode 100644 index 00000000..e1051782 --- /dev/null +++ b/debugger/debugger_backend/log_mode.py @@ -0,0 +1,13 @@ +import os + +# LOG_MODE_FILE = 'debugger/log_mode.txt' +LOG_MODE_FILE = os.path.join(os.path.dirname(__file__), 'log_mode.txt') +def set_log_mode(mode): + with open(LOG_MODE_FILE, 'w') as f: + f.write(mode) + +def get_log_mode(): + if os.path.exists(LOG_MODE_FILE): + with open(LOG_MODE_FILE, 'r') as f: + return f.read().strip() + return 'all' # Default to 'all' if the file doesn't exist diff --git a/debugger/debugger_backend/log_mode.txt b/debugger/debugger_backend/log_mode.txt new file mode 100644 index 00000000..baa60444 --- /dev/null +++ b/debugger/debugger_backend/log_mode.txt @@ -0,0 +1 @@ +all \ No newline at end of file diff --git a/debugger/debugger_backend/path_mngr.py b/debugger/debugger_backend/path_mngr.py new file mode 100644 index 00000000..0b47d49a --- /dev/null +++ b/debugger/debugger_backend/path_mngr.py @@ -0,0 +1,12 @@ +import os +from debugger_backend.DEFAULTS import ROOT_DIR + +def get_abspath(path: str): + return os.path.join(ROOT_DIR, path) + +def get_root_rel_path(path: str): + assert path.startswith(ROOT_DIR) + path = path[len(ROOT_DIR):] + while path.startswith(os.sep): + path = path[1:] + return path diff --git a/debugger/debugger_backend/project_scanner.py b/debugger/debugger_backend/project_scanner.py new file mode 100644 index 00000000..81a93df6 --- /dev/null +++ b/debugger/debugger_backend/project_scanner.py @@ -0,0 +1,149 @@ +import os +import json +import colorsys +from typing import Union +from debugger_backend.Directory import Directory +from debugger_backend.DEFAULTS import DEFAULT_COLOR, DEFAULT_TOGGLED, DEFAULT_SET_MANUALLY, TOGGLE_FILE, DEFAULT_EMOJI, ROOT_DIR +from debugger_backend.DebugFile import DebugFile +from collections import OrderedDict + +def merge_directories(json_dir: Directory, scanned_dir: Directory): + """ + Merges two Directory instances: one loaded from JSON (json_dir) and one built from scanning (scanned_dir). + The values from json_dir take precedence where attributes overlap. + It matches based on full directory and file structure, not just file names. + """ + # print(f"Merging JSON_DIR: {json_dir.path}\n with SCAN_DIR: {scanned_dir.path}") + json_abspaths, json_instances = json_dir.get_ordered_abspaths_and_instances() + # print(f"json_abspaths: {json_abspaths}") + scanned_abspaths, scanned_instances = scanned_dir.get_ordered_abspaths_and_instances() + # print(f"scanned_abspaths: {scanned_abspaths}") + + def find_matching_in_structure(scanned_child: Union[DebugFile, Directory], json_dir: Directory): + assert json_dir in json_instances, f"JSON_DIR: {json_dir.path} not in json_instances" + assert scanned_child in scanned_instances, f"SCANNED_CHILD: {scanned_child.path} not in scanned_instances" + scanned_id = scanned_instances.index(scanned_child) + scanned_abspath = scanned_abspaths[scanned_id] + json_instance = None + try: + json_id = json_abspaths.index(scanned_abspath) + json_instance = json_instances[json_id] + # print(f"Match found: {scanned_child.path} == {json_instance.path}") + except ValueError: + # print(f"SCANNED_ABSPATH: {scanned_abspath} not in JSON_ABSPATHS") + pass + return json_instance + + def construct_merged_dir(json_dir: Directory, scanned_dir: Directory): + for scanned_child in scanned_dir.children: + # Use the new recursive function to find the corresponding child in the JSON directory structure + matching_json_child = find_matching_in_structure(scanned_child, json_dir) + + if isinstance(scanned_child, DebugFile) and matching_json_child: + # Merge attributes from the JSON-loaded structure + scanned_child.color = matching_json_child.color + scanned_child.is_toggled = matching_json_child.is_toggled + scanned_child.set_manually = matching_json_child.set_manually + scanned_child.emoji = matching_json_child.emoji + + elif isinstance(scanned_child, Directory) and matching_json_child: + # Merge directory attributes + scanned_child.color = matching_json_child.color + scanned_child.is_toggled = matching_json_child.is_toggled + scanned_child.set_manually = matching_json_child.set_manually + scanned_child.emoji = matching_json_child.emoji + + # Recursively merge the subdirectories + construct_merged_dir(matching_json_child, scanned_child) + else: + scanned_child.color = DEFAULT_COLOR + scanned_child.is_toggled = DEFAULT_TOGGLED + scanned_child.set_manually = DEFAULT_SET_MANUALLY + scanned_child.emoji = DEFAULT_EMOJI + + construct_merged_dir(json_dir, scanned_dir) + + +def update_debug_toggles(save_to_file=True) -> Directory: + # print(f"[update_debug_toggles]: START") + json_loaded_dir = None + if os.path.exists(TOGGLE_FILE): + with open(TOGGLE_FILE, 'r', encoding='utf-8') as file: + try: + json_data = json.load(file) + json_loaded_dir = Directory("") + json_loaded_dir = Directory(path="", + color=json_data[0].get('color', DEFAULT_COLOR), + is_toggled=json_data[0].get('is_toggled', DEFAULT_TOGGLED), + set_manually=json_data[0].get('set_manually', DEFAULT_SET_MANUALLY), + emoji=json_data[0].get('emoji', DEFAULT_EMOJI) + ) + # print(f"Root: {json_loaded_dir}") + # print("Json Children 1:") + # [print(child.path) for child in json_loaded_dir.children] + + json_loaded_dir.load_from_json(json_data[0]['children']) # Assuming the root is in json_data[0] + # print("Json Children 2:") + # [print(child.path) for child in json_loaded_dir.children] + + except json.JSONDecodeError: + ValueError("Error: JSON file could not be decoded.") + else: + print("No JSON file found") + # 1. Create a directory structure from the filesystem scan + # print("Scanning directory...") + scanned_dir = Directory(path="", + color=json_loaded_dir.color if json_loaded_dir else DEFAULT_COLOR, + is_toggled=json_loaded_dir.is_toggled if json_loaded_dir else DEFAULT_TOGGLED, + set_manually=json_loaded_dir.set_manually if json_loaded_dir else DEFAULT_SET_MANUALLY, + emoji=json_loaded_dir.emoji if json_loaded_dir else DEFAULT_EMOJI + ) + # print(f"\n\nNum Children 1: {len(scanned_dir.children)}") + # [print(child.path) for child in scanned_dir.children] + scanned_dir.build_structure() + # print(f"\n\nNum Children 2: {len(scanned_dir.children)}") + # [print(child.path) for child in scanned_dir.children] + scanned_dir.prune_empty() + # print(f"\n\nNum Children 3: {len(scanned_dir.children)}") + # [print(child.path) for child in scanned_dir.children] + + # print("1.1 Merged Dir First Child: ", scanned_dir.children[0]) + # 4. Propagate the toggled state and color through the merged structure + scanned_dir.propagate_toggled_state() + # print(f"\n\nNum Children 4: {len(scanned_dir.children)}") + # [print(child.path) for child in scanned_dir.children] + + # 3. Merge the two directory structures + if json_loaded_dir: + merge_directories(json_loaded_dir, scanned_dir) + + # print(f"\n\nNum Children 5: {len(scanned_dir.children)}") + scanned_dir.propagate_color() + output = dir_to_output_format(scanned_dir) + # print(f"\n\nNum Children 6: {len(scanned_dir.children)}") + + + # 5. Write the updated structure back to the JSON file + if save_to_file: + with open(TOGGLE_FILE, 'w', encoding='utf-8') as file: + json.dump(output, file, ensure_ascii=False, indent=4) + # print(f"[update_debug_toggles]: END") + return scanned_dir + +def dir_to_output_format(input_dir): + root_node = { + "name": "root", + "color": input_dir.color, # Use input_dir's color + "is_toggled": input_dir.is_toggled, # Use input_dir's toggled state + "set_manually": input_dir.set_manually, # Use input_dir's set_manually + "emoji": input_dir.emoji, # Use input_dir's emoji + "children": input_dir.to_dict()["children"] + } + return [ordered(root_node)] + +def ordered(obj): + if isinstance(obj, dict): + return OrderedDict((k, ordered(v)) for k, v in obj.items()) + if isinstance(obj, list): + return [ordered(x) for x in obj] + return obj diff --git a/debugger/debugger_backend/requirements.txt b/debugger/debugger_backend/requirements.txt new file mode 100644 index 00000000..cf8e4eb5 --- /dev/null +++ b/debugger/debugger_backend/requirements.txt @@ -0,0 +1,3 @@ +Flask==2.0.1 +Flask-Cors==4.0.1 +Werkzeug==2.0.3 \ No newline at end of file diff --git a/debugger/debugger_gui/.gitignore b/debugger/debugger_gui/.gitignore new file mode 100644 index 00000000..4d29575d --- /dev/null +++ b/debugger/debugger_gui/.gitignore @@ -0,0 +1,23 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/debugger/debugger_gui/README.md b/debugger/debugger_gui/README.md new file mode 100644 index 00000000..58beeacc --- /dev/null +++ b/debugger/debugger_gui/README.md @@ -0,0 +1,70 @@ +# Getting Started with Create React App + +This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). + +## Available Scripts + +In the project directory, you can run: + +### `npm start` + +Runs the app in the development mode.\ +Open [http://localhost:3000](http://localhost:3000) to view it in your browser. + +The page will reload when you make changes.\ +You may also see any lint errors in the console. + +### `npm test` + +Launches the test runner in the interactive watch mode.\ +See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. + +### `npm run build` + +Builds the app for production to the `build` folder.\ +It correctly bundles React in production mode and optimizes the build for the best performance. + +The build is minified and the filenames include the hashes.\ +Your app is ready to be deployed! + +See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. + +### `npm run eject` + +**Note: this is a one-way operation. Once you `eject`, you can't go back!** + +If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. + +Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. + +You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. + +## Learn More + +You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). + +To learn React, check out the [React documentation](https://reactjs.org/). + +### Code Splitting + +This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) + +### Analyzing the Bundle Size + +This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) + +### Making a Progressive Web App + +This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) + +### Advanced Configuration + +This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) + +### Deployment + +This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) + +### `npm run build` fails to minify + +This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) diff --git a/debugger/debugger_gui/package-lock.json b/debugger/debugger_gui/package-lock.json new file mode 100644 index 00000000..07e6b39d --- /dev/null +++ b/debugger/debugger_gui/package-lock.json @@ -0,0 +1,19530 @@ +{ + "name": "debugger-gui", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "debugger-gui", + "version": "0.1.0", + "dependencies": { + "@emotion/react": "^11.11.4", + "@emotion/styled": "^11.11.5", + "@mui/icons-material": "^5.16.0", + "@mui/material": "^5.16.0", + "@testing-library/jest-dom": "^5.17.0", + "@testing-library/react": "^13.4.0", + "@testing-library/user-event": "^13.5.0", + "axios": "^1.7.2", + "emoji-mart": "^5.6.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-scripts": "5.0.1", + "web-vitals": "^2.1.4" + }, + "devDependencies": { + "cross-env": "^7.0.3" + } + }, + "node_modules/@adobe/css-tools": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.0.tgz", + "integrity": "sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.0.tgz", + "integrity": "sha512-INCKxTtbXtcNbUZ3YXutwMpEleqttcswhAdee7dhuoVrD2cnuc3PqtERBtxkX5nziX9vnBL8WXmSGwv8CuPV6g==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.0.tgz", + "integrity": "sha512-qETICbZSLe7uXv9VE8T/RWOdIE5qqyTucOt4zLYMafj2MRO271VGgLd4RACJMeBO37UPWhXiKMBk7YlJ0fOzQA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", + "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.0", + "@babel/generator": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.0", + "@babel/parser": "^7.26.0", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.26.0", + "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/core/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==", + "license": "MIT" + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/eslint-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.25.9.tgz", + "integrity": "sha512-5UXfgpK0j0Xr/xIdgdLEhOFxaDZ0bRPWJJchRpqOSur/3rZoPbqqki5mm0p4NE2cs28krBEiSM2MB7//afRSQQ==", + "license": "MIT", + "dependencies": { + "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || >=14.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0", + "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/@babel/eslint-parser/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "license": "Apache-2.0", + "engines": { + "node": ">=10" + } + }, + "node_modules/@babel/eslint-parser/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.0.tgz", + "integrity": "sha512-/AIkAmInnWwgEAJGQr9vY0c66Mj6kjkE2ZPB1PurTRaRAh3U+J45sAQMjQDJdh4WbR3l0x5xkimXBKyBXXAu2w==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.26.0", + "@babel/types": "^7.26.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.9.tgz", + "integrity": "sha512-C47lC7LIDCnz0h4vai/tpNOI95tCd5ZT3iBt/DBH5lXKHZsyNQv18yf1wIIg2ntiQNgmAvA+DgZ82iW8Qdym8g==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", + "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz", + "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/traverse": "^7.25.9", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.9.tgz", + "integrity": "sha512-ORPNZ3h6ZRkOyAa/SaHU+XsLZr0UQzRwuDQ0cczIA17nAzZ+85G5cVkOJIj7QavLZGSe8QXUmNFxSZzjcZF9bw==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "regexpu-core": "^6.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", + "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", + "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", + "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", + "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", + "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-wrap-function": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz", + "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz", + "integrity": "sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", + "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", + "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", + "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.26.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.1.tgz", + "integrity": "sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.26.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", + "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz", + "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz", + "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz", + "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.25.9.tgz", + "integrity": "sha512-smkNLL/O1ezy9Nhy4CNosc4Va+1wo5w4gzSZeLe6y6dM4mmHfYOCPolXQPHQxonZCF+ZyebxN9vqOolkYrSn5g==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-syntax-decorators": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead.", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-numeric-separator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-numeric-separator instead.", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", + "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead.", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.25.9.tgz", + "integrity": "sha512-ryzI0McXUPJnRCvMo4lumIKZUzhYUO/ScI+Mz4YVaTLt04DHNSjEUjKVvbzQjZFLuod/cYEc07mJWhzl6v4DPg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-flow": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.26.0.tgz", + "integrity": "sha512-B+O2DnPc0iG+YXFqOxv2WNuNU97ToWjOomUQ78DouOENWUaM5sVrmet9mcomUGQFwpJd//gvUagXBSdzO1fRKg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", + "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", + "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", + "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", + "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz", + "integrity": "sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-remap-async-to-generator": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", + "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-remap-async-to-generator": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz", + "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz", + "integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz", + "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", + "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", + "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/traverse": "^7.25.9", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", + "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/template": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", + "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz", + "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz", + "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz", + "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.9.tgz", + "integrity": "sha512-KRhdhlVk2nObA5AYa7QMgTMTVJdfHprfpAk4DjZVtllqRg9qarilstTKEhpVjyt+Npi8ThRyiV8176Am3CodPA==", + "license": "MIT", + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz", + "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-flow-strip-types": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.25.9.tgz", + "integrity": "sha512-/VVukELzPDdci7UUsWQaSkhgnjIWXnIyRpM02ldxaVoFK96c41So8JcKT3m0gYjyv7j5FNPGS5vfELrWalkbDA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-syntax-flow": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz", + "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", + "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz", + "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", + "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz", + "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", + "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz", + "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz", + "integrity": "sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-simple-access": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", + "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", + "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz", + "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz", + "integrity": "sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz", + "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz", + "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==", + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", + "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz", + "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", + "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz", + "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz", + "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", + "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-constant-elements": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.25.9.tgz", + "integrity": "sha512-Ncw2JFsJVuvfRsa2lSHiC55kETQVLSnsYGQ1JDDwkUeWGTL/8Tom8aLTnlqgoeuopWrbbGndrc9AlLYrIosrow==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.9.tgz", + "integrity": "sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz", + "integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-syntax-jsx": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.9.tgz", + "integrity": "sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw==", + "license": "MIT", + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-pure-annotations": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.9.tgz", + "integrity": "sha512-KQ/Takk3T8Qzj5TppkS1be588lkbTp5uj7w6a0LeQaTMSckU/wK0oJ/pih+T690tkgI5jfmg2TqDJvd41Sj1Cg==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz", + "integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "regenerator-transform": "^0.15.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz", + "integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", + "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.25.9.tgz", + "integrity": "sha512-nZp7GlEl+yULJrClz0SwHPqir3lc0zsPrDHQUcxGspSL7AKrexNSEfTbfqnDNJUO13bgKyfuOLMF8Xqtu8j3YQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.6", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", + "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", + "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz", + "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz", + "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz", + "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.25.9.tgz", + "integrity": "sha512-7PbZQZP50tzv2KGGnhh82GSyMB01yKY9scIjf1a+GfZCtInOWqUH5+1EBU4t9fyR5Oykkkc9vFTs4OHrhHXljQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/plugin-syntax-typescript": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz", + "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz", + "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz", + "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz", + "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz", + "integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-import-assertions": "^7.26.0", + "@babel/plugin-syntax-import-attributes": "^7.26.0", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.25.9", + "@babel/plugin-transform-async-generator-functions": "^7.25.9", + "@babel/plugin-transform-async-to-generator": "^7.25.9", + "@babel/plugin-transform-block-scoped-functions": "^7.25.9", + "@babel/plugin-transform-block-scoping": "^7.25.9", + "@babel/plugin-transform-class-properties": "^7.25.9", + "@babel/plugin-transform-class-static-block": "^7.26.0", + "@babel/plugin-transform-classes": "^7.25.9", + "@babel/plugin-transform-computed-properties": "^7.25.9", + "@babel/plugin-transform-destructuring": "^7.25.9", + "@babel/plugin-transform-dotall-regex": "^7.25.9", + "@babel/plugin-transform-duplicate-keys": "^7.25.9", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-dynamic-import": "^7.25.9", + "@babel/plugin-transform-exponentiation-operator": "^7.25.9", + "@babel/plugin-transform-export-namespace-from": "^7.25.9", + "@babel/plugin-transform-for-of": "^7.25.9", + "@babel/plugin-transform-function-name": "^7.25.9", + "@babel/plugin-transform-json-strings": "^7.25.9", + "@babel/plugin-transform-literals": "^7.25.9", + "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", + "@babel/plugin-transform-member-expression-literals": "^7.25.9", + "@babel/plugin-transform-modules-amd": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.25.9", + "@babel/plugin-transform-modules-systemjs": "^7.25.9", + "@babel/plugin-transform-modules-umd": "^7.25.9", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-new-target": "^7.25.9", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9", + "@babel/plugin-transform-numeric-separator": "^7.25.9", + "@babel/plugin-transform-object-rest-spread": "^7.25.9", + "@babel/plugin-transform-object-super": "^7.25.9", + "@babel/plugin-transform-optional-catch-binding": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9", + "@babel/plugin-transform-private-methods": "^7.25.9", + "@babel/plugin-transform-private-property-in-object": "^7.25.9", + "@babel/plugin-transform-property-literals": "^7.25.9", + "@babel/plugin-transform-regenerator": "^7.25.9", + "@babel/plugin-transform-regexp-modifiers": "^7.26.0", + "@babel/plugin-transform-reserved-words": "^7.25.9", + "@babel/plugin-transform-shorthand-properties": "^7.25.9", + "@babel/plugin-transform-spread": "^7.25.9", + "@babel/plugin-transform-sticky-regex": "^7.25.9", + "@babel/plugin-transform-template-literals": "^7.25.9", + "@babel/plugin-transform-typeof-symbol": "^7.25.9", + "@babel/plugin-transform-unicode-escapes": "^7.25.9", + "@babel/plugin-transform-unicode-property-regex": "^7.25.9", + "@babel/plugin-transform-unicode-regex": "^7.25.9", + "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.6", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "core-js-compat": "^3.38.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-react": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.25.9.tgz", + "integrity": "sha512-D3to0uSPiWE7rBrdIICCd0tJSIGpLaaGptna2+w7Pft5xMqLpA1sz99DK5TZ1TjGbdQ/VI1eCSZ06dv3lT4JOw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-transform-react-display-name": "^7.25.9", + "@babel/plugin-transform-react-jsx": "^7.25.9", + "@babel/plugin-transform-react-jsx-development": "^7.25.9", + "@babel/plugin-transform-react-pure-annotations": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.26.0.tgz", + "integrity": "sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-syntax-jsx": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.25.9", + "@babel/plugin-transform-typescript": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", + "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/template": "^7.25.9", + "@babel/types": "^7.25.9", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "license": "MIT" + }, + "node_modules/@csstools/normalize.css": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.1.1.tgz", + "integrity": "sha512-YAYeJ+Xqh7fUou1d1j9XHl44BmsuThiTr4iNrgCQ3J27IbhXsxXDGZ1cXv8Qvs99d4rBbLiSKy3+WZiet32PcQ==", + "license": "CC0-1.0" + }, + "node_modules/@csstools/postcss-cascade-layers": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz", + "integrity": "sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA==", + "license": "CC0-1.0", + "dependencies": { + "@csstools/selector-specificity": "^2.0.2", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-color-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz", + "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==", + "license": "CC0-1.0", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-font-format-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz", + "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==", + "license": "CC0-1.0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-hwb-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz", + "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==", + "license": "CC0-1.0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-ic-unit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz", + "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==", + "license": "CC0-1.0", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-is-pseudo-class": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz", + "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==", + "license": "CC0-1.0", + "dependencies": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-nested-calc": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz", + "integrity": "sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==", + "license": "CC0-1.0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-normalize-display-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", + "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==", + "license": "CC0-1.0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-oklab-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz", + "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==", + "license": "CC0-1.0", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-progressive-custom-properties": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", + "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", + "license": "CC0-1.0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/@csstools/postcss-stepped-value-functions": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz", + "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==", + "license": "CC0-1.0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-text-decoration-shorthand": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz", + "integrity": "sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==", + "license": "CC0-1.0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-trigonometric-functions": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", + "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==", + "license": "CC0-1.0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-unset-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", + "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", + "license": "CC0-1.0", + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/selector-specificity": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz", + "integrity": "sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==", + "license": "CC0-1.0", + "engines": { + "node": "^14 || ^16 || >=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss-selector-parser": "^6.0.10" + } + }, + "node_modules/@emotion/babel-plugin": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz", + "integrity": "sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.2.0", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.13.1", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.1.tgz", + "integrity": "sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.0", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz", + "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/react": { + "version": "11.13.3", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.13.3.tgz", + "integrity": "sha512-lIsdU6JNrmYfJ5EbUCf4xW1ovy5wKQ2CkPRM4xogziOxH1nXxBSjpC9YqbFAP7circxMfYp+6x676BqWcEiixg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.12.0", + "@emotion/cache": "^11.13.0", + "@emotion/serialize": "^1.3.1", + "@emotion/use-insertion-effect-with-fallbacks": "^1.1.0", + "@emotion/utils": "^1.4.0", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.2.tgz", + "integrity": "sha512-grVnMvVPK9yUVE6rkKfAJlYZgo0cu3l9iMC77V7DW6E1DUIrU68pSEXRmFZFOFB1QFo57TncmOcvcbMDWsL4yA==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.1", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" + }, + "node_modules/@emotion/styled": { + "version": "11.13.0", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.13.0.tgz", + "integrity": "sha512-tkzkY7nQhW/zC4hztlwucpT8QEZ6eUzpXDRhww/Eej4tFfO0FxQYWRyg/c5CCXa4d/f174kqeXYjuQRnhzf6dA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.12.0", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.0", + "@emotion/use-insertion-effect-with-fallbacks": "^1.1.0", + "@emotion/utils": "^1.4.0" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz", + "integrity": "sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.1.tgz", + "integrity": "sha512-BymCXzCG3r72VKJxaYVwOXATqXIZ85cuvg0YOUDxMGNrKc1DJRZk8MgV5wyXRyEayIMd4FuXJIUgTBXvDNW5cA==", + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "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/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/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==", + "license": "Python-2.0" + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.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==", + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "license": "BSD-3-Clause" + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", + "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/console/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/console/node_modules/@types/yargs": { + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/console/node_modules/jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/console/node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/core": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", + "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", + "license": "MIT", + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/reporters": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^27.5.1", + "jest-config": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-resolve-dependencies": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "jest-watcher": "^27.5.1", + "micromatch": "^4.0.4", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/core/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/core/node_modules/@types/yargs": { + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/core/node_modules/jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/core/node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/environment": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", + "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/environment/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/environment/node_modules/@types/yargs": { + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", + "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "@sinonjs/fake-timers": "^8.0.1", + "@types/node": "*", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/fake-timers/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/fake-timers/node_modules/@types/yargs": { + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/fake-timers/node_modules/jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/fake-timers/node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", + "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", + "license": "MIT", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/types": "^27.5.1", + "expect": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/globals/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/globals/node_modules/@types/yargs": { + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/globals/node_modules/diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "license": "MIT", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/globals/node_modules/expect": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", + "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/globals/node_modules/jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/globals/node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "license": "MIT", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/globals/node_modules/jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/globals/node_modules/jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", + "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-haste-map": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^8.1.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/reporters/node_modules/@types/yargs": { + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/reporters/node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/reporters/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", + "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9", + "source-map": "^0.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/source-map/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/test-result": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", + "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", + "license": "MIT", + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/test-result/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/test-result/node_modules/@types/yargs": { + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", + "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", + "license": "MIT", + "dependencies": { + "@jest/test-result": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-runtime": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", + "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/types": "^27.5.1", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-util": "^27.5.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/transform/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/transform/node_modules/@types/yargs": { + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/transform/node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/transform/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "license": "MIT" + }, + "node_modules/@mui/core-downloads-tracker": { + "version": "5.16.7", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.16.7.tgz", + "integrity": "sha512-RtsCt4Geed2/v74sbihWzzRs+HsIQCfclHeORh5Ynu2fS4icIKozcSubwuG7vtzq2uW3fOR1zITSP84TNt2GoQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/icons-material": { + "version": "5.16.7", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.16.7.tgz", + "integrity": "sha512-UrGwDJCXEszbDI7yV047BYU5A28eGJ79keTCP4cc74WyncuVrnurlmIRxaHL8YK+LI1Kzq+/JM52IAkNnv4u+Q==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.9" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@mui/material": "^5.0.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material": { + "version": "5.16.7", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.16.7.tgz", + "integrity": "sha512-cwwVQxBhK60OIOqZOVLFt55t01zmarKJiJUWbk0+8s/Ix5IaUzAShqlJchxsIQ4mSrWqgcKCCXKtIlG5H+/Jmg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/core-downloads-tracker": "^5.16.7", + "@mui/system": "^5.16.7", + "@mui/types": "^7.2.15", + "@mui/utils": "^5.16.6", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.10", + "clsx": "^2.1.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1", + "react-is": "^18.3.1", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/private-theming": { + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.16.6.tgz", + "integrity": "sha512-rAk+Rh8Clg7Cd7shZhyt2HGTTE5wYKNSJ5sspf28Fqm/PZ69Er9o6KX25g03/FG2dfpg5GCwZh/xOojiTfm3hw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/utils": "^5.16.6", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.16.6.tgz", + "integrity": "sha512-zaThmS67ZmtHSWToTiHslbI8jwrmITcN93LQaR2lKArbvS7Z3iLkwRoiikNWutx9MBs8Q6okKvbZq1RQYB3v7g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@emotion/cache": "^11.11.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "5.16.7", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.16.7.tgz", + "integrity": "sha512-Jncvs/r/d/itkxh7O7opOunTqbbSSzMTHzZkNLM+FjAOg+cYAZHrPDlYe1ZGKUYORwwb2XexlWnpZp0kZ4AHuA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/private-theming": "^5.16.6", + "@mui/styled-engine": "^5.16.6", + "@mui/types": "^7.2.15", + "@mui/utils": "^5.16.6", + "clsx": "^2.1.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.2.18", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.18.tgz", + "integrity": "sha512-uvK9dWeyCJl/3ocVnTOS6nlji/Knj8/tVqVX03UVTpdmTJYu/s4jtDd9Kvv0nRGE0CUSNW1UYAci7PYypjealg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.16.6.tgz", + "integrity": "sha512-tWiQqlhxAt3KENNiSRL+DIn9H5xNVK6Jjf70x3PnfQPz1MPBdh7yyIcAyVBT9xiw7hP3SomRhPR7hzBMBCjqEA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/types": "^7.2.15", + "@types/prop-types": "^15.7.12", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^18.3.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { + "version": "5.1.1-v1", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", + "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", + "license": "MIT", + "dependencies": { + "eslint-scope": "5.1.1" + } + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pmmmwh/react-refresh-webpack-plugin": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.15.tgz", + "integrity": "sha512-LFWllMA55pzB9D34w/wXUCf8+c+IYKuJDgxiZ3qMhl64KRMBHYM1I3VdGaD2BV5FNPV2/S2596bppxHbv2ZydQ==", + "license": "MIT", + "dependencies": { + "ansi-html": "^0.0.9", + "core-js-pure": "^3.23.3", + "error-stack-parser": "^2.0.6", + "html-entities": "^2.1.0", + "loader-utils": "^2.0.4", + "schema-utils": "^4.2.0", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">= 10.13" + }, + "peerDependencies": { + "@types/webpack": "4.x || 5.x", + "react-refresh": ">=0.10.0 <1.0.0", + "sockjs-client": "^1.4.0", + "type-fest": ">=0.17.0 <5.0.0", + "webpack": ">=4.43.0 <6.0.0", + "webpack-dev-server": "3.x || 4.x || 5.x", + "webpack-hot-middleware": "2.x", + "webpack-plugin-serve": "0.x || 1.x" + }, + "peerDependenciesMeta": { + "@types/webpack": { + "optional": true + }, + "sockjs-client": { + "optional": true + }, + "type-fest": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + }, + "webpack-hot-middleware": { + "optional": true + }, + "webpack-plugin-serve": { + "optional": true + } + } + }, + "node_modules/@pmmmwh/react-refresh-webpack-plugin/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rollup/plugin-babel": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", + "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.10.4", + "@rollup/pluginutils": "^3.1.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "@types/babel__core": "^7.1.9", + "rollup": "^1.20.0||^2.0.0" + }, + "peerDependenciesMeta": { + "@types/babel__core": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", + "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "@types/resolve": "1.17.1", + "builtin-modules": "^3.1.0", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/@rollup/plugin-replace": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz", + "integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==", + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "magic-string": "^0.25.7" + }, + "peerDependencies": { + "rollup": "^1.20.0 || ^2.0.0" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "license": "MIT", + "dependencies": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/@rollup/pluginutils/node_modules/@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "license": "MIT" + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "license": "MIT" + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.4.tgz", + "integrity": "sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==", + "license": "MIT" + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@surma/rollup-plugin-off-main-thread": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", + "integrity": "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==", + "license": "Apache-2.0", + "dependencies": { + "ejs": "^3.1.6", + "json5": "^2.2.0", + "magic-string": "^0.25.0", + "string.prototype.matchall": "^4.0.6" + } + }, + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", + "integrity": "sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz", + "integrity": "sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz", + "integrity": "sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz", + "integrity": "sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz", + "integrity": "sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz", + "integrity": "sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz", + "integrity": "sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz", + "integrity": "sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-preset": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.5.0.tgz", + "integrity": "sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig==", + "license": "MIT", + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "^5.4.0", + "@svgr/babel-plugin-remove-jsx-attribute": "^5.4.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "^5.0.1", + "@svgr/babel-plugin-replace-jsx-attribute-value": "^5.0.1", + "@svgr/babel-plugin-svg-dynamic-title": "^5.4.0", + "@svgr/babel-plugin-svg-em-dimensions": "^5.4.0", + "@svgr/babel-plugin-transform-react-native-svg": "^5.4.0", + "@svgr/babel-plugin-transform-svg-component": "^5.5.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/core": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-5.5.0.tgz", + "integrity": "sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ==", + "license": "MIT", + "dependencies": { + "@svgr/plugin-jsx": "^5.5.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^7.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz", + "integrity": "sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.12.6" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-jsx": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz", + "integrity": "sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.12.3", + "@svgr/babel-preset": "^5.5.0", + "@svgr/hast-util-to-babel-ast": "^5.5.0", + "svg-parser": "^2.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-svgo": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz", + "integrity": "sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ==", + "license": "MIT", + "dependencies": { + "cosmiconfig": "^7.0.0", + "deepmerge": "^4.2.2", + "svgo": "^1.2.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/webpack": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-5.5.0.tgz", + "integrity": "sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/plugin-transform-react-constant-elements": "^7.12.1", + "@babel/preset-env": "^7.12.1", + "@babel/preset-react": "^7.12.5", + "@svgr/core": "^5.5.0", + "@svgr/plugin-jsx": "^5.5.0", + "@svgr/plugin-svgo": "^5.5.0", + "loader-utils": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@testing-library/dom": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", + "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz", + "integrity": "sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg==", + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "^4.0.1", + "@babel/runtime": "^7.9.2", + "@types/testing-library__jest-dom": "^5.9.1", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.5.6", + "lodash": "^4.17.15", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=8", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/react": { + "version": "13.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.4.0.tgz", + "integrity": "sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "@testing-library/dom": "^8.5.0", + "@types/react-dom": "^18.0.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@testing-library/react/node_modules/@testing-library/dom": { + "version": "8.20.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.1.tgz", + "integrity": "sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.1.3", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@testing-library/react/node_modules/aria-query": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "license": "Apache-2.0", + "dependencies": { + "deep-equal": "^2.0.5" + } + }, + "node_modules/@testing-library/user-event": { + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz", + "integrity": "sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "license": "ISC", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "license": "MIT" + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", + "license": "MIT", + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/eslint": { + "version": "8.56.12", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz", + "integrity": "sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==", + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.1.tgz", + "integrity": "sha512-CRICJIl0N5cXDONAdlTv5ShATZ4HEwk6kDDIW2/w9qOWKg+NU/5F8wYRWCrONad0/UKkloNSmmyN/wX4rtpbVA==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/express/node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", + "license": "MIT" + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "license": "MIT" + }, + "node_modules/@types/http-proxy": { + "version": "1.17.15", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.15.tgz", + "integrity": "sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "license": "MIT", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/jest/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@types/jest/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "license": "MIT" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.8.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.2.tgz", + "integrity": "sha512-NzaRNFV+FZkvK/KLCsNdTvID0SThyrs5SHB6tsD/lajr22FGC73N2QeDPM2wHtVde8mgcXuSsHQkH5cX1pbPLw==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.8" + } + }, + "node_modules/@types/node-forge": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", + "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" + }, + "node_modules/@types/prettier": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.13", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", + "license": "MIT" + }, + "node_modules/@types/q": { + "version": "1.5.8", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.8.tgz", + "integrity": "sha512-hroOstUScF6zhIi+5+x0dzqrHA1EJi+Irri6b1fxolMTqqHIV/Cg77EtnQcZqZCu8hR3mX2BzIxN4/GzI68Kfw==", + "license": "MIT" + }, + "node_modules/@types/qs": { + "version": "6.9.16", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", + "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==", + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.12", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz", + "integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==", + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.11", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.11.tgz", + "integrity": "sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==", + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/resolve": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "license": "MIT" + }, + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-index": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/sockjs": { + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "license": "MIT" + }, + "node_modules/@types/testing-library__jest-dom": { + "version": "5.14.9", + "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz", + "integrity": "sha512-FSYhIjFlfOpGSRyVoMBMuS3ws5ehFQODymf3vlI7U1K8c7PHwWwFY7VREfmsuzHSOnoKs/9/Y983ayOs7eRzqw==", + "license": "MIT", + "dependencies": { + "@types/jest": "*" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", + "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/experimental-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.62.0.tgz", + "integrity": "sha512-RTXpeB3eMkpoclG3ZHft6vG/Z30azNHuqY6wKPBHlVMZFuEvrtlEDe8gMqDb+SO+9hjC/pLekeSCryf9vMZlCw==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "license": "ISC" + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", + "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", + "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.12.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", + "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", + "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", + "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", + "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", + "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "license": "Apache-2.0" + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead", + "license": "BSD-3-Clause" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "license": "MIT", + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "node_modules/acorn-globals/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^8" + } + }, + "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==", + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/address": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", + "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "license": "MIT", + "dependencies": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "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/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-html": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.9.tgz", + "integrity": "sha512-ozbS3LuenHVxNRh/wdnN16QapUHzauqSomAl1jwwJRRsGwFwtj644lIhxfWu0Fy0acCij2+AEgHvjscq3dlVXg==", + "engines": [ + "node >= 0.8.0" + ], + "license": "Apache-2.0", + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "engines": [ + "node >= 0.8.0" + ], + "license": "Apache-2.0", + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.reduce": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.7.tgz", + "integrity": "sha512-mzmiUCVwtiD4lgxYP8g7IYy8El8p2CSMePvIbTS7gchKir/L1fgJrk0yDKmAX6mnRQFKNADYIk8nNlTris5H1Q==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-array-method-boxes-properly": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "license": "MIT" + }, + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "license": "MIT" + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "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/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.2.tgz", + "integrity": "sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==", + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/axios": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/babel-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", + "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", + "license": "MIT", + "dependencies": { + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-jest/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/babel-jest/node_modules/@types/yargs": { + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/babel-loader": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.4.1.tgz", + "integrity": "sha512-nXzRChX+Z1GoE6yWavBQg6jDslyFF3SDjl2paADuoQtQW10JqShJt62R6eJQ5m/pjJFDT8xgKIWSP85OY8eXeA==", + "license": "MIT", + "dependencies": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.4", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "engines": { + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" + } + }, + "node_modules/babel-loader/node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", + "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/babel-plugin-named-asset-import": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz", + "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==", + "license": "MIT", + "peerDependencies": { + "@babel/core": "^7.1.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", + "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.6.2", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", + "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.2", + "core-js-compat": "^3.38.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", + "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-transform-react-remove-prop-types": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", + "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==", + "license": "MIT" + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", + "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", + "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^27.5.1", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-react-app": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz", + "integrity": "sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.16.0", + "@babel/plugin-proposal-class-properties": "^7.16.0", + "@babel/plugin-proposal-decorators": "^7.16.4", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0", + "@babel/plugin-proposal-numeric-separator": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.0", + "@babel/plugin-proposal-private-methods": "^7.16.0", + "@babel/plugin-transform-flow-strip-types": "^7.16.0", + "@babel/plugin-transform-react-display-name": "^7.16.0", + "@babel/plugin-transform-runtime": "^7.16.4", + "@babel/preset-env": "^7.16.4", + "@babel/preset-react": "^7.16.0", + "@babel/preset-typescript": "^7.16.0", + "@babel/runtime": "^7.16.3", + "babel-plugin-macros": "^3.1.0", + "babel-plugin-transform-react-remove-prop-types": "^0.4.24" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "license": "MIT" + }, + "node_modules/bfj": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.1.0.tgz", + "integrity": "sha512-I6MMLkn+anzNdCUp9hMRyui1HaNEUCco50lxbvNS4+EyXg8lN3nJ48PjPWtbH8UVS9CuMoaKE9U2V3l29DaRQw==", + "license": "MIT", + "dependencies": { + "bluebird": "^3.7.2", + "check-types": "^11.2.3", + "hoopy": "^0.1.4", + "jsonpath": "^1.1.1", + "tryer": "^1.0.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/bonjour-service": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", + "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "license": "BSD-2-Clause" + }, + "node_modules/browserslist": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "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": { + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "license": "MIT", + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001674", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001674.tgz", + "integrity": "sha512-jOsKlZVRnzfhLojb+Ykb+gyUSp9Xb57So+fAiFlLzzTKpqg8xxSav0e40c8/4F/v9N8QSvrRRaLeVzQbLqomYw==", + "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/case-sensitive-paths-webpack-plugin": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", + "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/check-types": { + "version": "11.2.3", + "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.2.3.tgz", + "integrity": "sha512-+67P1GkJRaxQD6PKK0Et9DhwQB+vGg3PM5+aavopCpZT1lj9jeqfvpgTLAWErNj8qApkkmXlu/Ug74kmhagkXg==", + "license": "MIT" + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", + "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==", + "license": "MIT" + }, + "node_modules/clean-css": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", + "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", + "license": "MIT", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/clean-css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "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/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "license": "MIT", + "dependencies": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/coa/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/coa/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/coa/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/coa/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/coa/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/coa/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/coa/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "license": "MIT" + }, + "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==", + "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==", + "license": "MIT" + }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "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/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "license": "MIT" + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", + "license": "MIT" + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, + "node_modules/core-js": { + "version": "3.38.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.1.tgz", + "integrity": "sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat": { + "version": "3.38.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz", + "integrity": "sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-pure": { + "version": "3.38.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.38.1.tgz", + "integrity": "sha512-BY8Etc1FZqdw1glX0XNOq2FDwfrg/VGqoZOZCdaL+UmdaqDwQwYXkMJT4t6In+zfEfOJDcM9T0KdbBeJg8KKCQ==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/css-blank-pseudo": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", + "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", + "license": "CC0-1.0", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "bin": { + "css-blank-pseudo": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-declaration-sorter": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", + "integrity": "sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==", + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/css-has-pseudo": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", + "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", + "license": "CC0-1.0", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "bin": { + "css-has-pseudo": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-loader": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", + "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==", + "license": "MIT", + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/css-minimizer-webpack-plugin": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz", + "integrity": "sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==", + "license": "MIT", + "dependencies": { + "cssnano": "^5.0.6", + "jest-worker": "^27.0.2", + "postcss": "^8.3.5", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@parcel/css": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-prefers-color-scheme": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", + "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", + "license": "CC0-1.0", + "bin": { + "css-prefers-color-scheme": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", + "license": "MIT" + }, + "node_modules/css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css-tree/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "license": "MIT" + }, + "node_modules/cssdb": { + "version": "7.11.2", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.11.2.tgz", + "integrity": "sha512-lhQ32TFkc1X4eTefGfYPvgovRSzIMofHkigfH8nWtyRL4XJLsRhJFreRvEgKzept7x1rjBuy3J/MurXLaFxW/A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + } + ], + "license": "CC0-1.0" + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "5.1.15", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.15.tgz", + "integrity": "sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==", + "license": "MIT", + "dependencies": { + "cssnano-preset-default": "^5.2.14", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/cssnano" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-preset-default": { + "version": "5.2.14", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz", + "integrity": "sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==", + "license": "MIT", + "dependencies": { + "css-declaration-sorter": "^6.3.1", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.1", + "postcss-convert-values": "^5.1.3", + "postcss-discard-comments": "^5.1.2", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.7", + "postcss-merge-rules": "^5.1.4", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.4", + "postcss-minify-selectors": "^5.2.1", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.1", + "postcss-normalize-repeat-style": "^5.1.1", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.1", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.3", + "postcss-reduce-initial": "^5.1.2", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "license": "MIT", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "license": "MIT", + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "license": "CC0-1.0" + }, + "node_modules/csso/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "license": "MIT" + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "license": "MIT", + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "license": "BSD-2-Clause" + }, + "node_modules/data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "license": "MIT", + "dependencies": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "license": "MIT" + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "license": "MIT" + }, + "node_modules/deep-equal": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", + "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.5", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.2", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "license": "BSD-2-Clause", + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "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/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "license": "MIT" + }, + "node_modules/detect-port-alt": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", + "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", + "license": "MIT", + "dependencies": { + "address": "^1.0.1", + "debug": "^2.6.0" + }, + "bin": { + "detect": "bin/detect-port", + "detect-port": "bin/detect-port" + }, + "engines": { + "node": ">= 4.2.1" + } + }, + "node_modules/detect-port-alt/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/detect-port-alt/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "license": "Apache-2.0" + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "license": "MIT" + }, + "node_modules/dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", + "license": "MIT", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "license": "MIT" + }, + "node_modules/dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "license": "MIT", + "dependencies": { + "utila": "~0.4" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "deprecated": "Use your platform's native DOMException instead", + "license": "MIT", + "dependencies": { + "webidl-conversions": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/domexception/node_modules/webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=10" + } + }, + "node_modules/dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", + "license": "BSD-2-Clause" + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "license": "MIT" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.49", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.49.tgz", + "integrity": "sha512-ZXfs1Of8fDb6z7WEYZjXpgIRF6MEu8JdeGA0A40aZq6OQbS+eJpnnV49epZRna2DU/YsEjSQuGtQPPtvt6J65A==", + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", + "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-mart": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/emoji-mart/-/emoji-mart-5.6.0.tgz", + "integrity": "sha512-eJp3QRe79pjwa+duv+n7+5YsNhRcMl812EcFVwrnRvYKoNPoQb5qxU8DG6Bgwji0akHdp6D4Ln6tYLG58MFSow==", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "license": "MIT", + "dependencies": { + "stackframe": "^1.3.4" + } + }, + "node_modules/es-abstract": { + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.3", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.13", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.15" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "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-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.1.0.tgz", + "integrity": "sha512-/SurEfycdyssORP/E+bj4sEu1CWw4EmLDsHynHwSXQ7utgbrMRWW195pTrCjFgFCddf/UkYm3oqKPRq5i8bJbw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "globalthis": "^1.0.4", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.7", + "iterator.prototype": "^1.1.3", + "safe-array-concat": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", + "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "license": "MIT", + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-react-app": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz", + "integrity": "sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.16.0", + "@babel/eslint-parser": "^7.16.3", + "@rushstack/eslint-patch": "^1.1.0", + "@typescript-eslint/eslint-plugin": "^5.5.0", + "@typescript-eslint/parser": "^5.5.0", + "babel-preset-react-app": "^10.0.1", + "confusing-browser-globals": "^1.0.11", + "eslint-plugin-flowtype": "^8.0.3", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-jest": "^25.3.0", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-react": "^7.27.1", + "eslint-plugin-react-hooks": "^4.3.0", + "eslint-plugin-testing-library": "^5.0.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "eslint": "^8.0.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", + "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-flowtype": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz", + "integrity": "sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==", + "license": "BSD-3-Clause", + "dependencies": { + "lodash": "^4.17.21", + "string-natural-compare": "^3.0.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@babel/plugin-syntax-flow": "^7.14.5", + "@babel/plugin-transform-react-jsx": "^7.14.9", + "eslint": "^8.1.0" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.31.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", + "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", + "license": "MIT", + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.8", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-jest": { + "version": "25.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz", + "integrity": "sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/experimental-utils": "^5.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^4.0.0 || ^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", + "license": "MIT", + "dependencies": { + "aria-query": "^5.3.2", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.1" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.37.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.2.tgz", + "integrity": "sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w==", + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.2", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.1.0", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.8", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.0", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.11", + "string.prototype.repeat": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", + "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-testing-library": { + "version": "5.11.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.11.1.tgz", + "integrity": "sha512-5eX9e1Kc2PqVRed3taaLnAAqPZGEX75C+M/rXzUAI3wIg/ZxzUm1OVAwfe/O+vE+6YXOLetSe9g5GKD2ecXipw==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^5.58.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0", + "npm": ">=6" + }, + "peerDependencies": { + "eslint": "^7.5.0 || ^8.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "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==", + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz", + "integrity": "sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==", + "license": "MIT", + "dependencies": { + "@types/eslint": "^7.29.0 || ^8.4.1", + "jest-worker": "^28.0.2", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0", + "webpack": "^5.0.0" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/jest-worker": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", + "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/eslint/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==", + "license": "Python-2.0" + }, + "node_modules/eslint/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==", + "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/eslint/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/eslint/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==", + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/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==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/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==", + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "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==", + "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==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/express": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.10", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "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==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "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==", + "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==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", + "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", + "license": "BSD-3-Clause" + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "license": "Apache-2.0", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "license": "MIT", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/file-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/filesize": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", + "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "license": "MIT", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "license": "MIT", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fork-ts-checker-webpack-plugin": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz", + "integrity": "sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.8.3", + "@types/json-schema": "^7.0.5", + "chalk": "^4.1.0", + "chokidar": "^3.4.2", + "cosmiconfig": "^6.0.0", + "deepmerge": "^4.2.2", + "fs-extra": "^9.0.0", + "glob": "^7.1.6", + "memfs": "^3.1.2", + "minimatch": "^3.0.4", + "schema-utils": "2.7.0", + "semver": "^7.3.2", + "tapable": "^1.0.0" + }, + "engines": { + "node": ">=10", + "yarn": ">=1.0.0" + }, + "peerDependencies": { + "eslint": ">= 6", + "typescript": ">= 2.7", + "vue-template-compiler": "*", + "webpack": ">= 4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + } + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", + "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==", + "license": "Unlicense" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "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/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "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==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "license": "ISC" + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "license": "BSD-2-Clause" + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "license": "MIT", + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "license": "MIT", + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "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==", + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "license": "MIT" + }, + "node_modules/gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "license": "MIT", + "dependencies": { + "duplexer": "^0.1.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "license": "MIT" + }, + "node_modules/harmony-reflect": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", + "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==", + "license": "(Apache-2.0 OR MPL-1.1)" + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "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/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/hoopy": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", + "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^1.0.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/html-entities": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "license": "MIT" + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "license": "MIT" + }, + "node_modules/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "license": "MIT", + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-webpack-plugin": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.3.tgz", + "integrity": "sha512-QSf1yjtSAsmf7rYBV7XX86uua4W/vkhIt0xNXKbsi2foEeW7vjJQz4bhnpL3xH+l1ryl1680uNv968Z+X6jSYg==", + "license": "MIT", + "dependencies": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/html-webpack-plugin" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.20.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", + "license": "MIT" + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "license": "MIT", + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz", + "integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==", + "license": "MIT", + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", + "license": "ISC" + }, + "node_modules/identity-obj-proxy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", + "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", + "license": "MIT", + "dependencies": { + "harmony-reflect": "^1.4.6" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "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/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ipaddr.js": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "node_modules/is-async-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", + "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "license": "MIT", + "dependencies": { + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", + "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "license": "MIT" + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-root": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", + "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "license": "MIT" + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", + "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/iterator.prototype": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.3.tgz", + "integrity": "sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ==", + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "reflect.getprototypeof": "^1.0.4", + "set-function-name": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", + "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", + "license": "MIT", + "dependencies": { + "@jest/core": "^27.5.1", + "import-local": "^3.0.2", + "jest-cli": "^27.5.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", + "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "execa": "^5.0.0", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-changed-files/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-changed-files/node_modules/@types/yargs": { + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-circus": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", + "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", + "license": "MIT", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-circus/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-circus/node_modules/@types/yargs": { + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-circus/node_modules/diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "license": "MIT", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-circus/node_modules/expect": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", + "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-circus/node_modules/jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-circus/node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "license": "MIT", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-circus/node_modules/jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-circus/node_modules/jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-circus/node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-cli": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", + "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", + "license": "MIT", + "dependencies": { + "@jest/core": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "prompts": "^2.0.1", + "yargs": "^16.2.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-cli/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-cli/node_modules/@types/yargs": { + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-cli/node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-config": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", + "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.8.0", + "@jest/test-sequencer": "^27.5.1", + "@jest/types": "^27.5.1", + "babel-jest": "^27.5.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.9", + "jest-circus": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-jasmine2": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-config/node_modules/@types/yargs": { + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-config/node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "license": "MIT", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-config/node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-diff/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", + "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", + "license": "MIT", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-each": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", + "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-each/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-each/node_modules/@types/yargs": { + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-each/node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "license": "MIT", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-each/node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-environment-jsdom": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", + "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", + "license": "MIT", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1", + "jsdom": "^16.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-environment-jsdom/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-environment-jsdom/node_modules/@types/yargs": { + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-environment-jsdom/node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", + "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", + "license": "MIT", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-environment-node/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-environment-node/node_modules/@types/yargs": { + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-environment-node/node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", + "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^27.5.1", + "jest-serializer": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "micromatch": "^4.0.4", + "walker": "^1.0.7" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-haste-map/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-haste-map/node_modules/@types/yargs": { + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-haste-map/node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", + "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", + "license": "MIT", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/@types/yargs": { + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-jasmine2/node_modules/diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "license": "MIT", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/expect": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", + "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "license": "MIT", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-leak-detector": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", + "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", + "license": "MIT", + "dependencies": { + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-leak-detector/node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "license": "MIT", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", + "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-mock/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-mock/node_modules/@types/yargs": { + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", + "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", + "license": "MIT", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", + "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", + "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-snapshot": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve-dependencies/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve-dependencies/node_modules/@types/yargs": { + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-resolve/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve/node_modules/@types/yargs": { + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-resolve/node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runner": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", + "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", + "license": "MIT", + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-leak-detector": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "source-map-support": "^0.5.6", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runner/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runner/node_modules/@types/yargs": { + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-runner/node_modules/jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runner/node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", + "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", + "license": "MIT", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/globals": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runtime/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runtime/node_modules/@types/yargs": { + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-runtime/node_modules/jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runtime/node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-serializer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", + "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", + "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.7.2", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.0.0", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^27.5.1", + "semver": "^7.3.2" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/@types/yargs": { + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-snapshot/node_modules/diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "license": "MIT", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/expect": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", + "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "license": "MIT", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", + "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "leven": "^3.1.0", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-validate/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-validate/node_modules/@types/yargs": { + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-validate/node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "license": "MIT", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-watch-typeahead": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-1.1.0.tgz", + "integrity": "sha512-Va5nLSJTN7YFtC2jd+7wsoe1pNe5K4ShLux/E5iHEwlB9AxaxmggY7to9KUqKojhaJw3aXqt5WAb4jGPOolpEw==", + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.3.1", + "chalk": "^4.0.0", + "jest-regex-util": "^28.0.0", + "jest-watcher": "^28.0.0", + "slash": "^4.0.0", + "string-length": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "jest": "^27.0.0 || ^28.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/@jest/console": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", + "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/@jest/console/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watch-typeahead/node_modules/@jest/schemas": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", + "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.24.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/@jest/test-result": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", + "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", + "license": "MIT", + "dependencies": { + "@jest/console": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/@jest/types": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", + "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "license": "MIT", + "dependencies": { + "@jest/schemas": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/@sinclair/typebox": { + "version": "0.24.51", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "license": "MIT" + }, + "node_modules/jest-watch-typeahead/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-watch-typeahead/node_modules/emittery": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", + "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-message-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-message-util/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-regex-util": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", + "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "license": "MIT", + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "license": "MIT", + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-watcher": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", + "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", + "license": "MIT", + "dependencies": { + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "jest-util": "^28.1.3", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-watcher/node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-watcher/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watch-typeahead/node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "license": "MIT", + "dependencies": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watch-typeahead/node_modules/string-length": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-5.0.1.tgz", + "integrity": "sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==", + "license": "MIT", + "dependencies": { + "char-regex": "^2.0.0", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watch-typeahead/node_modules/string-length/node_modules/char-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-2.0.1.tgz", + "integrity": "sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/jest-watch-typeahead/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/jest-watch-typeahead/node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/jest-watcher": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", + "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", + "license": "MIT", + "dependencies": { + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^27.5.1", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-watcher/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-watcher/node_modules/@types/yargs": { + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-watcher/node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jiti": { + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", + "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "license": "MIT", + "dependencies": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.6", + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/form-data": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.2.tgz", + "integrity": "sha512-sJe+TQb2vIaIyO783qN6BlMYWMw3WBOHA1Ay2qxsnjuafEOQFJ2JakedOQirT6D5XPRxDvS7AHYyem9fTpb4LQ==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "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==", + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "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==", + "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==", + "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==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonpath": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/jsonpath/-/jsonpath-1.1.1.tgz", + "integrity": "sha512-l6Cg7jRpixfbgoWgkrl77dgEj8RPvND0wMH6TwQmi9Qs4TFfS9u5cUFnbeKTwj5ga5Y3BTGGNI28k117LJ009w==", + "license": "MIT", + "dependencies": { + "esprima": "1.2.2", + "static-eval": "2.0.2", + "underscore": "1.12.1" + } + }, + "node_modules/jsonpath/node_modules/esprima": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.2.tgz", + "integrity": "sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "license": "CC0-1.0" + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "license": "MIT", + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/launch-editor": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.9.1.tgz", + "integrity": "sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w==", + "license": "MIT", + "dependencies": { + "picocolors": "^1.0.0", + "shell-quote": "^1.8.1" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "license": "MIT", + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "license": "MIT" + }, + "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==", + "license": "MIT" + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", + "license": "MIT" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "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==", + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "license": "MIT", + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "license": "MIT", + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", + "license": "CC0-1.0" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "license": "Unlicense", + "dependencies": { + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "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/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.1.tgz", + "integrity": "sha512-+Vyi+GCCOHnrJ2VPS+6aPoXN2k2jgUzDRhTFLjjTBn23qyXJXkjUWQgTL+mXpF5/A8ixLdCc6kWsoeOjKGejKQ==", + "license": "MIT", + "dependencies": { + "schema-utils": "^4.0.0", + "tapable": "^2.2.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "license": "ISC" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "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/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "license": "MIT", + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "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==", + "license": "MIT" + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "license": "MIT" + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "license": "(BSD-3-Clause OR GPL-2.0)", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/nwsapi": { + "version": "2.2.13", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.13.tgz", + "integrity": "sha512-cTGB9ptp9dY9A5VbMSe7fQBcl/tt22Vcqdq8+eN93rblOuE0aCFu4aZ2vMwct/2t+lFnosm8RkQW1I0Omb1UtQ==", + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", + "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.8.tgz", + "integrity": "sha512-qkHIGe4q0lSYMv0XI4SsBTJz3WaURhLvd0lKSgtVuOsJ2krg4SgMw3PIRQFMp07yi++UR3se2mkcLqsBNpBb/A==", + "license": "MIT", + "dependencies": { + "array.prototype.reduce": "^1.0.6", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "gopd": "^1.0.1", + "safe-array-concat": "^1.1.2" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "license": "MIT" + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "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": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "license": "MIT", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "license": "MIT" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/path-to-regexp": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "license": "MIT", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-up/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "license": "MIT", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "license": "MIT", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "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.7", + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-attribute-case-insensitive": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", + "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==", + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-browser-comments": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-4.0.0.tgz", + "integrity": "sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==", + "license": "CC0-1.0", + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "browserslist": ">=4", + "postcss": ">=8" + } + }, + "node_modules/postcss-calc": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" + }, + "peerDependencies": { + "postcss": "^8.2.2" + } + }, + "node_modules/postcss-clamp": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", + "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=7.6.0" + }, + "peerDependencies": { + "postcss": "^8.4.6" + } + }, + "node_modules/postcss-color-functional-notation": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz", + "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==", + "license": "CC0-1.0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-color-hex-alpha": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz", + "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-color-rebeccapurple": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz", + "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==", + "license": "CC0-1.0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-colormin": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.1.tgz", + "integrity": "sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-convert-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz", + "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-custom-media": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz", + "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/postcss-custom-properties": { + "version": "12.1.11", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.11.tgz", + "integrity": "sha512-0IDJYhgU8xDv1KY6+VgUwuQkVtmYzRwu+dMjnmdMafXYv86SWqfxkc7qdDvWS38vsjaEtv8e0vGOUQrAiMBLpQ==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-custom-selectors": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz", + "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==", + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/postcss-dir-pseudo-class": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz", + "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==", + "license": "CC0-1.0", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-discard-comments": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", + "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", + "license": "MIT", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "license": "MIT", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-empty": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "license": "MIT", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "license": "MIT", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-double-position-gradients": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz", + "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==", + "license": "CC0-1.0", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-env-function": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", + "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", + "license": "CC0-1.0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-flexbugs-fixes": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz", + "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==", + "license": "MIT", + "peerDependencies": { + "postcss": "^8.1.4" + } + }, + "node_modules/postcss-focus-visible": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", + "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", + "license": "CC0-1.0", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-focus-within": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", + "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", + "license": "CC0-1.0", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-font-variant": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", + "license": "MIT", + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-gap-properties": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", + "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", + "license": "CC0-1.0", + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-image-set-function": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz", + "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==", + "license": "CC0-1.0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-initial": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", + "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", + "license": "MIT", + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-lab-function": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", + "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", + "license": "CC0-1.0", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-load-config/node_modules/lilconfig": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/postcss-load-config/node_modules/yaml": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz", + "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/postcss-loader": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", + "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", + "license": "MIT", + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + } + }, + "node_modules/postcss-logical": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", + "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", + "license": "CC0-1.0", + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-media-minmax": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", + "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-merge-longhand": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz", + "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^5.1.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-merge-rules": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz", + "integrity": "sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^3.1.0", + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-gradients": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "license": "MIT", + "dependencies": { + "colord": "^2.9.1", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-params": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz", + "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.21.4", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-selectors": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", + "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz", + "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==", + "license": "MIT", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz", + "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==", + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "license": "ISC", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-nesting": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.2.0.tgz", + "integrity": "sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA==", + "license": "CC0-1.0", + "dependencies": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-normalize": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize/-/postcss-normalize-10.0.1.tgz", + "integrity": "sha512-+5w18/rDev5mqERcG3W5GZNMJa1eoYYNGo8gB7tEwaos0ajk3ZXAI4mHGcNT47NE+ZnZD1pEpUOFLvltIwmeJA==", + "license": "CC0-1.0", + "dependencies": { + "@csstools/normalize.css": "*", + "postcss-browser-comments": "^4", + "sanitize.css": "*" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "browserslist": ">= 4", + "postcss": ">= 8" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "license": "MIT", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-positions": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", + "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", + "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-string": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-unicode": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz", + "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "license": "MIT", + "dependencies": { + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-whitespace": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-opacity-percentage": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.3.tgz", + "integrity": "sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==", + "funding": [ + { + "type": "kofi", + "url": "https://ko-fi.com/mrcgrtz" + }, + { + "type": "liberapay", + "url": "https://liberapay.com/mrcgrtz" + } + ], + "license": "MIT", + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-ordered-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", + "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", + "license": "MIT", + "dependencies": { + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-overflow-shorthand": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", + "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", + "license": "CC0-1.0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-page-break": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", + "license": "MIT", + "peerDependencies": { + "postcss": "^8" + } + }, + "node_modules/postcss-place": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", + "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", + "license": "CC0-1.0", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-preset-env": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.3.tgz", + "integrity": "sha512-T1LgRm5uEVFSEF83vHZJV2z19lHg4yJuZ6gXZZkqVsqv63nlr6zabMH3l4Pc01FQCyfWVrh2GaUeCVy9Po+Aag==", + "license": "CC0-1.0", + "dependencies": { + "@csstools/postcss-cascade-layers": "^1.1.1", + "@csstools/postcss-color-function": "^1.1.1", + "@csstools/postcss-font-format-keywords": "^1.0.1", + "@csstools/postcss-hwb-function": "^1.0.2", + "@csstools/postcss-ic-unit": "^1.0.1", + "@csstools/postcss-is-pseudo-class": "^2.0.7", + "@csstools/postcss-nested-calc": "^1.0.0", + "@csstools/postcss-normalize-display-values": "^1.0.1", + "@csstools/postcss-oklab-function": "^1.1.1", + "@csstools/postcss-progressive-custom-properties": "^1.3.0", + "@csstools/postcss-stepped-value-functions": "^1.0.1", + "@csstools/postcss-text-decoration-shorthand": "^1.0.0", + "@csstools/postcss-trigonometric-functions": "^1.0.2", + "@csstools/postcss-unset-value": "^1.0.2", + "autoprefixer": "^10.4.13", + "browserslist": "^4.21.4", + "css-blank-pseudo": "^3.0.3", + "css-has-pseudo": "^3.0.4", + "css-prefers-color-scheme": "^6.0.3", + "cssdb": "^7.1.0", + "postcss-attribute-case-insensitive": "^5.0.2", + "postcss-clamp": "^4.1.0", + "postcss-color-functional-notation": "^4.2.4", + "postcss-color-hex-alpha": "^8.0.4", + "postcss-color-rebeccapurple": "^7.1.1", + "postcss-custom-media": "^8.0.2", + "postcss-custom-properties": "^12.1.10", + "postcss-custom-selectors": "^6.0.3", + "postcss-dir-pseudo-class": "^6.0.5", + "postcss-double-position-gradients": "^3.1.2", + "postcss-env-function": "^4.0.6", + "postcss-focus-visible": "^6.0.4", + "postcss-focus-within": "^5.0.4", + "postcss-font-variant": "^5.0.0", + "postcss-gap-properties": "^3.0.5", + "postcss-image-set-function": "^4.0.7", + "postcss-initial": "^4.0.1", + "postcss-lab-function": "^4.2.1", + "postcss-logical": "^5.0.4", + "postcss-media-minmax": "^5.0.0", + "postcss-nesting": "^10.2.0", + "postcss-opacity-percentage": "^1.1.2", + "postcss-overflow-shorthand": "^3.0.4", + "postcss-page-break": "^3.0.4", + "postcss-place": "^7.0.5", + "postcss-pseudo-class-any-link": "^7.1.6", + "postcss-replace-overflow-wrap": "^4.0.0", + "postcss-selector-not": "^6.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-pseudo-class-any-link": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", + "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", + "license": "CC0-1.0", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz", + "integrity": "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-replace-overflow-wrap": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", + "license": "MIT", + "peerDependencies": { + "postcss": "^8.0.3" + } + }, + "node_modules/postcss-selector-not": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz", + "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==", + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "svgo": "^2.7.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/postcss-svgo/node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/postcss-svgo/node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "license": "CC0-1.0" + }, + "node_modules/postcss-svgo/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-svgo/node_modules/svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "license": "MIT", + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "license": "MIT" + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "license": "MIT", + "dependencies": { + "asap": "~2.0.6" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "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/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "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==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "deprecated": "You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.\n\n(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)", + "license": "MIT", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "license": "MIT", + "dependencies": { + "performance-now": "^2.1.0" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-app-polyfill": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz", + "integrity": "sha512-sZ41cxiU5llIB003yxxQBYrARBqe0repqPTTYBTmMqTz9szeBbE37BehCE891NZsmdZqqP+xWKdT3eo3vOzN8w==", + "license": "MIT", + "dependencies": { + "core-js": "^3.19.2", + "object-assign": "^4.1.1", + "promise": "^8.1.0", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.9", + "whatwg-fetch": "^3.6.2" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/react-app-polyfill/node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT" + }, + "node_modules/react-dev-utils": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", + "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.16.0", + "address": "^1.1.2", + "browserslist": "^4.18.1", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "detect-port-alt": "^1.1.6", + "escape-string-regexp": "^4.0.0", + "filesize": "^8.0.6", + "find-up": "^5.0.0", + "fork-ts-checker-webpack-plugin": "^6.5.0", + "global-modules": "^2.0.0", + "globby": "^11.0.4", + "gzip-size": "^6.0.0", + "immer": "^9.0.7", + "is-root": "^2.1.0", + "loader-utils": "^3.2.0", + "open": "^8.4.0", + "pkg-up": "^3.1.0", + "prompts": "^2.4.2", + "react-error-overlay": "^6.0.11", + "recursive-readdir": "^2.2.2", + "shell-quote": "^1.7.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/react-dev-utils/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==", + "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/react-dev-utils/node_modules/loader-utils": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz", + "integrity": "sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/react-dev-utils/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==", + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-dev-utils/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==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-dev-utils/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==", + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-error-overlay": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", + "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==", + "license": "MIT" + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, + "node_modules/react-refresh": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", + "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-scripts": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", + "integrity": "sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.16.0", + "@pmmmwh/react-refresh-webpack-plugin": "^0.5.3", + "@svgr/webpack": "^5.5.0", + "babel-jest": "^27.4.2", + "babel-loader": "^8.2.3", + "babel-plugin-named-asset-import": "^0.3.8", + "babel-preset-react-app": "^10.0.1", + "bfj": "^7.0.2", + "browserslist": "^4.18.1", + "camelcase": "^6.2.1", + "case-sensitive-paths-webpack-plugin": "^2.4.0", + "css-loader": "^6.5.1", + "css-minimizer-webpack-plugin": "^3.2.0", + "dotenv": "^10.0.0", + "dotenv-expand": "^5.1.0", + "eslint": "^8.3.0", + "eslint-config-react-app": "^7.0.1", + "eslint-webpack-plugin": "^3.1.1", + "file-loader": "^6.2.0", + "fs-extra": "^10.0.0", + "html-webpack-plugin": "^5.5.0", + "identity-obj-proxy": "^3.0.0", + "jest": "^27.4.3", + "jest-resolve": "^27.4.2", + "jest-watch-typeahead": "^1.0.0", + "mini-css-extract-plugin": "^2.4.5", + "postcss": "^8.4.4", + "postcss-flexbugs-fixes": "^5.0.2", + "postcss-loader": "^6.2.1", + "postcss-normalize": "^10.0.1", + "postcss-preset-env": "^7.0.1", + "prompts": "^2.4.2", + "react-app-polyfill": "^3.0.0", + "react-dev-utils": "^12.0.1", + "react-refresh": "^0.11.0", + "resolve": "^1.20.0", + "resolve-url-loader": "^4.0.0", + "sass-loader": "^12.3.0", + "semver": "^7.3.5", + "source-map-loader": "^3.0.0", + "style-loader": "^3.3.1", + "tailwindcss": "^3.0.2", + "terser-webpack-plugin": "^5.2.5", + "webpack": "^5.64.4", + "webpack-dev-server": "^4.6.0", + "webpack-manifest-plugin": "^4.0.2", + "workbox-webpack-plugin": "^6.4.1" + }, + "bin": { + "react-scripts": "bin/react-scripts.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + }, + "peerDependencies": { + "react": ">= 16", + "typescript": "^3.2.1 || ^4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/recursive-readdir": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", + "license": "MIT", + "dependencies": { + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", + "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.1", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "globalthis": "^1.0.3", + "which-builtin-type": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "license": "MIT" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "license": "MIT" + }, + "node_modules/regenerator-transform": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.0.tgz", + "integrity": "sha512-TVILVSz2jY5D47F4mA4MppkBrafEaiUWJO/TcZHEIuI13AqoZMkK1WMA4Om1YkYbTx+9Ki1/tSUXbceyr9saRg==", + "license": "MIT" + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", + "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpu-core": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.1.1.tgz", + "integrity": "sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw==", + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.11.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "license": "MIT" + }, + "node_modules/regjsparser": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.11.2.tgz", + "integrity": "sha512-3OGZZ4HoLJkkAZx/48mTXJNlmqTGOzc0o9OWQPuWpkOlXXPbyN6OafCcoXUnBqE2D3f/T5L+pWc1kdEmnfnRsA==", + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~3.0.2" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "license": "MIT", + "dependencies": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-url-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz", + "integrity": "sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA==", + "license": "MIT", + "dependencies": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^7.0.35", + "source-map": "0.6.1" + }, + "engines": { + "node": ">=8.9" + }, + "peerDependencies": { + "rework": "1.0.1", + "rework-visit": "1.0.0" + }, + "peerDependenciesMeta": { + "rework": { + "optional": true + }, + "rework-visit": { + "optional": true + } + } + }, + "node_modules/resolve-url-loader/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "license": "ISC" + }, + "node_modules/resolve-url-loader/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "license": "MIT", + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/resolve-url-loader/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve.exports": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", + "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "2.79.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", + "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", + "license": "MIT", + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup-plugin-terser": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", + "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", + "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "jest-worker": "^26.2.1", + "serialize-javascript": "^4.0.0", + "terser": "^5.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0" + } + }, + "node_modules/rollup-plugin-terser/node_modules/jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/rollup-plugin-terser/node_modules/serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-regex-test": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-regex": "^1.1.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/sanitize.css": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", + "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==", + "license": "CC0-1.0" + }, + "node_modules/sass-loader": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", + "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", + "license": "MIT", + "dependencies": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + } + } + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "license": "ISC" + }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/schema-utils/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/schema-utils/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/schema-utils/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "license": "MIT" + }, + "node_modules/selfsigned": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", + "license": "MIT", + "dependencies": { + "@types/node-forge": "^1.3.0", + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "license": "ISC" + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "license": "ISC" + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "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==", + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "license": "MIT", + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "license": "MIT" + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-loader": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.2.tgz", + "integrity": "sha512-BokxPoLjyl3iOrgkWaakaxqnelAJSS+0V+De0kKIq6lyWrXuiPgYTGp6z3iHmqljKAaLXwZa+ctD8GccRJeVvg==", + "license": "MIT", + "dependencies": { + "abab": "^2.0.5", + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead", + "license": "MIT" + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility", + "license": "MIT" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", + "license": "MIT" + }, + "node_modules/static-eval": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.2.tgz", + "integrity": "sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg==", + "license": "MIT", + "dependencies": { + "escodegen": "^1.8.1" + } + }, + "node_modules/static-eval/node_modules/escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/static-eval/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/static-eval/node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "license": "MIT", + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/static-eval/node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "license": "MIT", + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/static-eval/node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/static-eval/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-eval/node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "license": "MIT", + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", + "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", + "license": "MIT", + "dependencies": { + "internal-slot": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-natural-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", + "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==", + "license": "MIT" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string.prototype.includes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", + "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.7", + "regexp.prototype.flags": "^1.5.2", + "set-function-name": "^2.0.2", + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "license": "BSD-2-Clause", + "dependencies": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", + "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-loader": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.4.tgz", + "integrity": "sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/stylehacks": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", + "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.21.4", + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/sucrase/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sucrase/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", + "license": "MIT" + }, + "node_modules/svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "deprecated": "This SVGO version is no longer supported. Upgrade to v2.x.x.", + "license": "MIT", + "dependencies": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/svgo/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/svgo/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/svgo/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/svgo/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/svgo/node_modules/css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "node_modules/svgo/node_modules/css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/svgo/node_modules/dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "node_modules/svgo/node_modules/domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/svgo/node_modules/domutils/node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "license": "BSD-2-Clause" + }, + "node_modules/svgo/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/svgo/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/svgo/node_modules/nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "~1.0.0" + } + }, + "node_modules/svgo/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "license": "MIT" + }, + "node_modules/tailwindcss": { + "version": "3.4.14", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.14.tgz", + "integrity": "sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA==", + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.0", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tempy": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz", + "integrity": "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==", + "license": "MIT", + "dependencies": { + "is-stream": "^2.0.0", + "temp-dir": "^2.0.0", + "type-fest": "^0.16.0", + "unique-string": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/type-fest": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", + "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terser": { + "version": "5.36.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.36.0.tgz", + "integrity": "sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==", + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.20", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "license": "MIT" + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/throat": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", + "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==", + "license": "MIT" + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "license": "MIT" + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "license": "MIT", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tryer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", + "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==", + "license": "MIT" + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "license": "Apache-2.0" + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", + "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", + "license": "0BSD" + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "license": "MIT", + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, + "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==", + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "license": "MIT", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/underscore": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", + "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "license": "MIT" + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "license": "MIT", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "license": "MIT", + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==", + "license": "MIT" + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "license": "MIT", + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "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.0" + }, + "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==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/util.promisify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.2", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-to-istanbul": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", + "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", + "license": "ISC", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.", + "license": "MIT", + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "license": "MIT", + "dependencies": { + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/watchpack": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", + "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "license": "MIT", + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/web-vitals": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-2.1.4.tgz", + "integrity": "sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg==", + "license": "Apache-2.0" + }, + "node_modules/webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=10.4" + } + }, + "node_modules/webpack": { + "version": "5.95.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.95.0.tgz", + "integrity": "sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", + "acorn": "^8.7.1", + "acorn-import-attributes": "^1.9.5", + "browserslist": "^4.21.10", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-middleware": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", + "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", + "license": "MIT", + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-server": { + "version": "4.15.2", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz", + "integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==", + "license": "MIT", + "dependencies": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.5", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "launch-editor": "^2.6.0", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.1.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.4", + "ws": "^8.13.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/webpack-manifest-plugin": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-4.1.1.tgz", + "integrity": "sha512-YXUAwxtfKIJIKkhg03MKuiFAD72PlrqCiwdwO4VEXdRO5V0ORCNwaOwAZawPZalCbmH9kBDmXnNeQOw+BIEiow==", + "license": "MIT", + "dependencies": { + "tapable": "^2.0.0", + "webpack-sources": "^2.2.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "peerDependencies": { + "webpack": "^4.44.2 || ^5.47.0" + } + }, + "node_modules/webpack-manifest-plugin/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-manifest-plugin/node_modules/webpack-sources": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", + "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==", + "license": "MIT", + "dependencies": { + "source-list-map": "^2.0.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "license": "Apache-2.0", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "license": "MIT", + "dependencies": { + "iconv-lite": "0.4.24" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", + "license": "MIT" + }, + "node_modules/whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "license": "MIT" + }, + "node_modules/whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "license": "MIT", + "dependencies": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "license": "MIT", + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.4.tgz", + "integrity": "sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==", + "license": "MIT", + "dependencies": { + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.0.5", + "is-finalizationregistry": "^1.0.2", + "is-generator-function": "^1.0.10", + "is-regex": "^1.1.4", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.15" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/workbox-background-sync": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.6.0.tgz", + "integrity": "sha512-jkf4ZdgOJxC9u2vztxLuPT/UjlH7m/nWRQ/MgGL0v8BJHoZdVGJd18Kck+a0e55wGXdqyHO+4IQTk0685g4MUw==", + "license": "MIT", + "dependencies": { + "idb": "^7.0.1", + "workbox-core": "6.6.0" + } + }, + "node_modules/workbox-broadcast-update": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-6.6.0.tgz", + "integrity": "sha512-nm+v6QmrIFaB/yokJmQ/93qIJ7n72NICxIwQwe5xsZiV2aI93MGGyEyzOzDPVz5THEr5rC3FJSsO3346cId64Q==", + "license": "MIT", + "dependencies": { + "workbox-core": "6.6.0" + } + }, + "node_modules/workbox-build": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-6.6.0.tgz", + "integrity": "sha512-Tjf+gBwOTuGyZwMz2Nk/B13Fuyeo0Q84W++bebbVsfr9iLkDSo6j6PST8tET9HYA58mlRXwlMGpyWO8ETJiXdQ==", + "license": "MIT", + "dependencies": { + "@apideck/better-ajv-errors": "^0.3.1", + "@babel/core": "^7.11.1", + "@babel/preset-env": "^7.11.0", + "@babel/runtime": "^7.11.2", + "@rollup/plugin-babel": "^5.2.0", + "@rollup/plugin-node-resolve": "^11.2.1", + "@rollup/plugin-replace": "^2.4.1", + "@surma/rollup-plugin-off-main-thread": "^2.2.3", + "ajv": "^8.6.0", + "common-tags": "^1.8.0", + "fast-json-stable-stringify": "^2.1.0", + "fs-extra": "^9.0.1", + "glob": "^7.1.6", + "lodash": "^4.17.20", + "pretty-bytes": "^5.3.0", + "rollup": "^2.43.1", + "rollup-plugin-terser": "^7.0.0", + "source-map": "^0.8.0-beta.0", + "stringify-object": "^3.3.0", + "strip-comments": "^2.0.1", + "tempy": "^0.6.0", + "upath": "^1.2.0", + "workbox-background-sync": "6.6.0", + "workbox-broadcast-update": "6.6.0", + "workbox-cacheable-response": "6.6.0", + "workbox-core": "6.6.0", + "workbox-expiration": "6.6.0", + "workbox-google-analytics": "6.6.0", + "workbox-navigation-preload": "6.6.0", + "workbox-precaching": "6.6.0", + "workbox-range-requests": "6.6.0", + "workbox-recipes": "6.6.0", + "workbox-routing": "6.6.0", + "workbox-strategies": "6.6.0", + "workbox-streams": "6.6.0", + "workbox-sw": "6.6.0", + "workbox-window": "6.6.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/workbox-build/node_modules/@apideck/better-ajv-errors": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.6.tgz", + "integrity": "sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==", + "license": "MIT", + "dependencies": { + "json-schema": "^0.4.0", + "jsonpointer": "^5.0.0", + "leven": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "ajv": ">=8" + } + }, + "node_modules/workbox-build/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/workbox-build/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/workbox-build/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/workbox-build/node_modules/source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "license": "BSD-3-Clause", + "dependencies": { + "whatwg-url": "^7.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/workbox-build/node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "license": "MIT", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/workbox-build/node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "license": "BSD-2-Clause" + }, + "node_modules/workbox-build/node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "license": "MIT", + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/workbox-cacheable-response": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-6.6.0.tgz", + "integrity": "sha512-JfhJUSQDwsF1Xv3EV1vWzSsCOZn4mQ38bWEBR3LdvOxSPgB65gAM6cS2CX8rkkKHRgiLrN7Wxoyu+TuH67kHrw==", + "deprecated": "workbox-background-sync@6.6.0", + "license": "MIT", + "dependencies": { + "workbox-core": "6.6.0" + } + }, + "node_modules/workbox-core": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.6.0.tgz", + "integrity": "sha512-GDtFRF7Yg3DD859PMbPAYPeJyg5gJYXuBQAC+wyrWuuXgpfoOrIQIvFRZnQ7+czTIQjIr1DhLEGFzZanAT/3bQ==", + "license": "MIT" + }, + "node_modules/workbox-expiration": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-6.6.0.tgz", + "integrity": "sha512-baplYXcDHbe8vAo7GYvyAmlS4f6998Jff513L4XvlzAOxcl8F620O91guoJ5EOf5qeXG4cGdNZHkkVAPouFCpw==", + "license": "MIT", + "dependencies": { + "idb": "^7.0.1", + "workbox-core": "6.6.0" + } + }, + "node_modules/workbox-google-analytics": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-6.6.0.tgz", + "integrity": "sha512-p4DJa6OldXWd6M9zRl0H6vB9lkrmqYFkRQ2xEiNdBFp9U0LhsGO7hsBscVEyH9H2/3eZZt8c97NB2FD9U2NJ+Q==", + "deprecated": "It is not compatible with newer versions of GA starting with v4, as long as you are using GAv3 it should be ok, but the package is not longer being maintained", + "license": "MIT", + "dependencies": { + "workbox-background-sync": "6.6.0", + "workbox-core": "6.6.0", + "workbox-routing": "6.6.0", + "workbox-strategies": "6.6.0" + } + }, + "node_modules/workbox-navigation-preload": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-6.6.0.tgz", + "integrity": "sha512-utNEWG+uOfXdaZmvhshrh7KzhDu/1iMHyQOV6Aqup8Mm78D286ugu5k9MFD9SzBT5TcwgwSORVvInaXWbvKz9Q==", + "license": "MIT", + "dependencies": { + "workbox-core": "6.6.0" + } + }, + "node_modules/workbox-precaching": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-6.6.0.tgz", + "integrity": "sha512-eYu/7MqtRZN1IDttl/UQcSZFkHP7dnvr/X3Vn6Iw6OsPMruQHiVjjomDFCNtd8k2RdjLs0xiz9nq+t3YVBcWPw==", + "license": "MIT", + "dependencies": { + "workbox-core": "6.6.0", + "workbox-routing": "6.6.0", + "workbox-strategies": "6.6.0" + } + }, + "node_modules/workbox-range-requests": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-6.6.0.tgz", + "integrity": "sha512-V3aICz5fLGq5DpSYEU8LxeXvsT//mRWzKrfBOIxzIdQnV/Wj7R+LyJVTczi4CQ4NwKhAaBVaSujI1cEjXW+hTw==", + "license": "MIT", + "dependencies": { + "workbox-core": "6.6.0" + } + }, + "node_modules/workbox-recipes": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-6.6.0.tgz", + "integrity": "sha512-TFi3kTgYw73t5tg73yPVqQC8QQjxJSeqjXRO4ouE/CeypmP2O/xqmB/ZFBBQazLTPxILUQ0b8aeh0IuxVn9a6A==", + "license": "MIT", + "dependencies": { + "workbox-cacheable-response": "6.6.0", + "workbox-core": "6.6.0", + "workbox-expiration": "6.6.0", + "workbox-precaching": "6.6.0", + "workbox-routing": "6.6.0", + "workbox-strategies": "6.6.0" + } + }, + "node_modules/workbox-routing": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.6.0.tgz", + "integrity": "sha512-x8gdN7VDBiLC03izAZRfU+WKUXJnbqt6PG9Uh0XuPRzJPpZGLKce/FkOX95dWHRpOHWLEq8RXzjW0O+POSkKvw==", + "license": "MIT", + "dependencies": { + "workbox-core": "6.6.0" + } + }, + "node_modules/workbox-strategies": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.6.0.tgz", + "integrity": "sha512-eC07XGuINAKUWDnZeIPdRdVja4JQtTuc35TZ8SwMb1ztjp7Ddq2CJ4yqLvWzFWGlYI7CG/YGqaETntTxBGdKgQ==", + "license": "MIT", + "dependencies": { + "workbox-core": "6.6.0" + } + }, + "node_modules/workbox-streams": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-6.6.0.tgz", + "integrity": "sha512-rfMJLVvwuED09CnH1RnIep7L9+mj4ufkTyDPVaXPKlhi9+0czCu+SJggWCIFbPpJaAZmp2iyVGLqS3RUmY3fxg==", + "license": "MIT", + "dependencies": { + "workbox-core": "6.6.0", + "workbox-routing": "6.6.0" + } + }, + "node_modules/workbox-sw": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-6.6.0.tgz", + "integrity": "sha512-R2IkwDokbtHUE4Kus8pKO5+VkPHD2oqTgl+XJwh4zbF1HyjAbgNmK/FneZHVU7p03XUt9ICfuGDYISWG9qV/CQ==", + "license": "MIT" + }, + "node_modules/workbox-webpack-plugin": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-6.6.0.tgz", + "integrity": "sha512-xNZIZHalboZU66Wa7x1YkjIqEy1gTR+zPM+kjrYJzqN7iurYZBctBLISyScjhkJKYuRrZUP0iqViZTh8rS0+3A==", + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "^2.1.0", + "pretty-bytes": "^5.4.1", + "upath": "^1.2.0", + "webpack-sources": "^1.4.3", + "workbox-build": "6.6.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "webpack": "^4.4.0 || ^5.9.0" + } + }, + "node_modules/workbox-webpack-plugin/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/workbox-webpack-plugin/node_modules/webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "license": "MIT", + "dependencies": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "node_modules/workbox-window": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-6.6.0.tgz", + "integrity": "sha512-L4N9+vka17d16geaJXXRjENLFldvkWy7JyGxElRD0JvBxvFEd8LOhr+uXCcar/NzAmIBRv9EZ+M+Qr4mOoBITw==", + "license": "MIT", + "dependencies": { + "@types/trusted-types": "^2.0.2", + "workbox-core": "6.6.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "license": "Apache-2.0" + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "license": "MIT" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC" + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/debugger/debugger_gui/package.json b/debugger/debugger_gui/package.json new file mode 100644 index 00000000..cb8176f9 --- /dev/null +++ b/debugger/debugger_gui/package.json @@ -0,0 +1,47 @@ +{ + "name": "debugger-gui", + "version": "0.1.0", + "private": true, + "dependencies": { + "@emotion/react": "^11.11.4", + "@emotion/styled": "^11.11.5", + "@mui/icons-material": "^5.16.0", + "@mui/material": "^5.16.0", + "@testing-library/jest-dom": "^5.17.0", + "@testing-library/react": "^13.4.0", + "@testing-library/user-event": "^13.5.0", + "axios": "^1.7.2", + "emoji-mart": "^5.6.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-scripts": "5.0.1", + "web-vitals": "^2.1.4" + }, + "scripts": { + "start": "cross-env PORT=6970 react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "devDependencies": { + "cross-env": "^7.0.3" + } +} diff --git a/debugger/debugger_gui/public/index.html b/debugger/debugger_gui/public/index.html new file mode 100644 index 00000000..aa0a2b3b --- /dev/null +++ b/debugger/debugger_gui/public/index.html @@ -0,0 +1,43 @@ + + + + + + + + + + + + + CL-Debug + + + +
+ + + diff --git a/debugger/debugger_gui/public/logo.png b/debugger/debugger_gui/public/logo.png new file mode 100644 index 00000000..716c9202 Binary files /dev/null and b/debugger/debugger_gui/public/logo.png differ diff --git a/debugger/debugger_gui/public/manifest.json b/debugger/debugger_gui/public/manifest.json new file mode 100644 index 00000000..080d6c77 --- /dev/null +++ b/debugger/debugger_gui/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/debugger/debugger_gui/public/robots.txt b/debugger/debugger_gui/public/robots.txt new file mode 100644 index 00000000..e9e57dc4 --- /dev/null +++ b/debugger/debugger_gui/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/debugger/debugger_gui/src/App.css b/debugger/debugger_gui/src/App.css new file mode 100644 index 00000000..04f3ff2a --- /dev/null +++ b/debugger/debugger_gui/src/App.css @@ -0,0 +1,29 @@ +.app-container { + display: flex; + flex-direction: column; + width: 90vw; + height: 100%; + padding-left: 5vw; + padding-right: 5vw; + padding-bottom: 5vh; + align-items: center; +} + +.app-header { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + + width: 100%; + height: 10vh; + + font-family: 'Consolas', monospace; + font-size: 3.45rem; + font-weight: bold; /* Changed from 900 to bold */ + color: #d8d8d8; + text-align: center; + + margin-bottom: 2vh; + margin-top: 2vh; +} \ No newline at end of file diff --git a/debugger/debugger_gui/src/App.js b/debugger/debugger_gui/src/App.js new file mode 100644 index 00000000..ef8d4122 --- /dev/null +++ b/debugger/debugger_gui/src/App.js @@ -0,0 +1,223 @@ +import React, { useState } from 'react'; +import { Container } from '@mui/material'; +// import PullButton from './components/pull-button/PullButton'; // Import the FetchButton component +import SyncSection from './components/sync-section/SyncSection'; // Import the FetchButton component + +import Tree from './components/tree/Tree'; // Import the Tree component +import './App.css'; + +const App = () => { + const [projectStructure, setProjectStructure] = useState(null); + const [expanded, setExpanded] = useState({}); + + const handleExpandClick = (id) => { + setExpanded((prevExpanded) => ({ ...prevExpanded, [id]: !prevExpanded[id] })); + }; + + const handleCheckboxChange = (nodeId, checked) => { + console.log("Handle checkbox change on node: ", nodeId, " with checked: ", checked); // Log the checkbox change + + const updateNode = (nodes, pathParts, checked, forceCheck = false) => { + return nodes.map((node) => { + if (node.name === pathParts[0]) { + // Check if the node should be force-checked due to a child being checked + const shouldCheck = checked || forceCheck; + + if (pathParts.length === 1) { + console.log("Node found: ", node.name, " With current checked: ", node.is_toggled); + return { ...node, is_toggled: shouldCheck, children: updateChildren(node.children, shouldCheck) }; + } + + // Recursively update children + if (node.children) { + const updatedChildren = updateNode(node.children, pathParts.slice(1), checked, shouldCheck); + return { ...node, is_toggled: shouldCheck, children: updatedChildren }; + } + + return { ...node, is_toggled: shouldCheck }; + } + return node; + }); + }; + + // Function to update the state of child nodes + const updateChildren = (children, checked) => { + if (!children) return []; + return children.map((child) => ({ + ...child, + is_toggled: checked, + children: updateChildren(child.children, checked) + })); + }; + + setProjectStructure((prevStructure) => { + if (!prevStructure) return prevStructure; + const pathParts = nodeId.split('/'); + const updatedStructure = updateNode(prevStructure, pathParts, checked); + return updatedStructure; + }); + }; + + const handleEmojiChange = (nodeId, emoji) => { + console.log("Handle emoji change on node: ", nodeId, " with emoji: ", emoji); + + // Function to recursively propagate the emoji to all children but not update the ancestors + const propagateEmojiToChildren = (node) => { + if (!node.children) return node; // If no children, return the node as is + + const updatedChildren = node.children.map((child) => ({ + ...child, + emoji, // Set the new emoji to the child node + children: propagateEmojiToChildren(child).children, // Recursively propagate to deeper children + })); + + return { ...node, children: updatedChildren }; // Update the node's children but not the node itself + }; + + const updateNode = (nodes, pathParts, emoji) => { + return nodes.map((node) => { + if (node.name === pathParts[0]) { + // Update the emoji of the node itself if this is the target node + let updatedNode = { ...node }; + + if (pathParts.length === 1) { + // This is the target node, update its emoji + updatedNode.emoji = emoji; + } + + // If this node has children and we haven't reached the target node yet + if (node.children && pathParts.length > 1) { + const updatedChildren = updateNode(node.children, pathParts.slice(1), emoji); + updatedNode = { ...updatedNode, children: updatedChildren }; + } + + // If we've reached the target node, propagate the emoji to its children + if (pathParts.length === 1 && node.children) { + updatedNode.children = propagateEmojiToChildren(node).children; + } + + return updatedNode; + } + + return node; // No match, return the node as is + }); + }; + + setProjectStructure((prevStructure) => { + if (!prevStructure) return prevStructure; + + const pathParts = nodeId.split('/'); + const updatedStructure = updateNode(prevStructure, pathParts, emoji); + + return updatedStructure; + }); + }; + + + + + const defaultColor = '#ff0000'; // Define the default color + + const handleColorChange = (nodeId, color) => { + console.log("Handle color change on node: ", nodeId, " with color: ", color); // Log the color change + const amount = 50; // Define the amount to lighten the color + + const updateNode = (nodes, pathParts, color, isOriginalParent = false) => { + console.log("HIT updateNode with color: ", color); // Log the color + return nodes.map((node) => { + if (node.name === pathParts[0]) { + let name = node.name; + let newColor = node.color; + let is_toggled = node.is_toggled; + let set_manually = node.set_manually; + + // If the node is the target node + if (pathParts.length === 1) { + console.log("Node found: ", node.name, " With current color: ", node.color); + console.log("Setting color to: ", color); + console.log("Is original parent: ", isOriginalParent, " Set manually: ", set_manually); + if (isOriginalParent) { + set_manually = true; + } + newColor = color; + } + + if (node.children) { + // Update children nodes + const updatedChildren = updateNode(node.children, pathParts.slice(1), color, isOriginalParent); + + // Check and propagate the color to children if needed + const propagatedChildren = updatedChildren.map((child) => { + console.log("Checking child: ", child.name); + console.log("Child set manually: ", child.set_manually); + if (!child.set_manually) { + console.log("Propagating color to child: ", child.name); + const newPath = [...pathParts.slice(1), child.name]; + return updateNode([child], newPath, lightenColor(color, amount), false)[0]; + } + return child; + }); + console.log("RETURN 1 node: ", node.name, " with color: ", newColor); + return { + ...node, + name: name, + color: newColor, + is_toggled: is_toggled, + set_manually: set_manually, + children: propagatedChildren + }; + } + console.log("RETURN 2 node: ", node.name, " with color: ", newColor); + return { ...node, name: name, color: newColor, is_toggled, set_manually}; + } + return node; + }); + }; + + // Helper function to lighten a color (example implementation) + const lightenColor = (color, amount = 50) => { + if (!color || typeof color !== 'string' || !color.startsWith('#') || color.length !== 7) { + throw new Error('Invalid color format. Expected format is #RRGGBB. But got: ' + color); + } + + const colorInt = parseInt(color.slice(1), 16); + const r = Math.min(255, (colorInt >> 16) + amount); + const g = Math.min(255, ((colorInt >> 8) & 0x00FF) + amount); + const b = Math.min(255, (colorInt & 0x0000FF) + amount); + const newColorInt = (r << 16) + (g << 8) + b; + // Corrected console.log statement + console.log(`Old color: ${color}, New color: #${newColorInt.toString(16).padStart(6, '0')}`); // Log the old and new color + return `#${newColorInt.toString(16).padStart(6, '0')}`; + }; + + setProjectStructure((prevStructure) => { + if (!prevStructure) return prevStructure; + const pathParts = nodeId.split('/'); + const updatedStructure = updateNode(prevStructure, pathParts, color, true); + console.log('Updated structure:', updatedStructure); + return updatedStructure; + }); + }; + + + return ( +
+
+ Cluster Labs Debugger v0 +
+ {/* */} + {/* */} + + +
+ ); +}; + +export default App; diff --git a/debugger/debugger_gui/src/assets/collapsed.png b/debugger/debugger_gui/src/assets/collapsed.png new file mode 100644 index 00000000..accf4f2d Binary files /dev/null and b/debugger/debugger_gui/src/assets/collapsed.png differ diff --git a/debugger/debugger_gui/src/assets/color-picker.png b/debugger/debugger_gui/src/assets/color-picker.png new file mode 100644 index 00000000..e47cabdb Binary files /dev/null and b/debugger/debugger_gui/src/assets/color-picker.png differ diff --git a/debugger/debugger_gui/src/assets/color-reset.png b/debugger/debugger_gui/src/assets/color-reset.png new file mode 100644 index 00000000..930e34e8 Binary files /dev/null and b/debugger/debugger_gui/src/assets/color-reset.png differ diff --git a/debugger/debugger_gui/src/assets/curved-arrow-clip-art.png b/debugger/debugger_gui/src/assets/curved-arrow-clip-art.png new file mode 100644 index 00000000..0d52464b Binary files /dev/null and b/debugger/debugger_gui/src/assets/curved-arrow-clip-art.png differ diff --git a/debugger/debugger_gui/src/assets/emojis.js b/debugger/debugger_gui/src/assets/emojis.js new file mode 100644 index 00000000..feea3656 --- /dev/null +++ b/debugger/debugger_gui/src/assets/emojis.js @@ -0,0 +1,128 @@ +// Description: List of emojis for the debugger +export const emojiList = { + "R": [ + '👽','👾','🤖','🦠','🚀','🛸', + ], + "S": [ + '😀','😃','😄','😁','😆','😅','🤣','😂','🙂','🙃','😉','😊', + '😇','🥰','😍','🤩','😘','😗','☺','😚','😙','😋','😛','😜', + '🤪','😝','🤑','🤗','🤭','🤫','🤔','🤐','🤨','😐', + '😑','😶','😏','😒','🙄','😬','🤥', + '😌','😔','😪','🤤','😴','😷','🤒','🤕','🤢','🤮','🤧', + '🥵','🥶','🥴','😵','🤯','🤠','🥳','😎','🤓','🧐', + '😕','😟','🙁','☹','😮','😯','😲','😳','🥺','😦','😧', + '😨','😰','😥','😢','😭','😱','😖','😣','😞','😓','😩','😫', + '🥱','😤','😡','😠','🤬','😈','👿','💀','☠','💩','🤡','👹', + '👺','👻','👋','🤚','🖐','✋','🖖','👌','🤏','✌','🤞','🤟','🤘','🤙','👈','👉', + '👆','🖕','👇','☝','👍','👎','✊','👊','🤛','🤜','👏','🙌','👐','🤲','🤝','🙏','✍','💅','🤳', + '💪','🦾','🦿','🦵','🦶','👂','🦻','👃','🧠','🦷','🦴','👀','👁', + '👅','👄','👶','🧒','👦','👧','🧑','👱','👨','🧔','👨‍🦰', + '👨‍🦱','👨‍🦳','👨‍🦲','👩','👩‍🦰','👩‍🦱','👩‍🦳','👩‍🦲','👱‍♀️', + '👱‍♂️','🧓','👴','👵','🙍','🙍‍♂️','🙍‍♀️','🙎','🙎‍♂️','🙎‍♀️','🙅','🙅‍♂️','🙅‍♀️','🙆','🙆‍♂️', + '🙆‍♀️','💁','💁‍♂️','💁‍♀️','🙋','🙋‍♂️','🙋‍♀️','🧏','🧏‍♂️','🧏‍♀️','🙇','🙇‍♂️','🙇‍♀️','🤦','🤦‍♂️', + '🤦‍♀️','🤷','🤷‍♂️','🤷‍♀️','👨‍⚕️','👩‍⚕️','👨‍🎓','👩‍🎓','👨‍🏫','👩‍🏫', + '👨‍⚖️','👩‍⚖️','👨‍🌾','👩‍🌾','👨‍🍳','👩‍🍳','👨‍🔧','👩‍🔧','👨‍🏭','👩‍🏭', + '👨‍💼','👩‍💼',,'👨‍🔬','👩‍🔬','👨‍💻','👩‍💻','👨‍🎤','👩‍🎤','👨‍🎨', + '👩‍🎨','👨‍✈️','👩‍✈️','👨‍🚀','👩‍🚀','👨‍🚒','👩‍🚒','👮','👮‍♂️','👮‍♀️','🕵','🕵️‍♂️', + '🕵️‍♀️','💂','💂‍♂️','💂‍♀️','👷','👷‍♂️','👷‍♀️','🤴','👸','👳','👳‍♂️','👳‍♀️','👲','🧕','🤵', + '👰','🤰','🤱','👼','🎅', + '🤶','🦸','🦸‍♂️','🦸‍♀️','🦹','🦹‍♂️','🦹‍♀️','🧙','🧙‍♂️','🧙‍♀️','🧚','🧚‍♂️','🧚‍♀️','🧛', + '🧛‍♂️','🧛‍♀️','🧜','🧜‍♂️','🧜‍♀️','🧝','🧝‍♂️','🧝‍♀️','🧞','🧞‍♂️','🧞‍♀️','🧟','🧟‍♂️','🧟‍♀️','💆', + '💆‍♂️','💆‍♀️','💇','💇‍♂️','💇‍♀️','🚶','🚶‍♂️','🚶‍♀️','🧍','🧍‍♂️','🧍‍♀️','🧎', + '🧎‍♂️','🧎‍♀️','👨‍🦯','👩‍🦯', + '👨‍🦼','👩‍🦼','👨‍🦽','👩‍🦽','🏃', + '🏃‍♂️','🏃‍♀️','💃','🕺','🕴','👯','👯‍♂️','👯‍♀️','🧖','🧖‍♂️','🧖‍♀️','🧗', + '🧗‍♂️','🧗‍♀️','🤺','🏇','⛷','🏂','🏌','🏌️‍♂️','🏌️‍♀️','🏄','🏄‍♂️','🏄‍♀️','🚣','🚣‍♂️','🚣‍♀️','🏊', + '🏊‍♂️','🏊‍♀️','⛹','⛹️‍♂️','⛹️‍♀️','🏋','🏋️‍♂️','🏋️‍♀️','🚴','🚴‍♂️','🚴‍♀️','🚵','🚵‍♂️','🚵‍♀️','🤸','🤸‍♂️', + '🤸‍♀️','🤼','🤼‍♂️','🤼‍♀️','🤽','🤽‍♂️','🤽‍♀️','🤾','🤾‍♂️','🤾‍♀️','🤹','🤹‍♂️','🤹‍♀️','🧘','🧘‍♂️','🧘‍♀️', + '🛀','🛌','🗣','👤','👥','👪','👣','🦰','🦱','🦳','🦲', + ], + "A": [ + '🐕','🦮','🐕‍🦺','🐩','🐺','🦊','🦝','🐱','😺','😸','😹','😻','😼','😽','🙀','😿','😾','🙈','🙉','🙊','🐵','🐒','🦍','🦧', + '🐈','🦁','🐯','🐅','🐆','🐴','🐎','🦄','🦓','🦌', + '🐮','🐂','🐃','🐄','🐷','🐖','🐗','🐽','🐏','🐑','🐐','🐪','🐫','🦙', + '🦒','🐘','🦏','🦛','🐭','🐁','🐀','🐹','🐰','🐇','🐿','🦔','🦇', + '🐻','🐨','🐼','🦥','🦦','🦨','🦘','🦡','🐾','🦃','🐔','🐓','🐣', + '🐤','🐥','🐦','🐧','🕊','🦅','🦆','🦢','🦉','🦩','🦚','🦜', + '🐸','🐊','🐢','🦎','🐍','🐲','🐉','🦕','🦖','🐳','🐋','🐬','🐟', + '🐠','🐡','🦈','🐙','🐚','🐌','🦋','🐛','🐜','🐝','🐞','🦗','🕷', + '🕸','🦂','🦟','🍇','🍈', + '🛎','💐','🌸','💮','🏵','🌹','🥀','🌺','🌻','🌼','🌷', + '🌱','🌲','🌳','🌴','🌵','🌾','🌿','☘','🍀','🍁','🍂','🍃','🍄','🎃','🎄','🎋','🎍', + ], + "F": [ + '🍉','🍊','🍋','🍌','🍍','🥭','🍎','🍏','🍐','🍑','🍒','🍓','🥝','🍅', + '🥥','🥑','🍆','🥔','🥕','🌽','🌶','🥒','🥬','🥦','🧄','🧅','🥜','🌰', + '🍞','🥐','🥖','🥨','🥯','🥞','🧇','🧀','🍖','🍗','🥩','🥓','🍔','🍟','🍕', + '🌭','🥪','🌮','🌯','🥙','🧆','🥚','🍳','🥘','🍲','🥣','🥗','🍿','🧈','🧂','🥫', + '🍱','🍘','🍙','🍚','🍛','🍜','🍝','🍠','🍢','🍣','🍤','🍥','🥮','🍡','🥟','🥠','🥡', + '🦀','🦞','🦐','🦑','🦪','🍦','🍧','🍨','🍩','🍪','🎂','🍰','🧁','🥧','🍫','🍬','🍭','🍮', + '🍯','🍼','🥛','☕','🍵','🍶','🍾','🍷','🍸','🍹','🍺','🍻','🥂','🥃','🥤','🧃', + '🧉','🧊','🥢','🍽','🍴','🥄','🔪', + ], + "G": [ + '⚽','⚾','🥎','🏀','🏐','🏈','🏉','🎾','🥏', + '🎳','🏏','🏑','🏒','🥍','🏓','🏸','🥊','🥋','🥅','⛳','⛸','🎣','🤿','🎽','🎿','🛷','🥌', + '♟','🃏','🀄','🎴','🎭','🖼','🎨','🧵','🎖','🏆','🏅','🥇','🥈','🥉', + '🎯','🪀','🪁','🔫','🎱','🔮','🎮','🕹','🎰','🎲','🧩','🧸','♠','♥','♦','♣', + ], + "W": [ + '🌍','🌎','🌏','🌐','🗺','🗾','🧭','🏔','⛰','🌋', + '🗻','🏕','🏖','🏜','🏝','🏞','🏟','🏛','🏗','🧱','🏘','🏚','🏠','🏡','🏢','🏣','🏤', + '🏥','🏦','🏨','🏩','🏪','🏫','🏬','🏭','🏯','🏰','💒','🗼','🗽','⛪','🕌','🛕','🕍','⛩', + '🕋','⛲','⛺','🌁','🌃','🏙','🌄','🌅','🌆','🌇','🌉','♨','🎠','🎡','🎢','💈','🎪','🚂', + '🚃','🚄','🚅','🚆','🚇','🚈','🚉','🚊','🚝','🚞','🚋','🚌','🚍','🚎','🚐','🚑','🚒','🚓', + '🚔','🚕','🚖','🚗','🚘','🚙','🚚','🚛','🚜','🏎','🏍','🛵','🦽','🦼','🛺','🚲','🛴','🛹', + '🚏','🛣','🛤','🛢','⛽','🚨','🚥','🚦','🛑','🚧','⚓','⛵','🛶','🚤','🛳','⛴','🛥', + '🚢','✈','🛩','🛫','🛬','🪂','💺','🚁','🚟','🚠','🚡','🛰','🧳','🎎','🎏','🎐','🎑','🧧','🎀','🎁', + '🎗','🎟','🎫','🏺', + ], + "T": [ + '⌛','⏳','⌚', + '⏰','⏱','⏲','🕰','🕛','🕧','🕐','🕜','🕑','🕝','🕒','🕞','🕓','🕟','🕔','🕠','🕕','🕡', + '🕖','🕢','🕗','🕣','🕘','🕤','🕙','🕥','🕚','🕦','🌑','🌒','🌓','🌔','🌕','🌖','🌗','🌘', + '🌙','🌚','🌛','🌜','🌡','☀','🌝','🌞','🪐','⭐','🌟','🌠','🌌','☁','⛅','⛈','🌤','🌥','🌦', + '🌧','🌨','🌩','🌪','🌫','🌬','🌀','🌈','🌂','☂','☔','⛱','⚡','❄','☃','⛄','☄','🔥','💧','🌊', + '🎆','🎇','🧨','✨','🎈','🎉','🎊', + ], + "X": [ + '💻','🖥','🖨','⌨','🖱','🖲','💽','💾','💿','📀','🧮','🎥','🎞','📽', + '🎬','📺','📷','📸','📹','📼','🔍','🔎','🕯','💡','🔦', + '🎙','🎚','🎛','🎤','🎧','📻','🎷','🎸', + '🎹','🎺','🎻','🪕','🥁','📱','📲','☎','📞','📟','📠','🔋', + '🔌','🏮','🪔','📔','📕','💌', + '📖','📗','📘','📙','📚','📓','📒','📃','📜','📄','📰','🗞','📑','🔖','🏷', + '💰','💴','💵','💶','💷','💸','💳','🧾','💹','✉','📧','📨','📩','📤', + '📥','📦','📫','📪','📬','📭','📮','🗳','✏','✒','🖋','🖊','🖌','🖍','📝', + '💼','📁','📂','🗂','📅','📆','🗒','🗓','📇','📈','📉','📊','📋','📌','📍', + '📎','🖇','📏','📐','✂','🗃','🗄','🗑','🔒','🔓','🔏','🔐','🔑','🗝','🔨','🪓', + '⛏','⚒','🛠','🗡','⚔','💣','🏹','🛡','🔧','🔩','⚙','🗜','⚖','🦯', + '🔗','⛓','🧰','🧲','⚗','🧪','🧫','🧬','🔬','🔭','📡','💉','🩸', + '💊','🩹','🩺','🚪','🛏','🛋','🪑','🚽','🚿','🛁','🪒', + '🧴','🧷','🧹','🧺','🧻','🧼','🧽','🧯','🛒','🚬','⚰','⚱','🧿', + ], + "O": [ + '💘','💝','💖','💗','💓','💞','💕','💟','❣','💔', + '❤','🧡','💛','💚','💙','💜','🤎','🖤','🤍', + '💋','💯','💢','💥','💫','💦','💨','🕳','💬','👁️‍🗨️','🗨', + '🗯','💭','💤','🧶','👓','🕶','🥽','🥼','🦺','👔','👕','👖','🧣','🧤','🧥', + '🧦','👗','👘','🥻','🩱','🩲','🩳','👙','👚','👛','👜','👝','🛍', + '🎒','👞','👟','🥾','🥿','👠','👡','🩰','👢','👑','👒','🎩', + '🎓','🧢','⛑','📿','💄','💍','💎','🔇','🔈','🔉','🔊','📢','📣', + '📯','🔔','🔕','🎼','🎵','🎶', + '🗿','🏧','🚮','🚰','♿','🚹','🚺','🚻','🚼','🚾','🛂','🛃','🛄', + '🛅','⚠','🚸','⛔','🚫','🚳','🚭','🚯','🚱','🚷','📵','🔞','☢','☣','⬆','↗', + '➡','↘','⬇','↙','⬅','↖','↕','↔','↩','↪','⤴','⤵','🔃','🔄','🔙','🔚','🔛','🔜', + '🔝','🛐','⚛','🕉','✡','☸','☯','✝','☦','☪','☮','🕎','🔯','♈','♉','♊', + '♋','♌','♍','♎','♏','♐','♑','♒','♓','⛎','🔀','🔁','🔂','▶','⏩','⏭', + '⏯','◀','⏪','⏮','🔼','⏫','🔽','⏬','⏸','⏹','⏺','⏏','🎦','🔅','🔆','📶', + '📳','📴','♀','♂','⚧','✖','➕','➖','➗','♾','‼','⁉','❓','❔','❕','❗','〰', + '💱','💲','⚕','♻','⚜','🔱','📛','🔰','⭕','✅','☑','✔','❌','❎','➰','➿','〽','✳', + '✴','❇','©','®','™','#️⃣','*️⃣','0️⃣','1️⃣','2️⃣','3️⃣','4️⃣','5️⃣','6️⃣','7️⃣','8️⃣','9️⃣', + '🔟','🔠','🔡','🔢','🔣','🔤','🅰','🆎','🅱','🆑','🆒','🆓','ℹ','🆔','Ⓜ','🆕','🆖', + '🅾','🆗','🅿','🆘','🆙','🆚','🈁','🈂','🈷','🈶','🈯','🉐','🈹','🈚','🈲','🉑','🈸', + '🈴','🈳','㊗','㊙','🈺','🈵','🔴','🟠','🟡','🟢','🔵','🟣','🟤','⚫','⚪','🟥', + '🟧','🟨','🟩','🟦','🟪','🟫','⬛','⬜','◼','◻','◾','◽','▪','▫','🔶','🔷','🔸','🔹', + '🔺','🔻','💠','🔘','🔳','🔲','🏁','🚩','🎌','🏴','🏳','🏳️‍⚧️','🏴‍☠️', + ], +} \ No newline at end of file diff --git a/debugger/debugger_gui/src/assets/expanded.png b/debugger/debugger_gui/src/assets/expanded.png new file mode 100644 index 00000000..97588440 Binary files /dev/null and b/debugger/debugger_gui/src/assets/expanded.png differ diff --git a/debugger/debugger_gui/src/assets/next-page.png b/debugger/debugger_gui/src/assets/next-page.png new file mode 100644 index 00000000..64e3502a Binary files /dev/null and b/debugger/debugger_gui/src/assets/next-page.png differ diff --git a/debugger/debugger_gui/src/assets/prev-page.png b/debugger/debugger_gui/src/assets/prev-page.png new file mode 100644 index 00000000..a3f2f9b5 Binary files /dev/null and b/debugger/debugger_gui/src/assets/prev-page.png differ diff --git a/debugger/debugger_gui/src/assets/pull.png b/debugger/debugger_gui/src/assets/pull.png new file mode 100644 index 00000000..adbcc814 Binary files /dev/null and b/debugger/debugger_gui/src/assets/pull.png differ diff --git a/debugger/debugger_gui/src/assets/push.png b/debugger/debugger_gui/src/assets/push.png new file mode 100644 index 00000000..146cd4aa Binary files /dev/null and b/debugger/debugger_gui/src/assets/push.png differ diff --git a/debugger/debugger_gui/src/components/color-reset/ColorReset.css b/debugger/debugger_gui/src/components/color-reset/ColorReset.css new file mode 100644 index 00000000..1ed9bf3e --- /dev/null +++ b/debugger/debugger_gui/src/components/color-reset/ColorReset.css @@ -0,0 +1,57 @@ +.color-button-container { + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-start; + padding: 0; + margin: 0; +} + +.color-button-img { + height: 100px; +} + +.color-button-content { + display: flex; + flex-direction: column; + justify-content: center; +} + +.color-button { + background-color: rgba(255, 255, 255, 0); + color: black; + border: none; + font-size: 1.45rem; + font-weight: bold; /* Changed from 900 to bold */ + font-family: 'Consolas', monospace; + cursor: pointer; + border-radius: 400px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 10px; +} + +.color-button:hover { + background-color: #9292926c; +} + +.color-button:active { + background-color: #929292ce; +} + +.color-button:focus { + outline: none; +} + +.color-button-container p { + color: #D9D9D9; + font-family: 'Consolas', monospace; + margin: 0; + text-align: center; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} \ No newline at end of file diff --git a/debugger/debugger_gui/src/components/color-reset/ColorReset.js b/debugger/debugger_gui/src/components/color-reset/ColorReset.js new file mode 100644 index 00000000..854cd9dd --- /dev/null +++ b/debugger/debugger_gui/src/components/color-reset/ColorReset.js @@ -0,0 +1,30 @@ +import React from 'react'; +import axios from 'axios'; +import pushImg from '../../assets/color-reset.png'; // Adjust the path according to your project structure +import './ColorReset.css'; // Import the CSS file + +const ColorReset = ({ projectStructure, setProjectStructure }) => { + const pushStructure = async () => { + try { + console.log("Resetting colors"); + const response = await axios.post('http://127.0.0.1:6969/reset_color'); + console.log('Project structure:', response.data); + setProjectStructure(response.data); + } catch (error) { + console.error('Error pushing project structure:', error); + } + }; + + return ( +
+
+ +

Wipe colors

+
+
+ ); +}; + +export default ColorReset; diff --git a/debugger/debugger_gui/src/components/emoji-picker/EmojiPicker.css b/debugger/debugger_gui/src/components/emoji-picker/EmojiPicker.css new file mode 100644 index 00000000..cec43b2e --- /dev/null +++ b/debugger/debugger_gui/src/components/emoji-picker/EmojiPicker.css @@ -0,0 +1,129 @@ +/* EmojiPicker.css */ + +.picker-wrapper { + position: relative; + display: inline-block; + } + + .picker-button { + font-size: 1.5rem; + background: none; + border: none; + cursor: pointer; + font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif; + } + + .emoji-popup { + position: absolute; + top: 2.5rem; + left: 0; + width: fit-content; + padding: 10px; + border: 7px solid #cccccc46; + border-radius: 35px; + background-color: #181818ef; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + z-index: 1000; + } + + .folder-section { + display: flex; + flex-direction: row; + justify-content: space-around; + height: fit-content; + width: 100%; + } + + .folder-item { + cursor: pointer; + padding: 10px; + text-align: left; + background-color: #00000000; + margin: 5px 0; + border-radius: 5px; + color: #ffffff; + } + + .folder-item:hover { + background-color: #ffffff35; + } + + .folder-item.active { + background-color: #ffffff15; /* Active color for the selected folder */ + } + + .folder-item.active:hover { + background-color: #ffffff35; /* Active color for the selected folder */ + } + + + + + .emoji-grid { + display: grid; + grid-template-columns: repeat(6, 1fr); + gap: 10px; + height: 260px; + width: 330px; + font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif; + } + + .emoji-item { + cursor: pointer; + padding: 5px; + text-align: center; + height: fit-content; + border-radius: 5px; + font-size: 1.5rem; + font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif; + } + + .emoji-item:hover { + background-color: #ffffff35; + } + + .pagination { + margin-top: 10px; + text-align: center; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + gap: 5px; + } + + .pagination button { + background-color: #00000000; + border: 0; + gap: 0; + width: 18px; + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; + padding: 0 16px; + border-radius: 50px; + color: #ffffff; + height: fit-content; + } + + .pagination button img { + height: 100px; + width: 30px; + object-fit: cover; + + } + + .pagination button:disabled { + cursor: default; + } + + .pagination button:hover { + background-color: #ffffff15; + } + + .selected-emoji-display { + margin-top: 20px; + font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif; + } + \ No newline at end of file diff --git a/debugger/debugger_gui/src/components/emoji-picker/EmojiPicker.js b/debugger/debugger_gui/src/components/emoji-picker/EmojiPicker.js new file mode 100644 index 00000000..3d793747 --- /dev/null +++ b/debugger/debugger_gui/src/components/emoji-picker/EmojiPicker.js @@ -0,0 +1,141 @@ +import React, { useState, useEffect } from "react"; +import { emojiList } from '../../assets/emojis'; +import PrevPage from '../../assets/prev-page.png'; +import NextPage from '../../assets/next-page.png'; +import './EmojiPicker.css'; // Import CSS file + +const EMOJIS_PER_PAGE = 30; + +const EmojiPicker = ({ defaultEmoji, handleEmojiChange }) => { + const folderNames = Object.keys(emojiList); // Get all folder names + const firstFolder = folderNames[0]; // Get the first folder name + const [showPicker, setShowPicker] = useState(false); + const [selectedEmoji, setSelectedEmoji] = useState(defaultEmoji || "😀"); // Initialize with the default emoji from props or fallback to smiley + const [currentPage, setCurrentPage] = useState(0); + const [currentFolder, setCurrentFolder] = useState(firstFolder); // Initialize with first folder + + // When defaultEmoji changes (e.g., from backend data), update selectedEmoji + useEffect(() => { + if (defaultEmoji) { + setSelectedEmoji(defaultEmoji); + } + }, [defaultEmoji]); + + // Handle folder click + const handleFolderClick = (folderName) => { + setCurrentFolder(folderName); // Set the current folder + setCurrentPage(0); // Reset page to 0 when switching folders + }; + + // Get the emojis for the selected folder + const emojis = currentFolder ? emojiList[currentFolder] : []; + const totalPages = Math.ceil(emojis.length / EMOJIS_PER_PAGE); + const currentEmojis = emojis.slice( + currentPage * EMOJIS_PER_PAGE, + (currentPage + 1) * EMOJIS_PER_PAGE + ); + + const handleEmojiClick = (emoji) => { + setSelectedEmoji(emoji); + setShowPicker(false); // Close the picker when an emoji is selected + handleEmojiChange(emoji); // Call the parent handler with the selected emoji + }; + + const togglePicker = () => { + setShowPicker((prev) => !prev); // Toggle the picker visibility + }; + + const goToNextFolder = () => { + const currentIndex = folderNames.indexOf(currentFolder); + const nextFolderIndex = currentIndex + 1; + if (nextFolderIndex < folderNames.length) { + setCurrentFolder(folderNames[nextFolderIndex]); + setCurrentPage(0); // Reset to the first page of the next folder + } + }; + + const goToPreviousFolder = () => { + const currentIndex = folderNames.indexOf(currentFolder); + const previousFolderIndex = currentIndex - 1; + if (previousFolderIndex >= 0) { + const previousFolder = folderNames[previousFolderIndex]; + setCurrentFolder(previousFolder); + const lastPageOfPreviousFolder = Math.ceil(emojiList[previousFolder].length / EMOJIS_PER_PAGE) - 1; + setCurrentPage(lastPageOfPreviousFolder); // Set to the last page of the previous folder + } + }; + + const goToNextPage = () => { + if (currentPage < totalPages - 1) { + setCurrentPage(currentPage + 1); + } else { + goToNextFolder(); + } + }; + + const goToPreviousPage = () => { + if (currentPage > 0) { + setCurrentPage(currentPage - 1); + } else { + goToPreviousFolder(); + } + }; + + const isLastFolder = currentFolder === folderNames[folderNames.length - 1]; + const isLastPageOfLastFolder = isLastFolder && currentPage === totalPages - 1; + const isFirstFolder = currentFolder === folderNames[0]; + const isFirstPageOfFirstFolder = isFirstFolder && currentPage === 0; + + return ( +
+ {/* The emoji picker icon that changes when an emoji is selected */} + + + {/* Hoverable emoji picker */} + {showPicker && ( +
+
+
+ +
+ {currentEmojis.map((emoji, index) => ( + handleEmojiClick(emoji)} + > + {emoji} + + ))} +
+ +
+
+ +
+ {folderNames.map((folderName, index) => ( +
handleFolderClick(folderName)} + > + {emojiList[folderName][0]} +
+ ))} +
+
+ )} +
+ ); +}; + +export default EmojiPicker; diff --git a/debugger/debugger_gui/src/components/pull-button/PullButton.css b/debugger/debugger_gui/src/components/pull-button/PullButton.css new file mode 100644 index 00000000..9cdbf300 --- /dev/null +++ b/debugger/debugger_gui/src/components/pull-button/PullButton.css @@ -0,0 +1,49 @@ +.pull-button-container { + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-start; + width: 40vw; +} + +.pull-button-img { + margin-right: 20px; + height: 100px; +} + +.pull-button-content { + display: flex; + flex-direction: column; + align-items: flex-start; +} + +.pull-button { + background-color: #A3FFA9; + color: black; + border: none; + padding: 10px 20px; + font-size: 1.45rem; + font-weight: bold; /* Changed from 900 to bold */ + font-family: 'Consolas', monospace; + cursor: pointer; + margin: 20px 0; + border-radius: 4px; +} + +.pull-button:hover { + background-color: #84d488; +} + +.pull-button:active { + background-color: #65a568; +} + +.pull-button:focus { + outline: none; +} + +.pull-button-container p { + color: #A3FFA9; + font-family: 'Consolas', monospace; + margin: 0; +} diff --git a/debugger/debugger_gui/src/components/pull-button/PullButton.js b/debugger/debugger_gui/src/components/pull-button/PullButton.js new file mode 100644 index 00000000..dcdf925a --- /dev/null +++ b/debugger/debugger_gui/src/components/pull-button/PullButton.js @@ -0,0 +1,32 @@ +import React from 'react'; +import { Button } from '@mui/material'; +import axios from 'axios'; +import pullImg from '../../assets/pull.png'; // Adjust the path according to your project structure +import './PullButton.css'; + + +const PullButton = ({ setProjectStructure }) => { + const pullStructure = async () => { + try { + const response = await axios.get('http://127.0.0.1:6969/pull_structure'); + console.log('Project structure:', response.data); + setProjectStructure(response.data); + } catch (error) { + console.error('Error fetching project structure:', error); + } + }; + + return ( +
+ Project Structure +
+ +

Pull debugger config from backend

+
+
+ ); +}; + +export default PullButton; diff --git a/debugger/debugger_gui/src/components/push-button/PushButton.css b/debugger/debugger_gui/src/components/push-button/PushButton.css new file mode 100644 index 00000000..bc2a3eaa --- /dev/null +++ b/debugger/debugger_gui/src/components/push-button/PushButton.css @@ -0,0 +1,50 @@ +.push-button-container { + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-start; + width: 40vw; + text-align: right; +} + +.push-button-img { + margin-left: 20px; + height: 100px; +} + +.push-button-content { + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.push-button { + background-color: #FFA3A4; + color: black; + border: none; + padding: 10px 20px; + font-size: 1.45rem; + font-weight: bold; /* Changed from 900 to bold */ + font-family: 'Consolas', monospace; + cursor: pointer; + margin: 20px 0; + border-radius: 4px; +} + +.push-button:hover { + background-color: #cc8182; +} + +.push-button:active { + background-color: #a86768; +} + +.push-button:focus { + outline: none; +} + +.push-button-container p { + color: #FFA3A4; + font-family: 'Consolas', monospace; + margin: 0; +} \ No newline at end of file diff --git a/debugger/debugger_gui/src/components/push-button/PushButton.js b/debugger/debugger_gui/src/components/push-button/PushButton.js new file mode 100644 index 00000000..a55c65f2 --- /dev/null +++ b/debugger/debugger_gui/src/components/push-button/PushButton.js @@ -0,0 +1,33 @@ +import React from 'react'; +import axios from 'axios'; +import pushImg from '../../assets/push.png'; // Adjust the path according to your project structure +import './PushButton.css'; // Import the CSS file + +const PushButton = ({ projectStructure, setProjectStructure }) => { + const pushStructure = async () => { + try { + console.log("Pushing project structure to backend: ", projectStructure); + const response = await axios.post('http://127.0.0.1:6969/push_structure', { + projectStructure // Include the projectStructure in the POST request body + }); + console.log('Project structure pushed:', response.data); + setProjectStructure(response.data); + } catch (error) { + console.error('Error pushing project structure:', error); + } + }; + + return ( +
+
+ +

Push debugger config to backend

+
+ Project Structure +
+ ); +}; + +export default PushButton; diff --git a/debugger/debugger_gui/src/components/sync-section/SyncSection.css b/debugger/debugger_gui/src/components/sync-section/SyncSection.css new file mode 100644 index 00000000..c64cc0fe --- /dev/null +++ b/debugger/debugger_gui/src/components/sync-section/SyncSection.css @@ -0,0 +1,16 @@ +.sync-section-container { + display: flex; + flex-direction: row; + justify-content: space-between; + width: 100vw; + align-items: center; + padding: 20px; + box-sizing: border-box; + gap: 0 +} + +.sync-section-container > div { + flex: 1; + display: flex; + justify-content: center; +} diff --git a/debugger/debugger_gui/src/components/sync-section/SyncSection.js b/debugger/debugger_gui/src/components/sync-section/SyncSection.js new file mode 100644 index 00000000..7e3588db --- /dev/null +++ b/debugger/debugger_gui/src/components/sync-section/SyncSection.js @@ -0,0 +1,17 @@ +import React from 'react'; +import PullButton from '../pull-button/PullButton'; +import PushButton from '../push-button/PushButton'; +import ColorReset from '../color-reset/ColorReset'; +import './SyncSection.css'; // Import the CSS file + +const SyncSection = ({ projectStructure, setProjectStructure }) => { + return ( +
+ + + +
+ ); +}; + +export default SyncSection; diff --git a/debugger/debugger_gui/src/components/tree/Tree.css b/debugger/debugger_gui/src/components/tree/Tree.css new file mode 100644 index 00000000..3e96dfc1 --- /dev/null +++ b/debugger/debugger_gui/src/components/tree/Tree.css @@ -0,0 +1,7 @@ +.tree-container { + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + align-items: center; + } \ No newline at end of file diff --git a/debugger/debugger_gui/src/components/tree/Tree.js b/debugger/debugger_gui/src/components/tree/Tree.js new file mode 100644 index 00000000..ae87a83f --- /dev/null +++ b/debugger/debugger_gui/src/components/tree/Tree.js @@ -0,0 +1,33 @@ +import React from 'react'; +import TreeNode from '../tree_node/TreeNode'; +import './Tree.css'; + +const Tree = ({ projectStructure, expanded, handleExpandClick, handleCheckboxChange, handleColorChange, handleEmojiChange}) => { + const renderTree = (node, parentId = '') => { + const nodeId = parentId ? `${parentId}/${node.name}` : node.name; + + return ( + + ); + }; + + if (!Array.isArray(projectStructure)) return null; // Ensure projectStructure is an array + + return ( +
+ {projectStructure.map((node) => renderTree(node))} +
+ ); +}; + +export default Tree; diff --git a/debugger/debugger_gui/src/components/tree_node/TreeNode.css b/debugger/debugger_gui/src/components/tree_node/TreeNode.css new file mode 100644 index 00000000..ee333577 --- /dev/null +++ b/debugger/debugger_gui/src/components/tree_node/TreeNode.css @@ -0,0 +1,88 @@ +.tree-node { + border: 1px solid #ccc; + margin-top: 10px; + margin-bottom: 10px; + padding: 10px; + border-radius: 4px; + width: 100%; + background-color: #181818; +} + +.tree-node-content { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + width: 100%; +} + +.tree-node-content-main { + display: flex; + align-items: center; + width: fit-content; +} + +.expand-button { + background: none; + border: none; + cursor: pointer; + margin-right: 10px; + padding: 0; /* Ensure no padding around the button */ + display: flex; + align-items: center; + justify-content: center; +} + +.expand-button img { + width: 16px; /* Adjust the width as needed */ + height: 16px; /* Adjust the height as needed */ + max-width: 100%; /* Ensure it doesn't exceed button size */ + max-height: 100%; /* Ensure it doesn't exceed button size */ +} + +.tree-node-text { + margin-left: 10px; + font-size: 1.45rem; + font-weight: 600; + font-family: 'Consolas', monospace; /* Specify fallback font */ + color: #ffffff; /* Adjust text color to ensure it's visible on the dark background */ +} + +.tree-node-children { + margin-left: 20px; + margin-right: 20px; + margin-bottom: 10px; + padding-left: 20px; + padding-right: 20px; + gap: 10px; +} + +.tree-node-content-secondary { + display: flex; + align-items: center; +} + +.color-picker-wrapper { + position: relative; +} + +.color-picker-icon { + display: inline-block; + width: 24px; + height: 24px; + cursor: pointer; + border: 1px solid #000; + border-radius: 4px; + overflow: hidden; + background-size: 100% 100%; /* Ensure the background color scales to fit */ + background-clip: content-box; + background-color: transparent; /* Ensure the background is transparent */ + -webkit-mask: url('../../assets/color-picker.png') center no-repeat; + mask: url('../../assets/color-picker.png') center no-repeat; + -webkit-mask-size: 20px; /* Adjust this value to fit your icon */ + mask-size: 20px; /* Adjust this value to fit your icon */ +} + +.color-picker-icon img { + display: none; +} diff --git a/debugger/debugger_gui/src/components/tree_node/TreeNode.js b/debugger/debugger_gui/src/components/tree_node/TreeNode.js new file mode 100644 index 00000000..25127b6e --- /dev/null +++ b/debugger/debugger_gui/src/components/tree_node/TreeNode.js @@ -0,0 +1,52 @@ +import './TreeNode.css'; +import React from 'react'; +import plusIcon from '../../assets/collapsed.png'; +import minusIcon from '../../assets/expanded.png'; +import colorIcon from '../../assets/color-picker.png'; // Import your custom color icon +import EmojiPicker from '../emoji-picker/EmojiPicker'; + +const TreeNode = ({ node, nodeId, expanded, handleExpandClick, handleCheckboxChange, handleColorChange, handleEmojiChange, renderTree }) => ( +
+
+
+ {node.children && node.children.length > 0 && ( + + )} + handleCheckboxChange(nodeId, e.target.checked)} + /> + {/* Pass node.emoji along with handleEmojiChange */} + handleEmojiChange(nodeId, emoji)} + /> + {node.name} +
+
+
+ handleColorChange(nodeId, e.target.value)} + style={{ display: 'none' }} // Hide the default color input + id={`color-picker-${nodeId}`} + /> + +
+
+
+ {node.children && expanded[nodeId] && ( +
+ {node.children.map((childNode) => renderTree(childNode, nodeId))} +
+ )} +
+); + +export default TreeNode; diff --git a/debugger/debugger_gui/src/index.css b/debugger/debugger_gui/src/index.css new file mode 100644 index 00000000..3a18e388 --- /dev/null +++ b/debugger/debugger_gui/src/index.css @@ -0,0 +1,14 @@ +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + background-color: #242121; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + monospace; +} diff --git a/debugger/debugger_gui/src/index.js b/debugger/debugger_gui/src/index.js new file mode 100644 index 00000000..54c5ef1a --- /dev/null +++ b/debugger/debugger_gui/src/index.js @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import App from './App'; +import './index.css'; + +ReactDOM.render( + , + document.getElementById('root') +); diff --git a/debugger/run.sh b/debugger/run.sh new file mode 100755 index 00000000..b11d28b1 --- /dev/null +++ b/debugger/run.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# The comment above is functional DO NOT REMOVE + +DEBUGGER_ABSPATH="$(readlink -f "${BASH_SOURCE[0]}")" +DEBUGGER_DIR_ABSPATH="$(dirname "$DEBUGGER_ABSPATH")" + +if [[ "$OSTYPE" == "darwin"* ]]; then + sed -i '' 's/\r//g' "$DEBUGGER_ABSPATH" +else + sed -i 's/\r//g' "$DEBUGGER_ABSPATH" +fi +chmod +x "$DEBUGGER_ABSPATH" + +cleanup() { + printf "\033[36mCleaning up debugger...\033[0m\n" + wait $! +} + +# Set trap to call cleanup on script exit +trap cleanup EXIT + +cd "$DEBUGGER_DIR_ABSPATH/debugger_gui" +npm install +npm start & until curl -s http://localhost:6970 > /dev/null; do + sleep 1 +done +cd "$DEBUGGER_DIR_ABSPATH/debugger_backend" + +# colorize print to cyan +printf "\033[36mSetting up virtual environment...\033[0m\n" +python -m venv .venv +# Activate the Python virtual environment +if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "win32" ]]; then + # Windows + source .venv/Scripts/activate +else + # macOS/Linux + source .venv/bin/activate +fi + +# colorize print to cyan +printf "\033[36mInstalling Python dependencies...\033[0m\n" +pip install -r requirements.txt + +cd "$DEBUGGER_DIR_ABSPATH" +printf "\033[36mStarting debugger server...\033[0m\n" +python -m debugger_backend.debugger_server + +printf "\033[36mDeactivating Python virtual environment...\033[0m\n" +deactivate diff --git a/debugger/setup.py b/debugger/setup.py new file mode 100644 index 00000000..6be2ed70 --- /dev/null +++ b/debugger/setup.py @@ -0,0 +1,8 @@ +from setuptools import setup, find_packages + +setup( + name="debug", + version="0.1", + packages=find_packages(), + py_modules=["debug"] +) diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 00000000..5a1f03f9 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,104 @@ +# Dependencies +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +package-lock.json +yarn.lock + +# Build outputs +dist/ +build/ +*.tgz +*.tar.gz + +# Environment variables +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# IDE and Editor files +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS generated files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Chrome Extension specific +*.crx +*.pem +*.zip + +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Coverage directory used by tools like istanbul +coverage/ +*.lcov + +# nyc test coverage +.nyc_output + +# Dependency directories +jspm_packages/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt + +# Storybook build outputs +.out +.storybook-out + +# Temporary folders +tmp/ +temp/ + +# webpack generated files +*.hot-update.js +*.hot-update.json diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 00000000..52f0e3dc --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,62 @@ +{ + "name": "open-swarm", + "version": "1.0.0", + "description": "Open Swarm — Agent Orchestrator frontend built with React", + "scripts": { + "build": "webpack --mode=production", + "build:watch": "webpack --mode=development --watch", + "dev": "webpack serve --mode=development --open", + "clean": "rm -rf dist" + }, + "dependencies": { + "@codemirror/lang-html": "^6.4.11", + "@codemirror/lang-json": "^6.0.2", + "@codemirror/lang-python": "^6.2.1", + "@codemirror/state": "^6.5.4", + "@codemirror/theme-one-dark": "^6.1.3", + "@codemirror/view": "^6.39.16", + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", + "@mui/icons-material": "^7.3.9", + "@mui/material": "^7.3.9", + "@reduxjs/toolkit": "^2.8.2", + "@types/react-syntax-highlighter": "^15.5.13", + "codemirror": "^6.0.2", + "framer-motion": "^12.35.2", + "html-to-image": "^1.11.13", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-markdown": "^10.1.0", + "react-redux": "^9.2.0", + "react-router-dom": "^7.13.1", + "react-syntax-highlighter": "^16.1.1", + "remark-gfm": "^4.0.1" + }, + "devDependencies": { + "@babel/core": "^7.28.0", + "@babel/preset-env": "^7.28.0", + "@babel/preset-react": "^7.27.1", + "@babel/preset-typescript": "^7.27.1", + "@types/react": "^18.2.0", + "@types/react-dom": "^18.2.0", + "@types/react-redux": "^7.1.34", + "babel-loader": "^9.2.1", + "css-loader": "^6.8.0", + "css-modules-types-loader": "^0.6.10", + "html-webpack-plugin": "^5.5.0", + "sass": "^1.89.2", + "sass-loader": "^16.0.5", + "style-loader": "^3.3.0", + "typescript": "^5.0.0", + "webpack": "^5.88.0", + "webpack-cli": "^5.1.0", + "webpack-dev-server": "^4.15.0" + }, + "babel": { + "presets": [ + "@babel/preset-env", + "@babel/preset-react", + "@babel/preset-typescript" + ] + } +} diff --git a/frontend/public/apple-touch-icon.png b/frontend/public/apple-touch-icon.png new file mode 100644 index 00000000..8b9f8611 Binary files /dev/null and b/frontend/public/apple-touch-icon.png differ diff --git a/frontend/public/favicon.ico b/frontend/public/favicon.ico new file mode 100644 index 00000000..c9a1b708 Binary files /dev/null and b/frontend/public/favicon.ico differ diff --git a/frontend/public/index.html b/frontend/public/index.html new file mode 100644 index 00000000..1c7c3101 --- /dev/null +++ b/frontend/public/index.html @@ -0,0 +1,14 @@ + + + + + + Open Swarm + + + + + +
+ + \ No newline at end of file diff --git a/frontend/public/logo.png b/frontend/public/logo.png new file mode 100644 index 00000000..8b9f8611 Binary files /dev/null and b/frontend/public/logo.png differ diff --git a/frontend/run/_utils.sh b/frontend/run/_utils.sh new file mode 100755 index 00000000..2d24af62 --- /dev/null +++ b/frontend/run/_utils.sh @@ -0,0 +1,202 @@ +#!/bin/bash +# Flag processing function with namespacing and global variable declaration +UTILS_FILE_ABSPATH="$(readlink -f "${BASH_SOURCE[0]}")" +if [[ "$OSTYPE" == "darwin"* ]]; then + # echo "In macOS utils sed START" + # echo "UTILS_ABSPATH: $UTILS_FILE_ABSPATH" + sed -i '' 's/\r//g' "$UTILS_FILE_ABSPATH" + # echo "In macOS utils sed END" +else + # echo "NOT in macOS utils START" + # echo "UTILS_ABSPATH: $UTILS_FILE_ABSPATH" + sed -i 's/\r//g' "$UTILS_FILE_ABSPATH" + # echo "NOT in macOS utils END" +fi +chmod +x "$UTILS_FILE_ABSPATH" + +RUN_DIR_ABSPATH="$(dirname "$UTILS_FILE_ABSPATH")" +FRONTEND_DIR_ABSPATH="$(dirname "$RUN_DIR_ABSPATH")" + +formatted_error() { + # Arguments: error message and array of conflicting flags + local initial_message="$1" + shift + local conflicting_flags=("$@") + + # Red color for the error message box + local COLOR_CODE='\033[0;31m' + local NC='\033[0m' # No Color + + # Start the error message with the initial message + local error_message="$initial_message" + + # Add each conflicting flag on a new line with indentation + for conflict in "${conflicting_flags[@]}"; do + error_message+="\n $conflict" # Replacing `\t` with four spaces + done + + # Prepare for printing by finding max length of each line in the message + local lines=() + local max_length=0 + + # Use printf to interpret new lines and calculate max length with spaces instead of tabs + while IFS= read -r line; do + # Substitute tabs with spaces for consistent width measurement + local line_with_spaces="${line//$'\t'/ }" + lines+=("$line_with_spaces") + if (( ${#line_with_spaces} > max_length )); then + max_length=${#line_with_spaces} + fi + done <<< "$(printf "$error_message")" + + # Create the top and bottom borders based on the maximum line length + local border=$(printf '%*s' "$((max_length + 4))" '' | tr ' ' '-') + + # Print the formatted error message with a red box + printf "\n${COLOR_CODE}%s${NC}\n" "$border" + for line in "${lines[@]}"; do + printf "${COLOR_CODE}| %-*s |${NC}\n" "$max_length" "$line" + done + printf "${COLOR_CODE}| %-*s |${NC}\n" "$max_length" "" + printf "${COLOR_CODE}%s${NC}\n" "$border" +} + + +function process_flags() { + local -n flags_to_commands="$1" # Reference to the dictionary of flags and commands + local -n exclusives="$2" # Reference to the list of exclusive flag groups + local namespace="$3" # Unique prefix for variables + local calling_script_name="$(basename "$(readlink -f "${BASH_SOURCE[1]}")")" + local caller_id="${FUNCNAME[1]}" + local should_exit=false + if [ "$caller_id" == "main" ]; then + caller_id="$calling_script_name" + fi + # echo "caller_id: $caller_id" + # echo "Initializing flags with namespace: $namespace" + + # Initialize flag variables with namespacing in the sourcing script context + for flag in "${!flags_to_commands[@]}"; do + # Convert flag to uppercase variable name and apply namespace, e.g., MYAPP_FLAG1 + local flag_var="${namespace}_${flag^^}" # Prefix and uppercase + flag_var="${flag_var//-}" # Remove dashes + eval "declare -g $flag_var=false" # Initialize as global false + + # Diagnostic output for each initialized variable + # echo "Initialized $flag_var as false" + done + + # echo "Parsing command line arguments: $@" + + # Parse command line arguments and set flags + local unsupported_flags=() + # local potential_typos=() + for arg in "$@"; do + # echo "Processing argument: $arg" + if [[ -n "${flags_to_commands[$arg]}" ]]; then + # echo "Flag found: $arg" + local flag_var="${namespace}_${arg^^}" # Prefix and uppercase + flag_var="${flag_var//-}" # Remove dashes + eval "declare -g $flag_var=true" # Set as global true + # echo "Set $flag_var to true" + # echo "Executing command for $arg: ${flags_to_commands[$arg]}" + eval "${flags_to_commands[$arg]}" + else + if [[ "$arg" == "-"* ]]; then + # echo "Flag not found: $arg" + unsupported_flags+=("$arg") + if [[ -n "${flags_to_commands[-$arg]}" ]]; then + # echo "Potential typo: $arg" + unsupported_flags+=("\t*Note: Potential typo detected.") + unsupported_flags+=("\tDid you mean: -$arg") + fi + fi + fi + done + # Check exclusive flag groups for conflicts + # echo "Checking exclusive flag groups" + for group in "${exclusives[@]}"; do + local count=0 + local conflicting_flags=() + for flag in $group; do + local flag_var="${namespace}_${flag^^}" + flag_var="${flag_var//-}" + if [[ "$(eval echo "\$$flag_var")" == "true" ]]; then + conflicting_flags+=("$flag") + # count=$((count + 1)) + # ec "$flag_var is true in exclusive group" + fi + done + if (( ${#conflicting_flags[@]} > 1 )); then + # echo "found conflicting flags" + formatted_error "Error: $caller_id\n-----------------------------------------\nIncompatible flags:\nThe flags below cannot be used together\n-----------------------------------------\n" "${conflicting_flags[@]}" + should_exit=true + fi + done + # echo "should_exit: $should_exit" + # echo "unsupported_flags: ${#unsupported_flags[@]}" + if (( ${#unsupported_flags[@]} > 0 )); then + # echo "found unsupported flags 2" + formatted_error "Error: $caller_id\n-------------------------------------------------\nUnsupported flags:\nThe flags below are not supported by this script\n-------------------------------------------------\n" "${unsupported_flags[@]}" + should_exit=true + fi + if [[ $should_exit == true ]]; then + exit 1 + fi +} + + + +formatted_echo() { + local COLOR_CODE='\033[0m' # No Color + local NC='\033[0m' # No Color variable + # local message="$2" + # If the second argument is empty, then theres no color specified, so we use the first argument as the message + if [[ -z "$2" ]]; then + message="$1" + else + message="$2" + fi + + declare -A format_flags + format_flags=( + [--red]="COLOR_CODE='\033[0;31m'" + [--green]="COLOR_CODE='\033[0;32m'" + [--yellow]="COLOR_CODE='\033[0;33m'" + [--blue]="COLOR_CODE='\033[0;34m'" + [--purple]="COLOR_CODE='\033[0;35m'" + [--cyan]="COLOR_CODE='\033[0;36m'" + ) + exclusive_format_flags=( + "--red --green --yellow --blue --purple --cyan" + ) + + # Pass the arguments with the namespace "FORMAT" + process_flags format_flags exclusive_format_flags "FORMAT" "$@" + + # Process the message + local text + text=$(printf "%b" "$message") + + # Expand any escaped characters in the input (e.g., \n) + local lines=() + local max_length=0 + + # Read the text line by line and find the maximum length + while IFS= read -r line; do + lines+=("$line") + if (( ${#line} > max_length )); then + max_length=${#line} + fi + done <<< "$text" + + # Create the top and bottom borders based on the maximum line length + local border=$(printf '%*s' "$((max_length + 4))" '' | tr ' ' '-') + + # Print the formatted box with selected color + printf "\n${COLOR_CODE}%s${NC}\n" "$border" + for line in "${lines[@]}"; do + printf "${COLOR_CODE}| %-*s |${NC}\n" "$max_length" "$line" + done + printf "${COLOR_CODE}%s${NC}\n" "$border" +} \ No newline at end of file diff --git a/frontend/run/dev.sh b/frontend/run/dev.sh new file mode 100755 index 00000000..b81fe212 --- /dev/null +++ b/frontend/run/dev.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# The comment above is shebang, DO NOT REMOVE +DEV_ABSPATH="$(readlink -f "${BASH_SOURCE[0]}")" +if [[ "$OSTYPE" == "darwin"* ]]; then + # echo "In macOS server sed START" + # echo "SERVER_ABSPATH: $SERVER_ABSPATH" + sed -i '' 's/\r//g' "$DEV_ABSPATH" + # echo "In macOS server sed END" +else + # echo "NOT in macOS server START" + # echo "SERVER_ABSPATH: $SERVER_ABSPATH" + sed -i 's/\r//g' "$DEV_ABSPATH" + # echo "NOT in macOS server START" +fi +chmod +x "$DEV_ABSPATH" +source "$(dirname "$DEV_ABSPATH")/_utils.sh" + + +formatted_echo --green "Installing dependencies..." +cd "$FRONTEND_DIR_ABSPATH" +npm install + +formatted_echo --green "Building with development mode..." +npm run dev + +# exit back to the dir that we were in before +cd - \ No newline at end of file diff --git a/frontend/src/app/Main.tsx b/frontend/src/app/Main.tsx new file mode 100644 index 00000000..b6161048 --- /dev/null +++ b/frontend/src/app/Main.tsx @@ -0,0 +1,173 @@ +import React, { useMemo, useEffect } from 'react'; +import { Provider } from 'react-redux'; +import { BrowserRouter, Routes, Route } from 'react-router-dom'; +import { ThemeProvider as MuiThemeProvider, createTheme, CssBaseline } from '@mui/material'; +import { store } from '../shared/state/store'; +import { useAppDispatch } from '@/shared/hooks'; +import { fetchSettings } from '@/shared/state/settingsSlice'; +import AppShell from './components/Layout/AppShell'; +import Dashboard from './pages/Dashboard/Dashboard'; +import DashboardSelection from './pages/DashboardSelection/DashboardSelection'; +import Templates from './pages/Templates/Templates'; +import Skills from './pages/Skills/Skills'; +import Tools from './pages/Tools/Tools'; +import Modes from './pages/Modes/Modes'; +import Commands from './pages/Commands/Commands'; +import Views from './pages/Views/Views'; +import { useKeyboardShortcuts } from '@/shared/hooks/useKeyboardShortcuts'; +import KeyboardShortcutsHelp from './components/KeyboardShortcutsHelp'; +import { ThemeProvider, useThemeMode, useClaudeTokens } from '@/shared/styles/ThemeContext'; +import { ClaudeTokens } from '@/shared/styles/claudeTokens'; + +function buildMuiTheme(c: ClaudeTokens, mode: 'light' | 'dark') { + return createTheme({ + palette: { + mode, + background: { + default: c.bg.page, + paper: c.bg.surface, + }, + primary: { + main: c.accent.primary, + dark: c.accent.pressed, + light: c.accent.hover, + }, + text: { + primary: c.text.primary, + secondary: c.text.muted, + disabled: c.text.tertiary, + }, + divider: c.border.medium, + error: { main: c.status.error }, + warning: { main: c.status.warning }, + success: { main: c.status.success }, + info: { main: c.status.info }, + }, + typography: { + fontFamily: c.font.sans, + h1: { fontWeight: 600 }, + h2: { fontWeight: 600 }, + h3: { fontWeight: 600 }, + h5: { fontWeight: 600 }, + h6: { fontWeight: 600 }, + button: { textTransform: 'none' as const, fontWeight: 500 }, + }, + shape: { + borderRadius: c.radius.xl, + }, + components: { + MuiCssBaseline: { + styleOverrides: { + body: { + backgroundColor: c.bg.page, + color: c.text.primary, + }, + }, + }, + MuiButton: { + styleOverrides: { + root: { + borderRadius: c.radius.lg, + transition: c.transition, + textTransform: 'none' as const, + '&:active': { transform: 'scale(0.98)' }, + }, + contained: { + boxShadow: 'none', + '&:hover': { boxShadow: 'none' }, + }, + }, + }, + MuiPaper: { + styleOverrides: { + root: { + boxShadow: c.shadow.md, + border: `1px solid ${c.border.subtle}`, + backgroundImage: 'none', + }, + }, + }, + MuiChip: { + styleOverrides: { + root: { + fontWeight: 500, + borderRadius: c.radius.md, + }, + }, + }, + MuiDialog: { + styleOverrides: { + paper: { + borderRadius: 16, + boxShadow: c.shadow.lg, + border: `1px solid ${c.border.subtle}`, + }, + }, + }, + MuiTooltip: { + styleOverrides: { + tooltip: { + backgroundColor: c.bg.inverse, + color: c.text.inverse, + fontSize: '0.75rem', + }, + }, + }, + }, + }); +} + +const ShortcutsProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { + useKeyboardShortcuts(); + return <>{children}; +}; + +const SettingsLoader: React.FC<{ children: React.ReactNode }> = ({ children }) => { + const dispatch = useAppDispatch(); + useEffect(() => { + dispatch(fetchSettings()); + }, [dispatch]); + return <>{children}; +}; + +const ThemedApp: React.FC = () => { + const c = useClaudeTokens(); + const { mode } = useThemeMode(); + const muiTheme = useMemo(() => buildMuiTheme(c, mode), [c, mode]); + + return ( + + + + + + + }> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + + + + + ); +}; + +const Main: React.FC = () => { + return ( + + + + + + ); +}; + +export default Main; diff --git a/frontend/src/app/components/CommandPicker.tsx b/frontend/src/app/components/CommandPicker.tsx new file mode 100644 index 00000000..32a38bbe --- /dev/null +++ b/frontend/src/app/components/CommandPicker.tsx @@ -0,0 +1,522 @@ +import React, { useState, useEffect, useMemo, useRef } from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import Paper from '@mui/material/Paper'; +import DescriptionIcon from '@mui/icons-material/Description'; +import PsychologyIcon from '@mui/icons-material/Psychology'; +import SmartToyOutlinedIcon from '@mui/icons-material/SmartToyOutlined'; +import QuestionAnswerOutlinedIcon from '@mui/icons-material/QuestionAnswerOutlined'; +import MapOutlinedIcon from '@mui/icons-material/MapOutlined'; +import CategoryOutlinedIcon from '@mui/icons-material/CategoryOutlined'; +import TuneOutlinedIcon from '@mui/icons-material/TuneOutlined'; +import InsertDriveFileOutlinedIcon from '@mui/icons-material/InsertDriveFileOutlined'; +import LanguageIcon from '@mui/icons-material/Language'; +import BuildOutlinedIcon from '@mui/icons-material/BuildOutlined'; +import SvgIcon from '@mui/material/SvgIcon'; +import ViewQuiltOutlinedIcon from '@mui/icons-material/ViewQuiltOutlined'; +import { useAppSelector, useAppDispatch } from '@/shared/hooks'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; +import { fetchBuiltinTools, fetchTools } from '@/shared/state/toolsSlice'; +import { fetchOutputs } from '@/shared/state/outputsSlice'; + +const XLogoIcon: React.FC<{ sx?: object }> = ({ sx }) => ( + + + +); + +const GoogleIcon: React.FC<{ sx?: object }> = ({ sx }) => ( + + + + + + +); + +const RedditIcon: React.FC<{ sx?: object }> = ({ sx }) => ( + + + +); + +const TOOL_GROUP_ICONS: Record> = { + Twitter: XLogoIcon, + Google: GoogleIcon, + Reddit: RedditIcon, + Web: LanguageIcon, + View: ViewQuiltOutlinedIcon, +}; + +export function getToolGroupIcon(groupName: string, size: number = 15): React.ReactNode { + const Icon = TOOL_GROUP_ICONS[groupName]; + if (Icon) return ; + return ; +} + +export interface CommandPickerItem { + id: string; + type: 'template' | 'skill' | 'mode' | 'context'; + category: string; + name: string; + description: string; + command: string; + icon: React.ReactNode; + toolNames?: string[]; + iconKey?: string; +} + +interface Props { + trigger: '/' | '@'; + filter: string; + onSelect: (item: CommandPickerItem) => void; + onClose: () => void; + visible: boolean; +} + +const MODE_ICON_MAP: Record> = { + smart_toy: SmartToyOutlinedIcon, + question_answer: QuestionAnswerOutlinedIcon, + map: MapOutlinedIcon, + category: CategoryOutlinedIcon, + tune: TuneOutlinedIcon, +}; + +function highlightMatch(text: string, query: string, color: string): React.ReactNode { + if (!query) return text; + const idx = text.toLowerCase().indexOf(query.toLowerCase()); + if (idx === -1) return text; + return ( + <> + {text.slice(0, idx)} + {text.slice(idx, idx + query.length)} + {text.slice(idx + query.length)} + + ); +} + +const CommandPicker: React.FC = ({ trigger, filter, onSelect, onClose, visible }) => { + const c = useClaudeTokens(); + const dispatch = useAppDispatch(); + const templates = useAppSelector((s) => s.templates.items); + const skills = useAppSelector((s) => s.skills.items); + const modesMap = useAppSelector((s) => s.modes.items); + const builtinTools = useAppSelector((s) => s.tools.builtinTools); + const customTools = useAppSelector((s) => s.tools.items); + const outputItems = useAppSelector((s) => s.outputs.items); + const [selectedIndex, setSelectedIndex] = useState(0); + const containerRef = useRef(null); + + const toolsLoaded = useAppSelector((s) => s.tools.loaded); + const builtinLoaded = useAppSelector((s) => s.tools.builtinLoaded); + const outputsLoaded = useAppSelector((s) => s.outputs.loaded); + + useEffect(() => { + if (!builtinLoaded) dispatch(fetchBuiltinTools()); + if (!toolsLoaded) dispatch(fetchTools()); + if (!outputsLoaded) dispatch(fetchOutputs()); + }, [dispatch, builtinLoaded, toolsLoaded, outputsLoaded]); + + const items: CommandPickerItem[] = useMemo(() => { + let all: CommandPickerItem[] = []; + + if (trigger === '/') { + const templateItems: CommandPickerItem[] = Object.values(templates).map((t) => ({ + id: t.id, + type: 'template' as const, + category: 'Templates', + name: t.name, + description: t.description || `Template with ${t.fields.length} fields`, + command: t.name.toLowerCase().replace(/\s+/g, '-'), + icon: , + })); + + const skillItems: CommandPickerItem[] = Object.values(skills).map((s) => ({ + id: s.id, + type: 'skill' as const, + category: 'Skills', + name: s.name, + description: s.description || 'Skill', + command: s.command || s.id, + icon: , + })); + + const modeItems: CommandPickerItem[] = Object.values(modesMap).map((m) => { + const IconComp = MODE_ICON_MAP[m.icon] || SmartToyOutlinedIcon; + return { + id: m.id, + type: 'mode' as const, + category: 'Modes', + name: m.name, + description: m.description || 'Switch to this mode', + command: m.name.toLowerCase().replace(/\s+/g, '-'), + icon: , + }; + }); + + all = [...templateItems, ...skillItems, ...modeItems]; + } else { + const atItems: CommandPickerItem[] = [ + { + id: 'file', + type: 'context' as const, + category: 'Context', + name: 'File', + description: 'Attach a file or folder as context', + command: 'file', + icon: , + }, + ]; + + const hasWebSearch = builtinTools.some((t) => t.name === 'WebSearch' && t.deferred); + const hasWebFetch = builtinTools.some((t) => t.name === 'WebFetch' && t.deferred); + if (hasWebSearch || hasWebFetch) { + const webTools = [hasWebSearch && 'WebSearch', hasWebFetch && 'WebFetch'].filter(Boolean) as string[]; + atItems.push({ + id: 'web', + type: 'context' as const, + category: 'Tools', + name: 'Web', + description: 'Search the web and fetch URLs', + command: 'web', + icon: , + toolNames: webTools, + iconKey: 'Web', + }); + } + + for (const tool of Object.values(customTools)) { + if (!tool.mcp_config || Object.keys(tool.mcp_config).length === 0) continue; + const services = tool.tool_permissions?._services as Record | undefined; + if (!services) continue; + const perms = tool.tool_permissions as Record; + const serviceGroups = (tool.tool_permissions?._service_groups ?? {}) as Record; + + const enabledServices: { name: string; tools: string[] }[] = []; + for (const [serviceName, serviceTools] of Object.entries(services)) { + const allToolNames = [...(serviceTools.read || []), ...(serviceTools.write || [])]; + const enabled = allToolNames.filter((name) => perms[name] !== 'deny'); + if (enabled.length > 0) enabledServices.push({ name: serviceName, tools: enabled }); + } + + if (enabledServices.length === 0) continue; + + const groupEntries = Object.entries(serviceGroups); + const emittedServices = new Set(); + + for (const [groupName, groupServiceNames] of groupEntries) { + const groupCmd = groupName.toLowerCase().replace(/\s+/g, '-'); + const groupServices = enabledServices.filter((s) => groupServiceNames.includes(s.name)); + if (groupServices.length === 0) continue; + groupServices.forEach((s) => emittedServices.add(s.name)); + + const groupIcon = getToolGroupIcon(groupName); + if (groupServices.length >= 2) { + const allTools = groupServices.flatMap((s) => s.tools); + atItems.push({ + id: `mcp-${tool.id}-group-${groupName}`, + type: 'context' as const, + category: tool.name, + name: groupName, + description: `Use all ${groupName} tools`, + command: groupCmd, + icon: groupIcon, + toolNames: allTools, + iconKey: groupName, + }); + for (const svc of groupServices) { + atItems.push({ + id: `mcp-${tool.id}-${svc.name}`, + type: 'context' as const, + category: tool.name, + name: svc.name, + description: `Use ${svc.name} tools from ${tool.name}`, + command: `${groupCmd}/${svc.name.toLowerCase().replace(/\s+/g, '-')}`, + icon: groupIcon, + toolNames: svc.tools, + iconKey: groupName, + }); + } + } else { + const svc = groupServices[0]; + atItems.push({ + id: `mcp-${tool.id}-${svc.name}`, + type: 'context' as const, + category: tool.name, + name: svc.name, + description: `Use ${svc.name} tools from ${tool.name}`, + command: svc.name.toLowerCase().replace(/\s+/g, '-'), + icon: groupIcon, + toolNames: svc.tools, + iconKey: groupName, + }); + } + } + + for (const svc of enabledServices) { + if (emittedServices.has(svc.name)) continue; + atItems.push({ + id: `mcp-${tool.id}-${svc.name}`, + type: 'context' as const, + category: tool.name, + name: svc.name, + description: `Use ${svc.name} tools from ${tool.name}`, + command: svc.name.toLowerCase().replace(/\s+/g, '-'), + icon: , + toolNames: svc.tools, + }); + } + } + + for (const out of Object.values(outputItems)) { + if (out.permission === 'deny') continue; + const cmd = out.name.toLowerCase().replace(/\s+/g, '-'); + atItems.push({ + id: `view-${out.id}`, + type: 'context' as const, + category: 'Views', + name: out.name, + description: out.description || `Render ${out.name} view`, + command: cmd, + icon: , + toolNames: ['RenderOutput'], + iconKey: 'View', + }); + } + + all = atItems; + } + + if (!filter) return all; + const lower = filter.toLowerCase(); + return all.filter( + (item) => + item.name.toLowerCase().includes(lower) || + item.command.toLowerCase().includes(lower) || + item.description.toLowerCase().includes(lower), + ); + }, [trigger, templates, skills, modesMap, builtinTools, customTools, outputItems, filter]); + + const flatItems = useMemo(() => { + const result: { item: CommandPickerItem; isGroupStart: boolean; category: string }[] = []; + let lastCat = ''; + for (const item of items) { + result.push({ item, isGroupStart: item.category !== lastCat, category: item.category }); + lastCat = item.category; + } + return result; + }, [items]); + + const getIconColor = (item: CommandPickerItem): string => { + switch (item.type) { + case 'template': return c.accent.primary; + case 'skill': return c.status.success; + case 'mode': { + const mode = modesMap[item.id]; + return mode?.color || c.accent.primary; + } + case 'context': return c.text.tertiary; + default: return c.text.tertiary; + } + }; + + useEffect(() => { + setSelectedIndex(0); + }, [filter, trigger]); + + useEffect(() => { + if (!containerRef.current) return; + const el = containerRef.current.querySelector(`[data-picker-idx="${selectedIndex}"]`); + if (el) el.scrollIntoView({ block: 'nearest' }); + }, [selectedIndex]); + + useEffect(() => { + if (!visible) return; + const handler = (e: KeyboardEvent) => { + switch (e.key) { + case 'ArrowDown': + e.preventDefault(); + setSelectedIndex((p) => (p < items.length - 1 ? p + 1 : p)); + break; + case 'ArrowUp': + e.preventDefault(); + setSelectedIndex((p) => (p > 0 ? p - 1 : p)); + break; + case 'Enter': + case 'Tab': + if (items[selectedIndex]) { + e.preventDefault(); + onSelect(items[selectedIndex]); + } + break; + case 'Escape': + e.preventDefault(); + onClose(); + break; + } + }; + window.addEventListener('keydown', handler); + return () => window.removeEventListener('keydown', handler); + }, [visible, items, selectedIndex, onSelect, onClose]); + + if (!visible || items.length === 0) return null; + + return ( + + + {flatItems.map(({ item, isGroupStart, category }, idx) => ( + + {isGroupStart && ( + + + {category} + + + )} + onSelect(item)} + onMouseEnter={() => setSelectedIndex(idx)} + sx={{ + display: 'flex', + alignItems: 'center', + gap: 1, + px: 1.25, + py: 0.5, + mx: 0.5, + borderRadius: '8px', + cursor: 'pointer', + bgcolor: idx === selectedIndex ? `${c.accent.primary}0a` : 'transparent', + '&:hover': { bgcolor: `${c.accent.primary}0a` }, + transition: 'background-color 60ms ease', + }} + > + + {item.icon} + + + {trigger}{highlightMatch(item.command, filter, c.accent.primary)} + + + {item.description} + + + + ))} + + + + {[ + { keys: '↑↓', label: 'navigate' }, + { keys: '↵', label: 'select' }, + { keys: 'esc', label: 'dismiss' }, + ].map(({ keys, label }) => ( + + + {keys} + + + {label} + + + ))} + + + ); +}; + +export default CommandPicker; diff --git a/frontend/src/app/components/DirectoryBrowser.tsx b/frontend/src/app/components/DirectoryBrowser.tsx new file mode 100644 index 00000000..768832d0 --- /dev/null +++ b/frontend/src/app/components/DirectoryBrowser.tsx @@ -0,0 +1,334 @@ +import React, { useState, useEffect, useCallback } from 'react'; +import Dialog from '@mui/material/Dialog'; +import DialogTitle from '@mui/material/DialogTitle'; +import DialogContent from '@mui/material/DialogContent'; +import DialogActions from '@mui/material/DialogActions'; +import Button from '@mui/material/Button'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import TextField from '@mui/material/TextField'; +import List from '@mui/material/List'; +import ListItemButton from '@mui/material/ListItemButton'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemText from '@mui/material/ListItemText'; +import CircularProgress from '@mui/material/CircularProgress'; +import IconButton from '@mui/material/IconButton'; +import Breadcrumbs from '@mui/material/Breadcrumbs'; +import Link from '@mui/material/Link'; +import FolderIcon from '@mui/icons-material/Folder'; +import InsertDriveFileOutlinedIcon from '@mui/icons-material/InsertDriveFileOutlined'; +import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; +import { BrowseResult } from '@/shared/state/settingsSlice'; + +const API_BASE = `http://${window.location.hostname}:8324/api/settings`; + +export interface ContextPath { + path: string; + type: 'file' | 'directory'; +} + +interface DirectoryBrowserProps { + open: boolean; + onClose: () => void; + onSelect: (item: ContextPath) => void; + initialPath?: string; +} + +const DirectoryBrowser: React.FC = ({ open, onClose, onSelect, initialPath }) => { + const c = useClaudeTokens(); + const [browseData, setBrowseData] = useState(null); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + const [manualPath, setManualPath] = useState(''); + const [selected, setSelected] = useState<{ name: string; type: 'file' | 'directory' } | null>(null); + + const browse = useCallback(async (path: string) => { + setLoading(true); + setError(null); + setSelected(null); + try { + const res = await fetch(`${API_BASE}/browse-directories?path=${encodeURIComponent(path)}`); + if (!res.ok) { + const data = await res.json(); + throw new Error(data.detail || 'Failed to browse'); + } + const data: BrowseResult = await res.json(); + setBrowseData(data); + setManualPath(data.current); + } catch (e: any) { + setError(e.message); + } finally { + setLoading(false); + } + }, []); + + useEffect(() => { + if (open) { + setSelected(null); + browse(initialPath || ''); + } + }, [open, initialPath, browse]); + + const handleNavigate = (dir: string) => { + if (browseData) browse(`${browseData.current}/${dir}`); + }; + + const handleGoUp = () => { + if (browseData?.parent) browse(browseData.parent); + }; + + const handleManualGo = () => { + if (manualPath.trim()) browse(manualPath.trim()); + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') handleManualGo(); + }; + + const handleConfirm = () => { + if (!browseData) return; + if (selected) { + const fullPath = `${browseData.current}/${selected.name}`; + onSelect({ path: fullPath, type: selected.type }); + } else { + onSelect({ path: browseData.current, type: 'directory' }); + } + onClose(); + }; + + const pathSegments = browseData?.current.split('/').filter(Boolean) ?? []; + const hasEntries = (browseData?.directories.length ?? 0) + (browseData?.files.length ?? 0) > 0; + + return ( + + + Browse Files & Folders + + + + setManualPath(e.target.value)} + onKeyDown={handleKeyDown} + size="small" + fullWidth + placeholder="Type a path..." + sx={{ + '& .MuiOutlinedInput-root': { + bgcolor: c.bg.page, + fontSize: '0.85rem', + fontFamily: c.font.mono, + }, + }} + /> + + + + {browseData && ( + + + + + + browse('/')} + sx={{ color: c.text.tertiary, fontSize: '0.78rem' }} + > + / + + {pathSegments.map((seg, i) => { + const fullPath = '/' + pathSegments.slice(0, i + 1).join('/'); + const isLast = i === pathSegments.length - 1; + return isLast ? ( + + {seg} + + ) : ( + browse(fullPath)} + sx={{ color: c.text.tertiary, fontSize: '0.78rem' }} + > + {seg} + + ); + })} + + + )} + + {error && ( + + {error} + + )} + + + {loading ? ( + + + + ) : !hasEntries ? ( + + + Empty directory + + + ) : ( + + {browseData?.directories.map((dir) => ( + handleNavigate(dir)} + onClick={() => + setSelected((prev) => + prev?.name === dir && prev.type === 'directory' ? null : { name: dir, type: 'directory' }, + ) + } + sx={{ + py: 0.75, + '&.Mui-selected': { bgcolor: `${c.accent.primary}0c` }, + '&:hover': { bgcolor: `${c.accent.primary}08` }, + }} + > + + + + + + ))} + {browseData?.files.map((file) => ( + + setSelected((prev) => + prev?.name === file && prev.type === 'file' ? null : { name: file, type: 'file' }, + ) + } + sx={{ + py: 0.75, + '&.Mui-selected': { bgcolor: `${c.accent.primary}0c` }, + '&:hover': { bgcolor: `${c.accent.primary}08` }, + }} + > + + + + + + ))} + + )} + + + + + {selected + ? `Selected: ${selected.name}` + : 'Click to select, double-click folders to open'} + + + + + + + + ); +}; + +export default DirectoryBrowser; diff --git a/frontend/src/app/components/ElementSelectionContext.tsx b/frontend/src/app/components/ElementSelectionContext.tsx new file mode 100644 index 00000000..cfe88433 --- /dev/null +++ b/frontend/src/app/components/ElementSelectionContext.tsx @@ -0,0 +1,80 @@ +import React, { createContext, useContext, useState, useRef, useCallback, RefObject } from 'react'; + +export interface SelectedElement { + id: string; + selectorPath: string; + tagName: string; + className: string; + outerHTML: string; + computedStyles: Record; + screenshot?: string; + boundingRect: { x: number; y: number; width: number; height: number }; + semanticType?: 'agent-card' | 'message' | 'tool-call' | 'tool-group' | 'view-card' | 'dom-element'; + semanticLabel?: string; + semanticData?: Record; +} + +interface ElementSelectionContextValue { + selectMode: boolean; + toggleSelectMode: () => void; + setSelectMode: (active: boolean) => void; + selectedElements: SelectedElement[]; + addSelectedElement: (el: SelectedElement) => void; + updateSelectedElement: (id: string, patch: Partial) => void; + removeSelectedElement: (id: string) => void; + clearSelectedElements: () => void; + iframeRef: RefObject; +} + +const ElementSelectionContext = createContext(null); + +export function useElementSelection() { + return useContext(ElementSelectionContext); +} + +export const ElementSelectionProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { + const [selectMode, setSelectMode] = useState(false); + const [selectedElements, setSelectedElements] = useState([]); + const iframeRef = useRef(null); + + const toggleSelectMode = useCallback(() => { + setSelectMode((prev) => !prev); + }, []); + + const addSelectedElement = useCallback((el: SelectedElement) => { + setSelectedElements((prev) => { + if (prev.some((e) => e.id === el.id)) return prev; + return [...prev, el]; + }); + }, []); + + const updateSelectedElement = useCallback((id: string, patch: Partial) => { + setSelectedElements((prev) => prev.map((e) => e.id === id ? { ...e, ...patch } : e)); + }, []); + + const removeSelectedElement = useCallback((id: string) => { + setSelectedElements((prev) => prev.filter((e) => e.id !== id)); + }, []); + + const clearSelectedElements = useCallback(() => { + setSelectedElements([]); + }, []); + + return ( + + {children} + + ); +}; diff --git a/frontend/src/app/components/GlobalApprovalOverlay.tsx b/frontend/src/app/components/GlobalApprovalOverlay.tsx new file mode 100644 index 00000000..4405e7bf --- /dev/null +++ b/frontend/src/app/components/GlobalApprovalOverlay.tsx @@ -0,0 +1,203 @@ +import React, { useMemo, useCallback, useState, useEffect } from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import Chip from '@mui/material/Chip'; +import IconButton from '@mui/material/IconButton'; +import ExpandLessIcon from '@mui/icons-material/ExpandLess'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import NotificationsActiveIcon from '@mui/icons-material/NotificationsActive'; +import { useAppDispatch, useAppSelector } from '@/shared/hooks'; +import { handleApproval, ApprovalRequest } from '@/shared/state/agentsSlice'; +import ApprovalBar, { BatchApprovalBar } from '@/app/pages/AgentChat/ApprovalBar'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; + +interface SessionApprovalGroup { + sessionId: string; + sessionName: string; + approvals: ApprovalRequest[]; +} + +const GlobalApprovalOverlay: React.FC = () => { + const c = useClaudeTokens(); + const dispatch = useAppDispatch(); + const sessions = useAppSelector((state) => state.agents.sessions); + const [collapsed, setCollapsed] = useState(false); + + const groups: SessionApprovalGroup[] = useMemo(() => { + const result: SessionApprovalGroup[] = []; + for (const [sessionId, session] of Object.entries(sessions)) { + if (session.pending_approvals.length > 0) { + result.push({ + sessionId, + sessionName: session.name || 'Agent', + approvals: session.pending_approvals, + }); + } + } + return result; + }, [sessions]); + + const totalApprovals = useMemo( + () => groups.reduce((sum, g) => sum + g.approvals.length, 0), + [groups], + ); + + useEffect(() => { + if (totalApprovals > 0) { + setCollapsed(false); + } + }, [totalApprovals]); + + const onApprove = useCallback( + (requestId: string, updatedInput?: Record) => { + dispatch(handleApproval({ requestId, behavior: 'allow', updatedInput })); + }, + [dispatch], + ); + + const onDeny = useCallback( + (requestId: string, message?: string) => { + dispatch(handleApproval({ requestId, behavior: 'deny', message })); + }, + [dispatch], + ); + + if (totalApprovals === 0) return null; + + return ( + + {/* Header */} + setCollapsed((v) => !v)} + sx={{ + display: 'flex', + alignItems: 'center', + gap: 1, + px: 2, + py: 1.25, + bgcolor: c.status.warningBg, + borderBottom: collapsed ? 'none' : `1px solid ${c.status.warning}20`, + cursor: 'pointer', + userSelect: 'none', + '&:hover': { bgcolor: `${c.status.warning}18` }, + transition: 'background-color 0.15s', + }} + > + + + Approval Required + + + + {collapsed ? : } + + + + {/* Content */} + {!collapsed && ( + + {groups.map((group) => ( + + {groups.length > 1 && ( + + {group.sessionName} + + )} + {group.approvals.length > 1 ? ( + + ) : ( + group.approvals.map((req) => ( + + )) + )} + + ))} + + )} + + ); +}; + +export default GlobalApprovalOverlay; diff --git a/frontend/src/app/components/KeyboardShortcutsHelp.tsx b/frontend/src/app/components/KeyboardShortcutsHelp.tsx new file mode 100644 index 00000000..117fc2ac --- /dev/null +++ b/frontend/src/app/components/KeyboardShortcutsHelp.tsx @@ -0,0 +1,93 @@ +import React, { useState, useEffect } from 'react'; +import Dialog from '@mui/material/Dialog'; +import DialogTitle from '@mui/material/DialogTitle'; +import DialogContent from '@mui/material/DialogContent'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; + +const shortcuts = [ + { key: 'd', description: 'Go to Dashboard' }, + { key: 't', description: 'Go to Templates' }, + { key: '1-9', description: 'Open agent by position' }, + { key: 'Shift+A', description: 'Approve all pending' }, + { key: 'Shift+D', description: 'Deny all pending' }, + { key: '?', description: 'Show this help' }, +]; + +const KeyboardShortcutsHelp: React.FC = () => { + const c = useClaudeTokens(); + const [open, setOpen] = useState(false); + + useEffect(() => { + const handler = (e: KeyboardEvent) => { + const target = e.target as HTMLElement; + if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable) return; + if (e.key === '?') { + setOpen((prev) => !prev); + } + }; + window.addEventListener('keydown', handler); + return () => window.removeEventListener('keydown', handler); + }, []); + + return ( + setOpen(false)} + PaperProps={{ + sx: { + bgcolor: c.bg.surface, + color: c.text.primary, + borderRadius: 4, + border: `1px solid ${c.border.subtle}`, + minWidth: 360, + boxShadow: c.shadow.lg, + }, + }} + > + + Keyboard Shortcuts + + + {shortcuts.map((s) => ( + + {s.description} + + + {s.key} + + + + ))} + + + ); +}; + +export default KeyboardShortcutsHelp; diff --git a/frontend/src/app/components/Layout/AppShell.tsx b/frontend/src/app/components/Layout/AppShell.tsx new file mode 100644 index 00000000..dddc98ba --- /dev/null +++ b/frontend/src/app/components/Layout/AppShell.tsx @@ -0,0 +1,303 @@ +import React, { useState, useEffect } from 'react'; +import { NavLink, Outlet, useNavigate, useLocation } from 'react-router-dom'; +import { openSettingsModal } from '@/shared/state/settingsSlice'; +import Box from '@mui/material/Box'; +import List from '@mui/material/List'; +import ListItemButton from '@mui/material/ListItemButton'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemText from '@mui/material/ListItemText'; +import Typography from '@mui/material/Typography'; +import IconButton from '@mui/material/IconButton'; +import Tooltip from '@mui/material/Tooltip'; +import Collapse from '@mui/material/Collapse'; +import DashboardIcon from '@mui/icons-material/Dashboard'; +import DescriptionIcon from '@mui/icons-material/Description'; +import PsychologyIcon from '@mui/icons-material/Psychology'; +import BuildIcon from '@mui/icons-material/Build'; +import TuneIcon from '@mui/icons-material/Tune'; +import TerminalIcon from '@mui/icons-material/Terminal'; +import ViewQuiltIcon from '@mui/icons-material/ViewQuilt'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import AddIcon from '@mui/icons-material/Add'; +import SettingsIcon from '@mui/icons-material/Settings'; +import Settings from '@/app/pages/Settings/Settings'; +import GlobalApprovalOverlay from '@/app/components/GlobalApprovalOverlay'; +import { useAppDispatch, useAppSelector } from '@/shared/hooks'; +import { fetchDashboards, createDashboard } from '@/shared/state/dashboardsSlice'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; + +const NAV_ITEMS = [ + { label: 'Templates', path: '/templates', icon: }, + { label: 'Skills', path: '/skills', icon: }, + { label: 'Tools', path: '/tools', icon: }, + { label: 'Modes', path: '/modes', icon: }, + { label: 'Commands', path: '/commands', icon: }, + { label: 'Views', path: '/views', icon: }, +]; + + +const AppShell: React.FC = () => { + const c = useClaudeTokens(); + const dispatch = useAppDispatch(); + const navigate = useNavigate(); + const location = useLocation(); + const [dashboardsExpanded, setDashboardsExpanded] = useState(false); + + const dashboardItems = useAppSelector((state) => state.dashboards.items); + const dashboardList = Object.values(dashboardItems).sort( + (a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime(), + ); + + useEffect(() => { + dispatch(fetchDashboards()); + }, [dispatch]); + + const isDashboardRoute = location.pathname === '/' || location.pathname.startsWith('/dashboard/'); + const activeDashboardId = location.pathname.startsWith('/dashboard/') + ? location.pathname.split('/dashboard/')[1] + : null; + + const handleDashboardsClick = () => { + if (isDashboardRoute && location.pathname === '/') { + setDashboardsExpanded((prev) => !prev); + } else { + navigate('/'); + setDashboardsExpanded(true); + } + }; + + const handleDashboardItemClick = (dashboardId: string) => { + navigate(`/dashboard/${dashboardId}`); + }; + + const handleCreateDashboard = async (e: React.MouseEvent) => { + e.stopPropagation(); + const result = await dispatch(createDashboard('Untitled Dashboard')); + if (createDashboard.fulfilled.match(result)) { + navigate(`/dashboard/${result.payload.id}`); + } + }; + + return ( + + + + + + + Open Swarm + + + Agent Orchestrator + + + + + + + + + + + + + + + {dashboardList.length > 0 && ( + + )} + + + 0} timeout={200}> + + {dashboardList.map((entry) => { + const isActive = activeDashboardId === entry.id; + return ( + handleDashboardItemClick(entry.id)} + sx={{ + display: 'flex', + alignItems: 'center', + gap: 0.5, + px: 1.5, + py: 0.75, + borderRadius: 0, + cursor: 'pointer', + bgcolor: isActive ? `${c.accent.primary}08` : 'transparent', + borderLeft: isActive ? `1.5px solid ${c.accent.primary}90` : '1.5px solid transparent', + '&:hover': { bgcolor: `${c.accent.primary}0C` }, + transition: 'background-color 0.15s, border-color 0.15s', + }} + > + {isActive && ( + + )} + + + {entry.name} + + + + ); + })} + + + + + + {NAV_ITEMS.map((item) => ( + + {({ isActive }) => ( + + + {item.icon} + + + + )} + + ))} + + + {/* Settings */} + + + Settings + + + dispatch(openSettingsModal())} + size="small" + sx={{ + color: c.text.tertiary, + '&:hover': { color: c.accent.primary, bgcolor: `${c.accent.primary}0A` }, + transition: c.transition, + }} + > + + + + + + + + + + + + + + ); +}; + +export default AppShell; diff --git a/frontend/src/app/components/RichPromptEditor.tsx b/frontend/src/app/components/RichPromptEditor.tsx new file mode 100644 index 00000000..707579b2 --- /dev/null +++ b/frontend/src/app/components/RichPromptEditor.tsx @@ -0,0 +1,366 @@ +import React, { useState, useRef, useCallback, useEffect } from 'react'; +import { createPortal } from 'react-dom'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import CommandPicker, { CommandPickerItem } from '@/app/components/CommandPicker'; +import { + SKILL_PILL_ATTR, + AttachedSkill, + createSkillPillElement, + serializeEditorContent, + detectEditorTrigger, + TriggerState, + EMPTY_TRIGGER, +} from '@/app/components/richEditorUtils'; +import TemplateInvokeModal from '@/app/pages/AgentChat/TemplateInvokeModal'; +import { useAppSelector } from '@/shared/hooks'; +import { PromptTemplate } from '@/shared/state/templatesSlice'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; + +interface RichPromptEditorProps { + value: string; + onChange: (value: string) => void; + label?: string; + placeholder?: string; + minRows?: number; + maxRows?: number; +} + +const LINE_HEIGHT = 1.5; +const FONT_SIZE = 0.85; + +const RichPromptEditor: React.FC = ({ + value, + onChange, + label = '', + placeholder = '', + minRows = 3, + maxRows = 8, +}) => { + const c = useClaudeTokens(); + const editorRef = useRef(null); + const wrapperRef = useRef(null); + const [focused, setFocused] = useState(false); + const [hasContent, setHasContent] = useState(false); + + const [attachedSkills, setAttachedSkills] = useState>({}); + const attachedSkillsRef = useRef(attachedSkills); + attachedSkillsRef.current = attachedSkills; + + const [picker, setPicker] = useState(EMPTY_TRIGGER); + const [pickerRect, setPickerRect] = useState(null); + const [selectedTemplate, setSelectedTemplate] = useState(null); + + const templates = useAppSelector((state) => state.templates.items); + const skills = useAppSelector((state) => state.skills.items); + + useEffect(() => { + if (picker.visible && wrapperRef.current) { + setPickerRect(wrapperRef.current.getBoundingClientRect()); + } else { + setPickerRect(null); + } + }, [picker.visible]); + + const minHeight = minRows * FONT_SIZE * LINE_HEIGHT; + const maxHeight = maxRows * FONT_SIZE * LINE_HEIGHT; + + const isLabelFloating = focused || hasContent; + + // Sync external value → editor on mount / when value changes externally + const lastEmittedRef = useRef(value); + useEffect(() => { + const editor = editorRef.current; + if (!editor) return; + if (value === lastEmittedRef.current) return; + // External change — update editor content + lastEmittedRef.current = value; + editor.textContent = value; + setHasContent(!!value); + }, [value]); + + const emitChange = useCallback(() => { + const editor = editorRef.current; + if (!editor) return; + const serialized = serializeEditorContent(editor, attachedSkillsRef.current); + lastEmittedRef.current = serialized; + onChange(serialized); + }, [onChange]); + + const updateHasContent = useCallback(() => { + const editor = editorRef.current; + if (!editor) return; + const text = (editor.textContent || '').replace(/\u200B/g, ''); + const hasPills = editor.querySelector(`[${SKILL_PILL_ATTR}]`) !== null; + setHasContent(text.trim().length > 0 || hasPills); + }, []); + + const syncAttachedSkills = useCallback(() => { + const editor = editorRef.current; + if (!editor) return; + const pillIds = new Set( + Array.from(editor.querySelectorAll(`[${SKILL_PILL_ATTR}]`)) + .map((el) => el.getAttribute(SKILL_PILL_ATTR)) + .filter(Boolean) as string[], + ); + setAttachedSkills((prev) => { + const prevKeys = Object.keys(prev); + if (prevKeys.length === pillIds.size && prevKeys.every((k) => pillIds.has(k))) return prev; + const next: Record = {}; + for (const [id, skill] of Object.entries(prev)) { + if (pillIds.has(id)) next[id] = skill; + } + return next; + }); + }, []); + + const removeSkillPill = useCallback((skillId: string) => { + const editor = editorRef.current; + if (!editor) return; + const pill = editor.querySelector(`[${SKILL_PILL_ATTR}="${skillId}"]`); + if (pill) pill.remove(); + setAttachedSkills((prev) => { + const { [skillId]: _, ...rest } = prev; + return rest; + }); + updateHasContent(); + emitChange(); + editor.focus(); + }, [updateHasContent, emitChange]); + + const detectTrigger = useCallback(() => { + const result = detectEditorTrigger(); + if (result) { + setPicker(result); + } else { + setPicker((p) => ({ ...p, visible: false })); + } + }, []); + + const handleInput = useCallback(() => { + updateHasContent(); + detectTrigger(); + syncAttachedSkills(); + emitChange(); + }, [updateHasContent, detectTrigger, syncAttachedSkills, emitChange]); + + const handleEditorClick = useCallback(() => { + detectTrigger(); + }, [detectTrigger]); + + const handlePickerSelect = (item: CommandPickerItem) => { + setPicker((p) => ({ ...p, visible: false })); + const editor = editorRef.current; + if (!editor) return; + editor.focus(); + + const { triggerNode, triggerOffset, filter } = picker; + if (triggerNode && triggerNode.parentNode && editor.contains(triggerNode)) { + const endOffset = Math.min(triggerOffset + 1 + filter.length, triggerNode.length); + const range = document.createRange(); + range.setStart(triggerNode, triggerOffset); + range.setEnd(triggerNode, endOffset); + range.deleteContents(); + const sel = window.getSelection(); + if (sel) { sel.removeAllRanges(); sel.addRange(range); } + } + + if (item.type === 'template') { + const tmpl = templates[item.id]; + if (!tmpl) return; + if (tmpl.fields.length === 0) { + document.execCommand('insertText', false, tmpl.template); + } else { + setSelectedTemplate(tmpl); + } + } else if (item.type === 'skill') { + const skill = skills[item.id]; + if (!skill) return; + if (editor.querySelector(`[${SKILL_PILL_ATTR}="${skill.id}"]`)) return; + + const pill = createSkillPillElement( + { id: skill.id, name: skill.name, content: skill.content }, + removeSkillPill, + c.font.mono, + c.status.error, + ); + + const sel = window.getSelection(); + if (sel && sel.rangeCount > 0) { + const range = sel.getRangeAt(0); + range.collapse(false); + range.insertNode(pill); + const spacer = document.createTextNode('\u200B'); + pill.after(spacer); + const newRange = document.createRange(); + newRange.setStartAfter(spacer); + newRange.collapse(true); + sel.removeAllRanges(); + sel.addRange(newRange); + } + + setAttachedSkills((prev) => ({ + ...prev, + [skill.id]: { id: skill.id, name: skill.name, content: skill.content }, + })); + } else if (item.type === 'mode') { + document.execCommand('insertText', false, item.name); + } else if (item.type === 'context') { + document.execCommand('insertText', false, `@${item.command} `); + } + + updateHasContent(); + emitChange(); + setTimeout(() => editor.focus(), 0); + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (picker.visible && ['ArrowDown', 'ArrowUp', 'Escape', 'Tab', 'Enter'].includes(e.key)) { + e.preventDefault(); + return; + } + if ((e.ctrlKey || e.metaKey) && ['b', 'i', 'u'].includes(e.key.toLowerCase())) { + e.preventDefault(); + return; + } + }; + + const handlePaste = useCallback((e: React.ClipboardEvent) => { + e.preventDefault(); + const plain = e.clipboardData.getData('text/plain'); + if (plain) document.execCommand('insertText', false, plain); + }, []); + + return ( + + {picker.visible && pickerRect && createPortal( +
+
+ setPicker((p) => ({ ...p, visible: false }))} + visible={picker.visible} + /> +
+
, + document.body, + )} + + editorRef.current?.focus()} + sx={{ + position: 'relative', + border: `1px solid ${focused ? c.accent.primary : c.border.medium}`, + borderRadius: '4px', + bgcolor: c.bg.page, + transition: 'border-color 0.15s', + '&:hover': { + borderColor: focused ? c.accent.primary : c.text.primary, + }, + cursor: 'text', + }} + > + {label && ( + + {label} + + )} + + +
setFocused(true)} + onBlur={() => setFocused(false)} + style={{ + width: '100%', + minHeight: `${minHeight}rem`, + maxHeight: `${maxHeight}rem`, + overflowY: 'auto', + background: 'transparent', + border: 'none', + outline: 'none', + color: c.text.primary, + fontSize: `${FONT_SIZE}rem`, + lineHeight: `${LINE_HEIGHT}`, + fontFamily: 'inherit', + wordBreak: 'break-word', + whiteSpace: 'pre-wrap', + }} + /> + {!hasContent && ( +
+ {placeholder} +
+ )} + + + + {selectedTemplate && ( + setSelectedTemplate(null)} + onApply={(rendered) => { + const editor = editorRef.current; + if (editor) { + document.execCommand('insertText', false, rendered); + } + setSelectedTemplate(null); + updateHasContent(); + emitChange(); + }} + /> + )} + + ); +}; + +export default RichPromptEditor; diff --git a/frontend/src/app/components/SelectionOverlay.tsx b/frontend/src/app/components/SelectionOverlay.tsx new file mode 100644 index 00000000..51e534e4 --- /dev/null +++ b/frontend/src/app/components/SelectionOverlay.tsx @@ -0,0 +1,240 @@ +import React, { useEffect, useState, useRef } from 'react'; +import ReactDOM from 'react-dom'; +import { OverlayState, DragRect, DragPreviewElement } from './useDomElementSelector'; +import { useElementSelection } from './ElementSelectionContext'; + +const HIGHLIGHT_COLOR = '#3b82f6'; +const HIGHLIGHT_BG = 'rgba(59, 130, 246, 0.08)'; +const SELECTED_BG = 'rgba(59, 130, 246, 0.06)'; +const DRAG_BG = 'rgba(59, 130, 246, 0.1)'; +const DRAG_BORDER = 'rgba(59, 130, 246, 0.5)'; +const PREVIEW_ADD_BORDER = 'rgba(59, 130, 246, 0.6)'; +const PREVIEW_ADD_BG = 'rgba(59, 130, 246, 0.1)'; +const PREVIEW_REMOVE_BORDER = 'rgba(239, 68, 68, 0.6)'; +const PREVIEW_REMOVE_BG = 'rgba(239, 68, 68, 0.1)'; + +interface PersistentRect { + id: string; + top: number; + left: number; + width: number; + height: number; + label: string; +} + +interface Props { + overlay: OverlayState; + dragRect: DragRect; + dragPreview?: DragPreviewElement[]; +} + +const SelectionOverlay: React.FC = ({ overlay, dragRect, dragPreview = [] }) => { + const ctx = useElementSelection(); + const [persistentRects, setPersistentRects] = useState([]); + const rafRef = useRef(null); + + useEffect(() => { + if (!ctx || ctx.selectedElements.length === 0) { + setPersistentRects([]); + return; + } + + const semanticEls = ctx.selectedElements.filter((e) => e.semanticType); + if (semanticEls.length === 0) { + setPersistentRects([]); + return; + } + + const updateRects = () => { + const rects: PersistentRect[] = []; + for (const sel of semanticEls) { + try { + const domEl = document.querySelector(sel.selectorPath); + if (domEl) { + const rect = domEl.getBoundingClientRect(); + rects.push({ + id: sel.id, + top: rect.top, + left: rect.left, + width: rect.width, + height: rect.height, + label: sel.semanticLabel || sel.tagName, + }); + } + } catch { + // selector might be invalid + } + } + setPersistentRects(rects); + rafRef.current = requestAnimationFrame(updateRects); + }; + + rafRef.current = requestAnimationFrame(updateRects); + return () => { + if (rafRef.current) cancelAnimationFrame(rafRef.current); + }; + }, [ctx?.selectedElements]); + + const hasHover = overlay.visible; + const hasPersistent = persistentRects.length > 0; + const hasDrag = dragRect.visible; + const hasPreview = dragPreview.length > 0; + + if (!hasHover && !hasPersistent && !hasDrag && !hasPreview) return null; + + return ReactDOM.createPortal( + <> + {/* Persistent highlights for already-selected elements */} + {persistentRects.map((r) => ( + +
+
+ ✓ {r.label} +
+ + ))} + + {/* Drag-select rectangle */} + {hasDrag && ( +
+ )} + + {/* Drag preview highlights */} + {dragPreview.map((p) => { + const isRemove = p.action === 'remove'; + const borderColor = isRemove ? PREVIEW_REMOVE_BORDER : PREVIEW_ADD_BORDER; + const bgColor = isRemove ? PREVIEW_REMOVE_BG : PREVIEW_ADD_BG; + const labelBg = isRemove ? '#ef4444' : HIGHLIGHT_COLOR; + const labelText = isRemove ? `− ${p.label}` : `+ ${p.label}`; + return ( + +
+
+ {labelText} +
+ + ); + })} + + {/* Hover highlight */} + {hasHover && ( + <> +
+
+ {overlay.label} +
+ + )} + , + document.body, + ); +}; + +export default SelectionOverlay; diff --git a/frontend/src/app/components/SlashCommandPicker.tsx b/frontend/src/app/components/SlashCommandPicker.tsx new file mode 100644 index 00000000..3982c054 --- /dev/null +++ b/frontend/src/app/components/SlashCommandPicker.tsx @@ -0,0 +1,165 @@ +import React, { useState, useEffect, useMemo } from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import Paper from '@mui/material/Paper'; +import List from '@mui/material/List'; +import ListItemButton from '@mui/material/ListItemButton'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemText from '@mui/material/ListItemText'; +import DescriptionIcon from '@mui/icons-material/Description'; +import PsychologyIcon from '@mui/icons-material/Psychology'; +import { useAppSelector } from '@/shared/hooks'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; + +export interface SlashItem { + id: string; + type: 'template' | 'skill'; + name: string; + description: string; + command: string; +} + +interface Props { + filter: string; + onSelect: (item: SlashItem) => void; + onClose: () => void; + visible: boolean; +} + +const SlashCommandPicker: React.FC = ({ filter, onSelect, onClose, visible }) => { + const c = useClaudeTokens(); + const templates = useAppSelector((state) => state.templates.items); + const skills = useAppSelector((state) => state.skills.items); + const [selectedIndex, setSelectedIndex] = useState(0); + + const items: SlashItem[] = useMemo(() => { + const all: SlashItem[] = [ + ...Object.values(templates).map((t) => ({ + id: t.id, + type: 'template' as const, + name: t.name, + description: t.description || `Template with ${t.fields.length} fields`, + command: t.name.toLowerCase().replace(/\s+/g, '-'), + })), + ...Object.values(skills).map((s) => ({ + id: s.id, + type: 'skill' as const, + name: s.name, + description: s.description || 'Skill', + command: s.command || s.id, + })), + ]; + + if (!filter) return all; + const lower = filter.toLowerCase(); + return all.filter( + (item) => + item.name.toLowerCase().includes(lower) || + item.command.toLowerCase().includes(lower) || + item.description.toLowerCase().includes(lower) + ); + }, [templates, skills, filter]); + + useEffect(() => { + setSelectedIndex(0); + }, [filter]); + + useEffect(() => { + if (!visible) return; + const handler = (e: KeyboardEvent) => { + if (e.key === 'ArrowDown') { + e.preventDefault(); + setSelectedIndex((prev) => Math.min(prev + 1, items.length - 1)); + } else if (e.key === 'ArrowUp') { + e.preventDefault(); + setSelectedIndex((prev) => Math.max(prev - 1, 0)); + } else if (e.key === 'Enter' && items[selectedIndex]) { + e.preventDefault(); + onSelect(items[selectedIndex]); + } else if (e.key === 'Escape') { + e.preventDefault(); + onClose(); + } + }; + window.addEventListener('keydown', handler); + return () => window.removeEventListener('keydown', handler); + }, [visible, items, selectedIndex, onSelect, onClose]); + + if (!visible || items.length === 0) return null; + + return ( + + + + Commands + + + + {items.map((item, i) => ( + onSelect(item)} + sx={{ + py: 0.75, + px: 1.5, + '&.Mui-selected': { bgcolor: 'rgba(174,86,48,0.06)' }, + '&:hover': { bgcolor: 'rgba(0,0,0,0.04)' }, + }} + > + + {item.type === 'template' ? ( + + ) : ( + + )} + + + + /{item.command} + + + {item.type} + + + } + secondary={ + + {item.description} + + } + /> + + ))} + + + ); +}; + +export default SlashCommandPicker; diff --git a/frontend/src/app/components/richEditorUtils.ts b/frontend/src/app/components/richEditorUtils.ts new file mode 100644 index 00000000..12cf528e --- /dev/null +++ b/frontend/src/app/components/richEditorUtils.ts @@ -0,0 +1,158 @@ +export const SKILL_PILL_ATTR = 'data-skill-id'; +export const SKILL_COLOR = '#7B61BD'; + +export interface AttachedSkill { + id: string; + name: string; + content: string; +} + +export function createSkillPillElement( + skill: AttachedSkill, + onRemove: (id: string) => void, + monoFont: string, + errorColor: string, +): HTMLSpanElement { + const pill = document.createElement('span'); + pill.setAttribute(SKILL_PILL_ATTR, skill.id); + pill.contentEditable = 'false'; + Object.assign(pill.style, { + display: 'inline-flex', + alignItems: 'center', + gap: '3px', + padding: '1px 4px 1px 6px', + margin: '0 1px', + borderRadius: '999px', + background: `${SKILL_COLOR}18`, + color: SKILL_COLOR, + fontSize: '0.72rem', + fontFamily: monoFont, + lineHeight: '1.8', + verticalAlign: 'baseline', + userSelect: 'none', + whiteSpace: 'nowrap' as const, + cursor: 'default', + }); + + const label = document.createElement('span'); + label.textContent = skill.name; + Object.assign(label.style, { maxWidth: '180px', overflow: 'hidden', textOverflow: 'ellipsis' }); + + const closeBtn = document.createElement('span'); + closeBtn.textContent = '\u00d7'; + Object.assign(closeBtn.style, { + cursor: 'pointer', + fontSize: '13px', + lineHeight: '1', + opacity: '0.6', + marginLeft: '1px', + fontWeight: '700', + borderRadius: '50%', + width: '14px', + height: '14px', + display: 'inline-flex', + alignItems: 'center', + justifyContent: 'center', + }); + closeBtn.addEventListener('mouseover', () => { closeBtn.style.opacity = '1'; closeBtn.style.color = errorColor; }); + closeBtn.addEventListener('mouseout', () => { closeBtn.style.opacity = '0.6'; closeBtn.style.color = 'inherit'; }); + closeBtn.addEventListener('mousedown', (e) => { e.preventDefault(); e.stopPropagation(); onRemove(skill.id); }); + + pill.appendChild(label); + pill.appendChild(closeBtn); + return pill; +} + +export function serializeEditorContent(editor: HTMLElement, skills: Record): string { + const parts: string[] = []; + let hasOutput = false; + + const walk = (parent: Node) => { + parent.childNodes.forEach((node) => { + if (node.nodeType === Node.TEXT_NODE) { + const t = (node.textContent || '').replace(/\u200B/g, ''); + if (t) hasOutput = true; + parts.push(t); + } else if (node.nodeType === Node.ELEMENT_NODE) { + const el = node as HTMLElement; + const sid = el.getAttribute(SKILL_PILL_ATTR); + if (sid && skills[sid]) { + hasOutput = true; + parts.push(`{{skill:${skills[sid].name}}}`); + return; + } + if (el.tagName === 'BR') { parts.push('\n'); return; } + if (el.tagName === 'DIV' || el.tagName === 'P') { + if (hasOutput) parts.push('\n'); + walk(el); + return; + } + walk(el); + } + }); + }; + + walk(editor); + return parts.join(''); +} + +export interface TriggerState { + visible: boolean; + trigger: '/' | '@'; + filter: string; + triggerNode: Text | null; + triggerOffset: number; +} + +export const EMPTY_TRIGGER: TriggerState = { + visible: false, + trigger: '/', + filter: '', + triggerNode: null, + triggerOffset: 0, +}; + +export function detectEditorTrigger(): TriggerState | null { + const sel = window.getSelection(); + if (!sel || sel.rangeCount === 0 || !sel.isCollapsed) return null; + + const { startContainer: node, startOffset: offset } = sel.getRangeAt(0); + if (node.nodeType !== Node.TEXT_NODE) return null; + + const textNode = node as Text; + const text = textNode.textContent || ''; + const before = text.slice(0, offset); + + let triggerIdx = -1; + let triggerChar: '/' | '@' | null = null; + for (let i = before.length - 1; i >= 0; i--) { + const ch = before[i]; + if (ch === ' ' || ch === '\n') break; + if (ch === '@') { + if (i === 0 || before[i - 1] === ' ' || before[i - 1] === '\n') { + triggerIdx = i; + triggerChar = '@'; + } + break; + } + if (ch === '/') { + if (i === 0 || before[i - 1] === ' ' || before[i - 1] === '\n') { + triggerIdx = i; + triggerChar = '/'; + break; + } + continue; + } + } + + if (triggerChar && triggerIdx >= 0) { + return { + visible: true, + trigger: triggerChar, + filter: before.slice(triggerIdx + 1), + triggerNode: textNode, + triggerOffset: triggerIdx, + }; + } + return null; +} diff --git a/frontend/src/app/components/useDomElementSelector.ts b/frontend/src/app/components/useDomElementSelector.ts new file mode 100644 index 00000000..fb2f4068 --- /dev/null +++ b/frontend/src/app/components/useDomElementSelector.ts @@ -0,0 +1,339 @@ +import { useEffect, useRef, useState, useCallback } from 'react'; +import { SelectedElement, useElementSelection } from './ElementSelectionContext'; + +const SELECT_ATTR = 'data-select-type'; +const SELECT_ID_ATTR = 'data-select-id'; +const SELECT_META_ATTR = 'data-select-meta'; + +export interface OverlayState { + visible: boolean; + top: number; + left: number; + width: number; + height: number; + label: string; +} + +export interface DragRect { + visible: boolean; + top: number; + left: number; + width: number; + height: number; +} + +const EMPTY_OVERLAY: OverlayState = { visible: false, top: 0, left: 0, width: 0, height: 0, label: '' }; +const EMPTY_DRAG: DragRect = { visible: false, top: 0, left: 0, width: 0, height: 0 }; + +const SEMANTIC_LABELS: Record = { + 'agent-card': 'Agent', + 'message': 'Message', + 'tool-call': 'Tool Call', + 'tool-group': 'Tool Group', + 'view-card': 'View', +}; + +function findSelectableAncestor(target: Element): Element | null { + let current: Element | null = target; + while (current) { + if (current.hasAttribute(SELECT_ATTR)) return current; + current = current.parentElement; + } + return null; +} + +function buildSemanticLabel(type: string, meta: Record): string { + const prefix = SEMANTIC_LABELS[type] || type; + if (meta.name) return `${prefix}: ${meta.name}`; + if (meta.role && meta.content) { + const truncated = String(meta.content).slice(0, 40); + return `${prefix} (${meta.role}): ${truncated}${String(meta.content).length > 40 ? '…' : ''}`; + } + if (meta.label) return `${prefix}: ${meta.label}`; + if (meta.tool) return `${prefix}: ${meta.tool}`; + return prefix; +} + +function rectsIntersect( + a: { top: number; left: number; bottom: number; right: number }, + b: { top: number; left: number; bottom: number; right: number }, +): boolean { + return a.left < b.right && a.right > b.left && a.top < b.bottom && a.bottom > b.top; +} + +function buildSelectedElement(el: Element): SelectedElement { + const type = el.getAttribute(SELECT_ATTR) || ''; + const selectId = el.getAttribute(SELECT_ID_ATTR) || ''; + let meta: Record = {}; + try { meta = JSON.parse(el.getAttribute(SELECT_META_ATTR) || '{}'); } catch {} + const rect = el.getBoundingClientRect(); + const semanticLabel = buildSemanticLabel(type, meta); + + return { + id: `sel-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`, + selectorPath: `[${SELECT_ATTR}="${type}"][${SELECT_ID_ATTR}="${selectId}"]`, + tagName: el.tagName, + className: '', + outerHTML: '', + computedStyles: {}, + boundingRect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height }, + semanticType: type as SelectedElement['semanticType'], + semanticLabel, + semanticData: { ...meta, selectId }, + }; +} + +export interface DragPreviewElement { + selectId: string; + top: number; + left: number; + width: number; + height: number; + label: string; + action: 'add' | 'remove'; +} + +const DRAG_THRESHOLD = 5; + +export interface DomSelectorState { + overlay: OverlayState; + dragRect: DragRect; + dragPreview: DragPreviewElement[]; +} + +export function useDomElementSelector(): DomSelectorState { + const ctx = useElementSelection(); + const [overlay, setOverlay] = useState(EMPTY_OVERLAY); + const [dragRect, setDragRect] = useState(EMPTY_DRAG); + const [dragPreview, setDragPreview] = useState([]); + const hoveredRef = useRef(null); + const rafRef = useRef(null); + const dragPreviewRafRef = useRef(null); + + const dragOriginRef = useRef<{ x: number; y: number } | null>(null); + const isDraggingRef = useRef(false); + const dragBoundsRef = useRef<{ left: number; top: number; right: number; bottom: number } | null>(null); + + const selectedIdsRef = useRef(new Map()); + useEffect(() => { + const map = new Map(); + for (const el of (ctx?.selectedElements ?? [])) { + if (el.semanticData?.selectId) { + map.set(el.semanticData.selectId as string, el.id); + } + } + selectedIdsRef.current = map; + }, [ctx?.selectedElements]); + + const handleMouseMove = useCallback((e: MouseEvent) => { + // If we're drawing a drag rectangle, update it instead of hover overlay + if (dragOriginRef.current) { + const origin = dragOriginRef.current; + const dx = e.clientX - origin.x; + const dy = e.clientY - origin.y; + + if (!isDraggingRef.current && (Math.abs(dx) > DRAG_THRESHOLD || Math.abs(dy) > DRAG_THRESHOLD)) { + isDraggingRef.current = true; + } + + if (isDraggingRef.current) { + const bounds = { + left: Math.min(origin.x, e.clientX), + top: Math.min(origin.y, e.clientY), + right: Math.max(origin.x, e.clientX), + bottom: Math.max(origin.y, e.clientY), + }; + dragBoundsRef.current = bounds; + + setOverlay(EMPTY_OVERLAY); + setDragRect({ + visible: true, + left: bounds.left, + top: bounds.top, + width: bounds.right - bounds.left, + height: bounds.bottom - bounds.top, + }); + + if (dragPreviewRafRef.current) cancelAnimationFrame(dragPreviewRafRef.current); + dragPreviewRafRef.current = requestAnimationFrame(() => { + const b = dragBoundsRef.current; + if (!b) return; + const allSelectables = document.querySelectorAll(`[${SELECT_ATTR}]`); + const preview: DragPreviewElement[] = []; + const seen = new Set(); + allSelectables.forEach((el) => { + const rect = el.getBoundingClientRect(); + if (rectsIntersect(b, { left: rect.left, top: rect.top, right: rect.right, bottom: rect.bottom })) { + const selectId = el.getAttribute(SELECT_ID_ATTR) || ''; + if (seen.has(selectId)) return; + seen.add(selectId); + const type = el.getAttribute(SELECT_ATTR) || ''; + let meta: Record = {}; + try { meta = JSON.parse(el.getAttribute(SELECT_META_ATTR) || '{}'); } catch {} + preview.push({ + selectId, + top: rect.top, + left: rect.left, + width: rect.width, + height: rect.height, + label: buildSemanticLabel(type, meta), + action: selectedIdsRef.current.has(selectId) ? 'remove' : 'add', + }); + } + }); + setDragPreview(preview); + }); + } + return; + } + + const target = e.target as Element; + if (!target) { + setOverlay(EMPTY_OVERLAY); + hoveredRef.current = null; + return; + } + + if (target.tagName === 'IFRAME') { + setOverlay(EMPTY_OVERLAY); + hoveredRef.current = null; + return; + } + + const selectable = findSelectableAncestor(target); + if (!selectable) { + setOverlay(EMPTY_OVERLAY); + hoveredRef.current = null; + return; + } + + hoveredRef.current = selectable; + + if (rafRef.current) cancelAnimationFrame(rafRef.current); + rafRef.current = requestAnimationFrame(() => { + const rect = selectable.getBoundingClientRect(); + const type = selectable.getAttribute(SELECT_ATTR) || ''; + let meta: Record = {}; + try { meta = JSON.parse(selectable.getAttribute(SELECT_META_ATTR) || '{}'); } catch {} + const label = buildSemanticLabel(type, meta); + setOverlay({ + visible: true, + top: rect.top, + left: rect.left, + width: rect.width, + height: rect.height, + label, + }); + }); + }, []); + + const handleMouseDown = useCallback((e: MouseEvent) => { + if (e.button !== 0) return; + const target = e.target as Element; + // Only start drag on "empty" canvas areas (not on selectable elements) + if (target && findSelectableAncestor(target)) return; + dragOriginRef.current = { x: e.clientX, y: e.clientY }; + isDraggingRef.current = false; + }, []); + + const handleMouseUp = useCallback((e: MouseEvent) => { + if (!dragOriginRef.current) return; + + if (isDraggingRef.current && ctx) { + const dr = { + left: Math.min(dragOriginRef.current.x, e.clientX), + top: Math.min(dragOriginRef.current.y, e.clientY), + right: Math.max(dragOriginRef.current.x, e.clientX), + bottom: Math.max(dragOriginRef.current.y, e.clientY), + }; + + const allSelectables = document.querySelectorAll(`[${SELECT_ATTR}]`); + const processed = new Set(); + + allSelectables.forEach((el) => { + const rect = el.getBoundingClientRect(); + const elRect = { + left: rect.left, + top: rect.top, + right: rect.right, + bottom: rect.bottom, + }; + + if (rectsIntersect(dr, elRect)) { + const selectId = el.getAttribute(SELECT_ID_ATTR) || ''; + if (processed.has(selectId)) return; + processed.add(selectId); + + const existingId = selectedIdsRef.current.get(selectId); + if (existingId) { + ctx.removeSelectedElement(existingId); + } else { + ctx.addSelectedElement(buildSelectedElement(el)); + } + } + }); + } + + dragOriginRef.current = null; + isDraggingRef.current = false; + dragBoundsRef.current = null; + setDragRect(EMPTY_DRAG); + setDragPreview([]); + if (dragPreviewRafRef.current) cancelAnimationFrame(dragPreviewRafRef.current); + }, [ctx]); + + const handleClick = useCallback((e: MouseEvent) => { + if (!ctx) return; + const target = hoveredRef.current; + if (!target) return; + + e.preventDefault(); + e.stopPropagation(); + + const selectId = target.getAttribute(SELECT_ID_ATTR) || ''; + const existing = ctx.selectedElements.find( + (el) => el.semanticData?.selectId === selectId, + ); + if (existing) { + ctx.removeSelectedElement(existing.id); + } else { + ctx.addSelectedElement(buildSelectedElement(target)); + } + }, [ctx]); + + useEffect(() => { + if (!ctx?.selectMode) { + setOverlay(EMPTY_OVERLAY); + setDragRect(EMPTY_DRAG); + setDragPreview([]); + hoveredRef.current = null; + dragOriginRef.current = null; + dragBoundsRef.current = null; + isDraggingRef.current = false; + return; + } + + document.addEventListener('mousemove', handleMouseMove, true); + document.addEventListener('mousedown', handleMouseDown, true); + document.addEventListener('mouseup', handleMouseUp, true); + document.addEventListener('click', handleClick, true); + + return () => { + document.removeEventListener('mousemove', handleMouseMove, true); + document.removeEventListener('mousedown', handleMouseDown, true); + document.removeEventListener('mouseup', handleMouseUp, true); + document.removeEventListener('click', handleClick, true); + if (rafRef.current) cancelAnimationFrame(rafRef.current); + if (dragPreviewRafRef.current) cancelAnimationFrame(dragPreviewRafRef.current); + setOverlay(EMPTY_OVERLAY); + setDragRect(EMPTY_DRAG); + setDragPreview([]); + hoveredRef.current = null; + dragOriginRef.current = null; + dragBoundsRef.current = null; + isDraggingRef.current = false; + }; + }, [ctx?.selectMode, handleMouseMove, handleMouseDown, handleMouseUp, handleClick]); + + return { overlay, dragRect, dragPreview }; +} diff --git a/frontend/src/app/pages/AgentChat/AgentChat.tsx b/frontend/src/app/pages/AgentChat/AgentChat.tsx new file mode 100644 index 00000000..a341c848 --- /dev/null +++ b/frontend/src/app/pages/AgentChat/AgentChat.tsx @@ -0,0 +1,638 @@ +import React, { useEffect, useLayoutEffect, useRef, useMemo, useState, useCallback } from 'react'; +import { useParams } from 'react-router-dom'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import Chip from '@mui/material/Chip'; +import IconButton from '@mui/material/IconButton'; +import Tooltip from '@mui/material/Tooltip'; +import CloseIcon from '@mui/icons-material/Close'; +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import { useAppDispatch, useAppSelector } from '@/shared/hooks'; +import { + sendMessage as sendMessageThunk, + launchAndSendFirstMessage, + generateTitle, + generateGroupMeta, + stopAgent, + handleApproval, + editMessage, + switchBranch, + updateSessionModel, + updateSessionMode, + fetchSession, + AgentMessage, +} from '@/shared/state/agentsSlice'; +import { fetchModes } from '@/shared/state/modesSlice'; +import { createSessionWs } from '@/shared/ws/WebSocketManager'; +import MessageBubble from './MessageBubble'; +import ToolCallBubble, { ToolPair } from './ToolCallBubble'; +import ToolGroupBubble, { RenderItem, ToolGroup, isToolGroup, isToolPair } from './ToolGroupBubble'; +import ApprovalBar, { BatchApprovalBar } from './ApprovalBar'; +import ChatInput, { ChatInputHandle } from './ChatInput'; +import { ContextPath } from '@/app/components/DirectoryBrowser'; +import BranchNavigator from './BranchNavigator'; +import DiffViewer from './DiffViewer'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; + +const CONTEXT_WINDOWS: Record = { + sonnet: 200_000, + opus: 200_000, + haiku: 200_000, +}; + +function stringifyContent(content: any): string { + if (content == null) return ''; + if (typeof content === 'string') return content; + return JSON.stringify(content); +} + +const thinkingDotsKeyframes = ` +@keyframes thinking-bounce { + 0%, 80%, 100% { transform: scale(0); opacity: 0.4; } + 40% { transform: scale(1); opacity: 1; } +} +`; + +const ThinkingBubble: React.FC = () => { + const c = useClaudeTokens(); + return ( + + + + {[0, 1, 2].map((i) => ( + + ))} + + + ); +}; + +interface AgentChatProps { + sessionId?: string; + onClose?: () => void; + embedded?: boolean; + initialContextPaths?: ContextPath[]; +} + +const AgentChat: React.FC = ({ sessionId: sessionIdProp, onClose, embedded, initialContextPaths }) => { + const c = useClaudeTokens(); + const STATUS_STYLES: Record = { + running: { color: c.status.success, bg: c.status.successBg }, + waiting_approval: { color: c.status.warning, bg: c.status.warningBg }, + completed: { color: c.text.tertiary, bg: c.bg.secondary }, + error: { color: c.status.error, bg: c.status.errorBg }, + stopped: { color: c.text.tertiary, bg: c.bg.secondary }, + }; + const { id: routeId } = useParams<{ id: string }>(); + const id = sessionIdProp || routeId; + const dispatch = useAppDispatch(); + const session = useAppSelector((state) => (id ? state.agents.sessions[id] : undefined)); + const modesMap = useAppSelector((state) => state.modes.items); + const scrollContainerRef = useRef(null); + const chatInputRef = useRef(null); + const isAtBottomRef = useRef(true); + const [showScrollButton, setShowScrollButton] = useState(false); + const [mode, setMode] = useState('agent'); + const [model, setModel] = useState('sonnet'); + + const wsRef = useRef | null>(null); + const initialContextApplied = useRef(false); + + const isDraft = session?.status === 'draft'; + + useEffect(() => { + if (!id || isDraft) return; + const ws = createSessionWs(id); + ws.connect(); + wsRef.current = ws; + dispatch(fetchSession(id)); + return () => { + ws.disconnect(); + wsRef.current = null; + }; + }, [id, isDraft, dispatch]); + + useEffect(() => { + if (initialContextApplied.current || !initialContextPaths?.length) return; + const timer = setTimeout(() => { + chatInputRef.current?.setContent('', initialContextPaths); + initialContextApplied.current = true; + }, 50); + return () => clearTimeout(timer); + }, [initialContextPaths]); + + useEffect(() => { + if (session) setMode(session.mode); + }, [session?.mode]); + + useEffect(() => { + if (session) setModel(session.model); + }, [session?.model]); + + useEffect(() => { + if (Object.keys(modesMap).length === 0) dispatch(fetchModes()); + }, [dispatch, modesMap]); + + const prevStatusRef = useRef(session?.status); + useEffect(() => { + const prev = prevStatusRef.current; + const curr = session?.status; + prevStatusRef.current = curr; + if (prev === 'running' && (curr === 'completed' || curr === 'stopped' || curr === 'error')) { + const currentMode = modesMap[mode]; + if (currentMode?.default_next_mode && modesMap[currentMode.default_next_mode]) { + setMode(currentMode.default_next_mode); + if (id && !isDraft) { + dispatch(updateSessionMode({ sessionId: id, mode: currentMode.default_next_mode as any })); + } + } + } + }, [session?.status, mode, modesMap, id, isDraft, dispatch]); + + const SCROLL_THRESHOLD = 50; + + const handleScroll = useCallback(() => { + const el = scrollContainerRef.current; + if (!el) return; + const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < SCROLL_THRESHOLD; + isAtBottomRef.current = atBottom; + setShowScrollButton(!atBottom); + }, []); + + const scrollToBottom = useCallback(() => { + const el = scrollContainerRef.current; + if (!el) return; + el.scrollTop = el.scrollHeight; + isAtBottomRef.current = true; + setShowScrollButton(false); + }, []); + + useLayoutEffect(() => { + if (isAtBottomRef.current) { + const el = scrollContainerRef.current; + if (el) el.scrollTop = el.scrollHeight; + } + }, [session?.messages.length, session?.streamingMessage?.content]); + + const handleSend = (prompt: string, images?: Array<{ data: string; media_type: string }>, contextPaths?: Array<{ path: string; type: 'file' | 'directory' }>, forcedTools?: string[], attachedSkills?: Array<{ id: string; name: string; content: string }>) => { + if (!id) return; + if (isDraft) { + const config: Record = { model, mode }; + if (session?.system_prompt) config.system_prompt = session.system_prompt; + if (session?.target_directory) config.target_directory = session.target_directory; + dispatch( + launchAndSendFirstMessage({ draftId: id, config, prompt, mode, model, images, contextPaths, forcedTools, attachedSkills }) + ).then((action) => { + if (launchAndSendFirstMessage.fulfilled.match(action)) { + dispatch(generateTitle({ sessionId: action.payload.session.id, prompt })); + } + }); + } else { + dispatch(sendMessageThunk({ sessionId: id, prompt, mode, model, images, contextPaths, forcedTools, attachedSkills })); + } + }; + + const handleModeChange = useCallback((newMode: string) => { + setMode(newMode); + if (id && !isDraft) dispatch(updateSessionMode({ sessionId: id, mode: newMode })); + }, [id, isDraft, dispatch]); + + const handleModelChange = useCallback((newModel: string) => { + setModel(newModel); + if (id && !isDraft) dispatch(updateSessionModel({ sessionId: id, model: newModel })); + }, [id, isDraft, dispatch]); + + const handleApprove = (requestId: string, updatedInput?: Record) => { + dispatch(handleApproval({ requestId, behavior: 'allow', updatedInput })); + }; + + const handleDeny = (requestId: string, message?: string) => { + dispatch(handleApproval({ requestId, behavior: 'deny', message })); + }; + + const handleStop = () => { + if (!id) return; + dispatch(stopAgent({ sessionId: id })); + }; + + const handleEdit = useCallback( + (messageId: string, newContent: string) => { + if (!id) return; + dispatch(editMessage({ sessionId: id, messageId, content: newContent })); + }, + [id, dispatch] + ); + + const activeBranchMessages = useMemo(() => { + if (!session) return []; + const branchId = session.active_branch_id || 'main'; + const branch = session.branches?.[branchId]; + + if (!branch || !branch.fork_point_message_id) { + return session.messages.filter((m) => m.branch_id === 'main' || m.branch_id === branchId); + } + + const forkIdx = session.messages.findIndex((m) => m.id === branch.fork_point_message_id); + const preMessages = session.messages + .slice(0, forkIdx) + .filter((m) => m.branch_id === (branch.parent_branch_id || 'main')); + const branchMessages = session.messages.filter((m) => m.branch_id === branchId); + return [...preMessages, ...branchMessages]; + }, [session?.messages, session?.active_branch_id, session?.branches]); + + const contextEstimate = useMemo(() => { + const limit = CONTEXT_WINDOWS[model] || 200_000; + let totalChars = 0; + if (session?.system_prompt) totalChars += session.system_prompt.length; + for (const msg of activeBranchMessages) { + totalChars += stringifyContent(msg.content).length; + } + if (session?.streamingMessage) { + totalChars += (session.streamingMessage.content || '').length; + } + const used = Math.round(totalChars / 4); + return { used, limit }; + }, [activeBranchMessages, session?.system_prompt, session?.streamingMessage?.content, model]); + + const sessionRunning = session?.status === 'running' || session?.status === 'waiting_approval'; + + const renderItems: RenderItem[] = useMemo(() => { + const isOutputCall = (m: AgentMessage) => + m.role === 'tool_call' && typeof m.content === 'object' && m.content.tool === 'RenderOutput'; + const isOutputResult = (m: AgentMessage) => { + if (m.role !== 'tool_result') return false; + try { + const parsed = typeof m.content === 'string' ? JSON.parse(m.content) : m.content; + return !!(parsed?.output_id && parsed?.frontend_code); + } catch { return false; } + }; + + const items: RenderItem[] = []; + let i = 0; + while (i < activeBranchMessages.length) { + const msg = activeBranchMessages[i]; + if (msg.role === 'tool_call' || msg.role === 'tool_result') { + const group: typeof activeBranchMessages = []; + while ( + i < activeBranchMessages.length && + (activeBranchMessages[i].role === 'tool_call' || + activeBranchMessages[i].role === 'tool_result') + ) { + group.push(activeBranchMessages[i]); + i++; + } + + const regular: typeof activeBranchMessages = []; + const outputItems: typeof activeBranchMessages = []; + for (const m of group) { + if (isOutputCall(m) || isOutputResult(m)) { outputItems.push(m); continue; } + regular.push(m); + } + + const calls = regular.filter((m) => m.role === 'tool_call'); + const results = regular.filter((m) => m.role === 'tool_result'); + const pairs: ToolPair[] = calls.map((call, idx) => ({ + type: 'tool_pair' as const, + id: `pair-${call.id}`, + call, + result: results[idx] || null, + })); + + const mcpServers = new Set( + calls.map((m) => { + const tool = typeof m.content === 'object' ? m.content.tool || '' : ''; + const match = tool.match(/^mcp__([^_]+(?:-[^_]+)*)__/); + return match ? match[1] : ''; + }).filter(Boolean) + ); + const allSameMcp = mcpServers.size === 1 && pairs.length > 0; + + if (allSameMcp) { + const mcpServer = [...mcpServers][0]; + const toolNames = new Set( + calls.map((m) => (typeof m.content === 'object' ? m.content.tool : '')) + ); + const label = + toolNames.size === 1 ? calls[0].content?.tool || 'Tool calls' : `${calls.length} tool calls`; + items.push({ + type: 'tool_group', + id: `group-${group[0].id}`, + pairs, + label, + callCount: calls.length, + mcpServer, + } satisfies ToolGroup); + } else if (pairs.length <= 2) { + items.push(...pairs); + } else if (pairs.length > 0) { + const toolNames = new Set( + calls.map((m) => (typeof m.content === 'object' ? m.content.tool : '')) + ); + const label = + toolNames.size === 1 ? calls[0].content?.tool || 'Tool calls' : `${calls.length} tool calls`; + items.push({ + type: 'tool_group', + id: `group-${group[0].id}`, + pairs, + label, + callCount: calls.length, + } satisfies ToolGroup); + } + + items.push(...outputItems); + } else { + items.push(msg); + i++; + } + } + return items; + }, [activeBranchMessages]); + + const groupMetaRequestedRef = useRef>(new Set()); + const groupMetaRefinedRef = useRef>(new Set()); + + useEffect(() => { + if (!id || isDraft) return; + const toolGroups = renderItems.filter(isToolGroup) as ToolGroup[]; + const meta = session?.tool_group_meta ?? {}; + + for (const group of toolGroups) { + const allDone = group.pairs.every((p) => p.result !== null); + + if (!groupMetaRequestedRef.current.has(group.id) && !meta[group.id]) { + groupMetaRequestedRef.current.add(group.id); + const toolCalls = group.pairs.map((p) => { + const c = p.call.content; + const tool = typeof c === 'object' ? c.tool || '' : ''; + const input = typeof c === 'object' ? c.input : ''; + const summary = typeof input === 'string' ? input.slice(0, 120) : JSON.stringify(input).slice(0, 120); + return { tool, input_summary: summary }; + }); + dispatch(generateGroupMeta({ sessionId: id, groupId: group.id, toolCalls })); + } + + if (allDone && meta[group.id] && !meta[group.id].is_refined && !groupMetaRefinedRef.current.has(group.id)) { + groupMetaRefinedRef.current.add(group.id); + const toolCalls = group.pairs.map((p) => { + const c = p.call.content; + const tool = typeof c === 'object' ? c.tool || '' : ''; + const input = typeof c === 'object' ? c.input : ''; + const summary = typeof input === 'string' ? input.slice(0, 120) : JSON.stringify(input).slice(0, 120); + return { tool, input_summary: summary }; + }); + const resultsSummary = group.pairs + .filter((p) => p.result) + .map((p) => { + const rc = p.result!.content; + const text = typeof rc === 'string' ? rc : typeof rc === 'object' && rc?.text ? rc.text : JSON.stringify(rc); + return text.slice(0, 150); + }); + dispatch(generateGroupMeta({ sessionId: id, groupId: group.id, toolCalls, resultsSummary, isRefinement: true })); + } + } + }, [renderItems, id, isDraft, session?.tool_group_meta, dispatch]); + + const getSiblingBranches = useCallback( + (messageId: string): string[] => { + if (!session?.branches) return []; + return Object.values(session.branches) + .filter((b) => b.fork_point_message_id === messageId) + .map((b) => b.id); + }, + [session?.branches] + ); + + if (!session) { + return ( + + + Session not found + + + ); + } + + const isActive = session.status === 'running' || session.status === 'waiting_approval' || session.status === 'draft'; + const statusStyle = STATUS_STYLES[session.status] || { color: c.text.tertiary, bg: c.bg.secondary }; + + return ( + + + {!embedded && ( + + + + {session.name} + {!isDraft && statusStyle && ( + + )} + + {!isDraft && ( + + + {session.model} + + + {session.branch_name} + + {session.cost_usd > 0 && ( + + ${session.cost_usd.toFixed(4)} + + )} + + )} + + {!isDraft && id && } + {onClose && ( + + + + )} + + )} + + + + {renderItems.map((item) => { + if (isToolGroup(item)) { + const groupMeta = session.tool_group_meta?.[item.id]; + return ; + } + if (isToolPair(item)) { + const isPending = item.result === null && sessionRunning; + return ; + } + const msg = item; + const siblings = getSiblingBranches(msg.id); + const hasBranches = siblings.length > 0; + const currentBranchIdx = hasBranches + ? siblings.indexOf(session.active_branch_id || 'main') + : 0; + + return ( + + + {hasBranches && ( + { + const prevBranch = siblings[Math.max(0, currentBranchIdx - 1)]; + if (prevBranch && id) dispatch(switchBranch({ sessionId: id, branchId: prevBranch })); + }} + onNext={() => { + const nextBranch = siblings[Math.min(siblings.length - 1, currentBranchIdx + 1)]; + if (nextBranch && id) dispatch(switchBranch({ sessionId: id, branchId: nextBranch })); + }} + /> + )} + + ); + })} + {session.streamingMessage && ( + session.streamingMessage.role === 'tool_call' ? ( + + ) : ( + + ) + )} + {session.status === 'running' && !session.streamingMessage && ( + + )} + + {showScrollButton && ( + + + + + + )} + + + {session.pending_approvals.length > 1 ? ( + + ) : ( + session.pending_approvals.map((req) => ( + + )) + )} + + + + + ); +}; + +export default AgentChat; diff --git a/frontend/src/app/pages/AgentChat/ApprovalBar.tsx b/frontend/src/app/pages/AgentChat/ApprovalBar.tsx new file mode 100644 index 00000000..5cb1ba71 --- /dev/null +++ b/frontend/src/app/pages/AgentChat/ApprovalBar.tsx @@ -0,0 +1,1160 @@ +import React, { useCallback, useState, useMemo } from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import Button from '@mui/material/Button'; +import TextField from '@mui/material/TextField'; +import Chip from '@mui/material/Chip'; +import Collapse from '@mui/material/Collapse'; +import IconButton from '@mui/material/IconButton'; +import SendIcon from '@mui/icons-material/Send'; +import CheckIcon from '@mui/icons-material/Check'; +import CloseIcon from '@mui/icons-material/Close'; +import TerminalIcon from '@mui/icons-material/Terminal'; +import DescriptionIcon from '@mui/icons-material/Description'; +import EditIcon from '@mui/icons-material/Edit'; +import SearchIcon from '@mui/icons-material/Search'; +import QuestionAnswerIcon from '@mui/icons-material/QuestionAnswer'; +import BuildIcon from '@mui/icons-material/Build'; +import ExtensionIcon from '@mui/icons-material/Extension'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import ExpandLessIcon from '@mui/icons-material/ExpandLess'; +import { ApprovalRequest } from '@/shared/state/agentsSlice'; +import { useAppSelector } from '@/shared/hooks'; +import { ToolDefinition } from '@/shared/state/toolsSlice'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; + +// --------------------------------------------------------------------------- +// Integration metadata (icons, colors) for known MCP servers +// --------------------------------------------------------------------------- + +interface IntegrationMeta { + label: string; + color: string; + icon: React.ReactNode; +} + +const GoogleIcon = ( + + + + + + +); + +const RedditIcon = ( + + + + +); + +const INTEGRATION_META: Record = { + 'Google Workspace': { label: 'Google Workspace', color: '#4285F4', icon: GoogleIcon }, + 'xbird': { label: 'X / Twitter', color: '#1DA1F2', icon: 𝕏 }, + 'Reddit': { label: 'Reddit', color: '#FF4500', icon: RedditIcon }, +}; + +// --------------------------------------------------------------------------- +// MCP tool name parser +// --------------------------------------------------------------------------- + +interface ParsedTool { + isMcp: boolean; + serverSlug: string; + actionName: string; + displayName: string; +} + +function parseMcpToolName(rawName: string): ParsedTool { + const m = rawName.match(/^mcp__([^_]+(?:-[^_]+)*)__(.+)$/); + if (!m) { + return { isMcp: false, serverSlug: '', actionName: rawName, displayName: rawName }; + } + const serverSlug = m[1]; + const actionName = m[2]; + const displayName = actionName + .replace(/_/g, ' ') + .replace(/\b\w/g, (ch) => ch.toUpperCase()); + return { isMcp: true, serverSlug, actionName, displayName }; +} + +function sanitizeServerName(name: string): string { + return name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, ''); +} + +// --------------------------------------------------------------------------- +// Look up MCP tool metadata from the Redux tools store +// --------------------------------------------------------------------------- + +interface McpToolMeta { + integration: IntegrationMeta | null; + description: string; + serverLabel: string; +} + +function useMcpToolMeta(parsed: ParsedTool): McpToolMeta { + const toolItems = useAppSelector((s) => s.tools.items); + + return useMemo(() => { + if (!parsed.isMcp) { + return { integration: null, description: '', serverLabel: '' }; + } + + const toolDef: ToolDefinition | undefined = Object.values(toolItems).find( + (t) => t.mcp_config && Object.keys(t.mcp_config).length > 0 && sanitizeServerName(t.name) === parsed.serverSlug + ); + + if (!toolDef) { + return { integration: null, description: '', serverLabel: parsed.serverSlug }; + } + + const description = toolDef.tool_permissions?._tool_descriptions?.[parsed.actionName] || ''; + const integration = INTEGRATION_META[toolDef.name] || null; + const serverLabel = toolDef.name; + + return { integration, description, serverLabel }; + }, [parsed, toolItems]); +} + +// --------------------------------------------------------------------------- +// Smart input summary for MCP tools +// --------------------------------------------------------------------------- + +function getMcpInputSummary(actionName: string, toolInput: Record): string { + const lower = actionName.toLowerCase(); + + if (lower.includes('gmail') || lower.includes('email') || lower.includes('mail')) { + const query = toolInput.query || toolInput.search_query || toolInput.q || ''; + const to = toolInput.to || toolInput.recipient || ''; + const subject = toolInput.subject || ''; + if (query) return `Search: "${query}"`; + if (to && subject) return `To ${to} — ${subject}`; + if (to) return `To ${to}`; + if (subject) return `Subject: ${subject}`; + } + + if (lower.includes('calendar') || lower.includes('event') || lower.includes('freebusy')) { + const summary = toolInput.summary || toolInput.title || toolInput.event_name || ''; + const start = toolInput.start || toolInput.start_time || toolInput.date || ''; + if (summary && start) return `${summary} — ${start}`; + if (summary) return summary; + if (start) return `Date: ${start}`; + } + + if (lower.includes('drive') || lower.includes('doc') || lower.includes('sheet') || lower.includes('slide')) { + const name = toolInput.name || toolInput.title || toolInput.filename || toolInput.file_name || ''; + const query = toolInput.query || toolInput.q || ''; + if (name) return name; + if (query) return `Search: "${query}"`; + } + + if (lower.includes('tweet') || lower.includes('post') || lower.includes('send') || lower.includes('reply')) { + const text = toolInput.text || toolInput.content || toolInput.body || toolInput.message || ''; + if (text) return text.length > 80 ? text.slice(0, 77) + '...' : text; + } + + if (lower.includes('search') || lower.includes('find') || lower.includes('query') || lower.includes('list')) { + const query = toolInput.query || toolInput.q || toolInput.search_query || toolInput.keyword || toolInput.term || ''; + if (query) return `"${query}"`; + } + + const stringVals: string[] = []; + for (const [key, val] of Object.entries(toolInput)) { + if (key.startsWith('_')) continue; + if (typeof val === 'string' && val.trim()) { + stringVals.push(val.trim()); + } + if (stringVals.length >= 2) break; + } + if (stringVals.length > 0) { + const joined = stringVals.join(' -- '); + return joined.length > 100 ? joined.slice(0, 97) + '...' : joined; + } + + return ''; +} + +// --------------------------------------------------------------------------- +// Shared components +// --------------------------------------------------------------------------- + +interface Props { + request: ApprovalRequest; + onApprove: (requestId: string, updatedInput?: Record) => void; + onDeny: (requestId: string, message?: string) => void; +} + +function getToolIcon(toolName: string) { + switch (toolName) { + case 'Bash': return ; + case 'Read': return ; + case 'Write': case 'Edit': return ; + case 'Grep': case 'Glob': return ; + case 'AskUserQuestion': return ; + default: return ; + } +} + +interface ToolPreviewProps { + request: ApprovalRequest; + tokens: ReturnType; +} + +const CodeBlock: React.FC<{ tokens: ReturnType; children: React.ReactNode }> = ({ tokens: c, children }) => ( + + {children} + +); + +const ToolPreview: React.FC = ({ request, tokens: c }) => { + const { tool_name, tool_input } = request; + + switch (tool_name) { + case 'Bash': { + return ( + + {tool_input.description && ( + + {tool_input.description} + + )} + {tool_input.command || '(empty command)'} + + ); + } + + case 'Read': + return ( + + + + {tool_input.file_path || tool_input.path || JSON.stringify(tool_input)} + + + ); + + case 'Write': + case 'Edit': { + const path = tool_input.file_path || tool_input.path || ''; + const content = tool_input.content || tool_input.new_content || tool_input.old_string; + return ( + + + + + {path} + + + {content && {typeof content === 'string' ? content : JSON.stringify(content, null, 2)}} + + ); + } + + case 'Grep': + case 'Glob': { + const pattern = tool_input.pattern || tool_input.glob_pattern || tool_input.query || ''; + const path = tool_input.path || tool_input.directory || ''; + return ( + + + + {path && ( + + in {path} + + )} + + + ); + } + + case 'AskUserQuestion': + return null; + + default: { + const preview = tool_input.command || tool_input.file_path || tool_input.path || tool_input.query || null; + if (preview) { + return {preview}; + } + return {JSON.stringify(tool_input, null, 2)}; + } + } +}; + +// --------------------------------------------------------------------------- +// QuestionForm (AskUserQuestion — unchanged) +// --------------------------------------------------------------------------- + +function getOptionKey(opt: any): string { + return opt.id || opt.value || opt.label || opt.text || String(opt); +} + +function getOptionLabel(opt: any): string { + return opt.label || opt.value || opt.text || String(opt); +} + +type Answers = Record; + +export interface QuestionFormProps { + request: ApprovalRequest; + onApprove: (requestId: string, updatedInput?: Record) => void; + onDeny: (requestId: string, message?: string) => void; + compact?: boolean; +} + +const OTHER_KEY = '__other__'; + +export const QuestionForm: React.FC = ({ request, onApprove, onDeny, compact }) => { + const c = useClaudeTokens(); + const questions: any[] = request.tool_input.questions || []; + const [answers, setAnswers] = useState(() => { + const init: Answers = {}; + questions.forEach((q: any, i: number) => { + init[i] = q.multiSelect ? [] : ''; + }); + return init; + }); + const [otherActive, setOtherActive] = useState>({}); + const [otherText, setOtherText] = useState>({}); + + const toggleOption = useCallback((qIdx: number, key: string, multi: boolean) => { + setAnswers((prev) => { + const copy = { ...prev }; + if (multi) { + const arr = Array.isArray(copy[qIdx]) ? [...(copy[qIdx] as string[])] : []; + const idx = arr.indexOf(key); + if (idx >= 0) arr.splice(idx, 1); + else arr.push(key); + copy[qIdx] = arr; + } else { + copy[qIdx] = copy[qIdx] === key ? '' : key; + } + return copy; + }); + if (key !== OTHER_KEY) { + if (!multi) { + setOtherActive((prev) => ({ ...prev, [qIdx]: false })); + setOtherText((prev) => ({ ...prev, [qIdx]: '' })); + } + } + }, []); + + const toggleOther = useCallback((qIdx: number, multi: boolean) => { + setOtherActive((prev) => { + const wasActive = !!prev[qIdx]; + if (wasActive) { + setOtherText((p) => ({ ...p, [qIdx]: '' })); + } + if (!multi && !wasActive) { + setAnswers((p) => ({ ...p, [qIdx]: '' })); + } + return { ...prev, [qIdx]: !wasActive }; + }); + }, []); + + const setTextAnswer = useCallback((qIdx: number, text: string) => { + setAnswers((prev) => ({ ...prev, [qIdx]: text })); + }, []); + + const handleSubmit = () => { + const answersDict: Record = {}; + questions.forEach((q: any, i: number) => { + const questionText = q.question || q.prompt || q.text || ''; + const hasOptions = Array.isArray(q.options) && q.options.length > 0; + let answer = answers[i]; + if (hasOptions && otherActive[i] && otherText[i]) { + if (q.multiSelect) { + const arr = Array.isArray(answer) ? [...answer] : []; + arr.push(otherText[i]); + answer = arr; + } else { + answer = otherText[i]; + } + } + if (Array.isArray(answer)) { + answersDict[questionText] = answer.join(', '); + } else { + answersDict[questionText] = answer || ''; + } + }); + onApprove(request.id, { ...request.tool_input, questions, answers: answersDict }); + }; + + const isSelected = (qIdx: number, key: string): boolean => { + const val = answers[qIdx]; + if (Array.isArray(val)) return val.includes(key); + return val === key; + }; + + return ( + + + + + + + Agent has a question + + + + + {questions.map((q: any, i: number) => { + const hasOptions = Array.isArray(q.options) && q.options.length > 0; + const multi = !!q.multiSelect; + const isOtherActive = !!otherActive[i]; + return ( + + {q.header && ( + + {q.header} + + )} + + {q.question || q.prompt || q.text || '(question)'} + + {hasOptions ? ( + + + {q.options.map((opt: any) => { + const key = getOptionKey(opt); + const selected = isSelected(i, key); + return ( + toggleOption(i, key, multi)} + sx={{ + fontSize: '0.78rem', + fontWeight: selected ? 600 : 400, + cursor: 'pointer', + color: selected ? c.accent.primary : c.text.secondary, + bgcolor: selected ? `${c.accent.primary}18` : 'transparent', + borderColor: selected ? c.accent.primary : c.border.medium, + borderWidth: 1, + borderStyle: 'solid', + transition: 'all 0.15s ease', + '&:hover': { + bgcolor: selected ? `${c.accent.primary}24` : `${c.text.secondary}0a`, + borderColor: selected ? c.accent.primary : c.text.secondary, + }, + }} + /> + ); + })} + toggleOther(i, multi)} + sx={{ + fontSize: '0.78rem', + fontWeight: isOtherActive ? 600 : 400, + fontStyle: 'italic', + cursor: 'pointer', + color: isOtherActive ? c.accent.primary : c.text.muted, + bgcolor: isOtherActive ? `${c.accent.primary}18` : 'transparent', + borderColor: isOtherActive ? c.accent.primary : c.border.subtle, + borderWidth: 1, + borderStyle: 'dashed', + transition: 'all 0.15s ease', + '&:hover': { + bgcolor: isOtherActive ? `${c.accent.primary}24` : `${c.text.secondary}0a`, + borderColor: isOtherActive ? c.accent.primary : c.border.medium, + }, + }} + /> + + {isOtherActive && ( + setOtherText((prev) => ({ ...prev, [i]: e.target.value }))} + fullWidth + size="small" + autoFocus + sx={{ + mt: 0.25, + '& .MuiOutlinedInput-root': { + color: c.text.primary, + fontSize: '0.82rem', + '& fieldset': { borderColor: c.border.medium }, + '&:hover fieldset': { borderColor: c.border.strong }, + '&.Mui-focused fieldset': { borderColor: c.accent.primary }, + }, + }} + /> + )} + + ) : ( + setTextAnswer(i, e.target.value)} + fullWidth + size="small" + multiline + maxRows={4} + sx={{ + '& .MuiOutlinedInput-root': { + color: c.text.primary, + fontSize: '0.82rem', + '& fieldset': { borderColor: c.border.medium }, + '&:hover fieldset': { borderColor: c.border.strong }, + '&.Mui-focused fieldset': { borderColor: c.accent.primary }, + }, + }} + /> + )} + + ); + })} + + + + + + + + ); +}; + +// --------------------------------------------------------------------------- +// GenericApprovalBar — redesigned for MCP tools +// --------------------------------------------------------------------------- + +const GenericApprovalBar: React.FC = ({ request, onApprove, onDeny }) => { + const c = useClaudeTokens(); + const [denyMessage, setDenyMessage] = useState(''); + const [showDenyInput, setShowDenyInput] = useState(false); + const [detailsExpanded, setDetailsExpanded] = useState(false); + + const parsed = useMemo(() => parseMcpToolName(request.tool_name), [request.tool_name]); + const meta = useMcpToolMeta(parsed); + + const accentColor = meta.integration?.color || c.status.warning; + const summary = parsed.isMcp ? getMcpInputSummary(parsed.actionName, request.tool_input) : ''; + + if (!parsed.isMcp) { + return ( + + + + {getToolIcon(request.tool_name)} + + + Permission Required + + + + + + + + + {showDenyInput && ( + setDenyMessage(e.target.value)} + fullWidth + size="small" + sx={{ + mb: 1.5, + '& .MuiOutlinedInput-root': { + color: c.text.primary, + fontSize: '0.8rem', + '& fieldset': { borderColor: c.border.strong }, + '&.Mui-focused fieldset': { borderColor: c.status.error }, + }, + }} + /> + )} + + + + {showDenyInput ? ( + + ) : ( + + )} + + + ); + } + + return ( + + {/* Header row */} + + + {meta.integration?.icon || } + + + + + + {parsed.displayName} + + + + {meta.description && ( + + {meta.description} + + )} + + + + {/* Input summary / details */} + + {summary && ( + setDetailsExpanded((v) => !v)} + > + + {summary} + + + {detailsExpanded ? : } + + + )} + + + + {JSON.stringify(request.tool_input, null, 2)} + + + + + + {/* Deny reason input */} + {showDenyInput && ( + + setDenyMessage(e.target.value)} + fullWidth + size="small" + autoFocus + sx={{ + '& .MuiOutlinedInput-root': { + color: c.text.primary, + fontSize: '0.8rem', + '& fieldset': { borderColor: c.border.strong }, + '&.Mui-focused fieldset': { borderColor: c.status.error }, + }, + }} + /> + + )} + + {/* Action buttons */} + + + {showDenyInput ? ( + + ) : ( + + )} + + + ); +}; + +// --------------------------------------------------------------------------- +// Entry point +// --------------------------------------------------------------------------- + +const ApprovalBar: React.FC = (props) => { + if (props.request.tool_name === 'AskUserQuestion') { + return ; + } + return ; +}; + +// --------------------------------------------------------------------------- +// BatchApprovalBar — grouped mass approve/deny when many approvals pending +// --------------------------------------------------------------------------- + +interface ToolGroup { + toolName: string; + parsed: ParsedTool; + requests: ApprovalRequest[]; +} + +interface BatchApprovalBarProps { + requests: ApprovalRequest[]; + onApprove: (requestId: string, updatedInput?: Record) => void; + onDeny: (requestId: string, message?: string) => void; +} + +export const BatchApprovalBar: React.FC = ({ requests, onApprove, onDeny }) => { + const c = useClaudeTokens(); + const [expandedGroup, setExpandedGroup] = useState(null); + + const questions = requests.filter((r) => r.tool_name === 'AskUserQuestion'); + const nonQuestions = requests.filter((r) => r.tool_name !== 'AskUserQuestion'); + + const groups = useMemo(() => { + const map = new Map(); + for (const req of nonQuestions) { + const existing = map.get(req.tool_name); + if (existing) { + existing.requests.push(req); + } else { + map.set(req.tool_name, { + toolName: req.tool_name, + parsed: parseMcpToolName(req.tool_name), + requests: [req], + }); + } + } + return Array.from(map.values()); + }, [nonQuestions]); + + const handleApproveAll = () => { + for (const req of nonQuestions) onApprove(req.id); + }; + + const handleDenyAll = () => { + for (const req of nonQuestions) onDeny(req.id); + }; + + const handleApproveGroup = (group: ToolGroup) => { + for (const req of group.requests) onApprove(req.id); + }; + + const handleDenyGroup = (group: ToolGroup) => { + for (const req of group.requests) onDeny(req.id); + }; + + return ( + + {questions.map((req) => ( + + ))} + + {nonQuestions.length > 1 && ( + + {/* Global actions bar */} + + + {nonQuestions.length} pending approvals + + + + + + {/* Per-group rows */} + {groups.map((group) => ( + setExpandedGroup((prev) => prev === group.toolName ? null : group.toolName)} + onApprove={onApprove} + onDeny={onDeny} + onApproveGroup={() => handleApproveGroup(group)} + onDenyGroup={() => handleDenyGroup(group)} + /> + ))} + + )} + + {nonQuestions.length === 1 && ( + + )} + + ); +}; + +// --------------------------------------------------------------------------- +// GroupRow — a single tool-name group within the batch bar +// --------------------------------------------------------------------------- + +interface GroupRowProps { + group: ToolGroup; + expanded: boolean; + onToggle: () => void; + onApprove: (requestId: string, updatedInput?: Record) => void; + onDeny: (requestId: string, message?: string) => void; + onApproveGroup: () => void; + onDenyGroup: () => void; +} + +const GroupRow: React.FC = ({ group, expanded, onToggle, onApprove, onDeny, onApproveGroup, onDenyGroup }) => { + const c = useClaudeTokens(); + const meta = useMcpToolMeta(group.parsed); + const accentColor = meta.integration?.color || c.status.warning; + + return ( + + + + {group.parsed.isMcp + ? (meta.integration?.icon || ) + : getToolIcon(group.toolName)} + + + + {group.parsed.isMcp ? group.parsed.displayName : group.toolName} + + + + + {group.requests.length > 1 && ( + <> + + + + )} + + + {expanded ? : } + + + + + + {group.requests.map((req) => ( + + ))} + + + + ); +}; + +export default ApprovalBar; diff --git a/frontend/src/app/pages/AgentChat/BranchNavigator.tsx b/frontend/src/app/pages/AgentChat/BranchNavigator.tsx new file mode 100644 index 00000000..4dbabb95 --- /dev/null +++ b/frontend/src/app/pages/AgentChat/BranchNavigator.tsx @@ -0,0 +1,53 @@ +import React from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import IconButton from '@mui/material/IconButton'; +import ChevronLeftIcon from '@mui/icons-material/ChevronLeft'; +import ChevronRightIcon from '@mui/icons-material/ChevronRight'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; + +interface Props { + currentIndex: number; + totalBranches: number; + onPrevious: () => void; + onNext: () => void; +} + +const BranchNavigator: React.FC = ({ currentIndex, totalBranches, onPrevious, onNext }) => { + const c = useClaudeTokens(); + if (totalBranches <= 1) return null; + + return ( + + + + + + {currentIndex + 1}/{totalBranches} + + + + + + ); +}; + +export default BranchNavigator; diff --git a/frontend/src/app/pages/AgentChat/ChatInput.tsx b/frontend/src/app/pages/AgentChat/ChatInput.tsx new file mode 100644 index 00000000..451bc4b6 --- /dev/null +++ b/frontend/src/app/pages/AgentChat/ChatInput.tsx @@ -0,0 +1,1148 @@ +import React, { useState, useRef, useCallback, useEffect, useMemo, forwardRef, useImperativeHandle } from 'react'; +import Box from '@mui/material/Box'; +import Menu from '@mui/material/Menu'; +import MenuItem from '@mui/material/MenuItem'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemText from '@mui/material/ListItemText'; +import Typography from '@mui/material/Typography'; +import IconButton from '@mui/material/IconButton'; +import Tooltip from '@mui/material/Tooltip'; +import Chip from '@mui/material/Chip'; +import MicNoneOutlinedIcon from '@mui/icons-material/MicNoneOutlined'; +import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward'; +import StopIcon from '@mui/icons-material/Stop'; +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import SmartToyOutlinedIcon from '@mui/icons-material/SmartToyOutlined'; +import QuestionAnswerOutlinedIcon from '@mui/icons-material/QuestionAnswerOutlined'; +import MapOutlinedIcon from '@mui/icons-material/MapOutlined'; +import CategoryOutlinedIcon from '@mui/icons-material/CategoryOutlined'; +import TuneOutlinedIcon from '@mui/icons-material/TuneOutlined'; +import CloseIcon from '@mui/icons-material/Close'; +import FolderOpenIcon from '@mui/icons-material/FolderOpen'; +import InsertDriveFileOutlinedIcon from '@mui/icons-material/InsertDriveFileOutlined'; +import Modal from '@mui/material/Modal'; +import CircularProgress from '@mui/material/CircularProgress'; +import AttachFileIcon from '@mui/icons-material/AttachFile'; +import AdsClickIcon from '@mui/icons-material/AdsClick'; +import CommandPicker, { CommandPickerItem, getToolGroupIcon } from '@/app/components/CommandPicker'; +import { useElementSelection, SelectedElement } from '@/app/components/ElementSelectionContext'; +import { ContextPath } from '@/app/components/DirectoryBrowser'; +import { + SKILL_PILL_ATTR, + AttachedSkill, + createSkillPillElement, + serializeEditorContent, + detectEditorTrigger, + TriggerState, + EMPTY_TRIGGER, +} from '@/app/components/richEditorUtils'; +import TemplateInvokeModal from './TemplateInvokeModal'; +import { useAppDispatch, useAppSelector } from '@/shared/hooks'; +import { fetchModes } from '@/shared/state/modesSlice'; +import { PromptTemplate } from '@/shared/state/templatesSlice'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; + +export interface AttachedImage { + data: string; + media_type: string; + preview: string; +} + +export interface ForcedToolGroup { + label: string; + tools: string[]; + icon?: React.ReactNode; + iconKey?: string; +} + +export type { AttachedSkill } from '@/app/components/richEditorUtils'; + +interface Props { + onSend: (message: string, images?: Array<{ data: string; media_type: string }>, contextPaths?: ContextPath[], forcedTools?: string[], attachedSkills?: Array<{ id: string; name: string; content: string }>) => void; + disabled?: boolean; + mode: string; + onModeChange: (mode: string) => void; + model: string; + onModelChange: (model: string) => void; + isRunning?: boolean; + onStop?: () => void; + autoRunMode?: boolean; + contextEstimate?: { used: number; limit: number }; + embedded?: boolean; + autoFocus?: boolean; +} + +export interface ChatInputHandle { + getConfig: () => { prompt: string; contextPaths: ContextPath[]; forcedTools: ForcedToolGroup[] }; + setContent: (prompt: string, contextPaths?: ContextPath[], forcedTools?: ForcedToolGroup[]) => void; +} + +const ICON_MAP: Record = { + smart_toy: , + question_answer: , + map: , + category: , + tune: , +}; + +const FALLBACK_MODE_BASE = { label: 'Agent', icon: ICON_MAP.smart_toy }; + +const MODEL_OPTIONS = [ + { value: 'sonnet', label: 'Sonnet', version: '4.6' }, + { value: 'opus', label: 'Opus', version: '4.6' }, + { value: 'haiku', label: 'Haiku', version: '3.5' }, +]; + +function formatTokenCount(n: number): string { + if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`; + if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`; + return String(n); +} + +const ContextRing: React.FC<{ used: number; limit: number; accentColor: string; trackColor: string }> = ({ used, limit, accentColor, trackColor }) => { + if (used === 0) return null; + const pct = Math.min((used / limit) * 100, 100); + const size = 20; + const strokeWidth = 2; + const radius = (size - strokeWidth) / 2; + const circumference = 2 * Math.PI * radius; + const dashOffset = circumference * (1 - pct / 100); + const tooltip = `${pct.toFixed(1)}% \u00B7 ${formatTokenCount(used)} / ${formatTokenCount(limit)} context used`; + + return ( + + + + + + + + + ); +}; + +const ChatInput = forwardRef(({ onSend, disabled, mode, onModeChange, model, onModelChange, isRunning, onStop, autoRunMode, contextEstimate, embedded, autoFocus }, ref) => { + const c = useClaudeTokens(); + const editorRef = useRef(null); + const containerRef = useRef(null); + const generalFileInputRef = useRef(null); + const dispatch = useAppDispatch(); + const elementSelection = useElementSelection(); + + useEffect(() => { + if (autoFocus) editorRef.current?.focus(); + }, [autoFocus]); + + const [hasContent, setHasContent] = useState(false); + const [attachedSkills, setAttachedSkills] = useState>({}); + const attachedSkillsRef = useRef(attachedSkills); + attachedSkillsRef.current = attachedSkills; + + const [picker, setPicker] = useState(EMPTY_TRIGGER); + const [selectedTemplate, setSelectedTemplate] = useState(null); + const templates = useAppSelector((state) => state.templates.items); + const skills = useAppSelector((state) => state.skills.items); + const modesMap = useAppSelector((state) => state.modes.items); + const modesArr = useMemo(() => Object.values(modesMap), [modesMap]); + + useEffect(() => { + if (modesArr.length === 0) dispatch(fetchModes()); + }, [dispatch, modesArr.length]); + + const [images, setImages] = useState([]); + const [lightboxSrc, setLightboxSrc] = useState(null); + const [isDragOver, setIsDragOver] = useState(false); + const [isUploading, setIsUploading] = useState(false); + const [contextPaths, setContextPaths] = useState([]); + const [forcedTools, setForcedTools] = useState([]); + const [copiedPathIdx, setCopiedPathIdx] = useState(null); + + useImperativeHandle(ref, () => ({ + getConfig: () => { + const editor = editorRef.current; + const prompt = editor ? serializeEditorContent(editor, attachedSkillsRef.current).trim() : ''; + return { prompt, contextPaths, forcedTools }; + }, + setContent: (prompt: string, newContextPaths?: ContextPath[], newForcedTools?: ForcedToolGroup[]) => { + const editor = editorRef.current; + if (editor) { + editor.textContent = prompt; + setHasContent(!!prompt); + } + if (newContextPaths) setContextPaths(newContextPaths); + if (newForcedTools) setForcedTools(newForcedTools); + }, + }), [contextPaths, forcedTools]); + + const [modeAnchor, setModeAnchor] = useState(null); + const [modelAnchor, setModelAnchor] = useState(null); + + const currentMode = modesMap[mode]; + const FALLBACK_MODE = { ...FALLBACK_MODE_BASE, color: c.accent.primary }; + const modeConf = currentMode + ? { label: currentMode.name, icon: ICON_MAP[currentMode.icon] || ICON_MAP.smart_toy, color: currentMode.color } + : FALLBACK_MODE; + + const updateHasContent = useCallback(() => { + const editor = editorRef.current; + if (!editor) return; + const text = (editor.textContent || '').replace(/\u200B/g, ''); + const hasPills = editor.querySelector(`[${SKILL_PILL_ATTR}]`) !== null; + setHasContent(text.trim().length > 0 || hasPills); + }, []); + + const syncAttachedSkills = useCallback(() => { + const editor = editorRef.current; + if (!editor) return; + const pillIds = new Set( + Array.from(editor.querySelectorAll(`[${SKILL_PILL_ATTR}]`)) + .map((el) => el.getAttribute(SKILL_PILL_ATTR)) + .filter(Boolean) as string[], + ); + setAttachedSkills((prev) => { + const prevKeys = Object.keys(prev); + if (prevKeys.length === pillIds.size && prevKeys.every((k) => pillIds.has(k))) return prev; + const next: Record = {}; + for (const [id, skill] of Object.entries(prev)) { + if (pillIds.has(id)) next[id] = skill; + } + return next; + }); + }, []); + + const removeSkillPill = useCallback((skillId: string) => { + const editor = editorRef.current; + if (!editor) return; + const pill = editor.querySelector(`[${SKILL_PILL_ATTR}="${skillId}"]`); + if (pill) pill.remove(); + setAttachedSkills((prev) => { + const { [skillId]: _, ...rest } = prev; + return rest; + }); + const text = (editor.textContent || '').replace(/\u200B/g, ''); + const hasPills = editor.querySelector(`[${SKILL_PILL_ATTR}]`) !== null; + setHasContent(text.trim().length > 0 || hasPills); + editor.focus(); + }, []); + + const addImageFiles = useCallback((files: FileList | File[]) => { + Array.from(files).forEach((file) => { + if (!file.type.startsWith('image/')) return; + const reader = new FileReader(); + reader.onload = () => { + const result = reader.result as string; + const base64 = result.split(',')[1]; + setImages((prev) => [ + ...prev, + { data: base64, media_type: file.type, preview: result }, + ]); + }; + reader.readAsDataURL(file); + }); + }, []); + + const uploadAndAttachFiles = useCallback(async (files: File[]) => { + if (files.length === 0) return; + setIsUploading(true); + try { + const formData = new FormData(); + files.forEach((f) => formData.append('files', f)); + const resp = await fetch(`http://${window.location.hostname}:8324/api/settings/upload-files`, { + method: 'POST', + body: formData, + }); + if (!resp.ok) throw new Error('Upload failed'); + const data = await resp.json(); + const newPaths: ContextPath[] = (data.files || []).map((f: { path: string }) => ({ + path: f.path, + type: 'file' as const, + })); + setContextPaths((prev) => [...prev, ...newPaths]); + } catch (err) { + console.error('File upload failed:', err); + } finally { + setIsUploading(false); + } + }, []); + + const handleSend = useCallback(() => { + const editor = editorRef.current; + if (!editor || disabled) return; + const serialized = serializeEditorContent(editor, attachedSkillsRef.current); + let trimmed = serialized.trim(); + if (!trimmed) return; + + const selectedEls = elementSelection?.selectedElements ?? []; + let allImages = images.length > 0 + ? images.map(({ data, media_type }) => ({ data, media_type })) + : []; + + if (selectedEls.length > 0) { + const lines: string[] = ['\n\n---\nSelected UI Elements:\n']; + selectedEls.forEach((el, i) => { + if (el.semanticType && el.semanticData) { + const typeLabel = { + 'agent-card': 'Agent Card', + 'message': 'Message', + 'tool-call': 'Tool Call', + 'tool-group': 'Tool Group', + 'view-card': 'View Card', + 'dom-element': 'Element', + }[el.semanticType] || el.semanticType; + lines.push(`${i + 1}. [${typeLabel}] ${el.semanticLabel || ''}`); + const { selectId, ...rest } = el.semanticData; + if (selectId) lines.push(` ID: ${selectId}`); + const metaStr = Object.entries(rest) + .filter(([, v]) => v != null) + .map(([k, v]) => `${k}: ${typeof v === 'string' ? v : JSON.stringify(v)}`) + .join(', '); + if (metaStr) lines.push(` ${metaStr}`); + } else { + const styleStr = Object.entries(el.computedStyles) + .map(([k, v]) => `${k}: ${v}`) + .join('; '); + lines.push(`${i + 1}. \`${el.selectorPath}\` (${el.tagName.toLowerCase()})`); + lines.push(` Selector: ${el.selectorPath}`); + lines.push(` HTML: ${el.outerHTML.length > 500 ? el.outerHTML.slice(0, 500) + '...' : el.outerHTML}`); + if (styleStr) lines.push(` Key styles: ${styleStr}`); + } + lines.push(''); + + if (el.screenshot) { + const base64 = el.screenshot.replace(/^data:image\/\w+;base64,/, ''); + allImages.push({ data: base64, media_type: 'image/png' }); + } + }); + trimmed += lines.join('\n'); + } + + const sendImages = allImages.length > 0 ? allImages : undefined; + const allForcedToolNames = forcedTools.flatMap((ft) => ft.tools); + const currentSkills = Object.values(attachedSkillsRef.current); + const sendSkills = currentSkills.length > 0 + ? currentSkills.map((s) => ({ id: s.id, name: s.name, content: s.content })) + : undefined; + onSend( + trimmed, + sendImages, + contextPaths.length > 0 ? contextPaths : undefined, + allForcedToolNames.length > 0 ? allForcedToolNames : undefined, + sendSkills, + ); + editor.innerHTML = ''; + setImages([]); + setContextPaths([]); + setForcedTools([]); + setAttachedSkills({}); + setHasContent(false); + elementSelection?.clearSelectedElements(); + }, [disabled, images, contextPaths, forcedTools, onSend, elementSelection]); + + const detectTrigger = useCallback(() => { + const result = detectEditorTrigger(); + if (result) { + setPicker(result); + } else { + setPicker((p) => ({ ...p, visible: false })); + } + }, []); + + const handleInput = useCallback(() => { + updateHasContent(); + detectTrigger(); + syncAttachedSkills(); + }, [updateHasContent, detectTrigger, syncAttachedSkills]); + + const handleEditorClick = useCallback(() => { + detectTrigger(); + }, [detectTrigger]); + + const handlePickerSelect = (item: CommandPickerItem) => { + setPicker((p) => ({ ...p, visible: false })); + const editor = editorRef.current; + if (!editor) return; + + editor.focus(); + + const { triggerNode, triggerOffset, filter } = picker; + if (triggerNode && triggerNode.parentNode && editor.contains(triggerNode)) { + const endOffset = Math.min(triggerOffset + 1 + filter.length, triggerNode.length); + const range = document.createRange(); + range.setStart(triggerNode, triggerOffset); + range.setEnd(triggerNode, endOffset); + range.deleteContents(); + const sel = window.getSelection(); + if (sel) { sel.removeAllRanges(); sel.addRange(range); } + } + + if (item.type === 'template') { + const tmpl = templates[item.id]; + if (!tmpl) return; + if (tmpl.fields.length === 0) { + document.execCommand('insertText', false, tmpl.template); + } else { + setSelectedTemplate(tmpl); + } + } else if (item.type === 'skill') { + const skill = skills[item.id]; + if (!skill) return; + if (editor.querySelector(`[${SKILL_PILL_ATTR}="${skill.id}"]`)) return; + + const pill = createSkillPillElement( + { id: skill.id, name: skill.name, content: skill.content }, + removeSkillPill, + c.font.mono, + c.status.error, + ); + + const sel = window.getSelection(); + if (sel && sel.rangeCount > 0) { + const range = sel.getRangeAt(0); + range.collapse(false); + range.insertNode(pill); + const spacer = document.createTextNode('\u200B'); + pill.after(spacer); + const newRange = document.createRange(); + newRange.setStartAfter(spacer); + newRange.collapse(true); + sel.removeAllRanges(); + sel.addRange(newRange); + } + + setAttachedSkills((prev) => ({ + ...prev, + [skill.id]: { id: skill.id, name: skill.name, content: skill.content }, + })); + } else if (item.type === 'mode') { + onModeChange(item.id); + } else if (item.type === 'context') { + if (item.command === 'file') { + generalFileInputRef.current?.click(); + } else if (item.toolNames && item.toolNames.length > 0) { + setForcedTools((prev) => [...prev, { label: item.name, tools: item.toolNames!, icon: item.icon, iconKey: item.iconKey }]); + } else { + document.execCommand('insertText', false, `@${item.command} `); + } + } + + updateHasContent(); + setTimeout(() => editor.focus(), 0); + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (picker.visible && ['ArrowDown', 'ArrowUp', 'Escape', 'Tab', 'Enter'].includes(e.key)) { + e.preventDefault(); + return; + } + if ((e.ctrlKey || e.metaKey) && ['b', 'i', 'u'].includes(e.key.toLowerCase())) { + e.preventDefault(); + return; + } + if (e.key === 'Enter' && !e.shiftKey && !autoRunMode) { + e.preventDefault(); + handleSend(); + } + }; + + const handlePaste = useCallback((e: React.ClipboardEvent) => { + const items = e.clipboardData?.items; + if (!items) return; + const imageFiles: File[] = []; + for (let i = 0; i < items.length; i++) { + if (items[i].type.startsWith('image/')) { + const file = items[i].getAsFile(); + if (file) imageFiles.push(file); + } + } + if (imageFiles.length > 0) { + e.preventDefault(); + addImageFiles(imageFiles); + return; + } + e.preventDefault(); + const plain = e.clipboardData.getData('text/plain'); + if (plain) document.execCommand('insertText', false, plain); + }, [addImageFiles]); + + const handleDragOver = useCallback((e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + if (e.dataTransfer.types.includes('Files')) { + setIsDragOver(true); + } + }, []); + + const handleDragLeave = useCallback((e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + setIsDragOver(false); + }, []); + + const handleDrop = useCallback((e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + setIsDragOver(false); + if (e.dataTransfer.files.length === 0) return; + const allFiles = Array.from(e.dataTransfer.files); + const imageFiles = allFiles.filter((f) => f.type.startsWith('image/')); + const otherFiles = allFiles.filter((f) => !f.type.startsWith('image/')); + if (imageFiles.length > 0) addImageFiles(imageFiles); + if (otherFiles.length > 0) uploadAndAttachFiles(otherFiles); + }, [addImageFiles, uploadAndAttachFiles]); + + const removeImage = useCallback((idx: number) => { + setImages((prev) => prev.filter((_, i) => i !== idx)); + }, []); + + const menuPaperProps = { + sx: { + bgcolor: c.bg.surface, + border: `1px solid ${c.border.subtle}`, + borderRadius: '10px', + minWidth: 140, + boxShadow: c.shadow.lg, + '& .MuiMenuItem-root': { + fontSize: '0.8rem', + color: c.text.secondary, + py: 0.75, + px: 1.5, + '&:hover': { bgcolor: c.bg.secondary }, + }, + }, + }; + + const selectedElements = elementSelection?.selectedElements ?? []; + const hasAttachments = images.length > 0 || contextPaths.length > 0 || forcedTools.length > 0 || selectedElements.length > 0; + + return ( + + {isDragOver && ( + + + + Drop files here + + + )} + + {isUploading && ( + + + + Attaching files… + + + )} + + setPicker((p) => ({ ...p, visible: false }))} + visible={picker.visible} + /> + + {images.length > 0 && ( + + {images.map((img, idx) => ( + setLightboxSrc(img.preview)} + > + + { e.stopPropagation(); removeImage(idx); }} + sx={{ + position: 'absolute', + top: -2, + right: -2, + width: 18, + height: 18, + bgcolor: c.bg.surface, + border: `1px solid ${c.border.medium}`, + color: c.text.tertiary, + '&:hover': { bgcolor: c.bg.secondary, color: c.text.primary }, + }} + > + + + + ))} + + )} + + {contextPaths.length > 0 && ( + 0 ? 0.25 : 1, pb: 0 }}> + {contextPaths.map((cp, idx) => { + const label = cp.path.split('/').filter(Boolean).slice(-2).join('/'); + return ( + + + : + } + label={label} + size="small" + onClick={() => { + navigator.clipboard.writeText(cp.path); + setCopiedPathIdx(idx); + setTimeout(() => setCopiedPathIdx((cur) => cur === idx ? null : cur), 1200); + }} + onDelete={() => setContextPaths((prev) => prev.filter((_, i) => i !== idx))} + sx={{ + bgcolor: `${c.accent.primary}12`, + color: c.accent.primary, + fontSize: '0.72rem', + fontFamily: c.font.mono, + height: 26, + maxWidth: 220, + cursor: 'pointer', + '& .MuiChip-label': { overflow: 'hidden', textOverflow: 'ellipsis' }, + '& .MuiChip-deleteIcon': { + color: c.accent.primary, + fontSize: 16, + '&:hover': { color: c.status.error }, + }, + }} + /> + + ); + })} + + )} + + {forcedTools.length > 0 && ( + 0 || contextPaths.length > 0) ? 0.25 : 1, pb: 0 }}> + {forcedTools.map((ft, idx) => ( + {ft.icon || getToolGroupIcon(ft.iconKey || ft.label, 14)}} + label={`@${ft.label.toLowerCase()}`} + size="small" + onDelete={() => setForcedTools((prev) => prev.filter((_, i) => i !== idx))} + sx={{ + bgcolor: `${c.status.info}15`, + color: c.status.info, + fontSize: '0.72rem', + fontFamily: c.font.mono, + height: 26, + maxWidth: 220, + '& .MuiChip-label': { overflow: 'hidden', textOverflow: 'ellipsis' }, + '& .MuiChip-deleteIcon': { + color: c.status.info, + fontSize: 16, + '&:hover': { color: c.status.error }, + }, + }} + /> + ))} + + )} + + {selectedElements.length > 0 && ( + 0 || contextPaths.length > 0 || forcedTools.length > 0) ? 0.25 : 1, pb: 0 }}> + {selectedElements.map((el) => { + const chipLabel = el.semanticLabel + ? el.semanticLabel + : el.className + ? `${el.tagName.toLowerCase()}.${el.className.split(' ')[0]}` + : el.tagName.toLowerCase(); + const tooltipText = el.semanticType + ? `${el.semanticType}: ${el.semanticLabel || el.selectorPath}` + : el.selectorPath; + return ( + + } + label={chipLabel} + size="small" + onDelete={() => elementSelection?.removeSelectedElement(el.id)} + sx={{ + bgcolor: 'rgba(59, 130, 246, 0.1)', + color: '#3b82f6', + fontSize: '0.72rem', + fontFamily: c.font.mono, + height: 26, + maxWidth: 220, + '& .MuiChip-label': { overflow: 'hidden', textOverflow: 'ellipsis' }, + '& .MuiChip-deleteIcon': { + color: '#3b82f6', + fontSize: 16, + '&:hover': { color: c.status.error }, + }, + '& .MuiChip-icon': { + color: '#3b82f6', + }, + }} + /> + + ); + })} + + )} + + +
+ {!hasContent && ( +
+ {disabled ? 'Agent is working...' : autoRunMode ? 'Describe what data to generate…' : `${modeConf.label}, @ for context, / for commands`} +
+ )} + + + + setModeAnchor(e.currentTarget)} + sx={{ + display: 'inline-flex', + alignItems: 'center', + gap: 0.5, + px: 1, + py: 0.375, + borderRadius: '999px', + cursor: 'pointer', + userSelect: 'none', + color: modeConf.color, + bgcolor: `${modeConf.color}14`, + '&:hover': { bgcolor: `${modeConf.color}22` }, + transition: 'background 0.15s', + }} + > + {modeConf.icon} + + {modeConf.label} + + + + + setModeAnchor(null)} + anchorOrigin={{ vertical: 'top', horizontal: 'left' }} + transformOrigin={{ vertical: 'bottom', horizontal: 'left' }} + slotProps={{ paper: menuPaperProps }} + > + {modesArr.map((m) => { + const icon = ICON_MAP[m.icon] || ICON_MAP.smart_toy; + return ( + { + onModeChange(m.id); + setModeAnchor(null); + }} + > + + {icon} + + + + ); + })} + + + setModelAnchor(e.currentTarget)} + sx={{ + display: 'inline-flex', + alignItems: 'center', + gap: 0.25, + px: 0.75, + py: 0.25, + borderRadius: '6px', + cursor: 'pointer', + userSelect: 'none', + color: c.text.muted, + '&:hover': { bgcolor: 'rgba(0,0,0,0.04)' }, + transition: 'background 0.15s', + }} + > + + {(() => { const m = MODEL_OPTIONS.find((m) => m.value === model); return m ? `${m.label} ${m.version}` : model; })()} + + + + + setModelAnchor(null)} + anchorOrigin={{ vertical: 'top', horizontal: 'left' }} + transformOrigin={{ vertical: 'bottom', horizontal: 'left' }} + slotProps={{ paper: menuPaperProps }} + > + {MODEL_OPTIONS.map((opt) => ( + { + onModelChange(opt.value); + setModelAnchor(null); + }} + > + + + ))} + + + + + {contextEstimate && ( + + )} + + {elementSelection && !autoRunMode && ( + + + + + + )} + + { + if (!e.target.files) return; + const all = Array.from(e.target.files); + const imgs = all.filter((f) => f.type.startsWith('image/')); + const rest = all.filter((f) => !f.type.startsWith('image/')); + if (imgs.length > 0) addImageFiles(imgs); + if (rest.length > 0) uploadAndAttachFiles(rest); + e.target.value = ''; + }} + /> + + generalFileInputRef.current?.click()} + sx={{ + color: c.text.tertiary, + p: 0.5, + '&:hover': { color: c.text.secondary, bgcolor: 'rgba(0,0,0,0.04)' }, + }} + > + + + + {!autoRunMode && (isRunning ? ( + + + + + + ) : hasContent ? ( + + + + + + ) : ( + + + + + + + + ))} + + + {selectedTemplate && ( + setSelectedTemplate(null)} + onApply={(rendered) => { + const editor = editorRef.current; + if (editor) { + editor.innerHTML = ''; + editor.textContent = rendered; + const range = document.createRange(); + range.selectNodeContents(editor); + range.collapse(false); + const sel = window.getSelection(); + if (sel) { sel.removeAllRanges(); sel.addRange(range); } + } + setSelectedTemplate(null); + setAttachedSkills({}); + setHasContent(!!rendered); + }} + /> + )} + + setLightboxSrc(null)} + sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }} + > + setLightboxSrc(null)} + sx={{ position: 'relative', outline: 'none', maxWidth: '90vw', maxHeight: '90vh' }} + > + setLightboxSrc(null)} + sx={{ + position: 'absolute', + top: -16, + right: -16, + bgcolor: c.bg.surface, + border: `1px solid ${c.border.medium}`, + color: c.text.secondary, + width: 32, + height: 32, + zIndex: 1, + '&:hover': { bgcolor: c.bg.secondary }, + boxShadow: c.shadow.md, + }} + > + + + e.stopPropagation()} + style={{ + maxWidth: '90vw', + maxHeight: '90vh', + borderRadius: 8, + boxShadow: '0 8px 32px rgba(0,0,0,0.4)', + display: 'block', + }} + /> + + + + + ); +}); + +ChatInput.displayName = 'ChatInput'; + +export default ChatInput; diff --git a/frontend/src/app/pages/AgentChat/DiffViewer.tsx b/frontend/src/app/pages/AgentChat/DiffViewer.tsx new file mode 100644 index 00000000..5957f23f --- /dev/null +++ b/frontend/src/app/pages/AgentChat/DiffViewer.tsx @@ -0,0 +1,139 @@ +import React, { useState, useEffect } from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import IconButton from '@mui/material/IconButton'; +import Tooltip from '@mui/material/Tooltip'; +import RefreshIcon from '@mui/icons-material/Refresh'; +import DifferenceIcon from '@mui/icons-material/Difference'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; + +const API_BASE = `http://${window.location.hostname}:8324/api/agents`; + +interface Props { + sessionId: string; +} + +const DiffViewer: React.FC = ({ sessionId }) => { + const c = useClaudeTokens(); + const [diff, setDiff] = useState(''); + const [loading, setLoading] = useState(false); + const [open, setOpen] = useState(false); + + const fetchDiff = async () => { + setLoading(true); + try { + const res = await fetch(`${API_BASE}/sessions/${sessionId}/diff`); + const data = await res.json(); + setDiff(data.diff || ''); + } catch { + setDiff('Failed to fetch diff'); + } + setLoading(false); + }; + + useEffect(() => { + if (open) fetchDiff(); + }, [open, sessionId]); + + if (!open) { + return ( + + setOpen(true)} sx={{ color: c.text.tertiary }}> + + + + ); + } + + return ( + + + + Worktree Changes + + + + + + + + setOpen(false)} sx={{ color: c.text.tertiary }}> + × + + + + + {loading ? ( + Loading... + ) : diff ? ( +
+            {diff.split('\n').map((line, i) => {
+              let color = c.text.muted;
+              if (line.startsWith('+') && !line.startsWith('+++')) color = c.status.success;
+              else if (line.startsWith('-') && !line.startsWith('---')) color = c.status.error;
+              else if (line.startsWith('@@')) color = c.accent.primary;
+              else if (line.startsWith('diff ') || line.startsWith('index ')) color = c.text.tertiary;
+
+              return (
+                
+                  {line}
+                  {'\n'}
+                
+              );
+            })}
+          
+ ) : ( + + No changes detected in the worktree. + + )} +
+
+ ); +}; + +export default DiffViewer; diff --git a/frontend/src/app/pages/AgentChat/MessageBubble.tsx b/frontend/src/app/pages/AgentChat/MessageBubble.tsx new file mode 100644 index 00000000..be7c43ba --- /dev/null +++ b/frontend/src/app/pages/AgentChat/MessageBubble.tsx @@ -0,0 +1,653 @@ +import React, { useState, useMemo } from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import IconButton from '@mui/material/IconButton'; +import TextField from '@mui/material/TextField'; +import Button from '@mui/material/Button'; +import Chip from '@mui/material/Chip'; +import Tooltip from '@mui/material/Tooltip'; +import Collapse from '@mui/material/Collapse'; +import Modal from '@mui/material/Modal'; +import EditIcon from '@mui/icons-material/Edit'; +import CloseIcon from '@mui/icons-material/Close'; +import AdsClickIcon from '@mui/icons-material/AdsClick'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import FolderOutlinedIcon from '@mui/icons-material/FolderOutlined'; +import InsertDriveFileOutlinedIcon from '@mui/icons-material/InsertDriveFileOutlined'; +import PsychologyOutlinedIcon from '@mui/icons-material/PsychologyOutlined'; +import BuildOutlinedIcon from '@mui/icons-material/BuildOutlined'; +import ReactMarkdown from 'react-markdown'; +import remarkGfm from 'remark-gfm'; +import { AgentMessage } from '@/shared/state/agentsSlice'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; +import { SKILL_COLOR } from '@/app/components/richEditorUtils'; +import ViewBubble from './ViewBubble'; + +const streamingCursorKeyframes = ` +@keyframes blink-cursor { + 0%, 100% { opacity: 1; } + 50% { opacity: 0; } +} +`; + +const StreamingCursor: React.FC = () => { + const c = useClaudeTokens(); + return ( + <> + + + + ); +}; + +const ELEMENT_SEPARATOR = '\n\n---\nSelected UI Elements:\n'; + +interface ParsedElement { + label: string; + selector: string; + isSemantic?: boolean; +} + +function parseElementContext(text: string): { userMessage: string; elements: ParsedElement[] } { + const sepIdx = text.indexOf(ELEMENT_SEPARATOR); + if (sepIdx === -1) return { userMessage: text, elements: [] }; + + const userMessage = text.slice(0, sepIdx); + const elementSection = text.slice(sepIdx + ELEMENT_SEPARATOR.length); + + const elements: ParsedElement[] = []; + const blocks = elementSection.split(/\n(?=\d+\.\s)/).filter(Boolean); + for (const block of blocks) { + const semanticMatch = block.match(/\d+\.\s+\[([^\]]+)\]\s*(.*)/); + if (semanticMatch) { + const typeLabel = semanticMatch[1]; + const rest = semanticMatch[2].trim(); + elements.push({ + label: `${typeLabel}: ${rest.split('\n')[0]}`, + selector: typeLabel, + isSemantic: true, + }); + continue; + } + + const labelMatch = block.match(/`([^`]+)`\s+\((\w+)\)/); + const selectorMatch = block.match(/Selector:\s*(.+)/); + if (labelMatch) { + elements.push({ + label: labelMatch[1], + selector: selectorMatch?.[1]?.trim() ?? labelMatch[1], + }); + } + } + + return { userMessage, elements }; +} + +const SKILL_PILL_RE = /\{\{skill:([^}]+)\}\}/g; + +function renderUserTextWithPills(text: string, c: ReturnType): React.ReactNode[] { + const parts: React.ReactNode[] = []; + let lastIndex = 0; + let match: RegExpExecArray | null; + const re = new RegExp(SKILL_PILL_RE.source, 'g'); + while ((match = re.exec(text)) !== null) { + if (match.index > lastIndex) { + parts.push(text.slice(lastIndex, match.index)); + } + const skillName = match[1]; + parts.push( + } + label={skillName} + size="small" + sx={{ + bgcolor: `${SKILL_COLOR}18`, + color: SKILL_COLOR, + fontSize: '0.72rem', + fontFamily: c.font.mono, + height: 20, + mx: 0.25, + verticalAlign: 'baseline', + '& .MuiChip-icon': { color: SKILL_COLOR }, + }} + />, + ); + lastIndex = re.lastIndex; + } + if (lastIndex < text.length) { + parts.push(text.slice(lastIndex)); + } + return parts; +} + +interface ContextGroup { + key: string; + icon: React.ReactNode; + color: string; + label: string; + chips: Array<{ label: string; tooltip?: string; icon: React.ReactNode }>; +} + +function buildContextGroups( + elements: ParsedElement[], + message: AgentMessage, +): ContextGroup[] { + const groups: ContextGroup[] = []; + + if (elements.length > 0) { + groups.push({ + key: 'elements', + icon: , + color: '#3b82f6', + label: `${elements.length} element${elements.length > 1 ? 's' : ''} selected`, + chips: elements.map((el) => ({ + label: el.label, + tooltip: el.selector, + icon: , + })), + }); + } + + const contextPaths = message.context_paths; + if (contextPaths && contextPaths.length > 0) { + const files = contextPaths.filter((cp) => cp.type === 'file'); + const dirs = contextPaths.filter((cp) => cp.type === 'directory'); + const allPaths = [...dirs, ...files]; + const label = [ + dirs.length > 0 ? `${dirs.length} folder${dirs.length > 1 ? 's' : ''}` : '', + files.length > 0 ? `${files.length} file${files.length > 1 ? 's' : ''}` : '', + ].filter(Boolean).join(', ') + ' attached'; + groups.push({ + key: 'paths', + icon: , + color: '#10b981', + label, + chips: allPaths.map((cp) => { + const name = cp.path.split('/').filter(Boolean).pop() || cp.path; + return { + label: name, + tooltip: cp.path, + icon: cp.type === 'directory' + ? + : , + }; + }), + }); + } + + const skills = message.attached_skills; + if (skills && skills.length > 0) { + groups.push({ + key: 'skills', + icon: , + color: SKILL_COLOR, + label: `${skills.length} skill${skills.length > 1 ? 's' : ''}`, + chips: skills.map((s) => ({ + label: s.name, + icon: , + })), + }); + } + + const forcedTools = message.forced_tools; + if (forcedTools && forcedTools.length > 0) { + groups.push({ + key: 'tools', + icon: , + color: '#f59e0b', + label: `${forcedTools.length} tool${forcedTools.length > 1 ? 's' : ''} requested`, + chips: forcedTools.map((t) => ({ + label: t, + icon: , + })), + }); + } + + return groups; +} + +const AttachedContextSection: React.FC<{ + elements: ParsedElement[]; + message: AgentMessage; + c: ReturnType; +}> = ({ elements, message, c }) => { + const [expanded, setExpanded] = useState(false); + const groups = useMemo(() => buildContextGroups(elements, message), [elements, message]); + + if (groups.length === 0) return null; + + return ( + + setExpanded(!expanded)} + sx={{ + display: 'inline-flex', + alignItems: 'center', + gap: 0.5, + cursor: 'pointer', + mb: 0.5, + '&:hover': { opacity: 0.8 }, + }} + > + {groups.map((g) => ( + + {g.icon} + + ))} + + {groups.map((g) => g.label).join(' · ')} + + + + + {groups.map((g) => ( + + + {g.label} + + + {g.chips.map((chip, i) => ( + + + + ))} + + + ))} + + + ); +}; + +const ImageLightbox: React.FC<{ + open: boolean; + src: string; + onClose: () => void; + c: ReturnType; +}> = ({ open, src, onClose, c }) => ( + + + + + + e.stopPropagation()} + style={{ + maxWidth: '90vw', + maxHeight: '90vh', + borderRadius: 8, + boxShadow: '0 8px 32px rgba(0,0,0,0.4)', + display: 'block', + }} + /> + + +); + +const MessageImageThumbnails: React.FC<{ + images: Array<{ data: string; media_type: string }>; + c: ReturnType; +}> = ({ images, c }) => { + const [lightboxSrc, setLightboxSrc] = useState(null); + + if (images.length === 0) return null; + + return ( + <> + + {images.map((img, idx) => { + const src = `data:${img.media_type};base64,${img.data}`; + return ( + setLightboxSrc(src)} + sx={{ + width: 64, + height: 64, + flexShrink: 0, + borderRadius: '8px', + overflow: 'hidden', + border: `1px solid ${c.border.subtle}`, + cursor: 'pointer', + transition: 'opacity 0.15s, transform 0.15s', + '&:hover': { opacity: 0.85, transform: 'scale(1.04)' }, + }} + > + + + ); + })} + + setLightboxSrc(null)} + c={c} + /> + + ); +}; + +interface Props { + message: AgentMessage; + onEdit?: (messageId: string, newContent: string) => void; + isStreaming?: boolean; +} + +const MessageBubble: React.FC = React.memo(({ message, onEdit, isStreaming }) => { + const c = useClaudeTokens(); + const [editing, setEditing] = useState(false); + const [editText, setEditText] = useState(''); + const { role, content } = message; + + if (role === 'system') { + return ( + + + {typeof content === 'string' ? content : JSON.stringify(content)} + + + ); + } + + if (role === 'tool_call') { + const toolData = typeof content === 'object' ? content : {}; + const toolInput = toolData.input || {}; + if (toolData.tool === 'RenderOutput') { + return ; + } + return null; + } + + if (role === 'tool_result') { + let parsedContent: any = null; + try { parsedContent = typeof content === 'string' ? JSON.parse(content) : content; } catch {} + if (parsedContent?.output_id && parsedContent?.frontend_code) { + return ( + + ); + } + return null; + } + + const isUser = role === 'user'; + const rawText = typeof content === 'string' ? content : JSON.stringify(content); + const { userMessage: displayText, elements: selectedElements } = isUser + ? parseElementContext(rawText) + : { userMessage: rawText, elements: [] }; + + const handleStartEdit = () => { + setEditText(rawText); + setEditing(true); + }; + + const handleCancelEdit = () => { + setEditing(false); + setEditText(''); + }; + + const handleSaveEdit = () => { + const trimmed = editText.trim(); + if (trimmed && trimmed !== rawText && onEdit) { + onEdit(message.id, trimmed); + } + setEditing(false); + setEditText(''); + }; + + const truncatedContent = typeof content === 'string' + ? content.slice(0, 200) + : JSON.stringify(content).slice(0, 200); + + return ( + + {isUser && onEdit && !editing && ( + + + + )} + + {isUser ? ( + editing ? ( + + setEditText(e.target.value)} + variant="outlined" + size="small" + autoFocus + onKeyDown={(e) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + handleSaveEdit(); + } + if (e.key === 'Escape') handleCancelEdit(); + }} + sx={{ + '& .MuiOutlinedInput-root': { + color: c.text.primary, + fontSize: '0.875rem', + '& fieldset': { borderColor: c.border.strong }, + '&:hover fieldset': { borderColor: c.text.tertiary }, + '&.Mui-focused fieldset': { borderColor: c.accent.primary }, + }, + }} + /> + + + + + + ) : ( + + {message.images && message.images.length > 0 && ( + + )} + + {renderUserTextWithPills(displayText, c)} + + + + ) + ) : ( + + {rawText} + {isStreaming && } + + )} + + + ); +}); + +export default MessageBubble; diff --git a/frontend/src/app/pages/AgentChat/TemplateInvokeModal.tsx b/frontend/src/app/pages/AgentChat/TemplateInvokeModal.tsx new file mode 100644 index 00000000..704109fa --- /dev/null +++ b/frontend/src/app/pages/AgentChat/TemplateInvokeModal.tsx @@ -0,0 +1,183 @@ +import React, { useState } from 'react'; +import Dialog from '@mui/material/Dialog'; +import DialogTitle from '@mui/material/DialogTitle'; +import DialogContent from '@mui/material/DialogContent'; +import DialogActions from '@mui/material/DialogActions'; +import Button from '@mui/material/Button'; +import TextField from '@mui/material/TextField'; +import MenuItem from '@mui/material/MenuItem'; +import FormControl from '@mui/material/FormControl'; +import InputLabel from '@mui/material/InputLabel'; +import Select from '@mui/material/Select'; +import Checkbox from '@mui/material/Checkbox'; +import ListItemText from '@mui/material/ListItemText'; +import OutlinedInput from '@mui/material/OutlinedInput'; +import Typography from '@mui/material/Typography'; +import Box from '@mui/material/Box'; +import { PromptTemplate, TemplateField } from '@/shared/state/templatesSlice'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; + +interface Props { + template: PromptTemplate; + open: boolean; + onClose: () => void; + onApply: (rendered: string) => void; +} + +const TemplateInvokeModal: React.FC = ({ template, open, onClose, onApply }) => { + const c = useClaudeTokens(); + const inputSx = { + '& .MuiOutlinedInput-root': { + color: c.text.primary, + '& fieldset': { borderColor: c.border.strong }, + '&:hover fieldset': { borderColor: c.text.tertiary }, + '&.Mui-focused fieldset': { borderColor: c.accent.primary }, + }, + '& .MuiInputLabel-root': { color: c.text.tertiary }, + '& .MuiInputLabel-root.Mui-focused': { color: c.accent.primary }, + }; + const [values, setValues] = useState>(() => { + const init: Record = {}; + for (const f of template.fields) { + init[f.name] = f.default ?? (f.type === 'multi-select' ? [] : ''); + } + return init; + }); + + const handleApply = () => { + let rendered = template.template; + for (const f of template.fields) { + const val = values[f.name]; + const str = Array.isArray(val) ? val.join(', ') : String(val ?? ''); + rendered = rendered.replace(new RegExp(`\\{\\{${f.name}\\}\\}`, 'g'), str); + } + onApply(rendered); + onClose(); + }; + + const renderField = (field: TemplateField) => { + const val = values[field.name]; + const update = (v: any) => setValues((prev) => ({ ...prev, [field.name]: v })); + + switch (field.type) { + case 'literal': + return ( + + {field.name} + + {field.default || ''} + + + ); + case 'select': + return ( + update(e.target.value)} + fullWidth + size="small" + sx={{ ...inputSx, mb: 2 }} + SelectProps={{ MenuProps: { PaperProps: { sx: { bgcolor: c.bg.surface, color: c.text.primary } } } }} + > + {(field.options || []).map((opt) => ( + {opt} + ))} + + ); + case 'multi-select': + return ( + + {field.name} + + + ); + case 'int': + case 'float': + return ( + update(field.type === 'int' ? parseInt(e.target.value) || '' : parseFloat(e.target.value) || '')} + fullWidth + size="small" + sx={{ ...inputSx, mb: 2 }} + /> + ); + default: + return ( + update(e.target.value)} + fullWidth + size="small" + multiline={field.name.toLowerCase().includes('description') || field.name.toLowerCase().includes('prompt')} + rows={field.name.toLowerCase().includes('description') || field.name.toLowerCase().includes('prompt') ? 3 : 1} + sx={{ ...inputSx, mb: 2 }} + /> + ); + } + }; + + return ( + + {template.name} + + {template.description && ( + {template.description} + )} + {template.fields.length === 0 ? ( + + This template has no input fields. It will be inserted as-is. + + ) : ( + template.fields.map(renderField) + )} + + + + + + + ); +}; + +export default TemplateInvokeModal; diff --git a/frontend/src/app/pages/AgentChat/ToolCallBubble.tsx b/frontend/src/app/pages/AgentChat/ToolCallBubble.tsx new file mode 100644 index 00000000..f058b69e --- /dev/null +++ b/frontend/src/app/pages/AgentChat/ToolCallBubble.tsx @@ -0,0 +1,1591 @@ +import React, { useState, useEffect, useCallback, useMemo } from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import Collapse from '@mui/material/Collapse'; +import IconButton from '@mui/material/IconButton'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import ExpandLessIcon from '@mui/icons-material/ExpandLess'; +import TerminalIcon from '@mui/icons-material/Terminal'; +import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline'; +import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline'; +import BlockIcon from '@mui/icons-material/Block'; +import EmailIcon from '@mui/icons-material/Email'; +import EventIcon from '@mui/icons-material/Event'; +import FolderIcon from '@mui/icons-material/Folder'; +import AttachFileIcon from '@mui/icons-material/AttachFile'; +import SearchIcon from '@mui/icons-material/Search'; +import SendIcon from '@mui/icons-material/Send'; +import ReactMarkdown from 'react-markdown'; +import remarkGfm from 'remark-gfm'; +import { AgentMessage } from '@/shared/state/agentsSlice'; +import { useClaudeTokens, useThemeMode } from '@/shared/styles/ThemeContext'; + +const GoogleServiceIcon: React.FC<{ service: string; size?: number }> = ({ service, size = 14 }) => { + if (service === 'gmail') { + return ( + + {/* Left blue bar */} + + {/* Right green bar */} + + {/* Red M chevron */} + + {/* Top-left blue triangle */} + + {/* Top-right yellow triangle */} + + {/* Top red V */} + + + ); + } + if (service === 'calendar') { + return ( + + + + 31 + + ); + } + if (service === 'drive' || service === 'sheets') { + return ( + + + + + + + ); + } + return null; +}; + +export interface ToolPair { + type: 'tool_pair'; + id: string; + call: AgentMessage; + result: AgentMessage | null; +} + +const pulsingKeyframes = ` +@keyframes tool-pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.4; } +} +@keyframes border-glow { + 0%, 100% { box-shadow: 0 0 0 0 rgba(var(--glow-rgb), 0); } + 50% { box-shadow: 0 0 10px 2px rgba(var(--glow-rgb), 0.12); } +} +`; + +const streamingCursorKeyframes = ` +@keyframes blink-cursor { + 0%, 100% { opacity: 1; } + 50% { opacity: 0; } +} +`; + +const ElapsedTimer: React.FC<{ startTime: string }> = ({ startTime }) => { + const c = useClaudeTokens(); + const [elapsed, setElapsed] = useState(0); + + useEffect(() => { + const start = new Date(startTime).getTime(); + const tick = () => setElapsed(Math.floor((Date.now() - start) / 1000)); + tick(); + const interval = setInterval(tick, 1000); + return () => clearInterval(interval); + }, [startTime]); + + const mins = Math.floor(elapsed / 60); + const secs = elapsed % 60; + const display = mins > 0 ? `${mins}m ${secs}s` : `${secs}s`; + + return ( + + + + {display} + + + ); +}; + +function formatElapsed(ms: number): string { + if (ms >= 60000) return `${Math.floor(ms / 60000)}m ${Math.round((ms % 60000) / 1000)}s`; + if (ms >= 1000) return `${(ms / 1000).toFixed(1)}s`; + return `${ms}ms`; +} + +function getToolData(call: AgentMessage) { + const content = typeof call.content === 'object' ? call.content : {}; + return { + toolName: content.tool || 'Unknown', + input: content.input || {}, + isDenied: content.approved === false, + toolId: content.id, + }; +} + +function isBashTool(name: string) { + return name === 'Bash' || name === 'bash'; +} + +export interface McpToolInfo { + isMcp: boolean; + serverSlug: string; + action: string; + service: string; + displayName: string; +} + +export function parseMcpToolName(rawName: string): McpToolInfo { + const m = rawName.match(/^mcp__([^_]+(?:-[^_]+)*)__(.+)$/); + if (!m) return { isMcp: false, serverSlug: '', action: '', service: '', displayName: rawName }; + const serverSlug = m[1]; + const action = m[2]; + const display = action.replace(/_/g, ' ').replace(/\b\w/g, (ch) => ch.toUpperCase()); + + const lower = action.toLowerCase(); + let service = ''; + if (lower.includes('gmail') || lower.includes('email') || lower.includes('mail')) service = 'gmail'; + else if (lower.includes('calendar') || lower.includes('event') || lower.includes('freebusy')) service = 'calendar'; + else if (lower.includes('drive') || lower.includes('file')) service = 'drive'; + else if (lower.includes('sheet') || lower.includes('spreadsheet')) service = 'sheets'; + else if (lower.includes('doc') || lower.includes('paragraph')) service = 'docs'; + else if (lower.includes('contact')) service = 'contacts'; + + return { isMcp: true, serverSlug, action, service, displayName: display }; +} + +function getMcpInputSummary(input: any): string { + if (!input || typeof input !== 'object') return ''; + const keys = Object.keys(input); + if (keys.length === 0) return ''; + if (keys.length === 1) { + const v = input[keys[0]]; + const s = typeof v === 'string' ? v : JSON.stringify(v); + return s.length > 60 ? s.slice(0, 60) + '…' : s; + } + return keys.slice(0, 3).map((k) => { + const v = input[k]; + const s = typeof v === 'string' ? v : JSON.stringify(v); + return `${k}: ${s.length > 30 ? s.slice(0, 30) + '…' : s}`; + }).join(' '); +} + +function getInputSummary(toolName: string, input: any): string { + try { + const mcp = parseMcpToolName(toolName); + if (mcp.isMcp) return getMcpInputSummary(input); + + const n = toolName.toLowerCase(); + if (isBashTool(toolName)) { + const cmd = input.command || ''; + return `$ ${cmd.slice(0, 80)}${cmd.length > 80 ? '…' : ''}`; + } + if (n === 'read') return input.file_path || input.path || ''; + if (n === 'write') return input.file_path || input.path || ''; + if (n === 'edit' || n === 'multiedit' || n === 'strreplace') + return input.file_path || input.path || ''; + if (n === 'glob') return input.pattern || input.glob || input.glob_pattern || ''; + if (n === 'grep' || n === 'ripgrep') { + const pat = input.pattern || input.regex || ''; + const path = input.path || input.directory || ''; + return path ? `/${pat}/ in ${path}` : `/${pat}/`; + } + if (n === 'websearch') return input.query || input.search_term || ''; + if (n === 'webfetch') return input.url || ''; + if (n === 'todoread' || n === 'todowrite') return 'todos'; + if (n === 'ls') return input.path || '.'; + return ''; + } catch { + return ''; + } +} + +function formatMcpInputDisplay(input: any): string { + if (!input || typeof input !== 'object') return String(input ?? ''); + return Object.entries(input) + .map(([k, v]) => { + const s = typeof v === 'string' ? v : JSON.stringify(v, null, 2); + return `${k}: ${s}`; + }) + .join('\n'); +} + +function formatInputDisplay(toolName: string, input: any): string { + try { + const mcp = parseMcpToolName(toolName); + if (mcp.isMcp) return formatMcpInputDisplay(input); + + const n = toolName.toLowerCase(); + if (isBashTool(toolName)) return input.command || ''; + if (n === 'read') { + const p = input.file_path || input.path || ''; + const parts = [p]; + if (input.offset) parts.push(`offset: ${input.offset}`); + if (input.limit) parts.push(`limit: ${input.limit}`); + return parts.join(' '); + } + if (n === 'write') { + const p = input.file_path || input.path || ''; + const content = input.content || ''; + const preview = content.length > 300 ? content.slice(0, 300) + '\n…' : content; + return `${p}\n\n${preview}`; + } + if (n === 'edit' || n === 'strreplace') { + const p = input.file_path || input.path || ''; + const old = input.old_string || input.old_text || ''; + const nw = input.new_string || input.new_text || ''; + const lines = [p, '']; + if (old) { + const oldPreview = old.length > 200 ? old.slice(0, 200) + '…' : old; + lines.push(`- ${oldPreview.split('\n').join('\n- ')}`); + } + if (nw) { + const nwPreview = nw.length > 200 ? nw.slice(0, 200) + '…' : nw; + lines.push(`+ ${nwPreview.split('\n').join('\n+ ')}`); + } + return lines.join('\n'); + } + if (n === 'multiedit') { + const p = input.file_path || input.path || ''; + const edits = input.edits || []; + const lines = [p]; + for (const e of edits.slice(0, 3)) { + const old = e.old_string || e.old_text || ''; + lines.push(` - ${old.split('\n')[0].slice(0, 60)}…`); + } + if (edits.length > 3) lines.push(` … +${edits.length - 3} more edits`); + return lines.join('\n'); + } + if (n === 'glob') return input.pattern || input.glob || input.glob_pattern || ''; + if (n === 'grep' || n === 'ripgrep') { + const pat = input.pattern || input.regex || ''; + const path = input.path || input.directory || ''; + const parts = [`pattern: ${pat}`]; + if (path) parts.push(`path: ${path}`); + if (input.include) parts.push(`include: ${input.include}`); + return parts.join('\n'); + } + if (n === 'websearch') return input.query || input.search_term || ''; + if (n === 'webfetch') return input.url || ''; + } catch {} + if (typeof input === 'string') return input; + return JSON.stringify(input, null, 2); +} + +interface ParsedBashResult { + type: 'bash'; + stdout: string; + stderr: string; + exitCode: number | null; +} + +interface ParsedTextResult { + type: 'text'; + content: string; + isError?: boolean; +} + +interface ParsedMcpResult { + type: 'mcp'; + service: string; + action: string; + data: Record; + rawText: string; +} + +type ParsedResult = ParsedBashResult | ParsedTextResult | ParsedMcpResult; + +function parseToolResult(toolName: string, rawText: string): ParsedResult { + if (isBashTool(toolName)) { + try { + const parsed = JSON.parse(rawText); + if (typeof parsed === 'object' && parsed !== null && 'stdout' in parsed) { + const exitMatch = (parsed.stdout || '').match(/[Ee]xit code:\s*(\d+)/); + return { + type: 'bash', + stdout: parsed.stdout || '', + stderr: parsed.stderr || '', + exitCode: exitMatch ? parseInt(exitMatch[1], 10) : null, + }; + } + } catch {} + } + + const mcp = parseMcpToolName(toolName); + if (mcp.isMcp) { + try { + let parsed = JSON.parse(rawText); + + if (Array.isArray(parsed) && parsed.some((b: any) => b?.type === 'text' && typeof b?.text === 'string')) { + const textContent = parsed + .filter((b: any) => b?.type === 'text') + .map((b: any) => b.text) + .join('\n'); + try { + parsed = JSON.parse(textContent); + } catch { + return { type: 'mcp', service: mcp.service, action: mcp.action, data: {}, rawText: textContent }; + } + } + + if (typeof parsed === 'object' && parsed !== null) { + return { type: 'mcp', service: mcp.service, action: mcp.action, data: parsed, rawText }; + } + } catch {} + return { type: 'mcp', service: mcp.service, action: mcp.action, data: {}, rawText }; + } + + try { + const parsed = JSON.parse(rawText); + if (typeof parsed === 'object' && parsed !== null) { + if ('stdout' in parsed) { + return { type: 'text', content: parsed.stdout || '' }; + } + if ('content' in parsed && typeof parsed.content === 'string') { + return { type: 'text', content: parsed.content, isError: !!parsed.is_error }; + } + if ('result' in parsed && typeof parsed.result === 'string') { + return { type: 'text', content: parsed.result }; + } + if ('output' in parsed && typeof parsed.output === 'string') { + return { type: 'text', content: parsed.output }; + } + const n = toolName.toLowerCase(); + if (n === 'glob' && Array.isArray(parsed)) { + return { type: 'text', content: parsed.join('\n') }; + } + } + } catch {} + + return { type: 'text', content: rawText }; +} + +export function getMcpShortAction(mcpInfo: McpToolInfo): string { + const { action, service } = mcpInfo; + let short = action; + if (service && action.toLowerCase().startsWith(service.toLowerCase() + '_')) { + short = action.slice(service.length + 1); + } + return short.replace(/_/g, ' ').replace(/\b\w/g, (ch) => ch.toUpperCase()); +} + +export function getResultSummary(toolName: string, rawText: string): string { + const parsed = parseToolResult(toolName, rawText); + + if (parsed.type === 'bash') { + const lines = parsed.stdout.split('\n').filter((l) => l.trim()).length; + if (parsed.exitCode !== null && parsed.exitCode !== 0) return `✗ exit ${parsed.exitCode}`; + if (parsed.stderr && !parsed.stdout) return '✗ stderr'; + return `✓ ${lines} line${lines !== 1 ? 's' : ''}`; + } + + if (parsed.type === 'mcp') { + const d = parsed.data; + if (parsed.service === 'gmail') { + const subj = d.subject || getGmailHeader(d, 'Subject'); + if (subj) return subj; + if (Array.isArray(d.messages)) return `${d.messages.length} email${d.messages.length !== 1 ? 's' : ''}`; + if (d.id || d.messageId) return '✓ done'; + } + if (parsed.service === 'calendar') { + if (d.summary) return d.summary.slice(0, 40); + if (Array.isArray(d.items)) return `${d.items.length} event${d.items.length !== 1 ? 's' : ''}`; + } + if (parsed.service === 'drive') { + if (d.name) return d.name; + if (Array.isArray(d.files)) return `${d.files.length} file${d.files.length !== 1 ? 's' : ''}`; + } + if (d.error || d.is_error) return '✗ error'; + return '✓ done'; + } + + const text = parsed.content; + const lines = text.split('\n'); + const lineCount = lines.length; + const n = toolName.toLowerCase(); + + try { + if (n === 'glob') { + const fileCount = lines.filter((l) => l.trim()).length; + return `${fileCount} file${fileCount !== 1 ? 's' : ''}`; + } + if (n === 'grep' || n === 'ripgrep') { + const matchCount = lines.filter((l) => l.trim()).length; + return `${matchCount} match${matchCount !== 1 ? 'es' : ''}`; + } + if (n === 'read') return `${lineCount} lines`; + if (n === 'write') { + if (text.toLowerCase().includes('success') || text.toLowerCase().includes('written')) + return '✓ written'; + return '✓ done'; + } + if (n === 'edit' || n === 'multiedit' || n === 'strreplace') { + if (text.toLowerCase().includes('success') || text.toLowerCase().includes('applied')) + return '✓ applied'; + return '✓ done'; + } + if (n === 'websearch') return 'results'; + if (n === 'webfetch') return `${lineCount} lines`; + if (parsed.isError) return '✗ error'; + } catch {} + + return `${lineCount} line${lineCount !== 1 ? 's' : ''}`; +} + +function getPromptPrefix(toolName: string): string { + if (isBashTool(toolName)) return '$ '; + const mcp = parseMcpToolName(toolName); + if (mcp.isMcp) return `❯ ${mcp.displayName} `; + return `❯ ${toolName} `; +} + +interface ToolCallBubbleProps { + call: AgentMessage; + result?: AgentMessage | null; + isPending?: boolean; + isStreaming?: boolean; + mcpCompact?: boolean; +} + +interface TermColors { + TERM_BG: string; + TERM_BORDER: string; + PROMPT_COLOR: string; + CMD_COLOR: string; + OUTPUT_COLOR: string; + PATH_COLOR: string; + ADD_COLOR: string; + DEL_COLOR: string; + STDERR_COLOR: string; + WARN_COLOR: string; + NUM_COLOR: string; + DIM_COLOR: string; + DIFF_HEADER_COLOR: string; + SCROLLBAR_THUMB: string; +} + +const darkTermColors: TermColors = { + TERM_BG: '#131520', + TERM_BORDER: '#1e2030', + PROMPT_COLOR: '#7ec699', + CMD_COLOR: '#e8ecf4', + OUTPUT_COLOR: '#a0aab8', + PATH_COLOR: '#82aaff', + ADD_COLOR: '#7ec699', + DEL_COLOR: '#ff8787', + STDERR_COLOR: '#ff8787', + WARN_COLOR: '#ffcb6b', + NUM_COLOR: '#f78c6c', + DIM_COLOR: '#555b6e', + DIFF_HEADER_COLOR: '#c792ea', + SCROLLBAR_THUMB: '#2a2d3e', +}; + +const lightTermColors: TermColors = { + TERM_BG: '#f4f3ee', + TERM_BORDER: '#e2e0d8', + PROMPT_COLOR: '#2d7a3e', + CMD_COLOR: '#2a2a28', + OUTPUT_COLOR: '#555550', + PATH_COLOR: '#3060a8', + ADD_COLOR: '#2d7a3e', + DEL_COLOR: '#c03030', + STDERR_COLOR: '#c03030', + WARN_COLOR: '#8a6518', + NUM_COLOR: '#c05020', + DIM_COLOR: '#9e9c95', + DIFF_HEADER_COLOR: '#7c4daa', + SCROLLBAR_THUMB: '#ccc9c0', +}; + +function useTermColors(): TermColors { + const { mode } = useThemeMode(); + return mode === 'dark' ? darkTermColors : lightTermColors; +} + +function colorizeInput(toolName: string, text: string, tc: TermColors): React.ReactNode { + const n = toolName.toLowerCase(); + const mcp = parseMcpToolName(toolName); + + if (mcp.isMcp) { + const lines = text.split('\n'); + return ( + <> + {lines.map((line, i) => { + const nl = i < lines.length - 1 ? '\n' : ''; + const colonIdx = line.indexOf(':'); + if (colonIdx > 0 && colonIdx < 30) { + return ( + + {line.slice(0, colonIdx + 1)} + {line.slice(colonIdx + 1)} + {nl} + + ); + } + return {line}{nl}; + })} + + ); + } + + if (isBashTool(toolName)) return {text}; + + if (n === 'edit' || n === 'strreplace' || n === 'multiedit') { + const lines = text.split('\n'); + return ( + <> + {lines.map((line, i) => { + const nl = i < lines.length - 1 ? '\n' : ''; + if (i === 0 && (line.startsWith('/') || line.includes('.'))) + return {line}{nl}; + if (line.startsWith('+ ')) + return {line}{nl}; + if (line.startsWith('- ')) + return {line}{nl}; + return {line}{nl}; + })} + + ); + } + + if (n === 'write') { + const lines = text.split('\n'); + return ( + <> + {lines.map((line, i) => { + const nl = i < lines.length - 1 ? '\n' : ''; + if (i === 0 && (line.startsWith('/') || line.includes('.'))) + return {line}{nl}; + return {line}{nl}; + })} + + ); + } + + if (n === 'read' || n === 'glob' || n === 'webfetch') { + if (/^\//.test(text) || text.includes('/')) + return {text}; + } + + if (n === 'grep' || n === 'ripgrep') { + const lines = text.split('\n'); + return ( + <> + {lines.map((line, i) => { + const nl = i < lines.length - 1 ? '\n' : ''; + if (line.startsWith('pattern:')) + return ( + + pattern: + {line.slice(9)} + {nl} + + ); + if (line.startsWith('path:')) + return ( + + path: + {line.slice(6)} + {nl} + + ); + return {line}{nl}; + })} + + ); + } + + return {text}; +} + +function colorizeOutput(toolName: string, text: string, tc: TermColors): React.ReactNode { + if (!text) return (empty); + + const lines = text.split('\n'); + const n = toolName.toLowerCase(); + + return ( + <> + {lines.map((line, i) => { + const nl = i < lines.length - 1 ? '\n' : ''; + const trimmed = line.trimStart(); + + if (/^\/\S+/.test(trimmed)) + return {line}{nl}; + + if (n === 'grep' || n === 'ripgrep') { + const grepMatch = line.match(/^(\S+?:\d+[:-])/); + if (grepMatch) { + return ( + + {grepMatch[1]} + {line.slice(grepMatch[1].length)} + {nl} + + ); + } + const fileHeader = line.match(/^(\S+\.\w+)$/); + if (fileHeader) + return {line}{nl}; + } + + if (line.startsWith('@@') && line.includes('@@')) + return {line}{nl}; + if (line.startsWith('+')) + return {line}{nl}; + if (line.startsWith('-')) + return {line}{nl}; + + if (/\b[Ee]rror\b/.test(line)) + return {line}{nl}; + if (/\b[Ww]arning\b/.test(line)) + return {line}{nl}; + + if (n === 'read') { + const lineNumMatch = line.match(/^(\s*\d+\s*[|:])/); + if (lineNumMatch) { + return ( + + {lineNumMatch[1]} + {line.slice(lineNumMatch[1].length)} + {nl} + + ); + } + } + + return {line}{nl}; + })} + + ); +} + + +function formatTimestamp(ts: string | number | undefined): string { + if (!ts) return ''; + try { + const d = typeof ts === 'number' ? new Date(ts) : new Date(ts); + if (isNaN(d.getTime())) return String(ts); + return d.toLocaleDateString('en-US', { + weekday: 'short', month: 'short', day: 'numeric', year: 'numeric', + hour: 'numeric', minute: '2-digit', + }); + } catch { return String(ts); } +} + +function stripHtml(html: string): string { + const tmp = document.createElement('div'); + tmp.innerHTML = html; + return tmp.textContent || tmp.innerText || ''; +} + +interface CardColors { + TC_BG: string; + TC_BORDER: string; + TC_HOVER: string; + TC_HEADING: string; + TC_BODY: string; + TC_MUTED: string; + TC_DIM: string; + TC_ACCENT: string; + TC_SUCCESS: string; + TC_WARNING: string; +} + +const darkCardColors: CardColors = { + TC_BG: 'rgba(255,255,255,0.03)', + TC_BORDER: 'rgba(255,255,255,0.06)', + TC_HOVER: 'rgba(255,255,255,0.05)', + TC_HEADING: '#C2C0B6', + TC_BODY: '#9C9A92', + TC_MUTED: '#85837C', + TC_DIM: 'rgba(156,154,146,0.5)', + TC_ACCENT: '#c4633a', + TC_SUCCESS: '#7AB948', + TC_WARNING: '#D1A041', +}; + +const lightCardColors: CardColors = { + TC_BG: 'rgba(0,0,0,0.03)', + TC_BORDER: 'rgba(0,0,0,0.08)', + TC_HOVER: 'rgba(0,0,0,0.05)', + TC_HEADING: '#3D3D3A', + TC_BODY: '#555550', + TC_MUTED: '#73726C', + TC_DIM: 'rgba(115,114,108,0.5)', + TC_ACCENT: '#ae5630', + TC_SUCCESS: '#265B19', + TC_WARNING: '#805C1F', +}; + +function useCardColors(): CardColors { + const { mode } = useThemeMode(); + return mode === 'dark' ? darkCardColors : lightCardColors; +} + +function getGmailHeader(msg: any, name: string): string { + if (msg.payload?.headers && Array.isArray(msg.payload.headers)) { + const h = msg.payload.headers.find( + (hdr: any) => (hdr.name || '').toLowerCase() === name.toLowerCase() + ); + if (h) return h.value || ''; + } + if (msg.headers && typeof msg.headers === 'object' && !Array.isArray(msg.headers)) { + return msg.headers[name] || msg.headers[name.toLowerCase()] || ''; + } + return ''; +} + +function extractEmailFields(msg: any) { + const subject = msg.subject || getGmailHeader(msg, 'Subject') || '(no subject)'; + const from = msg.from || msg.sender || getGmailHeader(msg, 'From') || ''; + const to = msg.to || msg.recipient || getGmailHeader(msg, 'To') || ''; + const rawDate = msg.date || msg.internalDate || msg.receivedAt || getGmailHeader(msg, 'Date') || ''; + const date = formatTimestamp(rawDate); + const snippet = msg.snippet || ''; + const body = msg.body || msg.text || msg.textBody || ''; + const htmlBody = msg.htmlBody || msg.html || ''; + const bodyPreview = body || (htmlBody ? stripHtml(htmlBody) : ''); + return { subject, from, to, date, snippet, bodyPreview }; +} + +const GmailCard: React.FC<{ data: Record; action: string; hideSubjectHeader?: boolean }> = ({ data, action, hideSubjectHeader }) => { + const c = useClaudeTokens(); + const { TC_BG, TC_BORDER, TC_HOVER, TC_HEADING, TC_BODY, TC_MUTED, TC_DIM, TC_ACCENT, TC_SUCCESS, TC_WARNING } = useCardColors(); + const email = extractEmailFields(data); + const labels = data.labelIds || data.labels || []; + const attachments = data.attachments || []; + + const isSend = action.includes('send'); + const isSearch = action.includes('search') || action.includes('list'); + const messages: any[] = data.messages || (isSearch && data.results ? data.results : []); + + if (messages.length > 0) { + return ( + + {messages.slice(0, 5).map((msg: any, i: number) => { + const m = extractEmailFields(msg); + return ( + + + + {m.subject} + + {m.date && ( + + {m.date} + + )} + + {m.from && ( + + {m.from} + + )} + {(m.snippet || m.bodyPreview) && ( + + {(m.snippet || m.bodyPreview).slice(0, 120)} + {(m.snippet || m.bodyPreview).length > 120 ? '…' : ''} + + )} + + ); + })} + {messages.length > 5 && ( + + +{messages.length - 5} more + + )} + + ); + } + + return ( + + {!hideSubjectHeader && ( + + {isSend ? ( + + ) : ( + + )} + + {email.subject} + + + )} + + + {(email.from || email.to || email.date) && ( + + {email.from && ( + + From + {email.from} + + )} + {email.to && ( + + To + {email.to} + + )} + {email.date && ( + + Date + {email.date} + + )} + + )} + + {labels.length > 0 && ( + + {labels.map((l: string, i: number) => ( + + {l} + + ))} + + )} + + {(email.snippet || email.bodyPreview) && ( + + {children} }} + > + {email.bodyPreview || email.snippet} + + + )} + + {attachments.length > 0 && ( + + {attachments.map((a: any, i: number) => ( + + + + {a.filename || a.name || 'attachment'} + + + ))} + + )} + + + ); +}; + +const CalendarCard: React.FC<{ data: Record; hideHeader?: boolean }> = ({ data, hideHeader }) => { + const c = useClaudeTokens(); + const { TC_BG, TC_BORDER, TC_HOVER, TC_HEADING, TC_BODY, TC_DIM, TC_SUCCESS } = useCardColors(); + const items: any[] = data.items || (Array.isArray(data) ? data : []); + const single = !items.length ? data : null; + + if (single && (single.summary || single.start)) { + const start = single.start?.dateTime || single.start?.date || single.start || ''; + const end = single.end?.dateTime || single.end?.date || single.end || ''; + return ( + + {!hideHeader && ( + + + + {single.summary || '(no title)'} + + + )} + + {start && ( + + Start + {formatTimestamp(start)} + + )} + {end && ( + + End + {formatTimestamp(end)} + + )} + {single.location && ( + + Where + {single.location} + + )} + {single.description && ( + +
+                {single.description.slice(0, 300)}
+                {single.description.length > 300 ? '…' : ''}
+              
+
+ )} +
+
+ ); + } + + if (items.length > 0) { + return ( + + {items.slice(0, 6).map((item: any, i: number) => ( + + + {item.summary || '(no title)'} + + + {formatTimestamp(item.start?.dateTime || item.start?.date || item.start)} + + + ))} + {items.length > 6 && ( + + +{items.length - 6} more + + )} + + ); + } + + return null; +}; + +const DriveCard: React.FC<{ data: Record }> = ({ data }) => { + const c = useClaudeTokens(); + const { TC_BG, TC_BORDER, TC_HOVER, TC_HEADING, TC_DIM, TC_WARNING } = useCardColors(); + const files: any[] = data.files || (Array.isArray(data) ? data : []); + const single = !files.length && data.name ? data : null; + + if (single) { + return ( + + + + + {single.name} + + {single.mimeType && ( + {single.mimeType} + )} + + + ); + } + + if (files.length > 0) { + return ( + + {files.slice(0, 8).map((f: any, i: number) => ( + + + {f.name || f.id} + {f.mimeType && ( + + {f.mimeType.split('/').pop()} + + )} + + ))} + + ); + } + + return null; +}; + +const GenericMcpCard: React.FC<{ data: Record }> = ({ data }) => { + const c = useClaudeTokens(); + const { TC_DIM, TC_BODY } = useCardColors(); + const entries = Object.entries(data).filter(([, v]) => v != null); + + if (entries.length === 0) + return (empty response); + + return ( + + {entries.slice(0, 20).map(([key, val], i) => { + const isLong = typeof val === 'string' && val.length > 100; + const isObj = typeof val === 'object'; + return ( + + + {key} + + {isObj ? ( +
+                {JSON.stringify(val, null, 2).slice(0, 500)}
+              
+ ) : isLong ? ( +
+                {String(val).slice(0, 500)}{String(val).length > 500 ? '…' : ''}
+              
+ ) : ( + {String(val)} + )} +
+ ); + })} + {entries.length > 20 && ( + + +{entries.length - 20} more fields + + )} +
+ ); +}; + +const McpResultCard: React.FC<{ parsed: ParsedMcpResult; compact?: boolean }> = ({ parsed, compact }) => { + const tc = useTermColors(); + const { service, action, data } = parsed; + + if (data.error || data.is_error) { + return ( + + + {data.error || data.message || JSON.stringify(data, null, 2)} + + + ); + } + + if (service === 'gmail') return ; + if (service === 'calendar') return ; + if (service === 'drive' || service === 'sheets') return ; + + return ; +}; + +const ToolCallBubble: React.FC = React.memo( + ({ call, result = null, isPending = false, isStreaming = false, mcpCompact = false }) => { + const c = useClaudeTokens(); + const tc = useTermColors(); + const [expanded, setExpanded] = useState(false); + + const { toolName, input, isDenied } = getToolData(call); + const mcpInfo = useMemo(() => parseMcpToolName(toolName), [toolName]); + const inputSummary = getInputSummary(toolName, input); + const formattedInput = useMemo(() => formatInputDisplay(toolName, input), [toolName, input]); + const showTimer = isPending && !isDenied && !isStreaming; + const showBody = expanded || isStreaming; + + const resultContent = result?.content; + const hasStructuredResult = + resultContent && typeof resultContent === 'object' && 'text' in resultContent; + const resultRawText: string = hasStructuredResult + ? resultContent.text + : typeof resultContent === 'string' + ? resultContent + : resultContent + ? JSON.stringify(resultContent, null, 2) + : ''; + const resultElapsedMs: number | null = hasStructuredResult + ? resultContent.elapsed_ms ?? null + : null; + + const parsedResult = useMemo( + () => (result ? parseToolResult(toolName, resultRawText) : null), + [result, toolName, resultRawText], + ); + const resultSummary = result ? getResultSummary(toolName, resultRawText) : null; + const isError = + resultSummary?.startsWith('✗') || + (parsedResult?.type === 'bash' && parsedResult.exitCode !== null && parsedResult.exitCode !== 0) || + (parsedResult?.type === 'text' && parsedResult.isError); + + const toggle = useCallback(() => { + if (!isStreaming) setExpanded((v) => !v); + }, [isStreaming]); + + const accentRgb = c.accent.primary + .replace('#', '') + .match(/.{2}/g) + ?.map((h) => parseInt(h, 16)) + .join(', ') || '189, 100, 57'; + + const promptPrefix = getPromptPrefix(toolName); + const shortAction = mcpInfo.isMcp ? getMcpShortAction(mcpInfo) : toolName; + + const serviceLabel = mcpInfo.isMcp && mcpInfo.service + ? mcpInfo.service.charAt(0).toUpperCase() + mcpInfo.service.slice(1) + : shortAction; + + const ServiceIcon = mcpInfo.isMcp && mcpInfo.service + ? + : null; + + const selectAttrs = { + 'data-select-type': 'tool-call' as const, + 'data-select-id': call.id, + 'data-select-meta': JSON.stringify({ tool: toolName, inputSummary }), + }; + + if (mcpCompact && mcpInfo.isMcp) { + return ( + + + + {ServiceIcon} + + {serviceLabel} + + {resultSummary && !isError && ( + + {resultSummary} + + )} + {!resultSummary && !showTimer && } + {showTimer && ( + <> + + + + )} + {isDenied && ( + + + denied + + )} + {result && !isDenied && ( + + {isError ? ( + + ) : ( + + )} + {resultElapsedMs != null && ( + + {formatElapsed(resultElapsedMs)} + + )} + + )} + + {showBody ? : } + + + + + + {parsedResult && parsedResult.type === 'mcp' ? ( + + ) : parsedResult ? ( +
+                  {parsedResult.type === 'text' ? parsedResult.content : ''}
+                
+ ) : null} + {!parsedResult && isPending && !isStreaming && ( + + + + )} + +
+
+ ); + } + + return ( + + + {isStreaming && } + + {/* Header */} + + {mcpInfo.isMcp && mcpInfo.service + ? + : (() => { + const n = toolName.toLowerCase(); + if (n.includes('search') || n === 'grep' || n === 'glob') + return ; + return ; + })() + } + + {mcpInfo.isMcp ? mcpInfo.displayName : toolName} + + {mcpInfo.isMcp && ( + + {mcpInfo.serverSlug} + + )} + {inputSummary && !isStreaming && ( + + {inputSummary} + + )} + {!inputSummary && } + {isStreaming && } + + {isDenied && ( + + + + denied + + + )} + {result && !isDenied && ( + + {isError ? ( + + ) : ( + + )} + + {resultSummary} + + {resultElapsedMs != null && ( + + {formatElapsed(resultElapsedMs)} + + )} + + )} + {showTimer && } + + {!isStreaming && ( + + {showBody ? ( + + ) : ( + + )} + + )} + + + {/* Unified terminal body */} + + + {/* Prompt + command */} +
+                
+                  {promptPrefix}
+                
+                {isStreaming ? (
+                  {call.content?.input ?? ''}
+                ) : (
+                  colorizeInput(toolName, formattedInput, tc)
+                )}
+                {isStreaming && (
+                  
+                )}
+              
+ + {/* Output */} + {parsedResult && parsedResult.type === 'mcp' ? ( + + ) : parsedResult ? ( +
+                  {parsedResult.type === 'bash' ? (
+                    <>
+                      {parsedResult.stdout.trim() &&
+                        colorizeOutput(toolName, parsedResult.stdout, tc)}
+                      {parsedResult.stderr.trim() && (
+                        <>
+                          {parsedResult.stdout.trim() && '\n'}
+                          {parsedResult.stderr}
+                        
+                      )}
+                      {!parsedResult.stdout.trim() && !parsedResult.stderr.trim() && (
+                        (no output)
+                      )}
+                    
+                  ) : (
+                    <>
+                      {parsedResult.isError ? (
+                        {parsedResult.content || '(empty)'}
+                      ) : (
+                        colorizeOutput(toolName, parsedResult.content, tc)
+                      )}
+                    
+                  )}
+                
+ ) : null} + + {/* Pending indicator when waiting for result */} + {!parsedResult && isPending && !isStreaming && ( + + + + )} + +
+
+
+ ); + } +); + +export default ToolCallBubble; diff --git a/frontend/src/app/pages/AgentChat/ToolGroupBubble.tsx b/frontend/src/app/pages/AgentChat/ToolGroupBubble.tsx new file mode 100644 index 00000000..1a11dc02 --- /dev/null +++ b/frontend/src/app/pages/AgentChat/ToolGroupBubble.tsx @@ -0,0 +1,198 @@ +import React, { useMemo, useState } from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import Collapse from '@mui/material/Collapse'; +import IconButton from '@mui/material/IconButton'; +import Chip from '@mui/material/Chip'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import ExpandLessIcon from '@mui/icons-material/ExpandLess'; +import TerminalIcon from '@mui/icons-material/Terminal'; +import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline'; +import { AgentMessage, ToolGroupMeta } from '@/shared/state/agentsSlice'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; +import { sanitizeSvgString } from '@/shared/sanitizeSvg'; +import ToolCallBubble, { ToolPair } from './ToolCallBubble'; + +export interface ToolGroup { + type: 'tool_group'; + id: string; + pairs: ToolPair[]; + label: string; + callCount: number; + mcpServer?: string; +} + +export type RenderItem = AgentMessage | ToolGroup | ToolPair; + +export function isToolGroup(item: RenderItem): item is ToolGroup { + return (item as ToolGroup).type === 'tool_group'; +} + +export function isToolPair(item: RenderItem): item is ToolPair { + return (item as ToolPair).type === 'tool_pair'; +} + +const GeneratedSvgIcon: React.FC<{ svg: string; size?: number; color: string }> = ({ svg, size = 16, color }) => { + const sanitized = useMemo(() => sanitizeSvgString(svg), [svg]); + if (!sanitized) return null; + return ( + + ); +}; + +const SkeletonPulse: React.FC<{ width: number; height: number; borderRadius?: number }> = ({ width, height, borderRadius = 4 }) => ( + +); + +interface Props { + group: ToolGroup; + isSessionRunning?: boolean; + meta?: ToolGroupMeta; +} + +const ToolGroupBubble: React.FC = React.memo(({ group, isSessionRunning = false, meta }) => { + const c = useClaudeTokens(); + const isMcp = !!group.mcpServer; + const [expanded, setExpanded] = useState(isMcp); + + const completedCount = group.pairs.filter((p) => p.result !== null).length; + const pendingCount = group.pairs.filter((p) => p.result === null).length; + const deniedCount = group.pairs.filter( + (p) => typeof p.call.content === 'object' && p.call.content.approved === false + ).length; + const allDone = pendingCount === 0 || !isSessionRunning; + + const displayName = meta?.name || group.label; + const hasSvg = !!meta?.svg; + + const toolNames = group.pairs.map((p) => { + const c2 = typeof p.call.content === 'object' ? p.call.content : {}; + return c2.tool || 'unknown'; + }); + + return ( + + + setExpanded(!expanded)} + sx={{ + display: 'flex', + alignItems: 'center', + gap: 0.75, + px: 1.5, + py: 0.7, + cursor: 'pointer', + '&:hover': { bgcolor: 'rgba(0,0,0,0.02)' }, + }} + > + {!meta ? ( + + ) : hasSvg ? ( + + ) : ( + + )} + + {!meta ? ( + + + + ) : ( + + {displayName} + + )} + + {deniedCount > 0 && ( + + {deniedCount} denied + + )} + {allDone && completedCount > 0 && ( + + + + {completedCount}/{group.callCount} + + + )} + {!allDone && pendingCount > 0 && ( + + {completedCount}/{group.callCount} + + )} + + + {expanded ? : } + + + + + + {group.pairs.map((pair) => ( + + ))} + + + + + ); +}); + +export default ToolGroupBubble; diff --git a/frontend/src/app/pages/AgentChat/ViewBubble.tsx b/frontend/src/app/pages/AgentChat/ViewBubble.tsx new file mode 100644 index 00000000..9ae3af4b --- /dev/null +++ b/frontend/src/app/pages/AgentChat/ViewBubble.tsx @@ -0,0 +1,277 @@ +import React, { useState, useMemo } from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import IconButton from '@mui/material/IconButton'; +import Collapse from '@mui/material/Collapse'; +import Dialog from '@mui/material/Dialog'; +import DialogContent from '@mui/material/DialogContent'; +import Icon from '@mui/material/Icon'; +import OpenInFullIcon from '@mui/icons-material/OpenInFull'; +import CloseIcon from '@mui/icons-material/Close'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import { useAppSelector } from '@/shared/hooks'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; +import ViewPreview from '../Views/ViewPreview'; + +interface Props { + toolInput: Record; + toolResult?: string | Record; + isStreaming?: boolean; +} + +const ViewBubble: React.FC = ({ toolInput, toolResult, isStreaming }) => { + const c = useClaudeTokens(); + const [expanded, setExpanded] = useState(false); + const [showInputs, setShowInputs] = useState(false); + + const outputId = toolInput?.output_id; + const inputData = toolInput?.input_data || {}; + const outputsMap = useAppSelector((state) => state.outputs.items); + const output = outputId ? outputsMap[outputId] : null; + + const parsedResult = useMemo(() => { + if (!toolResult) return null; + if (typeof toolResult === 'object') return toolResult; + try { return JSON.parse(toolResult as string); } catch { return null; } + }, [toolResult]); + + const frontendCode = parsedResult?.frontend_code || (output?.files?.['index.html'] ?? '') || ''; + const backendResult = parsedResult?.backend_result || null; + const outputName = parsedResult?.output_name || output?.name || 'View'; + const outputColor = c.accent.primary; + const outputIcon = output?.icon || 'view_quilt'; + const hasPreview = !!frontendCode.trim(); + const serveUrl = outputId ? `/api/outputs/${outputId}/serve/index.html` : undefined; + const inputEntries = Object.entries(inputData); + + if (isStreaming && !hasPreview) { + return ( + + + {outputIcon} + + {outputName} + + + + Rendering… + + + + ); + } + + return ( + <> + + + {/* Header */} + + {outputIcon} + + {outputName} + + {inputEntries.length > 0 && ( + setShowInputs(!showInputs)} + sx={{ + color: c.text.tertiary, + p: 0.5, + transform: showInputs ? 'rotate(180deg)' : 'rotate(0deg)', + transition: 'transform 0.2s ease', + }} + > + + + )} + {hasPreview && ( + setExpanded(true)} + sx={{ color: c.text.tertiary, p: 0.5, '&:hover': { color: outputColor } }} + > + + + )} + + + {/* Collapsible input params */} + + + {inputEntries.map(([key, val]) => { + const display = typeof val === 'string' ? val : JSON.stringify(val); + return ( + + + {key} + + + {display.length > 120 ? display.slice(0, 120) + '…' : display} + + + ); + })} + + + + {/* Preview */} + {hasPreview && ( + + + + )} + + {parsedResult?.error && ( + + + {parsedResult.error} + + + )} + + + + {/* Fullscreen dialog */} + setExpanded(false)} + maxWidth="lg" + fullWidth + PaperProps={{ + sx: { + height: '85vh', + display: 'flex', + flexDirection: 'column', + borderRadius: '12px', + overflow: 'hidden', + }, + }} + > + + {outputIcon} + {outputName} + setExpanded(false)} size="small" sx={{ color: c.text.tertiary }}> + + + + + + + + + ); +}; + +export default ViewBubble; diff --git a/frontend/src/app/pages/Commands/Commands.tsx b/frontend/src/app/pages/Commands/Commands.tsx new file mode 100644 index 00000000..a1bf45dd --- /dev/null +++ b/frontend/src/app/pages/Commands/Commands.tsx @@ -0,0 +1,588 @@ +import React, { useEffect, useMemo } from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import Paper from '@mui/material/Paper'; +import Chip from '@mui/material/Chip'; +import DescriptionIcon from '@mui/icons-material/Description'; +import PsychologyIcon from '@mui/icons-material/Psychology'; +import SmartToyOutlinedIcon from '@mui/icons-material/SmartToyOutlined'; +import AlternateEmailIcon from '@mui/icons-material/AlternateEmail'; +import KeyboardIcon from '@mui/icons-material/Keyboard'; +import TerminalIcon from '@mui/icons-material/Terminal'; +import InsertDriveFileOutlinedIcon from '@mui/icons-material/InsertDriveFileOutlined'; +import LanguageIcon from '@mui/icons-material/Language'; +import BuildOutlinedIcon from '@mui/icons-material/BuildOutlined'; +import ViewQuiltOutlinedIcon from '@mui/icons-material/ViewQuiltOutlined'; +import { useAppSelector, useAppDispatch } from '@/shared/hooks'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; +import { fetchBuiltinTools, fetchTools } from '@/shared/state/toolsSlice'; +import { getToolGroupIcon } from '@/app/components/CommandPicker'; +import { fetchOutputs } from '@/shared/state/outputsSlice'; +import { fetchTemplates } from '@/shared/state/templatesSlice'; +import { fetchSkills } from '@/shared/state/skillsSlice'; +import { fetchModes } from '@/shared/state/modesSlice'; + +interface SlashCommand { + id: string; + type: 'template' | 'skill' | 'mode'; + name: string; + description: string; + command: string; +} + +interface AtCommand { + prefix: string; + label: string; + description: string; + icon: React.ReactNode; + source: string; + isChild?: boolean; +} + +interface Shortcut { + key: string; + description: string; + category: 'navigation' | 'action'; +} + +const SHORTCUTS: Shortcut[] = [ + { key: 'd', description: 'Go to Dashboard', category: 'navigation' }, + { key: 't', description: 'Go to Templates', category: 'navigation' }, + { key: '1-9', description: 'Open agent by position', category: 'navigation' }, + { key: 'Shift+A', description: 'Approve all pending', category: 'action' }, + { key: 'Shift+D', description: 'Deny all pending', category: 'action' }, + { key: '?', description: 'Show shortcuts dialog', category: 'navigation' }, +]; + +const KeyBadge: React.FC<{ keys: string; c: any }> = ({ keys, c }) => ( + + + {keys} + + +); + +const SectionHeader: React.FC<{ + icon: React.ReactNode; + title: string; + subtitle: string; + count?: number; + c: any; +}> = ({ icon, title, subtitle, count, c }) => ( + + {icon} + + + + {title} + + {count !== undefined && ( + + )} + + {subtitle} + + +); + +const Commands: React.FC = () => { + const c = useClaudeTokens(); + const dispatch = useAppDispatch(); + const templates = useAppSelector((state) => state.templates.items); + const skills = useAppSelector((state) => state.skills.items); + const modesMap = useAppSelector((state) => state.modes.items); + const builtinTools = useAppSelector((state) => state.tools.builtinTools); + const customTools = useAppSelector((state) => state.tools.items); + const outputItems = useAppSelector((state) => state.outputs.items); + + const templatesLoaded = useAppSelector((state) => state.templates.loaded); + const skillsLoaded = useAppSelector((state) => state.skills.loaded); + const modesLoaded = useAppSelector((state) => state.modes.loaded); + const builtinLoaded = useAppSelector((state) => state.tools.builtinLoaded); + const toolsLoaded = useAppSelector((state) => state.tools.loaded); + const outputsLoaded = useAppSelector((state) => state.outputs.loaded); + + useEffect(() => { + if (!templatesLoaded) dispatch(fetchTemplates()); + if (!skillsLoaded) dispatch(fetchSkills()); + if (!modesLoaded) dispatch(fetchModes()); + if (!builtinLoaded) dispatch(fetchBuiltinTools()); + if (!toolsLoaded) dispatch(fetchTools()); + if (!outputsLoaded) dispatch(fetchOutputs()); + }, [dispatch, templatesLoaded, skillsLoaded, modesLoaded, builtinLoaded, toolsLoaded, outputsLoaded]); + + const slashCommands: SlashCommand[] = useMemo(() => [ + ...Object.values(templates).map((t) => ({ + id: t.id, + type: 'template' as const, + name: t.name, + description: t.description || `Template with ${t.fields.length} fields`, + command: t.name.toLowerCase().replace(/\s+/g, '-'), + })), + ...Object.values(skills).map((s) => ({ + id: s.id, + type: 'skill' as const, + name: s.name, + description: s.description || 'Skill', + command: s.command || s.id, + })), + ...Object.values(modesMap).map((m) => ({ + id: m.id, + type: 'mode' as const, + name: m.name, + description: m.description || 'Switch to this mode', + command: m.name.toLowerCase().replace(/\s+/g, '-'), + })), + ], [templates, skills, modesMap]); + + const atCommands: AtCommand[] = useMemo(() => { + const items: AtCommand[] = [ + { prefix: '@file', label: 'File', description: 'Attach a file or folder as context', icon: , source: 'builtin' }, + ]; + + const hasWebSearch = builtinTools.some((t) => t.name === 'WebSearch' && t.deferred); + const hasWebFetch = builtinTools.some((t) => t.name === 'WebFetch' && t.deferred); + if (hasWebSearch || hasWebFetch) { + items.push({ + prefix: '@web', + label: 'Web', + description: 'Search the web and fetch URLs', + icon: , + source: 'builtin', + }); + } + + for (const tool of Object.values(customTools)) { + if (!tool.mcp_config || Object.keys(tool.mcp_config).length === 0) continue; + const services = tool.tool_permissions?._services as Record | undefined; + if (!services) continue; + const perms = tool.tool_permissions as Record; + const serviceGroups = (tool.tool_permissions?._service_groups ?? {}) as Record; + + const enabledServices: { name: string }[] = []; + for (const [serviceName, serviceTools] of Object.entries(services)) { + const allToolNames = [...(serviceTools.read || []), ...(serviceTools.write || [])]; + const enabled = allToolNames.filter((name) => perms[name] !== 'deny'); + if (enabled.length > 0) enabledServices.push({ name: serviceName }); + } + + if (enabledServices.length === 0) continue; + + const groupEntries = Object.entries(serviceGroups); + const emittedServices = new Set(); + + for (const [groupName, groupServiceNames] of groupEntries) { + const groupCmd = groupName.toLowerCase().replace(/\s+/g, '-'); + const groupServices = enabledServices.filter((s) => groupServiceNames.includes(s.name)); + if (groupServices.length === 0) continue; + groupServices.forEach((s) => emittedServices.add(s.name)); + + const groupIcon = getToolGroupIcon(groupName, 18); + if (groupServices.length >= 2) { + items.push({ + prefix: `@${groupCmd}`, + label: groupName, + description: `Use all ${groupName} tools`, + icon: groupIcon, + source: tool.name, + }); + for (const svc of groupServices) { + items.push({ + prefix: `@${groupCmd}/${svc.name.toLowerCase().replace(/\s+/g, '-')}`, + label: svc.name, + description: `Use ${svc.name} tools from ${tool.name}`, + icon: groupIcon, + source: tool.name, + isChild: true, + }); + } + } else { + const svc = groupServices[0]; + items.push({ + prefix: `@${svc.name.toLowerCase().replace(/\s+/g, '-')}`, + label: svc.name, + description: `Use ${svc.name} tools from ${tool.name}`, + icon: groupIcon, + source: tool.name, + }); + } + } + + for (const svc of enabledServices) { + if (emittedServices.has(svc.name)) continue; + items.push({ + prefix: `@${svc.name.toLowerCase().replace(/\s+/g, '-')}`, + label: svc.name, + description: `Use ${svc.name} tools from ${tool.name}`, + icon: , + source: tool.name, + }); + } + } + + for (const out of Object.values(outputItems)) { + if (out.permission === 'deny') continue; + const cmd = out.name.toLowerCase().replace(/\s+/g, '-'); + items.push({ + prefix: `@${cmd}`, + label: out.name, + description: out.description || `Render ${out.name} view`, + icon: , + source: 'view', + }); + } + + return items; + }, [builtinTools, customTools, outputItems]); + + const navShortcuts = SHORTCUTS.filter((s) => s.category === 'navigation'); + const actionShortcuts = SHORTCUTS.filter((s) => s.category === 'action'); + + return ( + + + + Commands + + + Manage slash commands, context references, and keyboard shortcuts in one place. + + + + + {/* Slash Commands */} + + } + title="Slash Commands" + subtitle="Type / in chat to invoke templates, skills, and modes" + count={slashCommands.length} + c={c} + /> + + {slashCommands.length === 0 ? ( + + + + No slash commands yet. Create templates, skills, or modes to see them here. + + + ) : ( + + {slashCommands.map((cmd) => ( + + + {cmd.type === 'template' ? ( + + ) : cmd.type === 'mode' ? ( + + ) : ( + + )} + + + /{cmd.command} + + + + {cmd.description} + + + ))} + + )} + + + {/* @ Commands */} + + } + title="@ Context Commands" + subtitle="Type @ in chat to attach context and activate tools" + count={atCommands.length} + c={c} + /> + + {atCommands.length === 0 ? ( + + + + No @ commands yet. Install MCP tools to see them here. + + + ) : ( + + {atCommands.map((cmd) => ( + + + {cmd.icon} + + + {cmd.prefix} + + + + {cmd.description} + + + ))} + + )} + + + {/* Keyboard Shortcuts */} + + } + title="Keyboard Shortcuts" + subtitle="Press ? anywhere to see the quick-reference dialog" + count={SHORTCUTS.length} + c={c} + /> + + + {/* Navigation */} + + + Navigation + + + {navShortcuts.map((s) => ( + + + {s.description} + + + + ))} + + + + {/* Actions */} + + + Actions + + + {actionShortcuts.map((s) => ( + + + {s.description} + + + + ))} + + + + + + + ); +}; + +export default Commands; diff --git a/frontend/src/app/pages/Dashboard/AgentCard.tsx b/frontend/src/app/pages/Dashboard/AgentCard.tsx new file mode 100644 index 00000000..b1e6ddce --- /dev/null +++ b/frontend/src/app/pages/Dashboard/AgentCard.tsx @@ -0,0 +1,754 @@ +import React, { useState, useEffect, useRef, useCallback } from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import Chip from '@mui/material/Chip'; +import IconButton from '@mui/material/IconButton'; +import Button from '@mui/material/Button'; +import Tooltip from '@mui/material/Tooltip'; +import CheckIcon from '@mui/icons-material/Check'; +import CheckCircleIcon from '@mui/icons-material/CheckCircle'; +import CancelIcon from '@mui/icons-material/Cancel'; +import CloseIcon from '@mui/icons-material/Close'; +import DragIndicatorIcon from '@mui/icons-material/DragIndicator'; +import TerminalIcon from '@mui/icons-material/Terminal'; +import { motion } from 'framer-motion'; +import { + AgentSession, + handleApproval, + toggleExpandSession, + collapseSession, + closeSession, +} from '@/shared/state/agentsSlice'; +import { + setCardPosition, + setCardSize, +} from '@/shared/state/dashboardLayoutSlice'; +import { useAppDispatch } from '@/shared/hooks'; +import { QuestionForm } from '@/app/pages/AgentChat/ApprovalBar'; +import AgentChat from '@/app/pages/AgentChat/AgentChat'; +import { parseMcpToolName, getMcpShortAction } from '@/app/pages/AgentChat/ToolCallBubble'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; + +// --------------------------------------------------------------------------- +// Helper components & functions (unchanged) +// --------------------------------------------------------------------------- + +const GoogleServiceIcon: React.FC<{ service: string; size?: number }> = ({ service, size = 16 }) => { + if (service === 'gmail') { + return ( + + + + + + + + + ); + } + if (service === 'calendar') { + return ( + + + + 31 + + ); + } + if (service === 'drive' || service === 'sheets') { + return ( + + + + + + + ); + } + return null; +}; + +function formatDuration(createdAt: string): string { + const seconds = Math.floor((Date.now() - new Date(createdAt).getTime()) / 1000); + if (seconds < 60) return `${seconds}s`; + const minutes = Math.floor(seconds / 60); + if (minutes < 60) return `${minutes}m ${seconds % 60}s`; + const hours = Math.floor(minutes / 60); + return `${hours}h ${minutes % 60}m`; +} + +function summarizeToolInput(toolName: string, toolInput: Record): string { + const mcp = parseMcpToolName(toolName); + if (mcp.isMcp) { + const keys = Object.keys(toolInput || {}); + if (keys.length === 0) return ''; + if (keys.length === 1) { + const v = toolInput[keys[0]]; + const s = typeof v === 'string' ? v : JSON.stringify(v); + return s.length > 60 ? s.slice(0, 60) + '…' : s; + } + return keys.slice(0, 3).map((k) => { + const v = toolInput[k]; + const s = typeof v === 'string' ? v : JSON.stringify(v); + return `${k}: ${s.length > 30 ? s.slice(0, 30) + '…' : s}`; + }).join(' '); + } + switch (toolName) { + case 'Bash': + return toolInput.command || '(command)'; + case 'Read': + return toolInput.file_path || toolInput.path || '(file)'; + case 'Write': + case 'Edit': + return toolInput.file_path || toolInput.path || '(file)'; + case 'Grep': + return `/${toolInput.pattern || ''}/${toolInput.path ? ` in ${toolInput.path}` : ''}`; + case 'Glob': + return toolInput.glob_pattern || toolInput.pattern || '(pattern)'; + case 'AskUserQuestion': { + const questions = toolInput.questions; + if (Array.isArray(questions) && questions.length > 0) { + return questions[0].question || questions[0].prompt || questions[0].text || 'Question pending'; + } + return 'Question pending'; + } + default: { + return toolInput.command || toolInput.file_path || toolInput.path || toolInput.query + || JSON.stringify(toolInput).slice(0, 60); + } + } +} + +function getToolDisplayName(toolName: string): string { + const mcp = parseMcpToolName(toolName); + if (mcp.isMcp) return mcp.displayName; + return toolName; +} + +// --------------------------------------------------------------------------- +// Resize handle definitions +// --------------------------------------------------------------------------- + +type ResizeDir = 'n' | 's' | 'e' | 'w' | 'ne' | 'nw' | 'se' | 'sw'; + +const EDGE_THICKNESS = 6; +const CORNER_SIZE = 14; + +const CURSOR_MAP: Record = { + n: 'ns-resize', + s: 'ns-resize', + e: 'ew-resize', + w: 'ew-resize', + nw: 'nwse-resize', + se: 'nwse-resize', + ne: 'nesw-resize', + sw: 'nesw-resize', +}; + +const HANDLE_DEFS: { dir: ResizeDir; sx: Record }[] = [ + { dir: 'n', sx: { top: -EDGE_THICKNESS / 2, left: CORNER_SIZE, right: CORNER_SIZE, height: EDGE_THICKNESS } }, + { dir: 's', sx: { bottom: -EDGE_THICKNESS / 2, left: CORNER_SIZE, right: CORNER_SIZE, height: EDGE_THICKNESS } }, + { dir: 'w', sx: { left: -EDGE_THICKNESS / 2, top: CORNER_SIZE, bottom: CORNER_SIZE, width: EDGE_THICKNESS } }, + { dir: 'e', sx: { right: -EDGE_THICKNESS / 2, top: CORNER_SIZE, bottom: CORNER_SIZE, width: EDGE_THICKNESS } }, + { dir: 'nw', sx: { top: -EDGE_THICKNESS / 2, left: -EDGE_THICKNESS / 2, width: CORNER_SIZE, height: CORNER_SIZE } }, + { dir: 'ne', sx: { top: -EDGE_THICKNESS / 2, right: -EDGE_THICKNESS / 2, width: CORNER_SIZE, height: CORNER_SIZE } }, + { dir: 'sw', sx: { bottom: -EDGE_THICKNESS / 2, left: -EDGE_THICKNESS / 2, width: CORNER_SIZE, height: CORNER_SIZE } }, + { dir: 'se', sx: { bottom: -EDGE_THICKNESS / 2, right: -EDGE_THICKNESS / 2, width: CORNER_SIZE, height: CORNER_SIZE } }, +]; + +// --------------------------------------------------------------------------- +// AgentCard +// --------------------------------------------------------------------------- + +interface Props { + session: AgentSession; + expanded: boolean; + cardX: number; + cardY: number; + cardWidth: number; + cardHeight: number; + zoom?: number; + spawnFrom?: { x: number; y: number }; +} + +const MIN_W = 480; +const MIN_H = 120; +const EXPANDED_OVERLAY_H = 620; + +const SPAWN_SPRING = { type: 'spring' as const, stiffness: 400, damping: 28, mass: 0.6 }; + +const AgentCard: React.FC = ({ session, expanded, cardX, cardY, cardWidth, cardHeight, zoom = 1, spawnFrom }) => { + const c = useClaudeTokens(); + const dispatch = useAppDispatch(); + + const STATUS_COLORS: Record = { + running: { color: c.status.success, bg: c.status.successBg }, + waiting_approval: { color: c.status.warning, bg: c.status.warningBg }, + completed: { color: c.text.tertiary, bg: c.bg.secondary }, + error: { color: c.status.error, bg: c.status.errorBg }, + stopped: { color: c.text.tertiary, bg: c.bg.secondary }, + draft: { color: c.accent.primary, bg: c.bg.secondary }, + }; + + const [, setTick] = useState(0); + const isDraft = session.status === 'draft'; + + // ---- Drag via header (pointer events) ---- + const DRAG_THRESHOLD = 3; + const dragState = useRef<{ startX: number; startY: number; origX: number; origY: number } | null>(null); + const [isDragging, setIsDragging] = useState(false); + const [localDragPos, setLocalDragPos] = useState<{ x: number; y: number } | null>(null); + const didDrag = useRef(false); + + const handleDragPointerDown = useCallback((e: React.PointerEvent) => { + e.preventDefault(); + e.stopPropagation(); + dragState.current = { startX: e.clientX, startY: e.clientY, origX: cardX, origY: cardY }; + didDrag.current = false; + setIsDragging(true); + (e.currentTarget as HTMLElement).setPointerCapture(e.pointerId); + }, [cardX, cardY]); + + const handleDragPointerMove = useCallback((e: React.PointerEvent) => { + if (!dragState.current) return; + const rawDx = e.clientX - dragState.current.startX; + const rawDy = e.clientY - dragState.current.startY; + if (!didDrag.current && Math.sqrt(rawDx * rawDx + rawDy * rawDy) < DRAG_THRESHOLD) return; + didDrag.current = true; + setLocalDragPos({ + x: dragState.current.origX + rawDx / zoom, + y: dragState.current.origY + rawDy / zoom, + }); + }, [zoom]); + + const handleDragPointerUp = useCallback((e: React.PointerEvent) => { + if (!dragState.current) return; + if (didDrag.current) { + const dx = (e.clientX - dragState.current.startX) / zoom; + const dy = (e.clientY - dragState.current.startY) / zoom; + dispatch(setCardPosition({ + sessionId: session.id, + x: dragState.current.origX + dx, + y: dragState.current.origY + dy, + })); + } else if (expanded) { + dispatch(toggleExpandSession(session.id)); + } + dragState.current = null; + didDrag.current = false; + setLocalDragPos(null); + setIsDragging(false); + (e.currentTarget as HTMLElement).releasePointerCapture(e.pointerId); + }, [zoom, dispatch, session.id, expanded]); + + // ---- Unified edge / corner resize ---- + const resizeRef = useRef<{ + dir: ResizeDir; + startX: number; + startY: number; + origX: number; + origY: number; + origW: number; + origH: number; + } | null>(null); + const [isResizing, setIsResizing] = useState(false); + const [localResize, setLocalResize] = useState<{ x: number; y: number; w: number; h: number } | null>(null); + + const handleResizeDown = useCallback( + (dir: ResizeDir) => (e: React.PointerEvent) => { + e.preventDefault(); + e.stopPropagation(); + const effectiveW = Math.max(cardWidth, MIN_W); + const effectiveH = expanded ? Math.max(EXPANDED_OVERLAY_H, cardHeight) : cardHeight; + resizeRef.current = { + dir, + startX: e.clientX, + startY: e.clientY, + origX: cardX, + origY: cardY, + origW: effectiveW, + origH: effectiveH, + }; + setIsResizing(true); + (e.target as HTMLElement).setPointerCapture(e.pointerId); + }, + [cardX, cardY, cardWidth, cardHeight, expanded], + ); + + const computeResize = useCallback( + (e: React.PointerEvent) => { + if (!resizeRef.current) return null; + const { dir, startX, startY, origX, origY, origW, origH } = resizeRef.current; + const dx = (e.clientX - startX) / zoom; + const dy = (e.clientY - startY) / zoom; + + let newX = origX, newY = origY, newW = origW, newH = origH; + + if (dir.includes('e')) newW = origW + dx; + if (dir.includes('w')) { newW = origW - dx; newX = origX + dx; } + if (dir.includes('s')) newH = origH + dy; + if (dir.includes('n')) { newH = origH - dy; newY = origY + dy; } + + if (newW < MIN_W) { if (dir.includes('w')) newX = origX + origW - MIN_W; newW = MIN_W; } + if (newH < MIN_H) { if (dir.includes('n')) newY = origY + origH - MIN_H; newH = MIN_H; } + + return { x: newX, y: newY, w: newW, h: newH }; + }, + [zoom], + ); + + const handleResizeMove = useCallback( + (e: React.PointerEvent) => { + const result = computeResize(e); + if (result) setLocalResize(result); + }, + [computeResize], + ); + + const handleResizeUp = useCallback((e: React.PointerEvent) => { + if (!resizeRef.current) return; + const result = computeResize(e); + if (result) { + dispatch(setCardPosition({ sessionId: session.id, x: result.x, y: result.y })); + dispatch(setCardSize({ sessionId: session.id, width: result.w, height: result.h })); + } + resizeRef.current = null; + setLocalResize(null); + setIsResizing(false); + (e.target as HTMLElement).releasePointerCapture(e.pointerId); + }, [computeResize, dispatch, session.id]); + + const handleRemove = (e: React.MouseEvent) => { + e.stopPropagation(); + e.preventDefault(); + dispatch(closeSession({ sessionId: session.id })); + }; + + const handleCollapse = (e: React.MouseEvent) => { + e.stopPropagation(); + e.preventDefault(); + dispatch(collapseSession(session.id)); + }; + + useEffect(() => { + if (session.status === 'running' || session.status === 'waiting_approval') { + const interval = setInterval(() => setTick((t) => t + 1), 1000); + return () => clearInterval(interval); + } + }, [session.status]); + + const lastMessage = session.messages[session.messages.length - 1]; + const isStreaming = !!session.streamingMessage; + const previewContent = isStreaming + ? (session.streamingMessage!.role === 'tool_call' + ? `[${getToolDisplayName(session.streamingMessage!.tool_name || '')}] ${session.streamingMessage!.content}` + : session.streamingMessage!.content + ).slice(0, 120) + : lastMessage && typeof lastMessage.content === 'string' + ? lastMessage.content.slice(0, 120) + : ''; + const hasPending = session.pending_approvals.length > 0; + const pendingReq = session.pending_approvals[0]; + const statusStyle = STATUS_COLORS[session.status] || { color: c.text.tertiary, bg: c.bg.secondary }; + + const noTransition = isDragging || isResizing; + + const activeX = localResize?.x ?? localDragPos?.x ?? cardX; + const activeY = localResize?.y ?? localDragPos?.y ?? cardY; + const activeW = localResize?.w ?? cardWidth; + const activeH = localResize?.h ?? cardHeight; + + return ( + + dispatch(toggleExpandSession(session.id))} + sx={{ + position: 'relative', + width: localResize ? activeW : Math.max(cardWidth, MIN_W), + height: localResize ? activeH : (expanded ? Math.max(EXPANDED_OVERLAY_H, cardHeight) : 'auto'), + bgcolor: c.bg.surface, + border: hasPending && !expanded + ? `1px solid ${c.status.warning}` + : expanded + ? `1px solid ${c.border.strong}` + : `1px solid ${c.border.subtle}`, + borderRadius: 3, + p: 2, + cursor: expanded ? 'default' : 'pointer', + transition: noTransition ? 'none' : c.transition, + boxShadow: isDragging ? c.shadow.lg : expanded ? c.shadow.md : c.shadow.sm, + display: 'flex', + flexDirection: 'column', + overflow: 'hidden', + ...(!expanded && !isDragging && { + '&:hover': { + boxShadow: c.shadow.md, + borderColor: hasPending ? c.status.warning : c.border.strong, + }, + }), + }} + > + {/* Resize handles: 4 edges + 4 corners */} + {HANDLE_DEFS.map(({ dir, sx }) => ( + e.stopPropagation()} + sx={{ + position: 'absolute', + ...sx, + cursor: CURSOR_MAP[dir], + zIndex: 20, + userSelect: 'none', + touchAction: 'none', + }} + /> + ))} + + {/* Header: always visible – entire bar is draggable */} + + + + + + + {session.name} + + + + e.stopPropagation()} + sx={{ display: 'flex', alignItems: 'center', gap: 0.5, flexShrink: 0, ml: 0.5 }} + > + {expanded ? ( + + e.stopPropagation()} + sx={{ + color: c.text.ghost, + p: 0.5, + '&:hover': { color: c.text.secondary, bgcolor: c.bg.secondary }, + }} + > + + + + ) : ( + + e.stopPropagation()} + sx={{ + color: c.text.ghost, + p: 0.5, + '&:hover': { color: c.status.error, bgcolor: `${c.status.errorBg}` }, + }} + > + + + + )} + + + + {/* Metadata row */} + + + {session.model} + + + {session.mode} + + + {formatDuration(session.created_at)} + + {session.cost_usd > 0 && ( + + ${session.cost_usd.toFixed(4)} + + )} + + + {/* Expanded: inline chat fills remaining space */} + {expanded && ( + e.stopPropagation()} + sx={{ + mx: -2, + mb: -2, + flex: 1, + minHeight: 0, + borderTop: `1px solid ${c.border.subtle}`, + display: 'flex', + flexDirection: 'column', + overflow: 'hidden', + }} + > + dispatch(collapseSession(session.id))} + embedded + /> + + )} + + {/* Collapsed: preview + approval */} + {!expanded && ( + <> + {previewContent && ( + + {isStreaming && ( + + )} + + {previewContent} + + + )} + + {hasPending && pendingReq && pendingReq.tool_name === 'AskUserQuestion' ? ( + e.stopPropagation()}> + + dispatch(handleApproval({ requestId, behavior: 'allow', updatedInput })) + } + onDeny={(requestId) => + dispatch(handleApproval({ requestId, behavior: 'deny' })) + } + /> + + ) : hasPending ? ( + e.stopPropagation()} sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}> + {pendingReq && ( + + + {(() => { + const mcp = parseMcpToolName(pendingReq.tool_name); + if (mcp.isMcp && mcp.service) return ; + return ; + })()} + + + {getToolDisplayName(pendingReq.tool_name)} + + + {summarizeToolInput(pendingReq.tool_name, pendingReq.tool_input)} + + + + {session.pending_approvals.length === 1 && ( + + + dispatch(handleApproval({ requestId: pendingReq.id, behavior: 'allow' }))} + sx={{ color: c.status.success }} + > + + + + + dispatch(handleApproval({ requestId: pendingReq.id, behavior: 'deny' }))} + sx={{ color: c.status.error }} + > + + + + + )} + + )} + {session.pending_approvals.length > 1 && ( + + + {session.pending_approvals.length} pending approvals + + + + + )} + + ) : null} + + )} + + + ); +}; + +export default AgentCard; diff --git a/frontend/src/app/pages/Dashboard/CanvasControls.tsx b/frontend/src/app/pages/Dashboard/CanvasControls.tsx new file mode 100644 index 00000000..1e667a1e --- /dev/null +++ b/frontend/src/app/pages/Dashboard/CanvasControls.tsx @@ -0,0 +1,87 @@ +import React from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import IconButton from '@mui/material/IconButton'; +import Tooltip from '@mui/material/Tooltip'; +import RemoveIcon from '@mui/icons-material/Remove'; +import AddIcon from '@mui/icons-material/Add'; +import FitScreenIcon from '@mui/icons-material/FitScreen'; +import AutoAwesomeIcon from '@mui/icons-material/AutoAwesome'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; +import type { CanvasActions } from './useCanvasControls'; + +interface Props { + zoom: number; + actions: CanvasActions; + onTidy: () => void; +} + +const CanvasControls: React.FC = ({ zoom, actions, onTidy }) => { + const c = useClaudeTokens(); + const pct = Math.round(zoom * 100); + + return ( + + + + + + + + + + {pct}% + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +export default CanvasControls; diff --git a/frontend/src/app/pages/Dashboard/CloseAgentDialog.tsx b/frontend/src/app/pages/Dashboard/CloseAgentDialog.tsx new file mode 100644 index 00000000..541dbf83 --- /dev/null +++ b/frontend/src/app/pages/Dashboard/CloseAgentDialog.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import Dialog from '@mui/material/Dialog'; +import DialogTitle from '@mui/material/DialogTitle'; +import DialogContent from '@mui/material/DialogContent'; +import DialogContentText from '@mui/material/DialogContentText'; +import DialogActions from '@mui/material/DialogActions'; +import Button from '@mui/material/Button'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; + +interface Props { + open: boolean; + onCancel: () => void; + onConfirm: () => void; +} + +const CloseAgentDialog: React.FC = ({ open, onCancel, onConfirm }) => { + const c = useClaudeTokens(); + return ( + + + Agent still running + + + + This agent is still running. Closing it will pause the agent. + You can resume it later from the chat history. + + + + + + + + ); +}; + +export default CloseAgentDialog; diff --git a/frontend/src/app/pages/Dashboard/Dashboard.tsx b/frontend/src/app/pages/Dashboard/Dashboard.tsx new file mode 100644 index 00000000..f724b338 --- /dev/null +++ b/frontend/src/app/pages/Dashboard/Dashboard.tsx @@ -0,0 +1,400 @@ +import React, { useEffect, useCallback, useRef, useState } from 'react'; +import { useParams } from 'react-router-dom'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import DashboardIcon from '@mui/icons-material/Dashboard'; +import { useAppDispatch, useAppSelector } from '@/shared/hooks'; +import { store } from '@/shared/state/store'; +import { + fetchSessions, + fetchHistory, + collapseAllSessions, + collapseSession, + launchAndSendFirstMessage, + generateTitle, + resumeSession, +} from '@/shared/state/agentsSlice'; +import type { AgentConfig } from '@/shared/state/agentsSlice'; +import { + fetchLayout, + saveLayout, + reconcileSessions, + tidyLayout, + addViewCard, + resetLayout, +} from '@/shared/state/dashboardLayoutSlice'; +import { fetchOutputs } from '@/shared/state/outputsSlice'; +import { dashboardWs } from '@/shared/ws/WebSocketManager'; +import AgentCard from './AgentCard'; +import DashboardViewCard from './DashboardViewCard'; +import CanvasControls from './CanvasControls'; +import DashboardToolbar from './DashboardToolbar'; +import { useCanvasControls } from './useCanvasControls'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; +import type { ContextPath } from '@/app/components/DirectoryBrowser'; +import { ElementSelectionProvider, useElementSelection } from '@/app/components/ElementSelectionContext'; +import { useDomElementSelector } from '@/app/components/useDomElementSelector'; +import SelectionOverlay from '@/app/components/SelectionOverlay'; + +const DashboardSelectionOverlay: React.FC = () => { + const { overlay, dragRect, dragPreview } = useDomElementSelector(); + return ; +}; + +const DashboardInner: React.FC = () => { + const c = useClaudeTokens(); + const dispatch = useAppDispatch(); + const elementSelection = useElementSelection(); + const { id: dashboardId } = useParams<{ id: string }>(); + const dashboardName = useAppSelector((state) => + dashboardId ? state.dashboards.items[dashboardId]?.name : undefined, + ); + const sessions = useAppSelector((state) => state.agents.sessions); + const expandedSessionIds = useAppSelector((state) => state.agents.expandedSessionIds); + const cards = useAppSelector((state) => state.dashboardLayout.cards); + const viewCards = useAppSelector((state) => state.dashboardLayout.viewCards); + const layoutInitialized = useAppSelector((state) => state.dashboardLayout.initialized); + const zoomSensitivity = useAppSelector((state) => state.settings.data.zoom_sensitivity); + const newAgentShortcut = useAppSelector((state) => state.settings.data.new_agent_shortcut); + const outputs = useAppSelector((state) => state.outputs.items); + const sessionList = Object.values(sessions); + + const selectModeActive = elementSelection?.selectMode ?? false; + const canvas = useCanvasControls(zoomSensitivity, selectModeActive); + const toolbarRef = useRef(null); + + const [toolbarOpen, setToolbarOpen] = useState(false); + const spawnOriginsRef = useRef>({}); + const hasFittedRef = useRef(false); + + useEffect(() => { + if (!dashboardId) return; + hasFittedRef.current = false; + dispatch(resetLayout()); + dispatch(fetchSessions({ dashboardId })); + dispatch(fetchHistory({ dashboardId })); + dispatch(fetchLayout(dashboardId)); + dispatch(fetchOutputs()); + dashboardWs.connect(); + return () => dashboardWs.disconnect(); + }, [dispatch, dashboardId]); + + useEffect(() => { + if (!layoutInitialized || hasFittedRef.current) return; + hasFittedRef.current = true; + const timer = setTimeout(() => canvas.actions.fitToView(), 150); + return () => clearTimeout(timer); + }, [layoutInitialized, canvas.actions]); + + const prevSessionIdsRef = useRef(''); + + useEffect(() => { + if (!layoutInitialized) return; + const dashboardSessionIds = Object.values(sessions) + .filter((s) => s.dashboard_id === dashboardId) + .map((s) => s.id); + const liveIds = dashboardSessionIds.sort().join(','); + if (liveIds === prevSessionIdsRef.current) return; + prevSessionIdsRef.current = liveIds; + dispatch(reconcileSessions(dashboardSessionIds)); + }, [sessions, layoutInitialized, dispatch, dashboardId]); + + const cardsJson = JSON.stringify(cards); + const viewCardsJson = JSON.stringify(viewCards); + const skipInitialSave = useRef(true); + useEffect(() => { + if (!layoutInitialized || !dashboardId) return; + if (skipInitialSave.current) { + skipInitialSave.current = false; + return; + } + dispatch(saveLayout({ dashboardId, cards, viewCards })); + }, [cardsJson, viewCardsJson, layoutInitialized, dashboardId]); + + useEffect(() => { + const parts = newAgentShortcut.toLowerCase().split('+'); + const key = parts[parts.length - 1]; + const needsMeta = parts.includes('meta'); + const needsCtrl = parts.includes('ctrl'); + const needsShift = parts.includes('shift'); + const needsAlt = parts.includes('alt'); + + const handleShortcut = (e: KeyboardEvent) => { + if (e.key.toLowerCase() !== key) return; + if (needsMeta !== e.metaKey) return; + if (needsCtrl !== e.ctrlKey) return; + if (needsShift !== e.shiftKey) return; + if (needsAlt !== e.altKey) return; + e.preventDefault(); + setToolbarOpen(true); + }; + window.addEventListener('keydown', handleShortcut); + return () => window.removeEventListener('keydown', handleShortcut); + }, [newAgentShortcut]); + + const handleNewAgent = useCallback(() => { + setToolbarOpen(true); + }, []); + + const handleToolbarCancel = useCallback(() => { + setToolbarOpen(false); + }, []); + + const handleToolbarSend = useCallback( + ( + prompt: string, + mode: string, + model: string, + images?: Array<{ data: string; media_type: string }>, + contextPaths?: ContextPath[], + forcedTools?: string[], + attachedSkills?: Array<{ id: string; name: string; content: string }>, + ) => { + setToolbarOpen(false); + + const draftId = `draft-${Date.now().toString(36)}`; + + const toolbarEl = toolbarRef.current; + const vpEl = canvas.viewportRef.current; + if (toolbarEl && vpEl) { + const tr = toolbarEl.getBoundingClientRect(); + const vr = vpEl.getBoundingClientRect(); + const toolbarCenterX = tr.left + tr.width / 2; + const toolbarTopY = tr.top; + spawnOriginsRef.current[draftId] = { + x: (toolbarCenterX - vr.left - canvas.panX) / canvas.zoom, + y: (toolbarTopY - vr.top - canvas.panY) / canvas.zoom, + }; + } + + const config: AgentConfig = { name: 'New chat', model, mode, dashboard_id: dashboardId }; + + dispatch( + launchAndSendFirstMessage({ + draftId, + config, + prompt, + mode, + model, + images, + contextPaths: contextPaths?.map((cp) => ({ path: cp.path, type: cp.type })), + forcedTools, + attachedSkills, + expand: false, + }), + ).then((action) => { + if (launchAndSendFirstMessage.fulfilled.match(action)) { + const realId = action.payload.session.id; + dispatch(generateTitle({ sessionId: realId, prompt })); + spawnOriginsRef.current[realId] = spawnOriginsRef.current[draftId]; + delete spawnOriginsRef.current[draftId]; + } else { + delete spawnOriginsRef.current[draftId]; + } + }); + }, + [canvas.zoom, canvas.panX, canvas.panY, canvas.viewportRef, dispatch, dashboardId], + ); + + const handleAddView = useCallback((outputId: string) => { + dispatch(addViewCard({ outputId })); + }, [dispatch]); + + const handleHistoryResume = useCallback((sessionId: string) => { + dispatch(resumeSession({ sessionId })).then((action) => { + if (resumeSession.fulfilled.match(action)) { + dispatch(collapseSession(sessionId)); + } + }); + }, [dispatch]); + + const handleTidy = useCallback(() => { + dispatch(collapseAllSessions()); + dispatch(tidyLayout()); + + const { cards: tidied, viewCards: tidiedViews } = store.getState().dashboardLayout; + const allRects = [ + ...Object.values(tidied).map((c) => ({ x: c.x, y: c.y, width: c.width, height: c.height })), + ...Object.values(tidiedViews).map((c) => ({ x: c.x, y: c.y, width: c.width, height: c.height })), + ]; + canvas.actions.fitToCards(allRects); + }, [dispatch, canvas.actions]); + + const nonDraftCount = sessionList.filter((s) => s.status !== 'draft' && s.dashboard_id === dashboardId).length; + + const dotSize = Math.max(1, 1.5 * canvas.zoom); + const dotSpacing = 24 * canvas.zoom; + + return ( + <> + + + {/* Floating header overlay */} + + + + + + {dashboardName || 'Dashboard'} + · + + {nonDraftCount} agent{nonDraftCount !== 1 ? 's' : ''} running + + + + + + + {/* Canvas viewport */} + + {/* Dot grid background */} + + + {sessionList.length === 0 && Object.keys(viewCards).length === 0 ? ( + + + No agents running + + + Click "New Agent" to launch your first Claude Code instance + + + ) : ( +
+ {Object.values(cards).map((card) => { + const session = sessions[card.session_id]; + if (!session) return null; + const origin = spawnOriginsRef.current[session.id]; + if (origin) delete spawnOriginsRef.current[session.id]; + return ( + + ); + })} + {Object.values(viewCards).map((vc) => { + const output = outputs[vc.output_id]; + if (!output) return null; + return ( + + ); + })} +
+ )} +
+ + {/* Floating bottom toolbar */} + + + + + {/* Floating zoom controls */} + + + +
+ + ); +}; + +const Dashboard: React.FC = () => ( + + + +); + +export default Dashboard; diff --git a/frontend/src/app/pages/Dashboard/DashboardToolbar.tsx b/frontend/src/app/pages/Dashboard/DashboardToolbar.tsx new file mode 100644 index 00000000..9d45a827 --- /dev/null +++ b/frontend/src/app/pages/Dashboard/DashboardToolbar.tsx @@ -0,0 +1,639 @@ +import React, { useState, useRef, useEffect, useCallback, useMemo } from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import InputBase from '@mui/material/InputBase'; +import CircularProgress from '@mui/material/CircularProgress'; +import Tooltip, { tooltipClasses } from '@mui/material/Tooltip'; +import Icon from '@mui/material/Icon'; +import { styled } from '@mui/material/styles'; +import AddIcon from '@mui/icons-material/Add'; +import GridViewRoundedIcon from '@mui/icons-material/GridViewRounded'; +import StickyNote2OutlinedIcon from '@mui/icons-material/StickyNote2Outlined'; +import HistoryRoundedIcon from '@mui/icons-material/HistoryRounded'; +import SearchIcon from '@mui/icons-material/Search'; +import { motion } from 'framer-motion'; +import ChatInput from '@/app/pages/AgentChat/ChatInput'; +import type { ContextPath } from '@/app/components/DirectoryBrowser'; +import { useElementSelection } from '@/app/components/ElementSelectionContext'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; +import { useAppDispatch, useAppSelector } from '@/shared/hooks'; +import { searchHistory, clearHistorySearch } from '@/shared/state/agentsSlice'; +import type { ClaudeTokens } from '@/shared/styles/claudeTokens'; +import type { Output } from '@/shared/state/outputsSlice'; + +interface Props { + inputOpen: boolean; + onNewAgent: () => void; + onCancel: () => void; + onSend: ( + prompt: string, + mode: string, + model: string, + images?: Array<{ data: string; media_type: string }>, + contextPaths?: ContextPath[], + forcedTools?: string[], + attachedSkills?: Array<{ id: string; name: string; content: string }>, + ) => void; + onAddView: (outputId: string) => void; + onHistoryResume: (sessionId: string) => void; + dashboardId?: string; +} + +const BTN = 40; + +const WarmTooltip = styled( + ({ className, ...props }: React.ComponentProps & { className?: string }) => ( + + ) +)<{ tokens: ClaudeTokens }>(({ tokens: c }) => ({ + [`& .${tooltipClasses.tooltip}`]: { + backgroundColor: c.bg.inverse, + color: c.text.inverse, + fontFamily: c.font.sans, + fontSize: '0.78rem', + fontWeight: 500, + padding: '6px 12px', + borderRadius: c.radius.md, + boxShadow: c.shadow.md, + letterSpacing: '0.01em', + }, + [`& .${tooltipClasses.arrow}`]: { + color: c.bg.inverse, + }, +})); + +const MotionBox = motion.div; + +const HISTORY_PAGE_SIZE = 20; + +function formatRelativeTime(dateStr: string | null): string { + if (!dateStr) return ''; + const seconds = Math.floor((Date.now() - new Date(dateStr).getTime()) / 1000); + if (seconds < 60) return 'just now'; + const minutes = Math.floor(seconds / 60); + if (minutes < 60) return `${minutes}m ago`; + const hours = Math.floor(minutes / 60); + if (hours < 24) return `${hours}h ago`; + const days = Math.floor(hours / 24); + return `${days}d ago`; +} + +const DashboardToolbar = React.forwardRef( + ({ inputOpen, onNewAgent, onCancel, onSend, onAddView, onHistoryResume, dashboardId }, ref) => { + const c = useClaudeTokens(); + const dispatch = useAppDispatch(); + const elementSelection = useElementSelection(); + const containerRef = useRef(null); + const searchInputRef = useRef(null); + const historyInputRef = useRef(null); + const historyListRef = useRef(null); + const [mode, setMode] = useState('agent'); + const [model, setModel] = useState('sonnet'); + const [viewPickerOpen, setViewPickerOpen] = useState(false); + const [viewSearch, setViewSearch] = useState(''); + const [historyOpen, setHistoryOpen] = useState(false); + const [historyQuery, setHistoryQuery] = useState(''); + const shortcut = useAppSelector((s) => s.settings.data.new_agent_shortcut); + const outputs = useAppSelector((s) => s.outputs.items); + const historySearch = useAppSelector((s) => s.agents.historySearch); + + const outputList = useMemo(() => Object.values(outputs), [outputs]); + const filteredOutputs = useMemo(() => { + if (!viewSearch.trim()) return outputList; + const q = viewSearch.toLowerCase(); + return outputList.filter( + (o) => o.name.toLowerCase().includes(q) || o.description.toLowerCase().includes(q), + ); + }, [outputList, viewSearch]); + + const shortcutLabel = shortcut + .split('+') + .map((p) => { + if (p === 'Meta') return '⌘'; + if (p === 'Ctrl') return 'Ctrl'; + if (p === 'Alt') return '⌥'; + if (p === 'Shift') return '⇧'; + return p.toUpperCase(); + }) + .join(''); + + React.useImperativeHandle(ref, () => containerRef.current!, []); + + const handleSend = useCallback( + ( + message: string, + images?: Array<{ data: string; media_type: string }>, + contextPaths?: ContextPath[], + forcedTools?: string[], + attachedSkills?: Array<{ id: string; name: string; content: string }>, + ) => { + onSend(message, mode, model, images, contextPaths, forcedTools, attachedSkills); + }, + [onSend, mode, model], + ); + + const handleCloseHistory = useCallback(() => { + setHistoryOpen(false); + setHistoryQuery(''); + dispatch(clearHistorySearch()); + }, [dispatch]); + + const handleDismiss = useCallback(() => { + if (historyOpen) { + handleCloseHistory(); + } else if (viewPickerOpen) { + setViewPickerOpen(false); + setViewSearch(''); + } else { + onCancel(); + } + }, [historyOpen, viewPickerOpen, onCancel, handleCloseHistory]); + + const handleSelectView = useCallback((output: Output) => { + onAddView(output.id); + setViewPickerOpen(false); + setViewSearch(''); + }, [onAddView]); + + const handleOpenViewPicker = useCallback(() => { + setViewPickerOpen(true); + setViewSearch(''); + }, []); + + const handleOpenHistory = useCallback(() => { + setHistoryOpen(true); + setHistoryQuery(''); + dispatch(clearHistorySearch()); + dispatch(searchHistory({ q: '', limit: HISTORY_PAGE_SIZE, offset: 0, dashboardId })); + }, [dispatch, dashboardId]); + + const handleHistorySelect = useCallback((sessionId: string) => { + onHistoryResume(sessionId); + handleCloseHistory(); + }, [onHistoryResume, handleCloseHistory]); + + const handleHistoryLoadMore = useCallback(() => { + if (historySearch.loading || !historySearch.hasMore) return; + dispatch(searchHistory({ + q: historyQuery, + limit: HISTORY_PAGE_SIZE, + offset: historySearch.results.length, + dashboardId, + })); + }, [dispatch, historyQuery, historySearch.loading, historySearch.hasMore, historySearch.results.length, dashboardId]); + + const isExpanded = inputOpen || viewPickerOpen || historyOpen; + + useEffect(() => { + if (!inputOpen && elementSelection?.selectMode) { + elementSelection.setSelectMode(false); + } + }, [inputOpen, elementSelection]); + + useEffect(() => { + if (!isExpanded) return; + const handleKey = (e: KeyboardEvent) => { + if (e.key === 'Escape') { + e.preventDefault(); + handleDismiss(); + } + }; + window.addEventListener('keydown', handleKey); + return () => window.removeEventListener('keydown', handleKey); + }, [isExpanded, handleDismiss]); + + useEffect(() => { + if (!isExpanded) return; + const handleClick = (e: MouseEvent) => { + if (elementSelection?.selectMode) return; + const target = e.target as Node; + if (containerRef.current && !containerRef.current.contains(target)) { + const el = target instanceof Element ? target : target.parentElement; + if (el?.closest('[role="dialog"], [role="presentation"], .MuiModal-root, .MuiPopover-root')) { + return; + } + handleDismiss(); + } + }; + const t = setTimeout(() => document.addEventListener('mousedown', handleClick), 50); + return () => { + clearTimeout(t); + document.removeEventListener('mousedown', handleClick); + }; + }, [isExpanded, handleDismiss, elementSelection?.selectMode]); + + useEffect(() => { + if (viewPickerOpen) { + setTimeout(() => searchInputRef.current?.focus(), 60); + } + }, [viewPickerOpen]); + + useEffect(() => { + if (historyOpen) { + setTimeout(() => historyInputRef.current?.focus(), 60); + } + }, [historyOpen]); + + useEffect(() => { + if (!historyOpen) return; + const timer = setTimeout(() => { + dispatch(searchHistory({ q: historyQuery, limit: HISTORY_PAGE_SIZE, offset: 0, dashboardId })); + }, 300); + return () => clearTimeout(timer); + }, [historyQuery, historyOpen, dispatch, dashboardId]); + + const handleHistoryScroll = useCallback(() => { + const el = historyListRef.current; + if (!el) return; + if (el.scrollTop + el.clientHeight >= el.scrollHeight - 40) { + handleHistoryLoadMore(); + } + }, [handleHistoryLoadMore]); + + const placeholderItems = [ + { icon: StickyNote2OutlinedIcon, label: 'Add Notes', sub: 'Coming soon' }, + ]; + + return ( + + {inputOpen ? ( +
+ +
+ ) : historyOpen ? ( +
+ + + setHistoryQuery(e.target.value)} + placeholder="Search past chats..." + sx={{ + flex: 1, + fontSize: '0.85rem', + color: c.text.primary, + fontFamily: c.font.sans, + '& input::placeholder': { color: c.text.ghost, opacity: 1 }, + }} + /> + {historySearch.loading && historySearch.results.length === 0 && ( + + )} + + + {historySearch.results.length === 0 && !historySearch.loading ? ( + + + {historyQuery ? 'No matching chats' : 'No chat history yet'} + + + ) : ( + <> + {historySearch.results.map((entry) => ( + handleHistorySelect(entry.id)} + sx={{ + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + gap: 1.5, + px: 1.5, + py: 0.9, + cursor: 'pointer', + transition: 'background-color 0.1s', + '&:hover': { bgcolor: c.bg.elevated }, + }} + > + + {entry.name} + + + {formatRelativeTime(entry.closed_at)} + + + ))} + {historySearch.loading && historySearch.results.length > 0 && ( + + + + )} + + )} + +
+ ) : viewPickerOpen ? ( +
+ + + setViewSearch(e.target.value)} + placeholder="Search views..." + sx={{ + flex: 1, + fontSize: '0.85rem', + color: c.text.primary, + fontFamily: c.font.sans, + '& input::placeholder': { color: c.text.ghost, opacity: 1 }, + }} + /> + + + {filteredOutputs.length === 0 ? ( + + + {outputList.length === 0 ? 'No views created yet' : 'No matching views'} + + + ) : ( + filteredOutputs.map((output) => ( + handleSelectView(output)} + sx={{ + display: 'flex', + alignItems: 'center', + gap: 1.5, + px: 1.5, + py: 1, + cursor: 'pointer', + transition: 'background-color 0.1s', + '&:hover': { bgcolor: c.bg.elevated }, + }} + > + {output.thumbnail ? ( + + ) : ( + + + {output.icon || 'view_quilt'} + + + )} + + + {output.name} + + {output.description && ( + + {output.description} + + )} + + + )) + )} + +
+ ) : ( +
+ + + + + + + + + + Add View + + } + > + + + + + + + History + + } + > + + + + + + {placeholderItems.map(({ icon: PlaceholderIcon, label, sub }) => ( + + {label} + {sub} + + } + > + + + + + ))} +
+ )} +
+ ); + }, +); + +DashboardToolbar.displayName = 'DashboardToolbar'; + +export default DashboardToolbar; diff --git a/frontend/src/app/pages/Dashboard/DashboardViewCard.tsx b/frontend/src/app/pages/Dashboard/DashboardViewCard.tsx new file mode 100644 index 00000000..5ce8df47 --- /dev/null +++ b/frontend/src/app/pages/Dashboard/DashboardViewCard.tsx @@ -0,0 +1,360 @@ +import React, { useState, useRef, useCallback, useEffect } from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import IconButton from '@mui/material/IconButton'; +import Tooltip from '@mui/material/Tooltip'; +import CircularProgress from '@mui/material/CircularProgress'; +import RefreshIcon from '@mui/icons-material/Refresh'; +import BoltIcon from '@mui/icons-material/Bolt'; +import CloseIcon from '@mui/icons-material/Close'; +import GridViewRoundedIcon from '@mui/icons-material/GridViewRounded'; +import { Output, autoRunOutput, autoRunAgentOutput, executeOutput, OutputExecuteResult, getBackendCode } from '@/shared/state/outputsSlice'; +import { setViewCardPosition, setViewCardSize, removeViewCard } from '@/shared/state/dashboardLayoutSlice'; +import { useAppDispatch } from '@/shared/hooks'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; +import ViewPreview, { ViewPreviewHandle } from '@/app/pages/Views/ViewPreview'; +import { getDefault } from '@/app/pages/Views/InputSchemaForm'; + +type ResizeDir = 'n' | 's' | 'e' | 'w' | 'ne' | 'nw' | 'se' | 'sw'; + +const EDGE_THICKNESS = 6; +const CORNER_SIZE = 14; +const MIN_W = 320; +const MIN_H = 200; + +const CURSOR_MAP: Record = { + n: 'ns-resize', s: 'ns-resize', e: 'ew-resize', w: 'ew-resize', + nw: 'nwse-resize', se: 'nwse-resize', ne: 'nesw-resize', sw: 'nesw-resize', +}; + +const HANDLE_DEFS: { dir: ResizeDir; sx: Record }[] = [ + { dir: 'n', sx: { top: -EDGE_THICKNESS / 2, left: CORNER_SIZE, right: CORNER_SIZE, height: EDGE_THICKNESS } }, + { dir: 's', sx: { bottom: -EDGE_THICKNESS / 2, left: CORNER_SIZE, right: CORNER_SIZE, height: EDGE_THICKNESS } }, + { dir: 'w', sx: { left: -EDGE_THICKNESS / 2, top: CORNER_SIZE, bottom: CORNER_SIZE, width: EDGE_THICKNESS } }, + { dir: 'e', sx: { right: -EDGE_THICKNESS / 2, top: CORNER_SIZE, bottom: CORNER_SIZE, width: EDGE_THICKNESS } }, + { dir: 'nw', sx: { top: -EDGE_THICKNESS / 2, left: -EDGE_THICKNESS / 2, width: CORNER_SIZE, height: CORNER_SIZE } }, + { dir: 'ne', sx: { top: -EDGE_THICKNESS / 2, right: -EDGE_THICKNESS / 2, width: CORNER_SIZE, height: CORNER_SIZE } }, + { dir: 'sw', sx: { bottom: -EDGE_THICKNESS / 2, left: -EDGE_THICKNESS / 2, width: CORNER_SIZE, height: CORNER_SIZE } }, + { dir: 'se', sx: { bottom: -EDGE_THICKNESS / 2, right: -EDGE_THICKNESS / 2, width: CORNER_SIZE, height: CORNER_SIZE } }, +]; + +interface Props { + output: Output; + cardX: number; + cardY: number; + cardWidth: number; + cardHeight: number; + zoom?: number; +} + +const DashboardViewCard: React.FC = ({ output, cardX, cardY, cardWidth, cardHeight, zoom = 1 }) => { + const c = useClaudeTokens(); + const dispatch = useAppDispatch(); + const previewRef = useRef(null); + + const [inputData, setInputData] = useState>(() => getDefault(output.input_schema)); + const [backendResult, setBackendResult] = useState | null>(null); + const [autoRunning, setAutoRunning] = useState(false); + + const hasAutoRun = !!(output.auto_run_config?.enabled && output.auto_run_config?.prompt); + + // ---- Drag via header ---- + const DRAG_THRESHOLD = 3; + const dragState = useRef<{ startX: number; startY: number; origX: number; origY: number } | null>(null); + const [isDragging, setIsDragging] = useState(false); + const [localDragPos, setLocalDragPos] = useState<{ x: number; y: number } | null>(null); + const didDrag = useRef(false); + + const handleDragPointerDown = useCallback((e: React.PointerEvent) => { + e.preventDefault(); + e.stopPropagation(); + dragState.current = { startX: e.clientX, startY: e.clientY, origX: cardX, origY: cardY }; + didDrag.current = false; + setIsDragging(true); + (e.currentTarget as HTMLElement).setPointerCapture(e.pointerId); + }, [cardX, cardY]); + + const handleDragPointerMove = useCallback((e: React.PointerEvent) => { + if (!dragState.current) return; + const rawDx = e.clientX - dragState.current.startX; + const rawDy = e.clientY - dragState.current.startY; + if (!didDrag.current && Math.sqrt(rawDx * rawDx + rawDy * rawDy) < DRAG_THRESHOLD) return; + didDrag.current = true; + setLocalDragPos({ + x: dragState.current.origX + rawDx / zoom, + y: dragState.current.origY + rawDy / zoom, + }); + }, [zoom]); + + const handleDragPointerUp = useCallback((e: React.PointerEvent) => { + if (!dragState.current) return; + if (didDrag.current) { + const dx = (e.clientX - dragState.current.startX) / zoom; + const dy = (e.clientY - dragState.current.startY) / zoom; + dispatch(setViewCardPosition({ + outputId: output.id, + x: dragState.current.origX + dx, + y: dragState.current.origY + dy, + })); + } + dragState.current = null; + didDrag.current = false; + setLocalDragPos(null); + setIsDragging(false); + (e.currentTarget as HTMLElement).releasePointerCapture(e.pointerId); + }, [zoom, dispatch, output.id]); + + // ---- Resize ---- + const resizeRef = useRef<{ + dir: ResizeDir; startX: number; startY: number; + origX: number; origY: number; origW: number; origH: number; + } | null>(null); + const [isResizing, setIsResizing] = useState(false); + const [localResize, setLocalResize] = useState<{ x: number; y: number; w: number; h: number } | null>(null); + + const handleResizeDown = useCallback( + (dir: ResizeDir) => (e: React.PointerEvent) => { + e.preventDefault(); + e.stopPropagation(); + resizeRef.current = { + dir, startX: e.clientX, startY: e.clientY, + origX: cardX, origY: cardY, origW: cardWidth, origH: cardHeight, + }; + setIsResizing(true); + (e.target as HTMLElement).setPointerCapture(e.pointerId); + }, + [cardX, cardY, cardWidth, cardHeight], + ); + + const computeResize = useCallback( + (e: React.PointerEvent) => { + if (!resizeRef.current) return null; + const { dir, startX, startY, origX, origY, origW, origH } = resizeRef.current; + const dx = (e.clientX - startX) / zoom; + const dy = (e.clientY - startY) / zoom; + let newX = origX, newY = origY, newW = origW, newH = origH; + if (dir.includes('e')) newW = origW + dx; + if (dir.includes('w')) { newW = origW - dx; newX = origX + dx; } + if (dir.includes('s')) newH = origH + dy; + if (dir.includes('n')) { newH = origH - dy; newY = origY + dy; } + if (newW < MIN_W) { if (dir.includes('w')) newX = origX + origW - MIN_W; newW = MIN_W; } + if (newH < MIN_H) { if (dir.includes('n')) newY = origY + origH - MIN_H; newH = MIN_H; } + return { x: newX, y: newY, w: newW, h: newH }; + }, + [zoom], + ); + + const handleResizeMove = useCallback( + (e: React.PointerEvent) => { + const result = computeResize(e); + if (result) setLocalResize(result); + }, + [computeResize], + ); + + const handleResizeUp = useCallback((e: React.PointerEvent) => { + if (!resizeRef.current) return; + const result = computeResize(e); + if (result) { + dispatch(setViewCardPosition({ outputId: output.id, x: result.x, y: result.y })); + dispatch(setViewCardSize({ outputId: output.id, width: result.w, height: result.h })); + } + resizeRef.current = null; + setLocalResize(null); + setIsResizing(false); + (e.target as HTMLElement).releasePointerCapture(e.pointerId); + }, [computeResize, dispatch, output.id]); + + const handleRemove = (e: React.MouseEvent) => { + e.stopPropagation(); + dispatch(removeViewCard(output.id)); + }; + + const handleRefresh = (e: React.MouseEvent) => { + e.stopPropagation(); + previewRef.current?.reload(); + }; + + const handleAutoRun = async (e: React.MouseEvent) => { + e.stopPropagation(); + if (!output.auto_run_config?.prompt) return; + setAutoRunning(true); + + const config = output.auto_run_config; + const forcedToolNames = config.forced_tools?.flatMap((ft) => ft.tools) ?? []; + + try { + if (forcedToolNames.length > 0) { + const res = await dispatch(autoRunAgentOutput({ + prompt: config.prompt, + input_schema: output.input_schema, + output_id: output.id, + model: config.model, + forced_tools: forcedToolNames, + context_paths: config.context_paths, + })).unwrap(); + + // For agent-based auto-run, we execute with default input for now + // since the agent session result flow is complex for dashboard cards + const execRes = await dispatch(executeOutput({ + output_id: output.id, + input_data: inputData, + })).unwrap(); + setInputData(execRes.input_data); + setBackendResult(execRes.backend_result); + } else { + const res = await dispatch(autoRunOutput({ + prompt: config.prompt, + input_schema: output.input_schema, + backend_code: getBackendCode(output) ?? undefined, + context_paths: config.context_paths, + forced_tools: forcedToolNames.length > 0 ? forcedToolNames : undefined, + model: config.model, + })).unwrap(); + if (res.input_data) { + setInputData(res.input_data); + setBackendResult(res.backend_result); + } + } + } catch { + // Silently handle errors on dashboard + } finally { + setAutoRunning(false); + } + }; + + // Compute display position (prefer local drag/resize during interaction) + const displayX = localResize?.x ?? localDragPos?.x ?? cardX; + const displayY = localResize?.y ?? localDragPos?.y ?? cardY; + const displayW = localResize?.w ?? cardWidth; + const displayH = localResize?.h ?? cardHeight; + + return ( + + {/* Header */} + + + + {output.name} + + + + e.stopPropagation()} + sx={{ color: c.text.muted, p: 0.5, '&:hover': { color: c.text.primary } }} + > + + + + + {hasAutoRun && ( + + + e.stopPropagation()} + disabled={autoRunning} + sx={{ color: '#f59e0b', p: 0.5, '&:hover': { color: '#d97706' } }} + > + {autoRunning ? : } + + + + )} + + + e.stopPropagation()} + sx={{ color: c.text.ghost, p: 0.5, '&:hover': { color: c.status.error } }} + > + + + + + + {/* Preview body */} + + + + + {/* Resize handles */} + {HANDLE_DEFS.map(({ dir, sx }) => ( + + ))} + + ); +}; + +export default DashboardViewCard; diff --git a/frontend/src/app/pages/Dashboard/useCanvasControls.ts b/frontend/src/app/pages/Dashboard/useCanvasControls.ts new file mode 100644 index 00000000..68a333a2 --- /dev/null +++ b/frontend/src/app/pages/Dashboard/useCanvasControls.ts @@ -0,0 +1,323 @@ +import { useState, useCallback, useRef, useEffect, RefObject } from 'react'; + +const MIN_ZOOM = 0.15; +const MAX_ZOOM = 3.0; +const ZOOM_IN_FACTOR = 1.1; +const ZOOM_OUT_FACTOR = 1 / ZOOM_IN_FACTOR; +const FIT_PADDING = 200; + +// Maps the 1–100 user setting to an internal multiplier. +// 50 (default) → 0.004, 1 → 0.0004, 100 → 0.008 +function sensitivityToMultiplier(setting: number): number { + return 0.00008 * setting; +} + +interface CanvasState { + panX: number; + panY: number; + zoom: number; +} + +function clamp(val: number, min: number, max: number) { + return Math.min(max, Math.max(min, val)); +} + +export function useCanvasControls(zoomSensitivity: number = 50, panDisabled: boolean = false) { + const viewportRef = useRef(null); + const contentRef = useRef(null); + + const [state, setState] = useState({ panX: 0, panY: 0, zoom: 1 }); + const [isPanning, setIsPanning] = useState(false); + const [spaceHeld, setSpaceHeld] = useState(false); + + const panStartRef = useRef<{ x: number; y: number; panX: number; panY: number } | null>(null); + const spaceRef = useRef(false); + const sensitivityRef = useRef(zoomSensitivity); + sensitivityRef.current = zoomSensitivity; + + // Wheel zoom centered on cursor + useEffect(() => { + const el = viewportRef.current; + if (!el) return; + + const onWheel = (e: WheelEvent) => { + // Pinch-to-zoom on trackpads sets ctrlKey; plain scroll does not + const isPinchZoom = e.ctrlKey || e.metaKey; + + // Let scrollable children handle the event when appropriate + let target = e.target as HTMLElement | null; + while (target && target !== el) { + const style = getComputedStyle(target); + const overflowY = style.overflowY; + const overflowX = style.overflowX; + + const canScrollY = + target.scrollHeight > target.clientHeight && + (overflowY === 'auto' || overflowY === 'scroll'); + const canScrollX = + target.scrollWidth > target.clientWidth && + (overflowX === 'auto' || overflowX === 'scroll'); + + if ((canScrollY || canScrollX) && !isPinchZoom) { + return; + } + target = target.parentElement; + } + + e.preventDefault(); + + if (isPinchZoom) { + // Pinch gesture → zoom centered on cursor + const rect = el.getBoundingClientRect(); + const cx = e.clientX - rect.left; + const cy = e.clientY - rect.top; + + setState((prev) => { + const delta = e.deltaMode === 1 ? e.deltaY * 40 : e.deltaY; + const factor = Math.pow(2, -delta * sensitivityToMultiplier(sensitivityRef.current)); + const newZoom = clamp(prev.zoom * factor, MIN_ZOOM, MAX_ZOOM); + const ratio = newZoom / prev.zoom; + return { + panX: cx - (cx - prev.panX) * ratio, + panY: cy - (cy - prev.panY) * ratio, + zoom: newZoom, + }; + }); + } else { + // Two-finger scroll → pan + const dx = e.deltaMode === 1 ? e.deltaX * 40 : e.deltaX; + const dy = e.deltaMode === 1 ? e.deltaY * 40 : e.deltaY; + + setState((prev) => ({ + ...prev, + panX: prev.panX - dx, + panY: prev.panY - dy, + })); + } + }; + + el.addEventListener('wheel', onWheel, { passive: false }); + return () => el.removeEventListener('wheel', onWheel); + }, []); + + // Space key tracking + useEffect(() => { + const onKeyDown = (e: KeyboardEvent) => { + const t = e.target as HTMLElement; + if (e.code === 'Space' && !e.repeat && !(t instanceof HTMLInputElement || t instanceof HTMLTextAreaElement || t.isContentEditable)) { + e.preventDefault(); + spaceRef.current = true; + setSpaceHeld(true); + } + if (e.ctrlKey || e.metaKey) { + if (e.key === '0') { + e.preventDefault(); + setState({ panX: 0, panY: 0, zoom: 1 }); + } else if (e.key === '=' || e.key === '+') { + e.preventDefault(); + setState((prev) => { + const newZoom = clamp(prev.zoom * ZOOM_IN_FACTOR, MIN_ZOOM, MAX_ZOOM); + const el = viewportRef.current; + if (!el) return { ...prev, zoom: newZoom }; + const rect = el.getBoundingClientRect(); + const cx = rect.width / 2; + const cy = rect.height / 2; + const ratio = newZoom / prev.zoom; + return { panX: cx - (cx - prev.panX) * ratio, panY: cy - (cy - prev.panY) * ratio, zoom: newZoom }; + }); + } else if (e.key === '-') { + e.preventDefault(); + setState((prev) => { + const newZoom = clamp(prev.zoom * ZOOM_OUT_FACTOR, MIN_ZOOM, MAX_ZOOM); + const el = viewportRef.current; + if (!el) return { ...prev, zoom: newZoom }; + const rect = el.getBoundingClientRect(); + const cx = rect.width / 2; + const cy = rect.height / 2; + const ratio = newZoom / prev.zoom; + return { panX: cx - (cx - prev.panX) * ratio, panY: cy - (cy - prev.panY) * ratio, zoom: newZoom }; + }); + } + } + }; + const onKeyUp = (e: KeyboardEvent) => { + if (e.code === 'Space') { + spaceRef.current = false; + setSpaceHeld(false); + } + }; + + window.addEventListener('keydown', onKeyDown); + window.addEventListener('keyup', onKeyUp); + return () => { + window.removeEventListener('keydown', onKeyDown); + window.removeEventListener('keyup', onKeyUp); + }; + }, []); + + const handleMouseDown = useCallback((e: React.MouseEvent) => { + const isMiddle = e.button === 1; + const isBackgroundLeft = e.button === 0 && e.target === viewportRef.current; + const isSpaceDrag = e.button === 0 && spaceRef.current; + + if (panDisabled && !isMiddle && !isSpaceDrag) return; + + if (isMiddle || isBackgroundLeft || isSpaceDrag) { + e.preventDefault(); + setIsPanning(true); + panStartRef.current = { + x: e.clientX, + y: e.clientY, + panX: state.panX, + panY: state.panY, + }; + } + }, [state.panX, state.panY, panDisabled]); + + const handleMouseMove = useCallback((e: React.MouseEvent) => { + const start = panStartRef.current; + if (!start) return; + const dx = e.clientX - start.x; + const dy = e.clientY - start.y; + setState((prev) => ({ + ...prev, + panX: start.panX + dx, + panY: start.panY + dy, + })); + }, []); + + const handleMouseUp = useCallback(() => { + panStartRef.current = null; + setIsPanning(false); + }, []); + + // Clean up panning if mouse leaves the window + useEffect(() => { + const onUp = () => { + if (panStartRef.current) { + panStartRef.current = null; + setIsPanning(false); + } + }; + window.addEventListener('mouseup', onUp); + return () => window.removeEventListener('mouseup', onUp); + }, []); + + const zoomIn = useCallback(() => { + setState((prev) => { + const newZoom = clamp(prev.zoom * ZOOM_IN_FACTOR, MIN_ZOOM, MAX_ZOOM); + const el = viewportRef.current; + if (!el) return { ...prev, zoom: newZoom }; + const rect = el.getBoundingClientRect(); + const cx = rect.width / 2; + const cy = rect.height / 2; + const ratio = newZoom / prev.zoom; + return { panX: cx - (cx - prev.panX) * ratio, panY: cy - (cy - prev.panY) * ratio, zoom: newZoom }; + }); + }, []); + + const zoomOut = useCallback(() => { + setState((prev) => { + const newZoom = clamp(prev.zoom * ZOOM_OUT_FACTOR, MIN_ZOOM, MAX_ZOOM); + const el = viewportRef.current; + if (!el) return { ...prev, zoom: newZoom }; + const rect = el.getBoundingClientRect(); + const cx = rect.width / 2; + const cy = rect.height / 2; + const ratio = newZoom / prev.zoom; + return { panX: cx - (cx - prev.panX) * ratio, panY: cy - (cy - prev.panY) * ratio, zoom: newZoom }; + }); + }, []); + + const resetZoom = useCallback(() => { + setState({ panX: 0, panY: 0, zoom: 1 }); + }, []); + + const fitToView = useCallback(() => { + const viewport = viewportRef.current; + const content = contentRef.current; + if (!viewport || !content) return; + + const vRect = viewport.getBoundingClientRect(); + const children = content.children; + if (children.length === 0) { + setState({ panX: 0, panY: 0, zoom: 1 }); + return; + } + + setState((prev) => { + let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; + for (let i = 0; i < children.length; i++) { + const r = children[i].getBoundingClientRect(); + if (r.width === 0 && r.height === 0) continue; + const sx = (r.left - vRect.left - prev.panX) / prev.zoom; + const sy = (r.top - vRect.top - prev.panY) / prev.zoom; + minX = Math.min(minX, sx); + minY = Math.min(minY, sy); + maxX = Math.max(maxX, sx + r.width / prev.zoom); + maxY = Math.max(maxY, sy + r.height / prev.zoom); + } + + if (!isFinite(minX)) return { panX: 0, panY: 0, zoom: 1 }; + + const contentWidth = maxX - minX; + const contentHeight = maxY - minY; + const availW = vRect.width - FIT_PADDING * 2; + const availH = vRect.height - FIT_PADDING * 2; + const newZoom = clamp(Math.min(availW / contentWidth, availH / contentHeight), MIN_ZOOM, MAX_ZOOM); + const newPanX = (vRect.width - contentWidth * newZoom) / 2 - minX * newZoom; + const newPanY = (vRect.height - contentHeight * newZoom) / 2 - minY * newZoom; + + return { panX: newPanX, panY: newPanY, zoom: newZoom }; + }); + }, []); + + const fitToCards = useCallback((cardRects: Array<{ x: number; y: number; width: number; height: number }>) => { + const viewport = viewportRef.current; + if (!viewport || cardRects.length === 0) { + setState({ panX: 0, panY: 0, zoom: 1 }); + return; + } + + const vRect = viewport.getBoundingClientRect(); + + let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; + for (const card of cardRects) { + minX = Math.min(minX, card.x); + minY = Math.min(minY, card.y); + maxX = Math.max(maxX, card.x + card.width); + maxY = Math.max(maxY, card.y + card.height); + } + + if (!isFinite(minX)) { + setState({ panX: 0, panY: 0, zoom: 1 }); + return; + } + + const contentWidth = maxX - minX; + const contentHeight = maxY - minY; + const availW = vRect.width - FIT_PADDING * 2; + const availH = vRect.height - FIT_PADDING * 2; + const newZoom = clamp(Math.min(availW / contentWidth, availH / contentHeight), MIN_ZOOM, MAX_ZOOM); + const newPanX = (vRect.width - contentWidth * newZoom) / 2 - minX * newZoom; + const newPanY = (vRect.height - contentHeight * newZoom) / 2 - minY * newZoom; + + setState({ panX: newPanX, panY: newPanY, zoom: newZoom }); + }, []); + + return { + ...state, + isPanning, + spaceHeld, + viewportRef, + contentRef, + handlers: { + onMouseDown: handleMouseDown, + onMouseMove: handleMouseMove, + onMouseUp: handleMouseUp, + }, + actions: { zoomIn, zoomOut, resetZoom, fitToView, fitToCards }, + } as const; +} + +export type CanvasActions = ReturnType['actions']; diff --git a/frontend/src/app/pages/DashboardSelection/DashboardSelection.tsx b/frontend/src/app/pages/DashboardSelection/DashboardSelection.tsx new file mode 100644 index 00000000..3de1cc57 --- /dev/null +++ b/frontend/src/app/pages/DashboardSelection/DashboardSelection.tsx @@ -0,0 +1,334 @@ +import React, { useEffect, useState, useMemo } from 'react'; +import { useNavigate } from 'react-router-dom'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import Button from '@mui/material/Button'; +import TextField from '@mui/material/TextField'; +import IconButton from '@mui/material/IconButton'; +import Tooltip from '@mui/material/Tooltip'; +import Menu from '@mui/material/Menu'; +import MenuItem from '@mui/material/MenuItem'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemText from '@mui/material/ListItemText'; +import AddIcon from '@mui/icons-material/Add'; +import DashboardIcon from '@mui/icons-material/Dashboard'; +import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'; +import ContentCopyIcon from '@mui/icons-material/ContentCopy'; +import EditIcon from '@mui/icons-material/Edit'; +import MoreVertIcon from '@mui/icons-material/MoreVert'; +import SearchIcon from '@mui/icons-material/Search'; +import { useAppDispatch, useAppSelector } from '@/shared/hooks'; +import { + fetchDashboards, + createDashboard, + deleteDashboard, + duplicateDashboard, + renameDashboard, + Dashboard, +} from '@/shared/state/dashboardsSlice'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; + +function formatRelativeTime(dateStr: string | null): string { + if (!dateStr) return ''; + const seconds = Math.floor((Date.now() - new Date(dateStr).getTime()) / 1000); + if (seconds < 60) return 'just now'; + const minutes = Math.floor(seconds / 60); + if (minutes < 60) return `${minutes}m ago`; + const hours = Math.floor(minutes / 60); + if (hours < 24) return `${hours}h ago`; + const days = Math.floor(hours / 24); + return `${days}d ago`; +} + +const DashboardSelection: React.FC = () => { + const c = useClaudeTokens(); + const dispatch = useAppDispatch(); + const navigate = useNavigate(); + const items = useAppSelector((state) => state.dashboards.items); + const loading = useAppSelector((state) => state.dashboards.loading); + + const [search, setSearch] = useState(''); + const [menuAnchor, setMenuAnchor] = useState(null); + const [menuDashboard, setMenuDashboard] = useState(null); + const [renamingId, setRenamingId] = useState(null); + const [renameValue, setRenameValue] = useState(''); + + useEffect(() => { + dispatch(fetchDashboards()); + }, [dispatch]); + + const dashboards = useMemo(() => { + const all = Object.values(items).sort( + (a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime(), + ); + if (!search.trim()) return all; + const q = search.toLowerCase(); + return all.filter((d) => d.name.toLowerCase().includes(q)); + }, [items, search]); + + const handleCreate = async () => { + const result = await dispatch(createDashboard('Untitled Dashboard')); + if (createDashboard.fulfilled.match(result)) { + navigate(`/dashboard/${result.payload.id}`); + } + }; + + const handleOpenMenu = (e: React.MouseEvent, d: Dashboard) => { + e.stopPropagation(); + setMenuAnchor(e.currentTarget); + setMenuDashboard(d); + }; + + const handleCloseMenu = () => { + setMenuAnchor(null); + setMenuDashboard(null); + }; + + const handleDelete = () => { + if (menuDashboard) dispatch(deleteDashboard(menuDashboard.id)); + handleCloseMenu(); + }; + + const handleDuplicate = () => { + if (menuDashboard) dispatch(duplicateDashboard(menuDashboard.id)); + handleCloseMenu(); + }; + + const handleStartRename = () => { + const target = menuDashboard; + handleCloseMenu(); + if (target) { + setTimeout(() => { + setRenamingId(target.id); + setRenameValue(target.name); + }, 150); + } + }; + + const handleRenameSubmit = (id: string) => { + const trimmed = renameValue.trim(); + if (trimmed && trimmed !== items[id]?.name) { + dispatch(renameDashboard({ id, name: trimmed })); + } + setRenamingId(null); + }; + + return ( + + + + + Dashboards + + + + + + setSearch(e.target.value)} + InputProps={{ + startAdornment: ( + + ), + }} + sx={{ + width: 320, + '& .MuiOutlinedInput-root': { + bgcolor: c.bg.surface, + borderRadius: 2, + fontSize: '0.875rem', + '& fieldset': { borderColor: c.border.subtle }, + '&:hover fieldset': { borderColor: c.border.medium }, + }, + }} + /> + + + {loading ? ( + + Loading... + + ) : dashboards.length === 0 ? ( + + + {search ? 'No dashboards match your search' : 'No dashboards yet'} + + + {search ? 'Try a different search term' : 'Create your first dashboard to get started'} + + + ) : ( + + {dashboards.map((d) => ( + { + if (renamingId === d.id) return; + navigate(`/dashboard/${d.id}`); + }} + sx={{ + cursor: renamingId === d.id ? 'default' : 'pointer', + borderRadius: 3, + border: `1px solid ${c.border.subtle}`, + bgcolor: c.bg.surface, + overflow: 'hidden', + transition: 'all 0.2s ease', + '&:hover': { + borderColor: c.border.strong, + boxShadow: c.shadow.md, + transform: 'translateY(-2px)', + }, + '&:hover .card-actions': { opacity: 1 }, + display: 'flex', + flexDirection: 'column', + }} + > + + + + + handleOpenMenu(e, d)} + sx={{ + bgcolor: c.bg.surface, + color: c.text.muted, + boxShadow: c.shadow.sm, + '&:hover': { bgcolor: c.bg.elevated }, + }} + > + + + + + + + + {renamingId === d.id ? ( + setRenameValue(e.target.value)} + onBlur={() => handleRenameSubmit(d.id)} + onKeyDown={(e) => { + if (e.key === 'Enter') handleRenameSubmit(d.id); + if (e.key === 'Escape') setRenamingId(null); + }} + onClick={(e) => e.stopPropagation()} + sx={{ + '& .MuiOutlinedInput-root': { + fontSize: '0.95rem', + fontWeight: 600, + }, + }} + /> + ) : ( + + {d.name} + + )} + + Updated {formatRelativeTime(d.updated_at)} + + + + ))} + + )} + + + + + + Rename + + + + Duplicate + + + + Delete + + + + ); +}; + +export default DashboardSelection; diff --git a/frontend/src/app/pages/Modes/Modes.tsx b/frontend/src/app/pages/Modes/Modes.tsx new file mode 100644 index 00000000..3719b84d --- /dev/null +++ b/frontend/src/app/pages/Modes/Modes.tsx @@ -0,0 +1,538 @@ +import React, { useEffect, useState, useMemo } from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import Button from '@mui/material/Button'; +import Card from '@mui/material/Card'; +import CardContent from '@mui/material/CardContent'; +import CardActions from '@mui/material/CardActions'; +import Dialog from '@mui/material/Dialog'; +import DialogTitle from '@mui/material/DialogTitle'; +import DialogContent from '@mui/material/DialogContent'; +import DialogActions from '@mui/material/DialogActions'; +import TextField from '@mui/material/TextField'; +import IconButton from '@mui/material/IconButton'; +import Chip from '@mui/material/Chip'; +import CircularProgress from '@mui/material/CircularProgress'; +import Tooltip from '@mui/material/Tooltip'; +import FormControl from '@mui/material/FormControl'; +import InputLabel from '@mui/material/InputLabel'; +import Select from '@mui/material/Select'; +import MenuItem from '@mui/material/MenuItem'; +import Checkbox from '@mui/material/Checkbox'; +import ListItemText from '@mui/material/ListItemText'; +import OutlinedInput from '@mui/material/OutlinedInput'; +import AddIcon from '@mui/icons-material/Add'; +import EditIcon from '@mui/icons-material/Edit'; +import DeleteIcon from '@mui/icons-material/Delete'; +import TuneIcon from '@mui/icons-material/Tune'; +import LockIcon from '@mui/icons-material/Lock'; +import ArrowForwardIcon from '@mui/icons-material/ArrowForward'; +import SmartToyOutlinedIcon from '@mui/icons-material/SmartToyOutlined'; +import QuestionAnswerOutlinedIcon from '@mui/icons-material/QuestionAnswerOutlined'; +import MapOutlinedIcon from '@mui/icons-material/MapOutlined'; +import CategoryOutlinedIcon from '@mui/icons-material/CategoryOutlined'; +import { useAppDispatch, useAppSelector } from '@/shared/hooks'; +import { + fetchModes, + createMode, + updateMode, + deleteMode, + Mode, +} from '@/shared/state/modesSlice'; +import { fetchBuiltinTools, fetchTools } from '@/shared/state/toolsSlice'; +import { fetchTemplates } from '@/shared/state/templatesSlice'; +import { fetchSkills } from '@/shared/state/skillsSlice'; +import FolderOpenIcon from '@mui/icons-material/FolderOpen'; +import ExtensionIcon from '@mui/icons-material/Extension'; +import ListSubheader from '@mui/material/ListSubheader'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; +import DirectoryBrowser from '@/app/components/DirectoryBrowser'; +import RichPromptEditor from '@/app/components/RichPromptEditor'; + +const ICON_MAP: Record = { + smart_toy: , + question_answer: , + map: , + category: , + tune: , +}; + +const ICON_OPTIONS = [ + { value: 'smart_toy', label: 'Robot' }, + { value: 'question_answer', label: 'Q&A' }, + { value: 'map', label: 'Map' }, + { value: 'category', label: 'Category' }, + { value: 'tune', label: 'Tune' }, +]; + +const COLOR_OPTIONS = [ + { value: '#ae5630', label: 'Terra Cotta' }, + { value: '#4ade80', label: 'Green' }, + { value: '#fbbf24', label: 'Amber' }, + { value: '#f87171', label: 'Red' }, + { value: '#38bdf8', label: 'Sky' }, + { value: '#c084fc', label: 'Purple' }, + { value: '#fb923c', label: 'Orange' }, + { value: '#2dd4bf', label: 'Teal' }, +]; + +interface ModeForm { + name: string; + description: string; + system_prompt: string; + tools: string[]; + toolsEnabled: boolean; + default_next_mode: string; + icon: string; + color: string; + default_folder: string; +} + +const emptyForm: ModeForm = { + name: '', + description: '', + system_prompt: '', + tools: [], + toolsEnabled: false, + default_next_mode: '', + icon: 'smart_toy', + color: '#ae5630', + default_folder: '', +}; + +const ALL_BUILTIN_TOOL_NAMES = ['Read', 'Edit', 'Write', 'Bash', 'Glob', 'Grep', 'AskUserQuestion']; + +const Modes: React.FC = () => { + const c = useClaudeTokens(); + const dispatch = useAppDispatch(); + const { items, loading } = useAppSelector((s) => s.modes); + const toolItems = useAppSelector((s) => s.tools.items); + const modes = useMemo(() => Object.values(items), [items]); + + const mcpToolNames = useMemo(() => { + return Object.values(toolItems) + .filter((t) => t.mcp_config && Object.keys(t.mcp_config).length > 0 && t.auth_status !== 'none') + .map((t) => `mcp:${t.name}`); + }, [toolItems]); + + const [dialogOpen, setDialogOpen] = useState(false); + const [editingId, setEditingId] = useState(null); + const [form, setForm] = useState(emptyForm); + const [browseOpen, setBrowseOpen] = useState(false); + + useEffect(() => { + dispatch(fetchModes()); + dispatch(fetchBuiltinTools()); + dispatch(fetchTools()); + dispatch(fetchTemplates()); + dispatch(fetchSkills()); + }, [dispatch]); + + const openCreate = () => { + setEditingId(null); + setForm(emptyForm); + setDialogOpen(true); + }; + + const openEdit = (mode: Mode) => { + setEditingId(mode.id); + setForm({ + name: mode.name, + description: mode.description, + system_prompt: mode.system_prompt ?? '', + tools: mode.tools ?? [], + toolsEnabled: mode.tools !== null, + default_next_mode: mode.default_next_mode ?? '', + icon: mode.icon, + color: mode.color, + default_folder: mode.default_folder ?? '', + }); + setDialogOpen(true); + }; + + const handleSave = async () => { + const payload = { + name: form.name, + description: form.description, + system_prompt: form.system_prompt || null, + tools: form.toolsEnabled ? form.tools : null, + default_next_mode: form.default_next_mode || null, + icon: form.icon, + color: form.color, + default_folder: form.default_folder || null, + }; + + if (editingId) { + await dispatch(updateMode({ id: editingId, ...payload })); + } else { + await dispatch(createMode(payload as any)); + } + setDialogOpen(false); + }; + + const handleDelete = async (id: string) => { + await dispatch(deleteMode(id)); + }; + + const otherModes = modes.filter((m) => m.id !== editingId); + + return ( + + + + + Modes + + + Configure agent interaction modes with custom system prompts, tools, and auto-switching. + + + + + + {loading ? ( + + + + ) : modes.length === 0 ? ( + + + No modes defined yet. Create one to get started. + + ) : ( + + {modes.map((mode) => ( + + + + + {ICON_MAP[mode.icon] || ICON_MAP.smart_toy} + + + {mode.name} + + {mode.is_builtin && ( + } + label="Built-in" + size="small" + sx={{ bgcolor: c.bg.secondary, color: c.text.muted, fontSize: '0.7rem', height: 22 }} + /> + )} + + {mode.description && ( + + {mode.description} + + )} + + {mode.tools !== null ? ( + + ) : ( + + )} + {mode.system_prompt && ( + + )} + {mode.default_next_mode && ( + } + label={items[mode.default_next_mode]?.name || mode.default_next_mode} + size="small" + sx={{ bgcolor: 'rgba(251,191,36,0.15)', color: '#fbbf24', fontSize: '0.75rem', height: 24 }} + /> + )} + {mode.default_folder && ( + } + label={mode.default_folder.split('/').pop() || mode.default_folder} + size="small" + sx={{ bgcolor: 'rgba(56,189,248,0.15)', color: '#38bdf8', fontSize: '0.75rem', height: 24 }} + /> + )} + + + + + openEdit(mode)} sx={{ color: c.text.tertiary, '&:hover': { color: c.accent.primary } }}> + + + + {!mode.is_builtin && ( + + handleDelete(mode.id)} sx={{ color: c.text.tertiary, '&:hover': { color: c.status.error } }}> + + + + )} + + + ))} + + )} + + setDialogOpen(false)} + maxWidth="md" + fullWidth + PaperProps={{ + sx: { bgcolor: c.bg.surface, backgroundImage: 'none', borderRadius: 4, border: `1px solid ${c.border.subtle}` }, + }} + > + + {editingId ? 'Edit Mode' : 'New Mode'} + + + setForm({ ...form, name: e.target.value })} + fullWidth + size="small" + sx={{ '& .MuiOutlinedInput-root': { bgcolor: c.bg.page } }} + /> + setForm({ ...form, description: e.target.value })} + fullWidth + size="small" + sx={{ '& .MuiOutlinedInput-root': { bgcolor: c.bg.page } }} + /> + setForm({ ...form, system_prompt: v })} + placeholder="Instructions for the agent when using this mode... (@ for context, / for commands)" + minRows={3} + maxRows={8} + /> + + {/* Tools toggle + multi-select */} + + + setForm({ ...form, toolsEnabled: e.target.checked, tools: e.target.checked ? form.tools : [] })} + size="small" + sx={{ color: c.text.tertiary, '&.Mui-checked': { color: c.accent.primary }, p: 0 }} + /> + + Restrict tools {!form.toolsEnabled && (all tools allowed)} + + + {form.toolsEnabled && ( + + Allowed Tools + + + )} + + + + Default Next Mode + + + + {/* Default Folder */} + + + Default Folder + + + setForm({ ...form, default_folder: e.target.value })} + fullWidth + size="small" + placeholder="Not set (uses global default)" + sx={{ + '& .MuiOutlinedInput-root': { + bgcolor: c.bg.page, + fontFamily: 'monospace', + fontSize: '0.85rem', + }, + }} + /> + + + + + + + Icon + + + + Color + + + + + + + + + + + setBrowseOpen(false)} + onSelect={(item) => setForm({ ...form, default_folder: item.path })} + initialPath={form.default_folder || ''} + /> + + ); +}; + +export default Modes; diff --git a/frontend/src/app/pages/Settings/Settings.tsx b/frontend/src/app/pages/Settings/Settings.tsx new file mode 100644 index 00000000..b5310fff --- /dev/null +++ b/frontend/src/app/pages/Settings/Settings.tsx @@ -0,0 +1,561 @@ +import React, { useState, useEffect, useMemo, useCallback } from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import Paper from '@mui/material/Paper'; +import TextField from '@mui/material/TextField'; +import Button from '@mui/material/Button'; +import FormControl from '@mui/material/FormControl'; +import Select from '@mui/material/Select'; +import MenuItem from '@mui/material/MenuItem'; +import IconButton from '@mui/material/IconButton'; +import InputAdornment from '@mui/material/InputAdornment'; +import ToggleButton from '@mui/material/ToggleButton'; +import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; +import Slider from '@mui/material/Slider'; +import Snackbar from '@mui/material/Snackbar'; +import Alert from '@mui/material/Alert'; +import Dialog from '@mui/material/Dialog'; +import DialogTitle from '@mui/material/DialogTitle'; +import DialogContent from '@mui/material/DialogContent'; +import DialogActions from '@mui/material/DialogActions'; +import FolderOpenIcon from '@mui/icons-material/FolderOpen'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; +import LightModeIcon from '@mui/icons-material/LightMode'; +import DarkModeIcon from '@mui/icons-material/DarkMode'; +import SaveIcon from '@mui/icons-material/Save'; +import CloseIcon from '@mui/icons-material/Close'; +import KeyboardIcon from '@mui/icons-material/Keyboard'; +import { useAppDispatch, useAppSelector } from '@/shared/hooks'; +import { updateSettings, closeSettingsModal, AppSettings } from '@/shared/state/settingsSlice'; +import { fetchModes } from '@/shared/state/modesSlice'; +import { useClaudeTokens, useThemeMode } from '@/shared/styles/ThemeContext'; +import DirectoryBrowser from '@/app/components/DirectoryBrowser'; + +const Settings: React.FC = () => { + const open = useAppSelector((s) => s.settings.modalOpen); + const c = useClaudeTokens(); + const dispatch = useAppDispatch(); + const settings = useAppSelector((s) => s.settings.data); + const loaded = useAppSelector((s) => s.settings.loaded); + const modes = useAppSelector((s) => s.modes.items); + const { setMode: setThemeMode } = useThemeMode(); + + const modesList = useMemo(() => Object.values(modes), [modes]); + + const [form, setForm] = useState({ ...settings }); + const [showApiKey, setShowApiKey] = useState(false); + const [browseOpen, setBrowseOpen] = useState(false); + const [saved, setSaved] = useState(false); + const [recordingShortcut, setRecordingShortcut] = useState(false); + const [confirmDiscard, setConfirmDiscard] = useState(false); + + useEffect(() => { + dispatch(fetchModes()); + }, [dispatch]); + + useEffect(() => { + if (loaded) { + setForm({ ...settings }); + } + }, [loaded, settings]); + + const hasChanges = JSON.stringify(form) !== JSON.stringify(settings); + + const handleSave = async () => { + await dispatch(updateSettings(form)); + if (form.theme !== settings.theme) { + setThemeMode(form.theme); + } + setSaved(true); + }; + + const handleRequestClose = useCallback(() => { + if (hasChanges) { + setConfirmDiscard(true); + } else { + dispatch(closeSettingsModal()); + } + }, [hasChanges, dispatch]); + + const handleConfirmDiscard = useCallback(() => { + setConfirmDiscard(false); + setForm({ ...settings }); + dispatch(closeSettingsModal()); + }, [settings, dispatch]); + + const handleSaveAndClose = useCallback(async () => { + await dispatch(updateSettings(form)); + if (form.theme !== settings.theme) { + setThemeMode(form.theme); + } + setSaved(true); + setConfirmDiscard(false); + dispatch(closeSettingsModal()); + }, [dispatch, form, settings, setThemeMode]); + + const fieldSx = { + '& .MuiOutlinedInput-root': { + bgcolor: c.bg.page, + fontSize: '0.85rem', + }, + }; + + return ( + <> + + + + + Settings + + + Global defaults and application configuration. + + + + + + + + + + {/* Default System Prompt */} + + + Default System Prompt + + + A global system prompt prepended to every agent session, before any mode-specific instructions. + + setForm({ ...form, default_system_prompt: e.target.value || null })} + size="small" + fullWidth + multiline + minRows={3} + maxRows={10} + placeholder="Enter a default system prompt..." + sx={{ + ...fieldSx, + '& .MuiOutlinedInput-root': { + ...fieldSx['& .MuiOutlinedInput-root'], + fontFamily: c.font.mono, + fontSize: '0.8rem', + }, + '& textarea': { + '&::-webkit-scrollbar': { width: 5 }, + '&::-webkit-scrollbar-track': { background: 'transparent' }, + '&::-webkit-scrollbar-thumb': { background: c.border.medium, borderRadius: 3, '&:hover': { background: c.border.strong } }, + scrollbarWidth: 'thin', + scrollbarColor: `${c.border.medium} transparent`, + }, + }} + /> + + + {/* Default Folder */} + + + Default Folder + + + The working directory agents start in by default. Modes can override this per-mode. + + + setForm({ ...form, default_folder: e.target.value || null })} + size="small" + fullWidth + placeholder="Not set (uses project root)" + sx={{ + ...fieldSx, + '& .MuiOutlinedInput-root': { + ...fieldSx['& .MuiOutlinedInput-root'], + fontFamily: c.font.mono, + }, + }} + /> + + + + + {/* Default Model */} + + + Default Model + + + The default model for new agent sessions. + + + + + + + {/* Default Mode */} + + + Default Mode + + + The default interaction mode for new agent sessions. + + + + + + + {/* Default Max Turns */} + + + Default Max Turns + + + Maximum number of agent turns before auto-stopping. Leave empty for unlimited. + + setForm({ ...form, default_max_turns: e.target.value ? parseInt(e.target.value) : null })} + size="small" + fullWidth + placeholder="Unlimited" + inputProps={{ min: 1 }} + sx={fieldSx} + /> + + + {/* Zoom Sensitivity */} + + + Zoom Sensitivity + + + Controls how responsive scroll-to-zoom is on the dashboard canvas. Lower values suit trackpads; higher values suit mouse wheels. + + + setForm({ ...form, zoom_sensitivity: v as number })} + min={1} + max={100} + step={1} + valueLabelDisplay="auto" + marks={[ + { value: 1, label: 'Low' }, + { value: 50, label: 'Default' }, + { value: 100, label: 'High' }, + ]} + sx={{ + color: c.accent.primary, + '& .MuiSlider-markLabel': { color: c.text.tertiary, fontSize: '0.75rem' }, + '& .MuiSlider-valueLabel': { bgcolor: c.accent.primary }, + }} + /> + + + + {/* New Agent Shortcut */} + + + New Agent Shortcut + + + Keyboard shortcut to open the new agent input on the Dashboard. + + { + if (!recordingShortcut) return; + if (['Meta', 'Control', 'Shift', 'Alt'].includes(e.key)) return; + e.preventDefault(); + const parts: string[] = []; + if (e.metaKey) parts.push('Meta'); + if (e.ctrlKey) parts.push('Ctrl'); + if (e.altKey) parts.push('Alt'); + if (e.shiftKey) parts.push('Shift'); + parts.push(e.key.length === 1 ? e.key.toLowerCase() : e.key); + setForm({ ...form, new_agent_shortcut: parts.join('+') }); + setRecordingShortcut(false); + }} + onBlur={() => setRecordingShortcut(false)} + onClick={() => setRecordingShortcut(true)} + sx={{ + display: 'inline-flex', + alignItems: 'center', + gap: 1, + px: 2, + py: 1, + borderRadius: `${c.radius.md}px`, + border: `1px solid ${recordingShortcut ? c.accent.primary : c.border.medium}`, + bgcolor: c.bg.page, + cursor: 'pointer', + outline: 'none', + transition: 'border-color 0.15s', + '&:hover': { borderColor: c.accent.primary }, + }} + > + + {recordingShortcut ? ( + + Press shortcut… + + ) : ( + + {form.new_agent_shortcut + .split('+') + .map((p) => { + if (p === 'Meta') return '⌘'; + if (p === 'Ctrl') return 'Ctrl'; + if (p === 'Alt') return '⌥'; + if (p === 'Shift') return '⇧'; + return p.toUpperCase(); + }) + .join(' + ')} + + )} + + + + {/* Theme */} + + + Theme + + + Application color scheme. + + { if (v) setForm({ ...form, theme: v }); }} + size="small" + sx={{ + '& .MuiToggleButton-root': { + color: c.text.muted, + borderColor: c.border.medium, + textTransform: 'none', + px: 2.5, + gap: 0.75, + '&.Mui-selected': { + bgcolor: `${c.accent.primary}15`, + color: c.accent.primary, + borderColor: c.accent.primary, + '&:hover': { bgcolor: `${c.accent.primary}20` }, + }, + }, + }} + > + + Light + + + Dark + + + + + {/* Anthropic API Key */} + + + Anthropic API Key + + + Your API key for the Anthropic Claude API. Stored securely in the database. + + setForm({ ...form, anthropic_api_key: e.target.value || null })} + size="small" + fullWidth + placeholder="sk-ant-..." + sx={{ + ...fieldSx, + '& .MuiOutlinedInput-root': { + ...fieldSx['& .MuiOutlinedInput-root'], + fontFamily: c.font.mono, + }, + }} + InputProps={{ + endAdornment: ( + + setShowApiKey(!showApiKey)} + edge="end" + size="small" + sx={{ color: c.text.tertiary }} + > + {showApiKey ? : } + + + ), + }} + /> + + + + + + + + + + + setBrowseOpen(false)} + onSelect={(item) => setForm({ ...form, default_folder: item.path })} + initialPath={form.default_folder ?? ''} + /> + + setSaved(false)} + anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} + > + setSaved(false)} severity="success" sx={{ bgcolor: c.bg.surface, color: c.text.primary, border: `1px solid ${c.status.success}` }}> + Settings saved successfully + + + + + {/* Discard changes confirmation */} + setConfirmDiscard(false)} + PaperProps={{ + sx: { + bgcolor: c.bg.surface, + borderRadius: 3, + border: `1px solid ${c.border.subtle}`, + boxShadow: c.shadow.lg, + maxWidth: 400, + }, + }} + > + + Unsaved Changes + + + + You have unsaved changes. Would you like to save them before closing? + + + + + + + + + + ); +}; + +export default Settings; diff --git a/frontend/src/app/pages/Skills/SkillBuilderChat.tsx b/frontend/src/app/pages/Skills/SkillBuilderChat.tsx new file mode 100644 index 00000000..b4d962ab --- /dev/null +++ b/frontend/src/app/pages/Skills/SkillBuilderChat.tsx @@ -0,0 +1,401 @@ +import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import IconButton from '@mui/material/IconButton'; +import Button from '@mui/material/Button'; +import Tooltip from '@mui/material/Tooltip'; +import Fab from '@mui/material/Fab'; +import AutoFixHighIcon from '@mui/icons-material/AutoFixHigh'; +import CloseIcon from '@mui/icons-material/Close'; +import MinimizeIcon from '@mui/icons-material/Remove'; +import SaveIcon from '@mui/icons-material/Save'; +import RestartAltIcon from '@mui/icons-material/RestartAlt'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; +import { useAppDispatch, useAppSelector } from '@/shared/hooks'; +import { createDraftSession, removeDraftSession } from '@/shared/state/agentsSlice'; +import { createSkill } from '@/shared/state/skillsSlice'; +import AgentChat from '../AgentChat/AgentChat'; +import { ContextPath } from '@/app/components/DirectoryBrowser'; + +const SKILLS_WORKSPACE_API = `http://${window.location.hostname}:8324/api/skills`; +const POLL_INTERVAL_MS = 2000; + +export interface SkillPreviewData { + name: string; + description: string; + command: string; + content: string; +} + +interface SkillBuilderChatProps { + onSkillPreview: (data: SkillPreviewData | null) => void; + onSkillSaved: (message: string) => void; + expanded: boolean; + onExpandedChange: (expanded: boolean) => void; +} + +const MIN_W = 320; +const MAX_W = 700; +const MIN_H = 300; +const MAX_H = 900; + +const SkillBuilderChat: React.FC = ({ onSkillPreview, onSkillSaved, expanded, onExpandedChange }) => { + const c = useClaudeTokens(); + const dispatch = useAppDispatch(); + + const setExpanded = onExpandedChange; + const [panelWidth, setPanelWidth] = useState(420); + const [panelHeight, setPanelHeight] = useState(560); + const dragging = useRef<'left' | 'top' | 'corner' | null>(null); + const dragStartPos = useRef({ x: 0, y: 0 }); + const dragStartSize = useRef({ w: 0, h: 0 }); + + const onResizeStart = useCallback((edge: 'left' | 'top' | 'corner', e: React.PointerEvent) => { + dragging.current = edge; + dragStartPos.current = { x: e.clientX, y: e.clientY }; + dragStartSize.current = { w: panelWidth, h: panelHeight }; + (e.target as HTMLElement).setPointerCapture(e.pointerId); + document.body.style.cursor = edge === 'left' ? 'col-resize' : edge === 'top' ? 'row-resize' : 'nwse-resize'; + document.body.style.userSelect = 'none'; + }, [panelWidth, panelHeight]); + + const onResizeMove = useCallback((e: React.PointerEvent) => { + if (!dragging.current) return; + const dx = dragStartPos.current.x - e.clientX; + const dy = dragStartPos.current.y - e.clientY; + if (dragging.current === 'left' || dragging.current === 'corner') { + setPanelWidth(Math.min(MAX_W, Math.max(MIN_W, dragStartSize.current.w + dx))); + } + if (dragging.current === 'top' || dragging.current === 'corner') { + setPanelHeight(Math.min(MAX_H, Math.max(MIN_H, dragStartSize.current.h + dy))); + } + }, []); + + const onResizeEnd = useCallback(() => { + dragging.current = null; + document.body.style.cursor = ''; + document.body.style.userSelect = ''; + }, []); + + const [initialDraftId, setInitialDraftId] = useState(null); + const [workspacePath, setWorkspacePath] = useState(null); + const [stableWorkspaceId, setStableWorkspaceId] = useState(() => `skill-ws-${Date.now().toString(36)}`); + const draftCreated = useRef(false); + const [saving, setSaving] = useState(false); + const [currentPreview, setCurrentPreview] = useState(null); + + const effectiveSessionId = useAppSelector((state) => { + if (!initialDraftId) return null; + if (state.agents.sessions[initialDraftId]) return initialDraftId; + return state.agents.activeSessionId; + }); + + const agentStatus = useAppSelector((state) => { + if (!effectiveSessionId) return null; + return state.agents.sessions[effectiveSessionId]?.status ?? null; + }); + + const isAgentActive = agentStatus === 'running' || agentStatus === 'waiting_approval'; + + const initialContextPaths = useMemo( + () => workspacePath ? [{ path: workspacePath, type: 'directory' as const }] : undefined, + [workspacePath], + ); + + const initSession = useCallback(async () => { + const wsId = `skill-ws-${Date.now().toString(36)}`; + setStableWorkspaceId(wsId); + + try { + const res = await fetch(`${SKILLS_WORKSPACE_API}/workspace/seed`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ workspace_id: wsId }), + }); + const data = await res.json(); + setWorkspacePath(data.path); + const action = dispatch(createDraftSession({ + mode: 'skill-builder', + setActive: false, + targetDirectory: data.path, + })); + setInitialDraftId(action.payload.draftId); + } catch { + const action = dispatch(createDraftSession({ mode: 'skill-builder', setActive: false })); + setInitialDraftId(action.payload.draftId); + } + }, [dispatch]); + + useEffect(() => { + if (draftCreated.current) return; + draftCreated.current = true; + initSession(); + }, [initSession]); + + // Poll workspace for updates + const pollRef = useRef | null>(null); + const lastPollRef = useRef(''); + + const pollWorkspace = useCallback(async () => { + if (!stableWorkspaceId) return; + try { + const res = await fetch(`${SKILLS_WORKSPACE_API}/workspace/${stableWorkspaceId}`); + if (!res.ok) return; + const data = await res.json(); + const fingerprint = JSON.stringify(data); + if (fingerprint === lastPollRef.current) return; + lastPollRef.current = fingerprint; + + if (data.skill_content || data.meta) { + const meta = data.meta || {}; + const fm = data.frontmatter || {}; + const preview: SkillPreviewData = { + name: meta.name || fm.name || '', + description: meta.description || fm.description || '', + command: meta.command || (meta.name || fm.name || '').toLowerCase().replace(/\s+/g, '-'), + content: data.skill_content || '', + }; + setCurrentPreview(preview); + onSkillPreview(preview); + } + } catch { /* ignore polling errors */ } + }, [stableWorkspaceId, onSkillPreview]); + + useEffect(() => { + if (!expanded) return; + pollWorkspace(); + pollRef.current = setInterval(pollWorkspace, POLL_INTERVAL_MS); + return () => { + if (pollRef.current) clearInterval(pollRef.current); + }; + }, [expanded, pollWorkspace]); + + // Final poll when agent finishes + const prevAgentActive = useRef(false); + useEffect(() => { + if (prevAgentActive.current && !isAgentActive) { + setTimeout(pollWorkspace, 500); + } + prevAgentActive.current = isAgentActive; + }, [isAgentActive, pollWorkspace]); + + useEffect(() => { + return () => { + if (initialDraftId) { + dispatch(removeDraftSession(initialDraftId)); + } + }; + }, [initialDraftId, dispatch]); + + const handleSave = async () => { + if (!currentPreview || !currentPreview.name || !currentPreview.content) return; + setSaving(true); + try { + await dispatch(createSkill({ + name: currentPreview.name, + description: currentPreview.description, + content: currentPreview.content, + command: currentPreview.command, + })).unwrap(); + onSkillSaved(`Skill "${currentPreview.name}" saved successfully`); + } catch (err) { + console.error('Failed to save skill:', err); + } finally { + setSaving(false); + } + }; + + const handleReset = async () => { + if (initialDraftId) { + dispatch(removeDraftSession(initialDraftId)); + } + setCurrentPreview(null); + onSkillPreview(null); + lastPollRef.current = ''; + draftCreated.current = false; + + await initSession(); + draftCreated.current = true; + }; + + if (!expanded) { + return ( + + setExpanded(true)} + sx={{ + position: 'absolute', + bottom: 24, + right: 24, + bgcolor: c.accent.primary, + color: '#fff', + '&:hover': { bgcolor: c.accent.pressed }, + zIndex: 10, + width: 52, + height: 52, + boxShadow: c.shadow.lg, + }} + > + + + + ); + } + + return ( + + {/* Left resize handle */} + onResizeStart('left', e)} + onPointerMove={onResizeMove} + onPointerUp={onResizeEnd} + onPointerCancel={onResizeEnd} + sx={{ + position: 'absolute', left: 0, top: 12, bottom: 0, width: 6, + cursor: 'col-resize', zIndex: 2, + '&::after': { + content: '""', position: 'absolute', + top: 0, bottom: 0, left: 0, width: 2, + borderRadius: `${c.radius.lg}px 0 0 ${c.radius.lg}px`, + bgcolor: 'transparent', transition: 'background-color 0.15s', + }, + '&:hover::after, &:active::after': { bgcolor: c.accent.primary }, + }} + /> + {/* Top resize handle */} + onResizeStart('top', e)} + onPointerMove={onResizeMove} + onPointerUp={onResizeEnd} + onPointerCancel={onResizeEnd} + sx={{ + position: 'absolute', top: 0, left: 12, right: 0, height: 6, + cursor: 'row-resize', zIndex: 2, + '&::after': { + content: '""', position: 'absolute', + left: 0, right: 0, top: 0, height: 2, + borderRadius: `${c.radius.lg}px ${c.radius.lg}px 0 0`, + bgcolor: 'transparent', transition: 'background-color 0.15s', + }, + '&:hover::after, &:active::after': { bgcolor: c.accent.primary }, + }} + /> + {/* Top-left corner resize handle */} + onResizeStart('corner', e)} + onPointerMove={onResizeMove} + onPointerUp={onResizeEnd} + onPointerCancel={onResizeEnd} + sx={{ + position: 'absolute', top: 0, left: 0, width: 14, height: 14, + cursor: 'nwse-resize', zIndex: 3, + }} + /> + + {/* Header */} + + + + Skill Builder + + + {currentPreview && currentPreview.name && ( + + )} + + + + + + + + + setExpanded(false)} sx={{ color: c.text.tertiary, '&:hover': { color: c.text.primary } }}> + + + + + + { + setExpanded(false); + onSkillPreview(null); + }} + sx={{ color: c.text.tertiary, '&:hover': { color: c.status.error } }} + > + + + + + + {/* Chat area */} + + {effectiveSessionId ? ( + + ) : ( + + + Initializing skill builder... + + + )} + + + ); +}; + +export default SkillBuilderChat; diff --git a/frontend/src/app/pages/Skills/Skills.tsx b/frontend/src/app/pages/Skills/Skills.tsx new file mode 100644 index 00000000..7424cac5 --- /dev/null +++ b/frontend/src/app/pages/Skills/Skills.tsx @@ -0,0 +1,759 @@ +import React, { useEffect, useState, useMemo, useCallback } from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import Button from '@mui/material/Button'; +import Dialog from '@mui/material/Dialog'; +import DialogTitle from '@mui/material/DialogTitle'; +import DialogContent from '@mui/material/DialogContent'; +import DialogActions from '@mui/material/DialogActions'; +import TextField from '@mui/material/TextField'; +import IconButton from '@mui/material/IconButton'; +import Chip from '@mui/material/Chip'; +import CircularProgress from '@mui/material/CircularProgress'; +import Tooltip from '@mui/material/Tooltip'; +import Collapse from '@mui/material/Collapse'; +import Snackbar from '@mui/material/Snackbar'; +import Alert from '@mui/material/Alert'; +import InputAdornment from '@mui/material/InputAdornment'; +import ToggleButton from '@mui/material/ToggleButton'; +import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; +import AddIcon from '@mui/icons-material/Add'; +import EditIcon from '@mui/icons-material/Edit'; +import DeleteIcon from '@mui/icons-material/Delete'; +import TerminalIcon from '@mui/icons-material/Terminal'; +import DescriptionIcon from '@mui/icons-material/Description'; +import SearchIcon from '@mui/icons-material/Search'; +import DownloadIcon from '@mui/icons-material/Download'; +import OpenInNewIcon from '@mui/icons-material/OpenInNew'; +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight'; +import FolderIcon from '@mui/icons-material/Folder'; +import MoreHorizIcon from '@mui/icons-material/MoreHoriz'; +import CodeIcon from '@mui/icons-material/Code'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import AutoFixHighIcon from '@mui/icons-material/AutoFixHigh'; +import ReactMarkdown from 'react-markdown'; +import remarkGfm from 'remark-gfm'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; +import { useAppDispatch, useAppSelector } from '@/shared/hooks'; +import { + fetchSkills, + createSkill, + updateSkill, + deleteSkill, + Skill, +} from '@/shared/state/skillsSlice'; +import { + fetchAllRegistrySkills, + fetchSkillRegistryStats, + fetchSkillDetail, + RegistrySkill, + RegistrySkillDetail, +} from '@/shared/state/skillRegistrySlice'; +import SkillBuilderChat, { SkillPreviewData } from './SkillBuilderChat'; + +interface SkillForm { + name: string; + description: string; + content: string; + command: string; +} + +type Selection = + | { type: 'registry'; name: string } + | { type: 'local'; id: string } + | { type: 'builder-preview' } + | null; + +const emptyForm: SkillForm = { name: '', description: '', content: '', command: '' }; + +const SIDEBAR_W = 260; + +const Skills: React.FC = () => { + const c = useClaudeTokens(); + const dispatch = useAppDispatch(); + const { items, loading } = useAppSelector((s) => s.skills); + const { + skills: regSkills, + loading: regLoading, + stats: regStats, + detail: regDetail, + detailLoading: regDetailLoading, + } = useAppSelector((s) => s.skillRegistry); + const localSkills = Object.values(items); + + const [selection, setSelection] = useState(null); + const [searchFilter, setSearchFilter] = useState(''); + const [collapsedCats, setCollapsedCats] = useState>({}); + + const [contentView, setContentView] = useState<'preview' | 'raw'>('preview'); + const [dialogOpen, setDialogOpen] = useState(false); + const [editingId, setEditingId] = useState(null); + const [form, setForm] = useState(emptyForm); + const [snackbar, setSnackbar] = useState<{ open: boolean; message: string }>({ open: false, message: '' }); + const [builderPreview, setBuilderPreview] = useState(null); + const [builderOpen, setBuilderOpen] = useState(false); + + const handleBuilderPreview = useCallback((data: SkillPreviewData | null) => { + setBuilderPreview(data); + if (data) { + setSelection({ type: 'builder-preview' }); + } else if (selection?.type === 'builder-preview') { + setSelection(null); + } + }, [selection]); + + const handleBuilderSaved = useCallback((message: string) => { + setSnackbar({ open: true, message }); + dispatch(fetchSkills()); + }, [dispatch]); + + useEffect(() => { + dispatch(fetchSkills()); + dispatch(fetchSkillRegistryStats()); + dispatch(fetchAllRegistrySkills()); + }, [dispatch]); + + // Group registry skills by category + const regGrouped = useMemo(() => { + const groups: Record = {}; + const q = searchFilter.toLowerCase(); + for (const sk of regSkills) { + if (q && !sk.name.toLowerCase().includes(q) && !sk.description.toLowerCase().includes(q)) continue; + const cat = sk.category || 'General'; + if (!groups[cat]) groups[cat] = []; + groups[cat].push(sk); + } + return groups; + }, [regSkills, searchFilter]); + + const filteredLocal = useMemo(() => { + const q = searchFilter.toLowerCase(); + if (!q) return localSkills; + return localSkills.filter((s) => s.name.toLowerCase().includes(q) || s.description.toLowerCase().includes(q)); + }, [localSkills, searchFilter]); + + const categoryOrder = useMemo(() => Object.keys(regGrouped).sort(), [regGrouped]); + + const toggleCategory = (cat: string) => + setCollapsedCats((p) => ({ ...p, [cat]: !p[cat] })); + + // Selection handlers + const selectRegistry = (name: string) => { + setSelection({ type: 'registry', name }); + dispatch(fetchSkillDetail(name)); + }; + + const selectLocal = (id: string) => { + setSelection({ type: 'local', id }); + }; + + // Get active detail content + const selectedLocal: Skill | null = + selection?.type === 'local' ? items[selection.id] ?? null : null; + const selectedReg: RegistrySkillDetail | null = + selection?.type === 'registry' && regDetail?.name === selection.name ? regDetail : null; + + // CRUD + const openCreate = () => { + setEditingId(null); + setForm(emptyForm); + setDialogOpen(true); + }; + + const openEdit = (skill: Skill) => { + setEditingId(skill.id); + setForm({ name: skill.name, description: skill.description, content: skill.content, command: skill.command }); + setDialogOpen(true); + }; + + const handleSave = async () => { + if (editingId) { + await dispatch(updateSkill({ id: editingId, ...form })); + } else { + await dispatch(createSkill(form)); + } + setDialogOpen(false); + }; + + const handleDelete = async (id: string) => { + await dispatch(deleteSkill(id)); + if (selection?.type === 'local' && selection.id === id) setSelection(null); + }; + + const handleInstall = async () => { + if (!selectedReg) return; + await dispatch(createSkill({ + name: selectedReg.name, + description: selectedReg.description, + content: selectedReg.content, + command: selectedReg.name.toLowerCase().replace(/\s+/g, '-'), + })); + setSnackbar({ open: true, message: `Installed "${selectedReg.name}" as a local skill` }); + }; + + const handleEditInstall = () => { + if (!selectedReg) return; + setEditingId(null); + setForm({ + name: selectedReg.name, + description: selectedReg.description, + content: selectedReg.content, + command: selectedReg.name.toLowerCase().replace(/\s+/g, '-'), + }); + setDialogOpen(true); + }; + + const isSelected = (type: 'registry' | 'local', key: string) => { + if (!selection) return false; + if (type === 'registry') return selection.type === 'registry' && selection.name === key; + return selection.type === 'local' && selection.id === key; + }; + + // ─── Content preview with raw/preview toggle ─── + const ContentPreview: React.FC<{ content: string }> = ({ content }) => ( + + + { if (v) setContentView(v); }} + size="small" + sx={{ + '& .MuiToggleButton-root': { + color: c.text.tertiary, border: `1px solid ${c.border.medium}`, + textTransform: 'none', fontSize: '0.74rem', py: 0.25, px: 1.2, lineHeight: 1.4, + '&.Mui-selected': { bgcolor: c.bg.secondary, color: c.text.primary, borderColor: c.border.strong }, + '&:hover': { bgcolor: 'rgba(0,0,0,0.03)' }, + }, + }} + > + Preview + Raw + + + + {contentView === 'raw' ? ( + + + {content} + + + ) : ( + + {content} + + )} + + ); + + // ─── Sidebar row component ─── + const SidebarRow: React.FC<{ + label: string; + selected: boolean; + onClick: () => void; + icon?: React.ReactNode; + }> = ({ label, selected, onClick, icon }) => ( + + {icon ?? } + + {label} + + + ); + + return ( + + {/* ─── Left Sidebar ─── */} + + {/* Sidebar header */} + + Skills + + + setSearchFilter((p) => (p === '' ? ' ' : ''))} + sx={{ color: c.text.tertiary, '&:hover': { color: c.text.primary } }} + > + + + + + + + + + + + + + + + {/* Search input (toggled) */} + + + setSearchFilter(e.target.value)} + fullWidth + size="small" + autoFocus + InputProps={{ + startAdornment: ( + + + + ), + }} + sx={{ + '& .MuiOutlinedInput-root': { + bgcolor: c.bg.surface, borderRadius: `${c.radius.sm}px`, fontSize: '0.82rem', + '& fieldset': { borderColor: c.border.medium }, + }, + }} + /> + + + + {/* Scrollable tree */} + + {/* My Skills (local) */} + {filteredLocal.length > 0 && ( + + toggleCategory('__local')} + sx={{ + display: 'flex', alignItems: 'center', gap: 0.5, px: 1, py: 0.5, + cursor: 'pointer', userSelect: 'none', + '&:hover': { bgcolor: 'rgba(0,0,0,0.02)' }, borderRadius: `${c.radius.sm}px`, + }} + > + {collapsedCats['__local'] + ? + : } + + My Skills + + ({filteredLocal.length}) + + + + {filteredLocal.map((sk) => ( + selectLocal(sk.id)} + icon={} + /> + ))} + + + + )} + + {/* Registry categories */} + {(loading || regLoading) && regSkills.length === 0 && localSkills.length === 0 ? ( + + + + ) : ( + categoryOrder.map((cat) => { + const group = regGrouped[cat]; + if (!group || group.length === 0) return null; + const isCollapsed = !!collapsedCats[cat]; + return ( + + toggleCategory(cat)} + sx={{ + display: 'flex', alignItems: 'center', gap: 0.5, px: 1, py: 0.5, + cursor: 'pointer', userSelect: 'none', + '&:hover': { bgcolor: 'rgba(0,0,0,0.02)' }, borderRadius: `${c.radius.sm}px`, + }} + > + {isCollapsed + ? + : } + + {cat} + + ({group.length}) + + + + {group.map((sk) => ( + selectRegistry(sk.name)} + /> + ))} + + + + ); + }) + )} + + + + {/* ─── Right Detail Panel ─── */} + + {selection?.type === 'builder-preview' && builderPreview ? ( + + + + + {builderPreview.name || 'Untitled Skill'} + + + + + + {builderPreview.command && ( + + } + label={`/${builderPreview.command}`} + size="small" + sx={{ + bgcolor: 'rgba(174,86,48,0.08)', color: c.accent.primary, + fontWeight: 500, fontSize: '0.78rem', height: 26, + }} + /> + + )} + + + + Generated by Skill Builder + + + + {builderPreview.description && ( + + Description + + {builderPreview.description} + + + )} + + + + ) : !selection ? ( + + + Select a skill to view its details + + ) : selection.type === 'registry' ? ( + regDetailLoading && !selectedReg ? ( + + + + ) : selectedReg ? ( + + {/* Header row: name + actions */} + + + {selectedReg.name} + + + + + {selectedReg.repositoryUrl && ( + + + + + + )} + + + + + Added by Anthropic + + + + Description + + {selectedReg.description} + + + + + + ) : null + ) : selectedLocal ? ( + + {/* Header row: name + actions */} + + + {selectedLocal.name} + + + + openEdit(selectedLocal)} sx={{ color: c.text.tertiary, '&:hover': { color: c.accent.primary } }}> + + + + + handleDelete(selectedLocal.id)} sx={{ color: c.text.tertiary, '&:hover': { color: c.status.error } }}> + + + + + + + {selectedLocal.command && ( + + } + label={`/${selectedLocal.command}`} + size="small" + sx={{ + bgcolor: 'rgba(174,86,48,0.08)', color: c.accent.primary, + fontWeight: 500, fontSize: '0.78rem', height: 26, + }} + /> + + )} + + + Added by You + + + {selectedLocal.description && ( + + Description + + {selectedLocal.description} + + + )} + + + + ) : null} + + + {/* ─── Create/Edit Dialog ─── */} + setDialogOpen(false)} + maxWidth="md" + fullWidth + PaperProps={{ + sx: { bgcolor: c.bg.surface, backgroundImage: 'none', borderRadius: `${c.radius.lg}px`, border: `${c.border.width} solid ${c.border.subtle}`, boxShadow: c.shadow.lg }, + }} + > + + {editingId ? 'Edit Skill' : 'New Skill'} + + + setForm({ ...form, name: e.target.value })} + fullWidth + size="small" + sx={{ '& .MuiOutlinedInput-root': { bgcolor: c.bg.secondary } }} + /> + setForm({ ...form, description: e.target.value })} + fullWidth + size="small" + sx={{ '& .MuiOutlinedInput-root': { bgcolor: c.bg.secondary } }} + /> + setForm({ ...form, command: e.target.value })} + fullWidth + size="small" + placeholder="e.g. my-skill" + sx={{ '& .MuiOutlinedInput-root': { bgcolor: c.bg.secondary } }} + /> + setForm({ ...form, content: e.target.value })} + fullWidth + multiline + minRows={12} + maxRows={24} + sx={{ + '& .MuiOutlinedInput-root': { + bgcolor: c.bg.secondary, fontFamily: c.font.mono, fontSize: '0.85rem', + }, + }} + /> + + + + + + + + {/* ─── Skill Builder Chat ─── */} + + + {/* ─── Snackbar ─── */} + setSnackbar({ open: false, message: '' })} + anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} + > + setSnackbar({ open: false, message: '' })} + severity="success" + sx={{ bgcolor: c.status.successBg, color: c.status.success, border: `1px solid rgba(38,91,25,0.25)` }} + > + {snackbar.message} + + + + ); +}; + +export default Skills; diff --git a/frontend/src/app/pages/Templates/Templates.tsx b/frontend/src/app/pages/Templates/Templates.tsx new file mode 100644 index 00000000..15e60e0a --- /dev/null +++ b/frontend/src/app/pages/Templates/Templates.tsx @@ -0,0 +1,474 @@ +import React, { useEffect, useState } from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import Button from '@mui/material/Button'; +import IconButton from '@mui/material/IconButton'; +import Dialog from '@mui/material/Dialog'; +import DialogTitle from '@mui/material/DialogTitle'; +import DialogContent from '@mui/material/DialogContent'; +import DialogActions from '@mui/material/DialogActions'; +import TextField from '@mui/material/TextField'; +import MenuItem from '@mui/material/MenuItem'; +import Chip from '@mui/material/Chip'; +import CircularProgress from '@mui/material/CircularProgress'; +import AddIcon from '@mui/icons-material/Add'; +import DeleteIcon from '@mui/icons-material/Delete'; +import EditIcon from '@mui/icons-material/Edit'; +import RemoveCircleOutlineIcon from '@mui/icons-material/RemoveCircleOutline'; +import { useAppDispatch, useAppSelector } from '@/shared/hooks'; +import { + fetchTemplates, + createTemplate, + updateTemplate, + deleteTemplate, + PromptTemplate, + TemplateField, +} from '@/shared/state/templatesSlice'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; + +const FIELD_TYPES = ['str', 'int', 'float', 'select', 'multi-select', 'literal'] as const; + +interface EditorState { + name: string; + description: string; + template: string; + fields: TemplateField[]; + tags: string[]; +} + +const emptyEditor: EditorState = { + name: '', + description: '', + template: '', + fields: [], + tags: [], +}; + +const Templates: React.FC = () => { + const c = useClaudeTokens(); + + const inputSx = { + '& .MuiOutlinedInput-root': { + color: c.text.primary, + '& fieldset': { borderColor: c.border.strong }, + '&:hover fieldset': { borderColor: c.text.tertiary }, + '&.Mui-focused fieldset': { borderColor: c.accent.primary }, + }, + '& .MuiInputLabel-root': { color: c.text.tertiary }, + '& .MuiInputLabel-root.Mui-focused': { color: c.accent.primary }, + }; + + const claudePaperProps = { + sx: { + bgcolor: c.bg.surface, + color: c.text.primary, + borderRadius: 4, + border: `1px solid ${c.border.subtle}`, + maxHeight: '90vh', + }, + }; + + const dispatch = useAppDispatch(); + const { items, loading } = useAppSelector((s) => s.templates); + const templates = Object.values(items); + + const [editorOpen, setEditorOpen] = useState(false); + const [editingId, setEditingId] = useState(null); + const [editor, setEditor] = useState(emptyEditor); + const [tagInput, setTagInput] = useState(''); + + useEffect(() => { + dispatch(fetchTemplates()); + }, [dispatch]); + + const openNew = () => { + setEditingId(null); + setEditor(emptyEditor); + setTagInput(''); + setEditorOpen(true); + }; + + const openEdit = (t: PromptTemplate) => { + setEditingId(t.id); + setEditor({ + name: t.name, + description: t.description, + template: t.template, + fields: t.fields.map((f) => ({ ...f })), + tags: [...t.tags], + }); + setTagInput(''); + setEditorOpen(true); + }; + + const handleSave = async () => { + if (!editor.name.trim() || !editor.template.trim()) return; + if (editingId) { + await dispatch(updateTemplate({ id: editingId, ...editor })); + } else { + await dispatch(createTemplate(editor)); + } + setEditorOpen(false); + }; + + const handleDelete = async (id: string) => { + await dispatch(deleteTemplate(id)); + }; + + const addField = () => { + setEditor((prev) => ({ + ...prev, + fields: [...prev.fields, { name: '', type: 'str', required: true }], + })); + }; + + const updateField = (idx: number, patch: Partial) => { + setEditor((prev) => ({ + ...prev, + fields: prev.fields.map((f, i) => (i === idx ? { ...f, ...patch } : f)), + })); + }; + + const removeField = (idx: number) => { + setEditor((prev) => ({ + ...prev, + fields: prev.fields.filter((_, i) => i !== idx), + })); + }; + + const addTag = () => { + const tag = tagInput.trim(); + if (tag && !editor.tags.includes(tag)) { + setEditor((prev) => ({ ...prev, tags: [...prev.tags, tag] })); + setTagInput(''); + } + }; + + const removeTag = (tag: string) => { + setEditor((prev) => ({ ...prev, tags: prev.tags.filter((t) => t !== tag) })); + }; + + return ( + + + + + Prompt Templates + + + Create and manage reusable prompt templates with structured input fields. + + + + + + {loading ? ( + + + + ) : templates.length === 0 ? ( + + No templates yet + Click "New Template" to get started. + + ) : ( + + {templates.map((t) => ( + openEdit(t)} + > + + + {t.name} + + + { e.stopPropagation(); openEdit(t); }} + sx={{ color: c.text.tertiary, '&:hover': { color: c.accent.primary } }} + > + + + { e.stopPropagation(); handleDelete(t.id); }} + sx={{ color: c.text.tertiary, '&:hover': { color: c.status.error } }} + > + + + + + {t.description && ( + + {t.description} + + )} + + + {t.tags.map((tag) => ( + + ))} + + + ))} + + )} + + {/* Editor Dialog */} + setEditorOpen(false)} + maxWidth="md" + fullWidth + PaperProps={claudePaperProps} + > + + {editingId ? 'Edit Template' : 'New Template'} + + + setEditor((p) => ({ ...p, name: e.target.value }))} + fullWidth + size="small" + sx={inputSx} + /> + setEditor((p) => ({ ...p, description: e.target.value }))} + fullWidth + size="small" + multiline + rows={2} + sx={inputSx} + /> + setEditor((p) => ({ ...p, template: e.target.value }))} + fullWidth + size="small" + multiline + rows={5} + sx={{ + ...inputSx, + '& .MuiOutlinedInput-root': { + ...inputSx['& .MuiOutlinedInput-root'], + fontFamily: c.font.mono, + fontSize: '0.85rem', + }, + }} + /> + + {/* Fields */} + + + + Fields + + + + {editor.fields.map((field, idx) => ( + + updateField(idx, { name: e.target.value })} + size="small" + sx={{ ...inputSx, flex: 1 }} + /> + updateField(idx, { type: e.target.value as TemplateField['type'] })} + size="small" + sx={{ ...inputSx, minWidth: 130 }} + SelectProps={{ MenuProps: { PaperProps: { sx: { bgcolor: c.bg.surface, color: c.text.primary } } } }} + > + {FIELD_TYPES.map((ft) => ( + {ft} + ))} + + {(field.type === 'select' || field.type === 'multi-select') && ( + + updateField(idx, { + options: e.target.value.split(',').map((s) => s.trim()).filter(Boolean), + }) + } + size="small" + sx={{ ...inputSx, flex: 1 }} + /> + )} + updateField(idx, { default: e.target.value || undefined })} + size="small" + sx={{ ...inputSx, flex: 0.7 }} + /> + removeField(idx)} + sx={{ color: c.text.tertiary, mt: 0.5, '&:hover': { color: c.status.error } }} + > + + + + ))} + + + {/* Tags */} + + + Tags + + + {editor.tags.map((tag) => ( + removeTag(tag)} + sx={{ + bgcolor: c.bg.secondary, + color: c.text.muted, + '& .MuiChip-deleteIcon': { color: c.text.tertiary, '&:hover': { color: c.status.error } }, + }} + /> + ))} + setTagInput(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter') { + e.preventDefault(); + addTag(); + } + }} + size="small" + sx={{ ...inputSx, width: 140 }} + /> + + + + + + + + + + ); +}; + +export default Templates; diff --git a/frontend/src/app/pages/Tools/Tools.tsx b/frontend/src/app/pages/Tools/Tools.tsx new file mode 100644 index 00000000..b7834bfb --- /dev/null +++ b/frontend/src/app/pages/Tools/Tools.tsx @@ -0,0 +1,1761 @@ +import React, { useEffect, useState, useMemo, useCallback, useRef } from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import Button from '@mui/material/Button'; +import Card from '@mui/material/Card'; +import CardContent from '@mui/material/CardContent'; +import Dialog from '@mui/material/Dialog'; +import DialogTitle from '@mui/material/DialogTitle'; +import DialogContent from '@mui/material/DialogContent'; +import DialogActions from '@mui/material/DialogActions'; +import TextField from '@mui/material/TextField'; +import MenuItem from '@mui/material/MenuItem'; +import Select from '@mui/material/Select'; +import FormControl from '@mui/material/FormControl'; +import InputLabel from '@mui/material/InputLabel'; +import IconButton from '@mui/material/IconButton'; +import Chip from '@mui/material/Chip'; +import CircularProgress from '@mui/material/CircularProgress'; +import Tooltip from '@mui/material/Tooltip'; +import Collapse from '@mui/material/Collapse'; +import Menu from '@mui/material/Menu'; +import Snackbar from '@mui/material/Snackbar'; +import Alert from '@mui/material/Alert'; +import InputAdornment from '@mui/material/InputAdornment'; +import Avatar from '@mui/material/Avatar'; +import Switch from '@mui/material/Switch'; +import AddIcon from '@mui/icons-material/Add'; +import EditIcon from '@mui/icons-material/Edit'; +import DeleteIcon from '@mui/icons-material/Delete'; +import TerminalIcon from '@mui/icons-material/Terminal'; +import BuildIcon from '@mui/icons-material/Build'; +import ExtensionIcon from '@mui/icons-material/Extension'; +import DescriptionIcon from '@mui/icons-material/Description'; +import SearchIcon from '@mui/icons-material/Search'; +import QuestionAnswerIcon from '@mui/icons-material/QuestionAnswer'; +import LockIcon from '@mui/icons-material/Lock'; +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight'; +import ScheduleIcon from '@mui/icons-material/Schedule'; +import MapIcon from '@mui/icons-material/Map'; +import HourglassEmptyIcon from '@mui/icons-material/HourglassEmpty'; +import StorefrontIcon from '@mui/icons-material/Storefront'; +import OpenInNewIcon from '@mui/icons-material/OpenInNew'; +import DownloadIcon from '@mui/icons-material/Download'; +import StarIcon from '@mui/icons-material/Star'; +import SortIcon from '@mui/icons-material/Sort'; +import CloudIcon from '@mui/icons-material/Cloud'; +import PublicIcon from '@mui/icons-material/Public'; +import ToggleButton from '@mui/material/ToggleButton'; +import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; +import { useAppDispatch, useAppSelector } from '@/shared/hooks'; +import LinkIcon from '@mui/icons-material/Link'; +import CheckCircleIcon from '@mui/icons-material/CheckCircle'; +import SettingsIcon from '@mui/icons-material/Settings'; +import BlockIcon from '@mui/icons-material/Block'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import SecurityIcon from '@mui/icons-material/Security'; +import PanToolIcon from '@mui/icons-material/PanTool'; +import RefreshIcon from '@mui/icons-material/Refresh'; +import { + fetchTools, + fetchBuiltinTools, + fetchBuiltinPermissions, + updateBuiltinPermissions, + createTool, + updateTool, + deleteTool, + startOAuth, + fetchToolStatus, + discoverTools, + ToolDefinition, + BuiltinTool, +} from '@/shared/state/toolsSlice'; +import { + searchRegistry, + fetchRegistryStats, + McpServer, +} from '@/shared/state/mcpRegistrySlice'; +import { + fetchOutputs, + updateOutput, + Output, +} from '@/shared/state/outputsSlice'; + +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; +import ViewQuiltIcon from '@mui/icons-material/ViewQuilt'; + +interface CredentialField { + key: string; + label: string; + placeholder: string; + helpText?: string; +} + +interface Integration { + id: string; + name: string; + description: string; + mcp_config: Record; + color: string; + website: string; + icon: React.ReactNode; + credentialFields?: CredentialField[]; + connectLabel?: string; + connectInstructions?: string; + authType?: 'none' | 'oauth2' | 'env_vars'; +} + +const INTEGRATIONS: Integration[] = [ + { + id: 'xbird', + name: 'xbird', + description: 'Twitter/X research — search tweets, read profiles, threads, timelines.', + mcp_config: { type: 'stdio', command: 'bunx', args: ['@checkra1n/xbird'] }, + color: '#1DA1F2', + website: 'https://xbird.dev', + icon: '𝕏', + connectLabel: 'Connect 𝕏', + connectInstructions: 'Open x.com in your browser, press F12 → Application → Cookies → x.com, and copy the values for auth_token and ct0.', + credentialFields: [ + { key: 'TWITTER_AUTH_TOKEN', label: 'auth_token', placeholder: 'Paste auth_token cookie value' }, + { key: 'TWITTER_CT0', label: 'ct0', placeholder: 'Paste ct0 cookie value' }, + ], + }, + { + id: 'reddit', + name: 'Reddit', + description: 'Browse subreddits, search posts, get post details, analyze users. No API keys required.', + mcp_config: { type: 'stdio', command: 'npx', args: ['-y', 'reddit-mcp-buddy'] }, + color: '#FF4500', + website: 'https://github.com/karanb192/reddit-mcp-buddy', + icon: ( + + + + + ), + }, + { + id: 'google-workspace', + name: 'Google Workspace', + description: 'Including Google Docs, Sheets, Slides, Calendar, and Gmail. (Gemini CLI extension)', + mcp_config: { type: 'stdio', command: 'uvx', args: ['--from', 'google-workspace-mcp', 'google-workspace-worker'] }, + color: '#4285F4', + website: 'https://developers.google.com/gemini-api/docs/mcp', + icon: ( + + + + + + + ), + authType: 'oauth2', + }, +]; + +const CATEGORY_ORDER = ['filesystem', 'system', 'search', 'interaction', 'planning', 'scheduling']; + +interface ToolForm { + name: string; + description: string; + command: string; +} + +const emptyForm: ToolForm = { + name: '', + description: '', + command: '', +}; + +function cleanServerName(name: string): string { + const parts = name.split('/'); + return parts[parts.length - 1]; +} + +function serverToToolForm(srv: McpServer): ToolForm { + return { + name: srv.title || cleanServerName(srv.name), + description: srv.description, + command: '', + }; +} + +function serverToMcpConfig(srv: McpServer): Record { + if (srv.remoteUrl) { + return { type: srv.remoteType === 'sse' ? 'sse' : 'http', url: srv.remoteUrl }; + } + if (srv.repositoryUrl && srv.repositoryUrl.includes('github.com')) { + const match = srv.repositoryUrl.match(/github\.com\/([^/]+\/[^/]+?)(?:\.git)?(?:\/|$)/); + if (match) { + return { type: 'stdio', command: 'npx', args: ['-y', `github:${match[1]}`] }; + } + } + return {}; +} + +// --------------------------------------------------------------------------- +// ToolSection (reusable for Core / Extended built-in tool groups) +// --------------------------------------------------------------------------- + +interface ToolSectionProps { + label: string; + icon: React.ReactElement; + count: number; + open: boolean; + onToggle: () => void; + grouped: Record; + collapsedCategories: Record; + toggleCategory: (cat: string) => void; + expandedBuiltin: string | null; + toggleBuiltinExpand: (name: string) => void; + deferred?: boolean; + builtinPermissions: Record; + onPermissionChange: (toolName: string, policy: string) => void; + onCategoryPermissionChange: (toolNames: string[], policy: string) => void; + enabled: boolean; + onEnabledChange: (enabled: boolean) => void; +} + +const ToolSection: React.FC = ({ + label, icon, count, open, onToggle, + grouped, collapsedCategories, toggleCategory, + expandedBuiltin, toggleBuiltinExpand, deferred, + builtinPermissions, onPermissionChange, onCategoryPermissionChange, + enabled, onEnabledChange, +}) => { + const c = useClaudeTokens(); + + const CATEGORY_META: Record = { + filesystem: { label: 'Filesystem', color: c.status.success, icon: }, + system: { label: 'System', color: c.status.warning, icon: }, + search: { label: 'Search', color: '#3b82f6', icon: }, + interaction: { label: 'Interaction', color: '#a855f7', icon: }, + planning: { label: 'Planning', color: '#ec4899', icon: }, + scheduling: { label: 'Scheduling', color: '#14b8a6', icon: }, + }; + + const PermToggle = ({ value, onChange, size = 16 }: { value: string; onChange: (v: string) => void; size?: number }) => ( + e.stopPropagation()}> + onChange('always_allow')} sx={{ p: 0.4, borderRadius: 1, bgcolor: value === 'always_allow' ? `${c.status.success}20` : 'transparent', color: value === 'always_allow' ? c.status.success : c.text.ghost, '&:hover': { bgcolor: `${c.status.success}15`, color: c.status.success } }}> + onChange('ask')} sx={{ p: 0.4, borderRadius: 1, bgcolor: value === 'ask' ? `${c.status.warning}20` : 'transparent', color: value === 'ask' ? c.status.warning : c.text.ghost, '&:hover': { bgcolor: `${c.status.warning}15`, color: c.status.warning } }}> + onChange('deny')} sx={{ p: 0.4, borderRadius: 1, bgcolor: value === 'deny' ? `${c.status.error}20` : 'transparent', color: value === 'deny' ? c.status.error : c.text.ghost, '&:hover': { bgcolor: `${c.status.error}15`, color: c.status.error } }}> + + ); + + const getCatGroupPolicy = (tools: BuiltinTool[]) => { + const policies = tools.map((t) => builtinPermissions[t.name] || 'always_allow'); + if (policies.every((p) => p === 'always_allow')) return 'always_allow'; + if (policies.every((p) => p === 'deny')) return 'deny'; + if (policies.every((p) => p === 'ask')) return 'ask'; + return 'mixed'; + }; + + const allSectionTools = CATEGORY_ORDER.filter((cat) => grouped[cat]).flatMap((cat) => grouped[cat]); + const overallPolicy = getCatGroupPolicy(allSectionTools); + const categoryCount = CATEGORY_ORDER.filter((cat) => grouped[cat]).length; + const sectionDescription = deferred + ? 'On-demand tools loaded via ToolSearch for planning, scheduling, and extended operations' + : 'Built-in Claude Agent SDK tools for file operations, shell commands, and search'; + + const firstSentence = (desc: string) => { + if (!desc) return ''; + const match = desc.match(/^(.+?(?:\.|$))/); + return match ? match[1].trim() : desc.substring(0, 100); + }; + + return ( + + + enabled && onToggle()} + sx={{ display: 'flex', alignItems: 'center', gap: 2, cursor: enabled ? 'pointer' : 'default' }} + > + + {icon} + + + + {label} + + {deferred && ( + + )} + + {sectionDescription} + + e.stopPropagation()}> + onEnabledChange(checked)} + sx={{ + '& .MuiSwitch-switchBase.Mui-checked': { color: c.accent.primary }, + '& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track': { bgcolor: c.accent.primary }, + }} + /> + + {enabled && ( + + + + )} + + + + + + + + Tool Permissions + + + + + {CATEGORY_ORDER.filter((cat) => grouped[cat]).map((cat) => { + const meta = CATEGORY_META[cat] || CATEGORY_META.filesystem; + const catTools = grouped[cat]; + const colKey = `${deferred ? 'd_' : ''}${cat}`; + const isOpen = !collapsedCategories[colKey]; + const catPolicy = getCatGroupPolicy(catTools); + return ( + + toggleCategory(colKey)} + > + + + {meta.label} + + + onCategoryPermissionChange(catTools.map((t) => t.name), v)} /> + + + + {catTools.map((bt) => { + const toolPolicy = builtinPermissions[bt.name] || 'always_allow'; + return ( + + + {bt.name} + {bt.description && {firstSentence(bt.description)}} + + onPermissionChange(bt.name, v)} size={14} /> + + ); + })} + + + + ); + })} + + + + + ); +}; + +// --------------------------------------------------------------------------- +// Main Tools Page +// --------------------------------------------------------------------------- + +const Tools: React.FC = () => { + const c = useClaudeTokens(); + const dispatch = useAppDispatch(); + const { items, builtinTools, builtinPermissions, loading } = useAppSelector((s) => s.tools); + const { servers: regServers, total: regTotal, loading: regLoading, stats: regStats } = useAppSelector((s) => s.mcpRegistry); + const outputItems = useAppSelector((s) => s.outputs.items); + const outputs = useMemo(() => Object.values(outputItems), [outputItems]); + const allTools = Object.values(items); + const tools = allTools; + const uninstalledIntegrations = useMemo(() => INTEGRATIONS.filter((ig) => !allTools.find((t) => t.name === ig.name)), [allTools]); + const getIntegrationForTool = useCallback((tool: ToolDefinition) => INTEGRATIONS.find((ig) => ig.name === tool.name), []); + + const [dialogOpen, setDialogOpen] = useState(false); + const [editingId, setEditingId] = useState(null); + const [form, setForm] = useState(emptyForm); + + const [collapsedCategories, setCollapsedCategories] = useState>( + Object.fromEntries([ + ...CATEGORY_ORDER.map((cat) => [cat, true]), + ...CATEGORY_ORDER.map((cat) => [`d_${cat}`, true]), + ]), + ); + const [expandedBuiltin, setExpandedBuiltin] = useState(null); + const [coreSectionOpen, setCoreSectionOpen] = useState(false); + const [deferredSectionOpen, setDeferredSectionOpen] = useState(false); + const [customSectionOpen, setCustomSectionOpen] = useState(true); + + // Dropdown menu + const [menuAnchor, setMenuAnchor] = useState(null); + + // Registry browser + const [registryOpen, setRegistryOpen] = useState(false); + const [regQuery, setRegQuery] = useState(''); + const [regSort, setRegSort] = useState<'name' | 'stars'>('name'); + const [regSource, setRegSource] = useState<'' | 'community' | 'google'>(''); + const [expandedServer, setExpandedServer] = useState(null); + const [snackbar, setSnackbar] = useState<{ open: boolean; message: string; severity?: 'success' | 'error' }>({ open: false, message: '' }); + const debounceRef = useRef | null>(null); + + // MCP config dialog state + const [mcpConfigOpen, setMcpConfigOpen] = useState(false); + const [mcpConfigServer, setMcpConfigServer] = useState(null); + const [mcpAuthType, setMcpAuthType] = useState<'none' | 'env_vars'>('none'); + const [mcpCredentials, setMcpCredentials] = useState>({}); + const [mcpConfigJson, setMcpConfigJson] = useState(''); + const [mcpConfigError, setMcpConfigError] = useState(''); + + // Expanded MCP tool permissions state + const [expandedToolId, setExpandedToolId] = useState(null); + const [discovering, setDiscovering] = useState(false); + + // Integration toggle state + const [integrationLoading, setIntegrationLoading] = useState>({}); + + // Integration credentials dialog state + const [credDialogOpen, setCredDialogOpen] = useState(false); + const [credDialogToolId, setCredDialogToolId] = useState(null); + const [credDialogIntegration, setCredDialogIntegration] = useState(null); + const [credDialogValues, setCredDialogValues] = useState>({}); + const [credDialogSaving, setCredDialogSaving] = useState(false); + + const getInstalledIntegration = useCallback((integration: Integration): ToolDefinition | undefined => { + return allTools.find((t) => t.name === integration.name); + }, [allTools]); + + const handleIntegrationToggle = async (integration: Integration) => { + const existing = getInstalledIntegration(integration); + setIntegrationLoading((p) => ({ ...p, [integration.id]: true })); + try { + if (existing && existing.enabled !== false) { + await dispatch(updateTool({ id: existing.id, enabled: false })); + setSnackbar({ open: true, message: `Disabled ${integration.name}` }); + } else if (existing && existing.enabled === false) { + await dispatch(updateTool({ id: existing.id, enabled: true })); + if (integration.authType === 'oauth2' && existing.auth_status !== 'connected') { + setSnackbar({ open: true, message: `Enabled ${integration.name} — connect your account to discover tools` }); + } else { + setSnackbar({ open: true, message: `Enabled ${integration.name} — re-discovering tools…` }); + const discoverResult = await dispatch(discoverTools(existing.id)); + if (discoverTools.fulfilled.match(discoverResult)) { + setSnackbar({ open: true, message: `${integration.name} ready — tools discovered` }); + } else { + setSnackbar({ open: true, message: `${integration.name} enabled but discovery failed`, severity: 'error' }); + } + } + } else { + const result = await dispatch(createTool({ + name: integration.name, + description: integration.description, + command: '', + mcp_config: integration.mcp_config, + credentials: {}, + auth_type: integration.authType || 'none', + auth_status: 'configured', + })); + if (createTool.fulfilled.match(result)) { + const newTool = result.payload; + if (integration.authType === 'oauth2') { + setSnackbar({ open: true, message: `Enabled ${integration.name} — connect your account to discover tools` }); + } else { + setSnackbar({ open: true, message: `Enabled ${integration.name} — discovering tools…` }); + const discoverResult = await dispatch(discoverTools(newTool.id)); + if (discoverTools.fulfilled.match(discoverResult)) { + setSnackbar({ open: true, message: `${integration.name} ready — tools discovered` }); + } else { + setSnackbar({ open: true, message: `${integration.name} enabled but discovery failed — is ${integration.mcp_config.command || 'the server'} installed?`, severity: 'error' }); + } + } + } + } + } finally { + setIntegrationLoading((p) => ({ ...p, [integration.id]: false })); + } + }; + + const handleDiscover = async (toolId: string) => { + setDiscovering(true); + try { + const result = await dispatch(discoverTools(toolId)); + if (discoverTools.fulfilled.match(result)) { + setSnackbar({ open: true, message: 'Tools discovered successfully' }); + } else { + setSnackbar({ open: true, message: 'Discovery failed — is the MCP server running?', severity: 'error' }); + } + } finally { + setDiscovering(false); + } + }; + + const handlePermissionChange = async (toolId: string, toolName: string, policy: string) => { + const tool = items[toolId]; + if (!tool) return; + const updated = { ...tool.tool_permissions, [toolName]: policy }; + await dispatch(updateTool({ id: toolId, tool_permissions: updated })); + }; + + const handleGroupPermissionChange = async (toolId: string, names: string[], policy: string) => { + const tool = items[toolId]; + if (!tool) return; + const updated = { ...tool.tool_permissions }; + for (const name of names) updated[name] = policy; + await dispatch(updateTool({ id: toolId, tool_permissions: updated })); + }; + + const handleBulkReadOnly = async (toolId: string) => { + const tool = items[toolId]; + if (!tool?.tool_permissions?._categories) return; + const readNames: string[] = tool.tool_permissions._categories.read || []; + const updated = { ...tool.tool_permissions }; + for (const name of readNames) updated[name] = 'always_allow'; + await dispatch(updateTool({ id: toolId, tool_permissions: updated })); + }; + + const handleResetPermissions = async (toolId: string) => { + const tool = items[toolId]; + if (!tool?.tool_permissions) return; + const updated = { ...tool.tool_permissions }; + for (const key of Object.keys(updated)) { + if (!key.startsWith('_')) updated[key] = 'ask'; + } + await dispatch(updateTool({ id: toolId, tool_permissions: updated })); + }; + + const [expandedServices, setExpandedServices] = useState>({}); + + const [viewsSectionOpen, setViewsSectionOpen] = useState(false); + const [builtinSectionOpen, setBuiltinSectionOpen] = useState(true); + + useEffect(() => { + dispatch(fetchTools()); + dispatch(fetchBuiltinTools()); + dispatch(fetchBuiltinPermissions()); + dispatch(fetchOutputs()); + }, [dispatch]); + + const handleViewPermissionChange = async (viewId: string, permission: string) => { + await dispatch(updateOutput({ id: viewId, permission })); + }; + + const handleBuiltinPermissionChange = async (toolName: string, policy: string) => { + await dispatch(updateBuiltinPermissions({ [toolName]: policy })); + }; + + const handleBuiltinCategoryPermissionChange = async (toolNames: string[], policy: string) => { + const perms: Record = {}; + for (const name of toolNames) perms[name] = policy; + await dispatch(updateBuiltinPermissions(perms)); + }; + + // Built-in tool grouping + const coreTools = useMemo(() => builtinTools.filter((bt) => !bt.deferred), [builtinTools]); + const deferredTools = useMemo(() => builtinTools.filter((bt) => bt.deferred), [builtinTools]); + const groupTools = (list: BuiltinTool[]) => { + const g: Record = {}; + for (const bt of list) { if (!g[bt.category]) g[bt.category] = []; g[bt.category].push(bt); } + return g; + }; + const groupedCore = useMemo(() => groupTools(coreTools), [coreTools]); + const groupedDeferred = useMemo(() => groupTools(deferredTools), [deferredTools]); + + const coreSectionEnabled = useMemo( + () => !coreTools.every((t) => builtinPermissions[t.name] === 'deny'), + [coreTools, builtinPermissions], + ); + const deferredSectionEnabled = useMemo( + () => !deferredTools.every((t) => builtinPermissions[t.name] === 'deny'), + [deferredTools, builtinPermissions], + ); + const viewsSectionEnabled = useMemo( + () => !outputs.every((o) => o.permission === 'deny'), + [outputs], + ); + + const handleSectionEnabledChange = async (tools: BuiltinTool[], enabled: boolean) => { + const perms: Record = {}; + for (const t of tools) perms[t.name] = enabled ? 'ask' : 'deny'; + await dispatch(updateBuiltinPermissions(perms)); + }; + + const handleViewsSectionEnabledChange = async (enabled: boolean) => { + for (const out of outputs) { + await dispatch(updateOutput({ id: out.id, permission: enabled ? 'ask' : 'deny' })); + } + }; + + const toggleCategory = (cat: string) => setCollapsedCategories((p) => ({ ...p, [cat]: !p[cat] })); + const toggleBuiltinExpand = (name: string) => setExpandedBuiltin((p) => (p === name ? null : name)); + + // --------------- Dropdown handlers --------------- + + const handleMenuOpen = (e: React.MouseEvent) => setMenuAnchor(e.currentTarget); + const handleMenuClose = () => setMenuAnchor(null); + + const openCreate = () => { + handleMenuClose(); + setEditingId(null); + setForm(emptyForm); + setDialogOpen(true); + }; + + const openRegistryBrowser = () => { + handleMenuClose(); + setRegistryOpen(true); + setRegQuery(''); + setRegSort('name'); + setRegSource(''); + setExpandedServer(null); + dispatch(fetchRegistryStats()); + dispatch(searchRegistry({ q: '', limit: 20, offset: 0, sort: 'name', source: '' })); + }; + + // --------------- Tool CRUD --------------- + + const openEdit = (tool: ToolDefinition) => { + setEditingId(tool.id); + setForm({ name: tool.name, description: tool.description, command: tool.command }); + setDialogOpen(true); + }; + + const handleSave = async () => { + const payload = { name: form.name, description: form.description, command: form.command }; + if (editingId) { await dispatch(updateTool({ id: editingId, ...payload })); } else { await dispatch(createTool(payload)); } + setDialogOpen(false); + }; + + const handleDelete = async (id: string) => { await dispatch(deleteTool(id)); }; + + // --------------- Registry browser --------------- + + const handleRegSearch = useCallback((q: string, sort?: 'name' | 'stars', source?: '' | 'community' | 'google') => { + setRegQuery(q); + setExpandedServer(null); + const sortVal = sort ?? regSort; + const sourceVal = source ?? regSource; + if (debounceRef.current) clearTimeout(debounceRef.current); + debounceRef.current = setTimeout(() => { + dispatch(searchRegistry({ q, limit: 20, offset: 0, sort: sortVal, source: sourceVal })); + }, 300); + }, [dispatch, regSort, regSource]); + + const handleLoadMore = () => { + dispatch(searchRegistry({ q: regQuery, limit: 20, offset: regServers.length, sort: regSort, source: regSource })); + }; + + const handleRegSort = (sort: 'name' | 'stars') => { + setRegSort(sort); + setExpandedServer(null); + dispatch(searchRegistry({ q: regQuery, limit: 20, offset: 0, sort, source: regSource })); + }; + + const handleRegSourceFilter = (_: React.MouseEvent, val: '' | 'community' | 'google') => { + if (val === null) return; + setRegSource(val); + setExpandedServer(null); + dispatch(searchRegistry({ q: regQuery, limit: 20, offset: 0, sort: regSort, source: val })); + }; + + const openMcpConfigDialog = (srv: McpServer) => { + setMcpConfigServer(srv); + setMcpAuthType('none'); + setMcpCredentials({}); + const derivedConfig = serverToMcpConfig(srv); + setMcpConfigJson(JSON.stringify( + Object.keys(derivedConfig).length > 0 ? derivedConfig : {}, + null, 2, + )); + setMcpConfigError(''); + setMcpConfigOpen(true); + }; + + const handleMcpConfigSave = async () => { + if (!mcpConfigServer) return; + let parsedConfig: Record = {}; + try { parsedConfig = JSON.parse(mcpConfigJson); } catch { setMcpConfigError('Invalid JSON'); return; } + + const f = serverToToolForm(mcpConfigServer); + const isStdioConfig = parsedConfig.type === 'stdio' || !!parsedConfig.command; + const authStatus = (mcpAuthType !== 'none' || isStdioConfig) ? 'configured' : 'none'; + + await dispatch(createTool({ + name: f.name, + description: f.description, + command: '', + mcp_config: parsedConfig, + credentials: mcpCredentials, + auth_type: mcpAuthType, + auth_status: authStatus, + })); + + setMcpConfigOpen(false); + setSnackbar({ open: true, message: `Installed "${f.name}" as MCP tool` }); + }; + + const handleInstall = async (srv: McpServer) => { + const f = serverToToolForm(srv); + const mcpConfig = serverToMcpConfig(srv); + const hasConfig = Object.keys(mcpConfig).length > 0; + + if (srv.source === 'google' && srv.remoteUrl && hasConfig) { + await dispatch(createTool({ + name: f.name, + description: f.description, + command: '', + mcp_config: mcpConfig, + credentials: {}, + auth_type: 'oauth2', + auth_status: 'configured', + })); + setSnackbar({ open: true, message: `Installed "${f.name}" — click "Connect Google" to authorize` }); + } else if (hasConfig && mcpConfig.type === 'stdio') { + const result = await dispatch(createTool({ + name: f.name, + description: f.description, + command: '', + mcp_config: mcpConfig, + credentials: {}, + auth_type: 'none', + auth_status: 'configured', + })); + if (createTool.fulfilled.match(result)) { + const newTool = result.payload; + setSnackbar({ open: true, message: `Installed "${f.name}" — discovering tools…` }); + const discoverResult = await dispatch(discoverTools(newTool.id)); + if (discoverTools.fulfilled.match(discoverResult)) { + setSnackbar({ open: true, message: `${f.name} ready — tools discovered` }); + } else { + setSnackbar({ open: true, message: `${f.name} installed but discovery failed — the MCP server may need setup first`, severity: 'error' }); + } + } + } else { + openMcpConfigDialog(srv); + } + }; + + const handleEditInstall = (srv: McpServer) => { + setRegistryOpen(false); + const f = serverToToolForm(srv); + setEditingId(null); + setForm(f); + setDialogOpen(true); + }; + + const handleOAuthConnect = async (toolId: string) => { + const result = await dispatch(startOAuth(toolId)); + if (startOAuth.fulfilled.match(result)) { + const { auth_url } = result.payload; + const popup = window.open(auth_url, 'oauth', 'width=500,height=700,left=200,top=100'); + + const afterConnect = async () => { + const statusResult = await dispatch(fetchToolStatus(toolId)); + if (fetchToolStatus.fulfilled.match(statusResult) && statusResult.payload.auth_status === 'connected') { + setSnackbar({ open: true, message: 'Google account connected! Discovering tools…' }); + setExpandedToolId(toolId); + dispatch(discoverTools(toolId)); + } else { + setSnackbar({ open: true, message: 'Google account connected!' }); + } + }; + + const onMessage = (event: MessageEvent) => { + if (event.data?.type === 'oauth_complete' && event.data?.tool_id === toolId) { + afterConnect(); + window.removeEventListener('message', onMessage); + } + }; + window.addEventListener('message', onMessage); + + const pollInterval = setInterval(() => { + if (popup?.closed) { + clearInterval(pollInterval); + afterConnect(); + window.removeEventListener('message', onMessage); + } + }, 1000); + } else { + setSnackbar({ open: true, message: 'OAuth failed — make sure GOOGLE_OAUTH_CLIENT_ID is set in backend .env', severity: 'error' }); + } + }; + + const openCredentialsDialog = (toolId: string, integration: Integration) => { + const tool = items[toolId]; + const existing = tool?.credentials || {}; + const initial: Record = {}; + for (const field of integration.credentialFields || []) { + initial[field.key] = existing[field.key] || ''; + } + setCredDialogToolId(toolId); + setCredDialogIntegration(integration); + setCredDialogValues(initial); + setCredDialogOpen(true); + }; + + const handleCredentialsSave = async () => { + if (!credDialogToolId || !credDialogIntegration) return; + const hasEmpty = (credDialogIntegration.credentialFields || []).some((f) => !credDialogValues[f.key]?.trim()); + if (hasEmpty) return; + + setCredDialogSaving(true); + try { + const result = await dispatch(updateTool({ + id: credDialogToolId, + credentials: credDialogValues, + auth_type: 'env_vars', + auth_status: 'connected', + })); + if (updateTool.fulfilled.match(result)) { + setCredDialogOpen(false); + setSnackbar({ open: true, message: `${credDialogIntegration.name} connected! Re-discovering tools…` }); + dispatch(discoverTools(credDialogToolId)); + } else { + setSnackbar({ open: true, message: 'Failed to save credentials', severity: 'error' }); + } + } finally { + setCredDialogSaving(false); + } + }; + + const handleDisconnectIntegration = async (toolId: string, integration: Integration) => { + await dispatch(updateTool({ + id: toolId, + credentials: {}, + auth_type: 'none', + auth_status: 'configured', + })); + setSnackbar({ open: true, message: `${integration.name} disconnected` }); + }; + + return ( + + {/* Header */} + + + Tool Library + Define and manage custom tools for your Claude Code agents. + + + + + + + Create Custom + + + + Browse MCP Registry + + + + + + {/* Built-in Tool Sets */} + + setBuiltinSectionOpen((v) => !v)} + sx={{ display: 'flex', alignItems: 'center', gap: 0.5, mb: 1, cursor: 'pointer', userSelect: 'none', '&:hover .section-arrow': { color: c.text.secondary } }} + > + {builtinSectionOpen ? : } + + Built-in Tool Sets + + + + + + {/* Core Tools */} + {coreTools.length > 0 && ( + } count={coreTools.length} open={coreSectionOpen} onToggle={() => setCoreSectionOpen((v) => !v)} grouped={groupedCore} collapsedCategories={collapsedCategories} toggleCategory={toggleCategory} expandedBuiltin={expandedBuiltin} toggleBuiltinExpand={toggleBuiltinExpand} builtinPermissions={builtinPermissions} onPermissionChange={handleBuiltinPermissionChange} onCategoryPermissionChange={handleBuiltinCategoryPermissionChange} enabled={coreSectionEnabled} onEnabledChange={(v) => handleSectionEnabledChange(coreTools, v)} /> + )} + + {/* Extended Tools */} + {deferredTools.length > 0 && ( + } count={deferredTools.length} open={deferredSectionOpen} onToggle={() => setDeferredSectionOpen((v) => !v)} grouped={groupedDeferred} collapsedCategories={collapsedCategories} toggleCategory={toggleCategory} expandedBuiltin={expandedBuiltin} toggleBuiltinExpand={toggleBuiltinExpand} deferred builtinPermissions={builtinPermissions} onPermissionChange={handleBuiltinPermissionChange} onCategoryPermissionChange={handleBuiltinCategoryPermissionChange} enabled={deferredSectionEnabled} onEnabledChange={(v) => handleSectionEnabledChange(deferredTools, v)} /> + )} + + {/* Views */} + {outputs.length > 0 && ( + + + viewsSectionEnabled && setViewsSectionOpen((v) => !v)} + sx={{ display: 'flex', alignItems: 'center', gap: 2, cursor: viewsSectionEnabled ? 'pointer' : 'default' }} + > + + + + + + Views + + + Dashboard views and data displays for your agent + + e.stopPropagation()}> + handleViewsSectionEnabledChange(checked)} + sx={{ + '& .MuiSwitch-switchBase.Mui-checked': { color: c.accent.primary }, + '& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track': { bgcolor: c.accent.primary }, + }} + /> + + {viewsSectionEnabled && ( + + + + )} + + + + + + {outputs.map((out) => { + const perm = out.permission || 'ask'; + return ( + + + + + + + + {out.name} + + + {out.description && ( + + {out.description} + + )} + + + e.stopPropagation()}> + handleViewPermissionChange(out.id, 'always_allow')} sx={{ p: 0.4, borderRadius: 1, bgcolor: perm === 'always_allow' ? `${c.status.success}20` : 'transparent', color: perm === 'always_allow' ? c.status.success : c.text.ghost, '&:hover': { bgcolor: `${c.status.success}15`, color: c.status.success } }}> + handleViewPermissionChange(out.id, 'ask')} sx={{ p: 0.4, borderRadius: 1, bgcolor: perm === 'ask' ? `${c.status.warning}20` : 'transparent', color: perm === 'ask' ? c.status.warning : c.text.ghost, '&:hover': { bgcolor: `${c.status.warning}15`, color: c.status.warning } }}> + handleViewPermissionChange(out.id, 'deny')} sx={{ p: 0.4, borderRadius: 1, bgcolor: perm === 'deny' ? `${c.status.error}20` : 'transparent', color: perm === 'deny' ? c.status.error : c.text.ghost, '&:hover': { bgcolor: `${c.status.error}15`, color: c.status.error } }}> + + + ); + })} + + + + + )} + + + + + + {/* Custom Tool Sets */} + + setCustomSectionOpen((v) => !v)} sx={{ display: 'flex', alignItems: 'center', gap: 0.5, mb: 1, cursor: 'pointer', userSelect: 'none', '&:hover .section-arrow': { color: c.text.secondary } }}> + {customSectionOpen ? : } + + Custom Tool Sets + + + + {loading ? ( + + ) : (tools.length === 0 && uninstalledIntegrations.length === 0) ? ( + + + No custom tools defined yet. Create one to get started. + + ) : ( + + {uninstalledIntegrations.map((ig) => { + const isLoading = !!integrationLoading[ig.id]; + return ( + + + + + {ig.icon} + + + + {ig.name} + } label="docs" size="small" sx={{ bgcolor: c.bg.secondary, color: c.text.ghost, fontSize: '0.65rem', height: 18, '& .MuiChip-label': { px: 0.4 }, '& .MuiChip-icon': { ml: 0.4, fontSize: 10 } }} /> + + {ig.description} + + + {isLoading && } + handleIntegrationToggle(ig)} + disabled={isLoading} + sx={{ + '& .MuiSwitch-switchBase.Mui-checked': { color: ig.color }, + '& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track': { bgcolor: ig.color }, + }} + /> + + + + + ); + })} + {tools.map((tool) => { + const ig = getIntegrationForTool(tool); + const isExpanded = expandedToolId === tool.id; + const isMcp = tool.mcp_config && Object.keys(tool.mcp_config).length > 0; + const isStdio = isMcp && (tool.mcp_config.type === 'stdio' || !!tool.mcp_config.command); + const canDiscover = isMcp && (isStdio || tool.auth_status !== 'none'); + const perms = tool.tool_permissions || {}; + const services = perms._services as Record | undefined; + const descriptions = (perms._tool_descriptions || {}) as Record; + const serviceNames = services ? Object.keys(services) : []; + const hasPerms = serviceNames.length > 0; + const totalToolCount = serviceNames.reduce((acc, s) => acc + (services![s].read?.length || 0) + (services![s].write?.length || 0), 0); + + const toDisplayName = (name: string, serviceName?: string) => { + let display = name.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()); + if (serviceName) { + const svcLower = serviceName.toLowerCase(); + const variants = [svcLower, svcLower.replace(/s$/, '')]; + for (const v of variants) { + display = display.replace(new RegExp(`\\b${v}\\b`, 'gi'), '').trim(); + } + display = display.replace(/\s{2,}/g, ' ').trim(); + } + return display; + }; + + const firstSentence = (desc: string) => { + if (!desc) return ''; + const match = desc.match(/^(.+?(?:\.|$))/); + return match ? match[1].trim() : desc.substring(0, 100); + }; + + const getGroupPolicy = (names: string[]) => { + if (names.length === 0) return 'ask'; + const policies = names.map((n) => perms[n] || 'ask'); + if (policies.every((p) => p === 'always_allow')) return 'always_allow'; + if (policies.every((p) => p === 'deny')) return 'deny'; + if (policies.every((p) => p === 'ask')) return 'ask'; + return 'mixed'; + }; + + const PermToggle = ({ value, onChange, size = 16 }: { value: string; onChange: (v: string) => void; size?: number }) => ( + e.stopPropagation()}> + onChange('always_allow')} sx={{ p: 0.4, borderRadius: 1, bgcolor: value === 'always_allow' ? `${c.status.success}20` : 'transparent', color: value === 'always_allow' ? c.status.success : c.text.ghost, '&:hover': { bgcolor: `${c.status.success}15`, color: c.status.success } }}> + onChange('ask')} sx={{ p: 0.4, borderRadius: 1, bgcolor: value === 'ask' ? `${c.status.warning}20` : 'transparent', color: value === 'ask' ? c.status.warning : c.text.ghost, '&:hover': { bgcolor: `${c.status.warning}15`, color: c.status.warning } }}> + onChange('deny')} sx={{ p: 0.4, borderRadius: 1, bgcolor: value === 'deny' ? `${c.status.error}20` : 'transparent', color: value === 'deny' ? c.status.error : c.text.ghost, '&:hover': { bgcolor: `${c.status.error}15`, color: c.status.error } }}> + + ); + + const ServiceGroup = ({ serviceName, data }: { serviceName: string; data: { read?: string[]; write?: string[] } }) => { + const svcKey = `${tool.id}:${serviceName}`; + const isOpen = expandedServices[svcKey] ?? false; + const allNames = [...(data.read || []), ...(data.write || [])]; + const svcPolicy = getGroupPolicy(allNames); + const count = allNames.length; + + return ( + + setExpandedServices((p) => ({ ...p, [svcKey]: !isOpen }))} + > + + + {serviceName} + + + handleGroupPermissionChange(tool.id, allNames, v)} /> + + + + {(data.read?.length || 0) > 0 && ( + + + + + Read-only + + + handleGroupPermissionChange(tool.id, data.read!, v)} size={14} /> + + {data.read!.map((name) => ( + + + {toDisplayName(name, serviceName)} + {descriptions[name] && {firstSentence(descriptions[name])}} + + handlePermissionChange(tool.id, name, v)} size={14} /> + + ))} + + )} + {(data.write?.length || 0) > 0 && ( + + + + + Write / delete + + + handleGroupPermissionChange(tool.id, data.write!, v)} size={14} /> + + {data.write!.map((name) => ( + + + {toDisplayName(name, serviceName)} + {descriptions[name] && {firstSentence(descriptions[name])}} + + handlePermissionChange(tool.id, name, v)} size={14} /> + + ))} + + )} + + + + ); + }; + + const isDisabled = tool.enabled === false; + + return ( + + + !isDisabled && setExpandedToolId(isExpanded ? null : tool.id)} + > + {ig && ( + + {ig.icon} + + )} + + + {tool.name} + {isMcp && } label={isStdio ? 'MCP · stdio' : 'MCP'} size="small" sx={{ bgcolor: `${c.status.warning}20`, color: c.status.warning, fontSize: '0.75rem', height: 24 }} />} + {tool.command && } label={`/${tool.command}`} size="small" sx={{ bgcolor: 'rgba(174,86,48,0.12)', color: c.accent.hover, fontSize: '0.72rem', height: 22 }} />} + {tool.auth_status === 'connected' && !ig && ( + } label={tool.connected_account_email ? `Connected · ${tool.connected_account_email}` : 'Connected'} size="small" sx={{ bgcolor: c.status.successBg, color: c.status.success, fontSize: '0.7rem', height: 20, '& .MuiChip-icon': { color: c.status.success } }} /> + )} + {tool.auth_status === 'configured' && !ig?.credentialFields && ( + } label="Configured" size="small" sx={{ bgcolor: c.status.warningBg, color: c.status.warning, fontSize: '0.7rem', height: 20, '& .MuiChip-icon': { color: c.status.warning } }} /> + )} + {ig && totalToolCount > 0 && ( + + )} + {ig && ( + } label="docs" size="small" sx={{ bgcolor: c.bg.secondary, color: c.text.ghost, fontSize: '0.65rem', height: 18, '& .MuiChip-label': { px: 0.4 }, '& .MuiChip-icon': { ml: 0.4, fontSize: 10 } }} /> + )} + + {tool.description && {tool.description}} + + {!isDisabled && tool.auth_type === 'oauth2' && tool.auth_status !== 'connected' && ( + + )} + {!isDisabled && ig?.credentialFields && tool.auth_status !== 'connected' && ( + + )} + {!isDisabled && ig && tool.auth_status === 'connected' && ( + + } + label={tool.connected_account_email ? `Connected · ${tool.connected_account_email}` : 'Connected'} + size="small" + onDelete={ig.credentialFields ? (e: React.SyntheticEvent) => { e.stopPropagation(); handleDisconnectIntegration(tool.id, ig); } : undefined} + onClick={(e) => e.stopPropagation()} + sx={{ bgcolor: c.status.successBg, color: c.status.success, fontSize: '0.7rem', height: 22, '& .MuiChip-icon': { color: c.status.success }, '& .MuiChip-deleteIcon': { color: c.status.success, '&:hover': { color: c.status.error } }, flexShrink: 0 }} + /> + + )} + {ig && ( + e.stopPropagation()}> + {!!integrationLoading[ig.id] && } + handleIntegrationToggle(ig)} + disabled={!!integrationLoading[ig.id]} + sx={{ + '& .MuiSwitch-switchBase.Mui-checked': { color: ig.color }, + '& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track': { bgcolor: ig.color }, + }} + /> + + )} + {!isDisabled && ( + + + {!ig && ( + <> + { e.stopPropagation(); openEdit(tool); }} sx={{ color: c.text.ghost, '&:hover': { color: c.accent.primary } }}> + { e.stopPropagation(); handleDelete(tool.id); }} sx={{ color: c.text.ghost, '&:hover': { color: c.status.error } }}> + + )} + + )} + + + + + + + + + Tool Permissions + {hasPerms && } + + + {hasPerms && ( + <> + + + + + + + + )} + + handleDiscover(tool.id)} + disabled={discovering || !canDiscover} + sx={{ color: c.text.ghost, '&:hover': { color: c.accent.primary } }} + > + {discovering ? : } + + + + + + {!hasPerms ? ( + + + No tools discovered yet + + {!canDiscover && ( + Connect the tool first to discover available permissions + )} + + ) : ( + + {serviceNames.map((svc) => ( + + ))} + + )} + + + + ); + })} + + )} + + + + {/* Create/Edit Tool Dialog */} + setDialogOpen(false)} maxWidth="md" fullWidth PaperProps={{ sx: { bgcolor: c.bg.surface, backgroundImage: 'none', borderRadius: 4, border: `1px solid ${c.border.subtle}` } }}> + {editingId ? 'Edit Tool' : 'New Tool'} + + setForm({ ...form, name: e.target.value })} fullWidth size="small" sx={{ '& .MuiOutlinedInput-root': { bgcolor: c.bg.page } }} /> + setForm({ ...form, description: e.target.value })} fullWidth size="small" sx={{ '& .MuiOutlinedInput-root': { bgcolor: c.bg.page } }} /> + setForm({ ...form, command: e.target.value })} fullWidth size="small" placeholder="e.g. my-tool" sx={{ '& .MuiOutlinedInput-root': { bgcolor: c.bg.page } }} /> + + + + + + + + {/* Registry Browser Dialog */} + setRegistryOpen(false)} + maxWidth="md" + fullWidth + PaperProps={{ sx: { bgcolor: c.bg.page, backgroundImage: 'none', borderRadius: 4, border: `1px solid ${c.border.subtle}`, height: '80vh' } }} + > + + + MCP Registry + {regStats && ( + + )} + + + + handleRegSearch(e.target.value)} + fullWidth + size="small" + autoFocus + InputProps={{ + startAdornment: ( + + + + ), + }} + sx={{ '& .MuiOutlinedInput-root': { bgcolor: c.bg.surface, borderRadius: 2 } }} + /> + + All + Community + Google + + + handleRegSort(regSort === 'name' ? 'stars' : 'name')} + sx={{ + color: regSort === 'stars' ? '#c89c00' : c.text.ghost, + border: '1px solid', + borderColor: regSort === 'stars' ? '#c89c0040' : c.border.medium, + borderRadius: 1.5, + px: 1, + flexShrink: 0, + transition: 'all 0.15s', + '&:hover': { borderColor: '#c89c00', color: '#c89c00' }, + }} + > + + + + + + + {regLoading && regServers.length === 0 ? ( + + + + ) : regServers.length === 0 ? ( + + + No servers found matching "{regQuery}" + + ) : ( + + + Showing {regServers.length} of {regTotal.toLocaleString()} results + + {regServers.map((srv) => { + const isExpanded = expandedServer === srv.name; + return ( + + setExpandedServer(isExpanded ? null : srv.name)} + sx={{ + display: 'flex', alignItems: 'center', gap: 1.5, + px: 1.5, py: 1, borderRadius: 1.5, cursor: 'pointer', + transition: 'background 0.15s', + '&:hover': { bgcolor: c.bg.secondary }, + ...(isExpanded && { bgcolor: c.bg.secondary }), + }} + > + + {srv.iconUrl ? null : (srv.title || cleanServerName(srv.name)).charAt(0).toUpperCase()} + + + + + {srv.title || cleanServerName(srv.name)} + + {srv.version && } + {srv.remoteType && } + {srv.source === 'google' ? ( + } label="Google" size="small" sx={{ bgcolor: `${c.status.info}15`, color: c.status.info, fontSize: '0.65rem', height: 18, '& .MuiChip-label': { px: 0.4 }, '& .MuiChip-icon': { ml: 0.4, color: c.status.info } }} /> + ) : ( + } label="Community" size="small" sx={{ bgcolor: 'rgba(174,86,48,0.08)', color: c.accent.primary, fontSize: '0.65rem', height: 18, '& .MuiChip-label': { px: 0.4 }, '& .MuiChip-icon': { ml: 0.4, color: c.accent.primary } }} /> + )} + {srv.stars != null && ( + + + + {srv.stars >= 1000 ? `${(srv.stars / 1000).toFixed(1)}k` : srv.stars.toLocaleString()} + + + )} + + + {srv.description} + + + + + + + + + {srv.description} + + + + + {srv.name} + + {srv.remoteUrl && ( + + Endpoint + {srv.remoteUrl} + + )} + + {srv.websiteUrl && ( + } + label="Website" + size="small" + sx={{ bgcolor: c.bg.secondary, color: c.text.muted, fontSize: '0.7rem', height: 22 }} + /> + )} + {srv.repositoryUrl && ( + } + label="Repository" + size="small" + sx={{ bgcolor: c.bg.secondary, color: c.text.muted, fontSize: '0.7rem', height: 22 }} + /> + )} + + + + + + + + + + + ); + })} + + {regServers.length < regTotal && ( + + + + )} + + )} + + + + + + + {/* MCP Config Dialog */} + setMcpConfigOpen(false)} + maxWidth="sm" + fullWidth + PaperProps={{ sx: { bgcolor: c.bg.surface, backgroundImage: 'none', borderRadius: 4, border: `1px solid ${c.border.subtle}` } }} + > + + + Configure MCP Tool + + + {mcpConfigServer && ( + + + {mcpConfigServer.iconUrl ? null : (mcpConfigServer.title || cleanServerName(mcpConfigServer.name)).charAt(0).toUpperCase()} + + + + {mcpConfigServer.title || cleanServerName(mcpConfigServer.name)} + + {mcpConfigServer.description} + + + )} + + { setMcpConfigJson(e.target.value); try { JSON.parse(e.target.value); setMcpConfigError(''); } catch { setMcpConfigError('Invalid JSON'); } }} + fullWidth + multiline + minRows={3} + maxRows={8} + error={!!mcpConfigError} + helperText={mcpConfigError || 'Transport config passed to claude_agent_sdk (type, url, command, args, etc.)'} + sx={{ '& .MuiOutlinedInput-root': { bgcolor: c.bg.page, fontFamily: c.font.mono, fontSize: '0.85rem' } }} + /> + + + Authentication Type + + + + {mcpAuthType !== 'none' && ( + + + Environment Variables + + {Object.entries(mcpCredentials).map(([key, val]) => ( + setMcpCredentials({ ...mcpCredentials, [key]: e.target.value })} + fullWidth + size="small" + type={key.toLowerCase().includes('secret') ? 'password' : 'text'} + sx={{ '& .MuiOutlinedInput-root': { bgcolor: c.bg.elevated, fontFamily: c.font.mono, fontSize: '0.85rem' } }} + /> + ))} + {mcpAuthType === 'env_vars' && ( + + )} + + )} + + + + + + + + {/* Integration Credentials Dialog */} + setCredDialogOpen(false)} + maxWidth="sm" + fullWidth + PaperProps={{ sx: { bgcolor: c.bg.surface, backgroundImage: 'none', borderRadius: 4, border: `1px solid ${c.border.subtle}` } }} + > + + {credDialogIntegration && ( + + {credDialogIntegration.icon} + + )} + {credDialogIntegration?.connectLabel || 'Connect'} + + + {credDialogIntegration?.connectInstructions && ( + + {credDialogIntegration.connectInstructions} + + )} + {(credDialogIntegration?.credentialFields || []).map((field) => ( + setCredDialogValues((prev) => ({ ...prev, [field.key]: e.target.value }))} + fullWidth + size="small" + helperText={field.helpText} + sx={{ '& .MuiOutlinedInput-root': { bgcolor: c.bg.page, fontFamily: c.font.mono, fontSize: '0.85rem' } }} + /> + ))} + + + + + + + + {/* Install success snackbar */} + setSnackbar({ open: false, message: '' })} + anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} + > + setSnackbar({ open: false, message: '' })} severity={snackbar.severity || 'success'} sx={{ bgcolor: snackbar.severity === 'error' ? '#2e1a1a' : c.status.successBg, color: snackbar.severity === 'error' ? '#f87171' : c.status.success, border: `1px solid ${snackbar.severity === 'error' ? '#ef444440' : `${c.status.success}40`}` }}> + {snackbar.message} + + + + ); +}; + +export default Tools; diff --git a/frontend/src/app/pages/Views/CodeEditor.tsx b/frontend/src/app/pages/Views/CodeEditor.tsx new file mode 100644 index 00000000..9de45c50 --- /dev/null +++ b/frontend/src/app/pages/Views/CodeEditor.tsx @@ -0,0 +1,87 @@ +import React, { useRef, useEffect, useMemo } from 'react'; +import { EditorState } from '@codemirror/state'; +import { EditorView, keymap, placeholder as cmPlaceholder } from '@codemirror/view'; +import { defaultKeymap, indentWithTab } from '@codemirror/commands'; +import { basicSetup } from 'codemirror'; +import { oneDark } from '@codemirror/theme-one-dark'; +import { html } from '@codemirror/lang-html'; +import { python } from '@codemirror/lang-python'; +import { json } from '@codemirror/lang-json'; +import { useThemeMode } from '@/shared/styles/ThemeContext'; + +type Language = 'html' | 'python' | 'json'; + +interface Props { + value: string; + onChange: (value: string) => void; + language: Language; + placeholder?: string; +} + +const langExtension = (lang: Language) => { + switch (lang) { + case 'html': return html(); + case 'python': return python(); + case 'json': return json(); + } +}; + +const CodeEditor: React.FC = ({ value, onChange, language, placeholder }) => { + const containerRef = useRef(null); + const viewRef = useRef(null); + const onChangeRef = useRef(onChange); + const { mode } = useThemeMode(); + + onChangeRef.current = onChange; + + const extensions = useMemo(() => { + const exts = [ + basicSetup, + langExtension(language), + keymap.of([...defaultKeymap, indentWithTab]), + EditorView.updateListener.of((update) => { + if (update.docChanged) { + onChangeRef.current(update.state.doc.toString()); + } + }), + EditorView.theme({ + '&': { height: '100%', fontSize: '13px' }, + '.cm-scroller': { overflow: 'auto', fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace' }, + '.cm-gutters': { border: 'none' }, + }), + ]; + if (mode === 'dark') exts.push(oneDark); + if (placeholder) exts.push(cmPlaceholder(placeholder)); + return exts; + }, [language, mode, placeholder]); + + useEffect(() => { + if (!containerRef.current) return; + + const state = EditorState.create({ doc: value, extensions }); + const view = new EditorView({ state, parent: containerRef.current }); + viewRef.current = view; + + return () => { + view.destroy(); + viewRef.current = null; + }; + // Recreate the editor when extensions change (language/theme switch) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [extensions]); + + useEffect(() => { + const view = viewRef.current; + if (!view) return; + const current = view.state.doc.toString(); + if (current !== value) { + view.dispatch({ + changes: { from: 0, to: current.length, insert: value }, + }); + } + }, [value]); + + return
; +}; + +export default CodeEditor; diff --git a/frontend/src/app/pages/Views/InputSchemaForm.tsx b/frontend/src/app/pages/Views/InputSchemaForm.tsx new file mode 100644 index 00000000..a8c5b8da --- /dev/null +++ b/frontend/src/app/pages/Views/InputSchemaForm.tsx @@ -0,0 +1,313 @@ +import React from 'react'; +import Box from '@mui/material/Box'; +import TextField from '@mui/material/TextField'; +import Switch from '@mui/material/Switch'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import Select from '@mui/material/Select'; +import MenuItem from '@mui/material/MenuItem'; +import InputLabel from '@mui/material/InputLabel'; +import FormControl from '@mui/material/FormControl'; +import Typography from '@mui/material/Typography'; +import IconButton from '@mui/material/IconButton'; +import Button from '@mui/material/Button'; +import AddIcon from '@mui/icons-material/Add'; +import RemoveCircleOutlineIcon from '@mui/icons-material/RemoveCircleOutline'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; + +interface SchemaNode { + type?: string; + properties?: Record; + items?: SchemaNode; + required?: string[]; + enum?: string[]; + description?: string; + default?: any; +} + +interface Props { + schema: SchemaNode; + value: any; + onChange: (value: any) => void; + label?: string; + depth?: number; +} + +function getDefault(schema: SchemaNode): any { + if (schema.default !== undefined) return schema.default; + switch (schema.type) { + case 'string': return ''; + case 'number': return 0; + case 'boolean': return false; + case 'array': return []; + case 'object': { + const obj: Record = {}; + if (schema.properties) { + for (const [k, v] of Object.entries(schema.properties)) { + obj[k] = getDefault(v); + } + } + return obj; + } + default: return ''; + } +} + +const STRING_STUBS: Record = { + name: 'Jane Smith', first_name: 'Jane', last_name: 'Smith', firstName: 'Jane', lastName: 'Smith', + email: 'jane@example.com', url: 'https://example.com', website: 'https://example.com', + phone: '+1 (555) 123-4567', address: '123 Main St, Springfield', + city: 'Springfield', state: 'CA', country: 'US', zip: '90210', + title: 'Sample Title', subject: 'Hello World', message: 'This is a sample message.', + description: 'A brief description of the item.', content: 'Lorem ipsum dolor sit amet.', + username: 'janesmith', password: 'P@ssw0rd!', token: 'tok_sample_abc123', + id: 'item_001', uuid: '550e8400-e29b-41d4-a716-446655440000', + date: '2025-03-15', time: '14:30', datetime: '2025-03-15T14:30:00Z', + color: '#4a90d9', label: 'Important', tag: 'sample', category: 'general', + query: 'search term', search: 'example query', text: 'Sample text content', + path: '/home/user/file.txt', file: 'document.pdf', filename: 'report.pdf', + company: 'Acme Corp', organization: 'Acme Corp', +}; + +function stubString(key: string): string { + const lower = key.toLowerCase().replace(/[-_]/g, ''); + for (const [pattern, val] of Object.entries(STRING_STUBS)) { + if (lower === pattern.toLowerCase().replace(/[-_]/g, '') || lower.endsWith(pattern.toLowerCase().replace(/[-_]/g, ''))) { + return val; + } + } + return `sample_${key}`; +} + +function getStubbed(schema: SchemaNode, key?: string): any { + if (schema.default !== undefined) return schema.default; + if (schema.enum && schema.enum.length > 0) return schema.enum[0]; + switch (schema.type) { + case 'string': return stubString(key || 'value'); + case 'number': return 42; + case 'integer': return 7; + case 'boolean': return true; + case 'array': { + if (!schema.items) return []; + return [getStubbed(schema.items, key ? `${key}_item` : 'item')]; + } + case 'object': { + const obj: Record = {}; + if (schema.properties) { + for (const [k, v] of Object.entries(schema.properties)) { + obj[k] = getStubbed(v, k); + } + } + return obj; + } + default: return stubString(key || 'value'); + } +} + +const InputSchemaForm: React.FC = ({ schema, value, onChange, label, depth = 0 }) => { + const c = useClaudeTokens(); + + if (schema.enum && schema.enum.length > 0) { + return ( + + {label && {label}} + + {schema.description && ( + + {schema.description} + + )} + + ); + } + + if (schema.type === 'boolean') { + return ( + + onChange(e.target.checked)} + size="small" + /> + } + label={ + + {label || 'Toggle'} + + } + /> + {schema.description && ( + + {schema.description} + + )} + + ); + } + + if (schema.type === 'number' || schema.type === 'integer') { + return ( + onChange(Number(e.target.value))} + sx={{ + mb: 1.5, + '& .MuiOutlinedInput-root': { fontSize: '0.85rem' }, + '& .MuiFormHelperText-root': { fontSize: '0.7rem' }, + }} + /> + ); + } + + if (schema.type === 'string') { + return ( + onChange(e.target.value)} + multiline={(value?.length ?? 0) > 80} + sx={{ + mb: 1.5, + '& .MuiOutlinedInput-root': { fontSize: '0.85rem' }, + '& .MuiFormHelperText-root': { fontSize: '0.7rem' }, + }} + /> + ); + } + + if (schema.type === 'array' && schema.items) { + const items = Array.isArray(value) ? value : []; + return ( + 0 ? 1.5 : 0, + borderLeft: depth > 0 ? `2px solid ${c.border.subtle}` : 'none', + }} + > + {label && ( + + {label} + + )} + {schema.description && ( + + {schema.description} + + )} + {items.map((item: any, i: number) => ( + + + { + const updated = [...items]; + updated[i] = newVal; + onChange(updated); + }} + label={`Item ${i + 1}`} + depth={depth + 1} + /> + + { + const updated = items.filter((_: any, idx: number) => idx !== i); + onChange(updated); + }} + sx={{ color: c.status.error, mt: 0.5 }} + > + + + + ))} + + + ); + } + + if (schema.type === 'object' && schema.properties) { + const obj = typeof value === 'object' && value !== null ? value : {}; + return ( + 0 ? 1.5 : 0, + borderLeft: depth > 0 ? `2px solid ${c.border.subtle}` : 'none', + }} + > + {label && ( + + {label} + + )} + {schema.description && ( + + {schema.description} + + )} + {Object.entries(schema.properties).map(([key, propSchema]) => ( + onChange({ ...obj, [key]: newVal })} + label={key + (schema.required?.includes(key) ? ' *' : '')} + depth={depth + 1} + /> + ))} + + ); + } + + return ( + onChange(e.target.value)} + sx={{ mb: 1.5, '& .MuiOutlinedInput-root': { fontSize: '0.85rem' } }} + /> + ); +}; + +export { getDefault, getStubbed }; +export default InputSchemaForm; diff --git a/frontend/src/app/pages/Views/ViewCard.tsx b/frontend/src/app/pages/Views/ViewCard.tsx new file mode 100644 index 00000000..7f03b71e --- /dev/null +++ b/frontend/src/app/pages/Views/ViewCard.tsx @@ -0,0 +1,152 @@ +import React from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import IconButton from '@mui/material/IconButton'; +import Tooltip from '@mui/material/Tooltip'; +import EditIcon from '@mui/icons-material/Edit'; +import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'; +import PlayArrowIcon from '@mui/icons-material/PlayArrow'; +import Icon from '@mui/material/Icon'; +import { Output } from '@/shared/state/outputsSlice'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; + +interface Props { + output: Output; + onClick: () => void; + onDelete: () => void; + onRun: () => void; +} + +const ViewCard: React.FC = ({ output, onClick, onDelete, onRun }) => { + const c = useClaudeTokens(); + + return ( + + + {output.thumbnail ? ( + + ) : ( + + {output.icon} + + )} + + + { e.stopPropagation(); onRun(); }} + sx={{ + bgcolor: c.bg.surface, + color: c.accent.primary, + boxShadow: c.shadow.sm, + '&:hover': { bgcolor: c.bg.elevated }, + }} + > + + + + + { e.stopPropagation(); onDelete(); }} + sx={{ + bgcolor: c.bg.surface, + color: c.status.error, + boxShadow: c.shadow.sm, + '&:hover': { bgcolor: c.bg.elevated }, + }} + > + + + + + + + + + {output.name} + + + {output.description || 'No description'} + + + + ); +}; + +export default ViewCard; diff --git a/frontend/src/app/pages/Views/ViewEditor.tsx b/frontend/src/app/pages/Views/ViewEditor.tsx new file mode 100644 index 00000000..aa051d99 --- /dev/null +++ b/frontend/src/app/pages/Views/ViewEditor.tsx @@ -0,0 +1,1516 @@ +import React, { useState, useMemo, useEffect, useRef, useCallback, PointerEvent as ReactPointerEvent } from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import Button from '@mui/material/Button'; +import IconButton from '@mui/material/IconButton'; +import TextField from '@mui/material/TextField'; +import Tabs from '@mui/material/Tabs'; +import Tab from '@mui/material/Tab'; +import Tooltip from '@mui/material/Tooltip'; +import Switch from '@mui/material/Switch'; +import CircularProgress from '@mui/material/CircularProgress'; +import ArrowBackIcon from '@mui/icons-material/ArrowBack'; +import SaveIcon from '@mui/icons-material/Save'; +import PlayArrowIcon from '@mui/icons-material/PlayArrow'; +import HtmlIcon from '@mui/icons-material/Code'; +import PythonIcon from '@mui/icons-material/Terminal'; +import SchemaIcon from '@mui/icons-material/DataObject'; +import JsIcon from '@mui/icons-material/Javascript'; +import CssIcon from '@mui/icons-material/Style'; +import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile'; +import FolderIcon from '@mui/icons-material/Folder'; +import AddIcon from '@mui/icons-material/Add'; +import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'; +import BoltIcon from '@mui/icons-material/Bolt'; +import Collapse from '@mui/material/Collapse'; +import Chip from '@mui/material/Chip'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline'; +import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline'; +import { useAppDispatch, useAppSelector } from '@/shared/hooks'; +import { createDraftSession, removeDraftSession, AgentMessage } from '@/shared/state/agentsSlice'; +import { createOutput, updateOutput, Output, executeOutput, OutputExecuteResult, autoRunOutput, autoRunAgentOutput, cleanupAutoRunAgent, AutoRunConfig, SERVE_BASE } from '@/shared/state/outputsSlice'; +import { createSessionWs } from '@/shared/ws/WebSocketManager'; +import { useClaudeTokens } from '@/shared/styles/ThemeContext'; +import AgentChat from '../AgentChat/AgentChat'; +import ChatInput, { ChatInputHandle } from '../AgentChat/ChatInput'; +import RefreshIcon from '@mui/icons-material/Refresh'; +import ViewPreview, { ViewPreviewHandle } from './ViewPreview'; +import InputSchemaForm, { getDefault, getStubbed } from './InputSchemaForm'; +import AutoFixHighIcon from '@mui/icons-material/AutoFixHigh'; +import CodeEditor from './CodeEditor'; +import { ElementSelectionProvider } from '@/app/components/ElementSelectionContext'; +import { captureViewThumbnail } from './captureViewThumbnail'; + +const WORKSPACE_API = `http://${window.location.hostname}:8324/api/outputs/workspace`; +const POLL_INTERVAL_MS = 2000; + +function getFileIcon(filename: string): React.ReactNode { + const ext = filename.split('.').pop()?.toLowerCase(); + const size = 15; + switch (ext) { + case 'html': case 'htm': return ; + case 'py': return ; + case 'json': return ; + case 'js': case 'jsx': case 'ts': case 'tsx': return ; + case 'css': case 'scss': case 'less': return ; + default: return ; + } +} + +function getEditorLanguage(filename: string): string { + const ext = filename.split('.').pop()?.toLowerCase(); + switch (ext) { + case 'html': case 'htm': return 'html'; + case 'py': return 'python'; + case 'json': return 'json'; + case 'js': case 'jsx': return 'javascript'; + case 'ts': case 'tsx': return 'typescript'; + case 'css': case 'scss': return 'css'; + case 'md': return 'markdown'; + default: return 'plaintext'; + } +} + +interface FileTreeNode { + name: string; + path: string; + isDir: boolean; + children?: FileTreeNode[]; +} + +function buildFileTree(filePaths: string[]): FileTreeNode[] { + const root: FileTreeNode[] = []; + const sorted = [...filePaths].sort(); + + for (const fp of sorted) { + const parts = fp.split('/'); + let current = root; + let pathSoFar = ''; + + for (let i = 0; i < parts.length; i++) { + const part = parts[i]; + pathSoFar = pathSoFar ? `${pathSoFar}/${part}` : part; + const isLast = i === parts.length - 1; + + let existing = current.find(n => n.name === part && n.isDir === !isLast); + if (!existing) { + if (isLast) { + existing = { name: part, path: fp, isDir: false }; + } else { + existing = { name: part, path: pathSoFar, isDir: true, children: [] }; + } + current.push(existing); + } + if (!isLast) { + current = existing.children!; + } + } + } + + return root; +} + +interface LogEntryProps { + msg: AgentMessage; + c: ReturnType; +} + +const LogEntry: React.FC = ({ msg, c }) => { + const [open, setOpen] = useState(false); + + if (msg.role === 'user') return null; + + if (msg.role === 'assistant') { + const text = typeof msg.content === 'string' + ? msg.content + : Array.isArray(msg.content) + ? msg.content.filter((b: any) => b.type === 'text').map((b: any) => b.text).join('') + : JSON.stringify(msg.content); + if (!text.trim()) return null; + return ( + + setOpen(!open)} + sx={{ display: 'flex', alignItems: 'center', gap: 0.5, cursor: 'pointer', '&:hover': { opacity: 0.8 } }} + > + + + {text.slice(0, 120)}{text.length > 120 ? '…' : ''} + + + + + {text} + + + + ); + } + + if (msg.role === 'tool_call') { + const tc = typeof msg.content === 'object' ? msg.content as Record : {}; + return ( + + setOpen(!open)} + sx={{ display: 'flex', alignItems: 'center', gap: 0.5, cursor: 'pointer', '&:hover': { opacity: 0.8 } }} + > + + + {tc.input && ( + + {JSON.stringify(tc.input).slice(0, 80)}… + + )} + + + + + {JSON.stringify(tc.input, null, 2)} + + + + + ); + } + + if (msg.role === 'tool_result') { + const content = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content); + return ( + + setOpen(!open)} + sx={{ display: 'flex', alignItems: 'center', gap: 0.5, cursor: 'pointer', '&:hover': { opacity: 0.8 } }} + > + + + result ({content.length > 60 ? `${content.length} chars` : content.slice(0, 60)}) + + + + + {content} + + + + ); + } + + if (msg.role === 'system') { + const text = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content); + return ( + + {text} + + ); + } + + return null; +}; + +interface AutoRunLogProps { + messages: AgentMessage[]; + status: string | null; + logEndRef: React.RefObject; + c: ReturnType; +} + +const AutoRunLog: React.FC = ({ messages, status, logEndRef, c }) => { + const isRunning = status === 'running' || status === 'waiting_approval'; + const isDone = status === 'completed' || status === 'stopped'; + const isError = status === 'error'; + + return ( + + + {isRunning && } + {isDone && } + {isError && } + + {isRunning ? 'Agent running…' : isDone ? 'Agent completed' : isError ? 'Agent error' : 'Execution log'} + + + {messages.length} messages + + + + {messages.map((msg) => ( + + ))} +
+ + + ); +}; + +interface ConsoleEntry { + timestamp: number; + inputData: Record; + stdout: string | null; + stderr: string | null; + backendResult: Record | null; + error: string | null; + source: string; + running?: boolean; +} + +interface ConsolePanelProps { + entry: ConsoleEntry | null; + c: ReturnType; +} + +const ConsolePanel: React.FC = ({ entry, c }) => { + const sectionSx = { mb: 2 }; + const labelBase = { fontSize: '0.7rem' as const, fontWeight: 600, fontFamily: c.font.mono, mb: 0.5 }; + const codeBoxSx = { bgcolor: '#161b22', borderRadius: 1, p: 1.5, border: '1px solid #21262d' }; + const preStyle = { fontSize: '0.72rem', fontFamily: c.font.mono, color: '#c9d1d9', whiteSpace: 'pre-wrap' as const, wordBreak: 'break-all' as const, m: 0 }; + + if (!entry) { + return ( + + {'>_'} + No execution output yet + Run the backend to see results here + + ); + } + + if (entry.running) { + return ( + + + Executing backend… + + ); + } + + return ( + + + + + {new Date(entry.timestamp).toLocaleTimeString()} + + + {entry.error && ( + + )} + + + + ▸ Input Data + + {JSON.stringify(entry.inputData, null, 2)} + + + + {entry.stdout && ( + + ▸ stdout + + {entry.stdout} + + + )} + + {entry.stderr && ( + + ▸ stderr + + {entry.stderr} + + + )} + + {entry.backendResult && ( + + ▸ Result + + {JSON.stringify(entry.backendResult, null, 2)} + + + )} + + {entry.error && ( + + ✗ Error + + {entry.error} + + + )} + + {!entry.stdout && !entry.stderr && !entry.backendResult && !entry.error && ( + + No backend code to execute. Only input data was sent to the view. + + )} + + + ); +}; + +interface FileTreeItemProps { + node: FileTreeNode; + depth: number; + activeFile: string; + onSelect: (path: string) => void; + onDelete?: (path: string) => void; + c: ReturnType; +} + +const PROTECTED_FILES = new Set(['index.html', 'schema.json', 'meta.json']); + +const FileTreeItem: React.FC = ({ node, depth, activeFile, onSelect, onDelete, c }) => { + const [open, setOpen] = useState(true); + + if (node.isDir) { + return ( + <> + setOpen(!open)} + sx={{ + display: 'flex', + alignItems: 'center', + gap: 0.5, + pl: 1.5 + depth * 1, + pr: 1, + py: 0.5, + cursor: 'pointer', + '&:hover': { bgcolor: c.bg.surface }, + }} + > + + + + {node.name} + + + + {node.children?.map((child) => ( + + ))} + + + ); + } + + const isActive = activeFile === node.path; + const canDelete = onDelete && !PROTECTED_FILES.has(node.path); + + return ( + onSelect(node.path)} + sx={{ + display: 'flex', + alignItems: 'center', + gap: 0.75, + pl: 1.5 + depth * 1 + 1.25, + pr: 0.5, + py: 0.5, + cursor: 'pointer', + bgcolor: isActive ? c.bg.elevated : 'transparent', + borderLeft: isActive ? `2px solid ${c.accent.primary}` : '2px solid transparent', + '&:hover': { bgcolor: isActive ? c.bg.elevated : c.bg.surface }, + '&:hover .delete-btn': { opacity: 1 }, + transition: 'background-color 0.1s', + }} + > + + {getFileIcon(node.name)} + + + {node.name} + + {canDelete && ( + { e.stopPropagation(); onDelete(node.path); }} + sx={{ opacity: 0, p: 0.25, color: c.text.ghost, '&:hover': { color: '#ef4444' }, transition: 'opacity 0.15s, color 0.15s' }} + > + + + )} + + ); +}; + +interface Props { + output: Output | null; + onClose: () => void; +} + +const ViewEditor: React.FC = ({ output, onClose }) => { + const c = useClaudeTokens(); + const dispatch = useAppDispatch(); + const isNew = !output; + + const [name, setName] = useState(output?.name ?? ''); + const [description, setDescription] = useState(output?.description ?? ''); + + const initialFiles = useMemo>(() => { + if (!output) return {}; + const f = { ...output.files }; + if (!f['schema.json'] && output.input_schema) { + f['schema.json'] = JSON.stringify(output.input_schema, null, 2); + } + return f; + }, [output]); + + const [files, setFiles] = useState>(initialFiles); + + const TAB_PREVIEW = 0; + const TAB_CODE = 1; + const TAB_TEST_INPUT = 2; + const TAB_AUTO_RUN = 3; + const TAB_CONSOLE = 4; + + const [activeTab, setActiveTab] = useState(TAB_PREVIEW); + const [activeFile, setActiveFile] = useState('index.html'); + const [saving, setSaving] = useState(false); + const [executeResult, setExecuteResult] = useState(null); + const [showConsole, setShowConsole] = useState(false); + const [consoleEntry, setConsoleEntry] = useState(null); + const [hasNewConsoleOutput, setHasNewConsoleOutput] = useState(false); + + const savedAutoRun = output?.auto_run_config; + const [autoRunEnabled, setAutoRunEnabled] = useState(savedAutoRun?.enabled ?? false); + const [autoRunMode, setAutoRunMode] = useState(savedAutoRun?.mode ?? 'agent'); + const [autoRunModel, setAutoRunModel] = useState(savedAutoRun?.model ?? 'sonnet'); + const [autoRunning, setAutoRunning] = useState(false); + const autoRunInputRef = useRef(null); + const autoRunInitialized = useRef(false); + const previewRef = useRef(null); + + const [autoRunSessionId, setAutoRunSessionId] = useState(null); + const autoRunWsRef = useRef | null>(null); + const autoRunLogEndRef = useRef(null); + + const autoRunSession = useAppSelector((state) => + autoRunSessionId ? state.agents.sessions[autoRunSessionId] : null + ); + const autoRunMessages = autoRunSession?.messages ?? []; + const autoRunSessionStatus = autoRunSession?.status ?? null; + + const SIDEBAR_MIN = 280; + const SIDEBAR_MAX = 800; + const [sidebarWidth, setSidebarWidth] = useState(420); + const dragging = useRef(false); + const dragStartX = useRef(0); + const dragStartWidth = useRef(0); + + const onDragStart = useCallback((e: ReactPointerEvent) => { + dragging.current = true; + dragStartX.current = e.clientX; + dragStartWidth.current = sidebarWidth; + (e.target as HTMLElement).setPointerCapture(e.pointerId); + document.body.style.cursor = 'col-resize'; + document.body.style.userSelect = 'none'; + }, [sidebarWidth]); + + const onDragMove = useCallback((e: ReactPointerEvent) => { + if (!dragging.current) return; + const delta = e.clientX - dragStartX.current; + setSidebarWidth(Math.min(SIDEBAR_MAX, Math.max(SIDEBAR_MIN, dragStartWidth.current + delta))); + }, []); + + const onDragEnd = useCallback(() => { + dragging.current = false; + document.body.style.cursor = ''; + document.body.style.userSelect = ''; + }, []); + + const [initialDraftId, setInitialDraftId] = useState(null); + const [workspacePath, setWorkspacePath] = useState(null); + const [stableWorkspaceId] = useState(() => `ws-${Date.now().toString(36)}`); + const draftCreated = useRef(false); + + useEffect(() => { + if (draftCreated.current) return; + draftCreated.current = true; + + (async () => { + const seedBody: Record = { workspace_id: stableWorkspaceId }; + if (output) { + const seedFiles: Record = { ...output.files }; + if (output.input_schema && !seedFiles['schema.json']) { + seedFiles['schema.json'] = JSON.stringify(output.input_schema, null, 2); + } + seedBody.files = seedFiles; + seedBody.meta = { name: output.name, description: output.description }; + } + try { + const res = await fetch(`${WORKSPACE_API}/seed`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(seedBody), + }); + const data = await res.json(); + setWorkspacePath(data.path); + const action = dispatch(createDraftSession({ + mode: 'view-builder', + setActive: false, + targetDirectory: data.path, + })); + setInitialDraftId(action.payload.draftId); + } catch { + const action = dispatch(createDraftSession({ mode: 'view-builder', setActive: false })); + setInitialDraftId(action.payload.draftId); + } + })(); + }, [dispatch, output, stableWorkspaceId]); + + const effectiveSessionId = useAppSelector((state) => { + if (!initialDraftId) return null; + if (state.agents.sessions[initialDraftId]) return initialDraftId; + return state.agents.activeSessionId; + }); + + const agentStatus = useAppSelector((state) => { + if (!effectiveSessionId) return null; + return state.agents.sessions[effectiveSessionId]?.status ?? null; + }); + + const isLaunched = !!effectiveSessionId && effectiveSessionId !== initialDraftId; + const isAgentActive = agentStatus === 'running' || agentStatus === 'waiting_approval'; + + const workspaceId = workspacePath ? stableWorkspaceId : null; + + const initialContextPaths = useMemo( + () => workspacePath ? [{ path: workspacePath, type: 'directory' as const }] : undefined, + [workspacePath], + ); + + const pollRef = useRef | null>(null); + const lastPollRef = useRef(''); + + const nameSetByMeta = useRef(false); + const [fileVersion, setFileVersion] = useState(0); + + const pollWorkspace = useCallback(async () => { + if (!workspaceId) return; + try { + const res = await fetch(`${WORKSPACE_API}/${workspaceId}`); + if (!res.ok) return; + const data = await res.json(); + const fingerprint = JSON.stringify(data); + if (fingerprint === lastPollRef.current) return; + lastPollRef.current = fingerprint; + + if (data.files) { + setFiles(data.files); + setFileVersion(v => v + 1); + } + + if (data.meta) { + if (data.meta.name && !nameSetByMeta.current) { + nameSetByMeta.current = true; + setName((prev) => prev || data.meta.name); + } + if (data.meta.description) { + setDescription((prev) => prev || data.meta.description); + } + } + } catch {} + }, [workspaceId]); + + useEffect(() => { + if (!workspaceId) return; + pollWorkspace(); + pollRef.current = setInterval(pollWorkspace, POLL_INTERVAL_MS); + return () => { + if (pollRef.current) clearInterval(pollRef.current); + }; + }, [workspaceId, pollWorkspace]); + + const prevAgentActive = useRef(false); + useEffect(() => { + if (prevAgentActive.current && !isAgentActive && workspaceId) { + setTimeout(pollWorkspace, 500); + } + prevAgentActive.current = isAgentActive; + }, [isAgentActive, workspaceId, pollWorkspace]); + + useEffect(() => { + return () => { + if (initialDraftId) { + dispatch(removeDraftSession(initialDraftId)); + } + }; + }, [initialDraftId, dispatch]); + + const schemaText = files['schema.json'] ?? '{"type":"object","properties":{},"required":[]}'; + + const parsedSchema = useMemo(() => { + try { return JSON.parse(schemaText); } catch { return { type: 'object', properties: {} }; } + }, [schemaText]); + + const testInputDefault = useMemo(() => getDefault(parsedSchema), [parsedSchema]); + const [testInput, setTestInput] = useState>(testInputDefault); + + useEffect(() => { + setTestInput(getDefault(parsedSchema)); + }, [schemaText]); + + useEffect(() => { + if (autoRunInitialized.current || !savedAutoRun) return; + if (!autoRunEnabled) return; + autoRunInitialized.current = true; + const timer = setTimeout(() => { + autoRunInputRef.current?.setContent( + savedAutoRun.prompt || '', + savedAutoRun.context_paths?.map((cp) => ({ path: cp.path, type: (cp.type as 'file' | 'directory') || 'file' })), + savedAutoRun.forced_tools, + ); + }, 100); + return () => clearTimeout(timer); + }, [savedAutoRun, autoRunEnabled]); + + const savedRef = useRef(!!output); + + const getAutoRunConfig = (): AutoRunConfig => { + const config = autoRunInputRef.current?.getConfig(); + return { + enabled: autoRunEnabled, + prompt: config?.prompt ?? '', + context_paths: config?.contextPaths?.map((cp) => ({ path: cp.path, type: cp.type })) ?? [], + forced_tools: (config?.forcedTools ?? []).map(({ label, tools, iconKey }) => ({ label, tools, iconKey })), + mode: autoRunMode, + model: autoRunModel, + }; + }; + + const buildBody = () => { + let schema: Record; + try { schema = JSON.parse(schemaText); } catch { schema = { type: 'object', properties: {} }; } + + const outputFiles = { ...files }; + delete outputFiles['meta.json']; + delete outputFiles['schema.json']; + + return { + name: name || 'Untitled View', + description, + icon: 'view_quilt', + input_schema: schema, + files: outputFiles, + auto_run_config: getAutoRunConfig(), + }; + }; + + const captureThumbnailAsync = (outputId: string) => { + captureViewThumbnail(files['index.html'] ?? '', testInput, files) + .then((thumbnail) => { + if (thumbnail) { + dispatch(updateOutput({ id: outputId, thumbnail })); + } + }) + .catch(() => {}); + }; + + const handleSave = async (close = true) => { + setSaving(true); + try { + const body = buildBody(); + let savedId: string; + if (output) { + await dispatch(updateOutput({ id: output.id, ...body })).unwrap(); + savedId = output.id; + } else { + const created = await dispatch(createOutput(body)).unwrap(); + savedId = created.id; + } + savedRef.current = true; + if (close) onClose(); + captureThumbnailAsync(savedId); + } catch (err: any) { + console.error('Failed to save output:', err); + } finally { + setSaving(false); + } + }; + + const handleClose = async () => { + if (!savedRef.current && (files['index.html'] ?? '').trim()) { + try { + const body = buildBody(); + let savedId: string; + if (output) { + await dispatch(updateOutput({ id: output.id, ...body })).unwrap(); + savedId = output.id; + } else { + const created = await dispatch(createOutput(body)).unwrap(); + savedId = created.id; + } + captureThumbnailAsync(savedId); + } catch {} + } + onClose(); + }; + + const handleRunPreview = async () => { + if (!output) { + setExecuteResult(null); + return; + } + setConsoleEntry({ timestamp: Date.now(), inputData: testInput, stdout: null, stderr: null, backendResult: null, error: null, source: 'execute', running: true }); + setHasNewConsoleOutput(true); + try { + const res = await dispatch( + executeOutput({ output_id: output.id, input_data: testInput }) + ).unwrap(); + setExecuteResult(res); + setConsoleEntry({ timestamp: Date.now(), inputData: res.input_data, stdout: res.stdout ?? null, stderr: res.stderr ?? null, backendResult: res.backend_result, error: res.error, source: 'execute' }); + } catch (e: any) { + setConsoleEntry({ timestamp: Date.now(), inputData: testInput, stdout: null, stderr: null, backendResult: null, error: e?.message || 'Execution failed', source: 'execute' }); + } + }; + + const handleAutoRun = async () => { + const config = autoRunInputRef.current?.getConfig(); + if (!config?.prompt?.trim()) return; + setAutoRunning(true); + + let schema: Record; + try { schema = JSON.parse(schemaText); } catch { schema = { type: 'object', properties: {} }; } + const forcedToolNames = config.forcedTools.flatMap((ft) => ft.tools); + + if (forcedToolNames.length > 0 && output?.id) { + try { + const res = await dispatch(autoRunAgentOutput({ + prompt: config.prompt, + input_schema: schema, + output_id: output.id, + model: autoRunModel, + forced_tools: forcedToolNames, + context_paths: config.contextPaths.map((cp) => ({ path: cp.path, type: cp.type })), + })).unwrap(); + setAutoRunSessionId(res.session_id); + const ws = createSessionWs(res.session_id); + ws.connect(); + autoRunWsRef.current = ws; + } catch { + setAutoRunning(false); + } + } else { + try { + const backendCode = files['backend.py'] ?? null; + const res = await dispatch(autoRunOutput({ + prompt: config.prompt, + input_schema: schema, + backend_code: backendCode || undefined, + context_paths: config.contextPaths.map((cp) => ({ path: cp.path, type: cp.type })), + forced_tools: forcedToolNames.length > 0 ? forcedToolNames : undefined, + model: autoRunModel, + })).unwrap(); + if (res.input_data) { + setTestInput(res.input_data); + setExecuteResult({ + output_id: output?.id ?? '', + output_name: name, + frontend_code: files['index.html'] ?? '', + input_data: res.input_data, + backend_result: res.backend_result, + stdout: res.stdout ?? null, + stderr: res.stderr ?? null, + error: res.error, + }); + setConsoleEntry({ timestamp: Date.now(), inputData: res.input_data, stdout: res.stdout ?? null, stderr: res.stderr ?? null, backendResult: res.backend_result, error: res.error, source: 'auto-run' }); + setHasNewConsoleOutput(true); + setActiveTab(TAB_PREVIEW); + } + } catch {} + setAutoRunning(false); + } + }; + + useEffect(() => { + if (!autoRunSessionId || !autoRunSessionStatus) return; + if (autoRunSessionStatus !== 'completed' && autoRunSessionStatus !== 'error' && autoRunSessionStatus !== 'stopped') return; + + let extracted = false; + for (const msg of autoRunMessages) { + if (msg.role !== 'tool_call' || typeof msg.content !== 'object') continue; + const tc = msg.content as { tool?: string; input?: Record }; + if (tc.tool !== 'RenderOutput' || !tc.input?.input_data) continue; + setTestInput(tc.input.input_data); + setExecuteResult({ + output_id: output?.id ?? '', + output_name: name, + frontend_code: files['index.html'] ?? '', + input_data: tc.input.input_data, + backend_result: null, + stdout: null, + stderr: null, + error: null, + }); + setConsoleEntry({ timestamp: Date.now(), inputData: tc.input.input_data, stdout: null, stderr: null, backendResult: null, error: null, source: 'agent' }); + setHasNewConsoleOutput(true); + setActiveTab(TAB_PREVIEW); + extracted = true; + break; + } + + if (!extracted && autoRunSessionStatus === 'error') { + const lastSys = [...autoRunMessages].reverse().find((m) => m.role === 'system'); + if (lastSys) { + const errMsg = typeof lastSys.content === 'string' ? lastSys.content : JSON.stringify(lastSys.content); + setExecuteResult({ + output_id: output?.id ?? '', + output_name: name, + frontend_code: files['index.html'] ?? '', + input_data: {}, + backend_result: null, + stdout: null, + stderr: null, + error: errMsg, + }); + setConsoleEntry({ timestamp: Date.now(), inputData: {}, stdout: null, stderr: null, backendResult: null, error: errMsg, source: 'agent' }); + setHasNewConsoleOutput(true); + } + } + + setAutoRunning(false); + + if (autoRunWsRef.current) { + autoRunWsRef.current.disconnect(); + autoRunWsRef.current = null; + } + cleanupAutoRunAgent(autoRunSessionId).catch(() => {}); + setTimeout(() => setAutoRunSessionId(null), 300); + }, [autoRunSessionId, autoRunSessionStatus]); + + useEffect(() => { + autoRunLogEndRef.current?.scrollIntoView({ behavior: 'smooth' }); + }, [autoRunMessages.length]); + + useEffect(() => { + return () => { + if (autoRunWsRef.current) { + autoRunWsRef.current.disconnect(); + autoRunWsRef.current = null; + } + if (autoRunSessionId) { + cleanupAutoRunAgent(autoRunSessionId).catch(() => {}); + } + }; + }, [autoRunSessionId]); + + const workspaceServeUrl = workspaceId + ? `${SERVE_BASE}/workspace/${workspaceId}/serve/index.html` + : undefined; + + const filePaths = useMemo(() => Object.keys(files).filter(p => p !== 'meta.json').sort(), [files]); + const fileTree = useMemo(() => buildFileTree(filePaths), [filePaths]); + + const updateFile = useCallback((path: string, content: string) => { + setFiles(prev => ({ ...prev, [path]: content })); + }, []); + + const [newFileName, setNewFileName] = useState(''); + const [showNewFileInput, setShowNewFileInput] = useState(false); + const newFileInputRef = useRef(null); + + useEffect(() => { + if (showNewFileInput) { + setTimeout(() => newFileInputRef.current?.focus(), 50); + } + }, [showNewFileInput]); + + const addFile = useCallback((fileName: string) => { + const trimmed = fileName.trim(); + if (!trimmed || files[trimmed] != null) return; + setFiles(prev => ({ ...prev, [trimmed]: '' })); + setActiveFile(trimmed); + setShowNewFileInput(false); + setNewFileName(''); + if (workspaceId) { + fetch(`${WORKSPACE_API}/${workspaceId}/file/${encodeURIComponent(trimmed)}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ content: '' }), + }).catch(() => {}); + } + }, [files, workspaceId]); + + const deleteFile = useCallback((filePath: string) => { + setFiles(prev => { + const next = { ...prev }; + delete next[filePath]; + return next; + }); + if (activeFile === filePath) { + const remaining = filePaths.filter(p => p !== filePath); + setActiveFile(remaining[0] ?? 'index.html'); + } + if (workspaceId) { + fetch(`${WORKSPACE_API}/${workspaceId}/file/${encodeURIComponent(filePath)}`, { + method: 'DELETE', + }).catch(() => {}); + } + }, [activeFile, filePaths, workspaceId]); + + const activeFileContent = files[activeFile] ?? ''; + + return ( + + + {/* Left panel — AgentChat */} + + {effectiveSessionId ? ( + + ) : ( + + + Initializing agent... + + + )} + + + {/* Resize handle */} + + + {/* Right panel */} + + {/* Header bar */} + + + + + + setName(e.target.value)} + placeholder="View name" + variant="standard" + sx={{ + flex: 1, + maxWidth: 220, + '& .MuiInput-input': { fontSize: '0.9rem', fontWeight: 600, color: c.text.primary }, + '& .MuiInput-underline:before': { borderColor: 'transparent' }, + '& .MuiInput-underline:hover:before': { borderColor: c.border.medium }, + }} + /> + + setDescription(e.target.value)} + placeholder="Description" + variant="standard" + size="small" + sx={{ + flex: 2, + '& .MuiInput-input': { fontSize: '0.78rem', color: c.text.muted }, + '& .MuiInput-underline:before': { borderColor: 'transparent' }, + }} + /> + + {autoRunEnabled && ( + + )} + + + + {/* Tab bar */} + + setActiveTab(v)} + sx={{ + flex: 1, + minHeight: 36, + '& .MuiTab-root': { + minHeight: 36, + fontSize: '0.78rem', + textTransform: 'none', + fontWeight: 500, + py: 0, + }, + '& .MuiTabs-indicator': { + bgcolor: c.accent.primary, + }, + }} + > + + + + + {showConsole && } + + {activeTab === TAB_PREVIEW && ( + <> + + previewRef.current?.reload()} + sx={{ color: c.text.muted }} + > + + + + {output && ( + + + + + + )} + + )} + + { + if (showConsole && activeTab === TAB_CONSOLE) { + setShowConsole(false); + setActiveTab(TAB_PREVIEW); + } else if (showConsole) { + setShowConsole(false); + if (activeTab === TAB_CONSOLE) setActiveTab(TAB_PREVIEW); + } else { + setShowConsole(true); + setHasNewConsoleOutput(false); + setActiveTab(TAB_CONSOLE); + } + }} + sx={{ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + cursor: 'pointer', + px: 0.75, + py: 0.5, + mr: 1, + borderRadius: 1, + position: 'relative', + bgcolor: showConsole ? c.accent.primary + '15' : 'transparent', + '&:hover': { bgcolor: showConsole ? c.accent.primary + '25' : c.bg.elevated }, + transition: 'background-color 0.15s', + }} + > + + {'>_'} + + {hasNewConsoleOutput && !showConsole && ( + + )} + + + + + {/* Tab content */} + + {activeTab === TAB_PREVIEW && ( + + )} + {activeTab === TAB_CODE && ( + + {/* File tree sidebar */} + + + + Files + + + setShowNewFileInput(true)} + sx={{ p: 0.25, color: c.text.ghost, '&:hover': { color: c.accent.primary } }} + > + + + + + + + {fileTree.map((node) => ( + + ))} + {filePaths.length === 0 && ( + + No files yet + + )} + + + {showNewFileInput && ( + + setNewFileName(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter') { addFile(newFileName); } + if (e.key === 'Escape') { setShowNewFileInput(false); setNewFileName(''); } + }} + onBlur={() => { + if (newFileName.trim()) { addFile(newFileName); } + else { setShowNewFileInput(false); setNewFileName(''); } + }} + placeholder="path/to/file.js" + variant="standard" + fullWidth + autoFocus + sx={{ + '& .MuiInput-input': { + fontSize: '0.74rem', + fontFamily: c.font.mono, + color: c.text.primary, + py: 0.25, + }, + '& .MuiInput-underline:before': { borderColor: c.border.subtle }, + '& .MuiInput-underline:after': { borderColor: c.accent.primary }, + }} + /> + + )} + + {/* Editor area */} + + {activeFile && files[activeFile] != null ? ( + updateFile(activeFile, val)} + language={getEditorLanguage(activeFile)} + placeholder={`// ${activeFile}`} + /> + ) : ( + + + Select a file to edit + + + )} + + + )} + {activeTab === TAB_TEST_INPUT && ( + + + + + + + + + + )} + {activeTab === TAB_CONSOLE && ( + + )} + + + setAutoRunEnabled(v)} + size="small" + sx={{ + '& .MuiSwitch-switchBase.Mui-checked': { color: '#f59e0b' }, + '& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track': { bgcolor: '#f59e0b' }, + }} + /> + + {autoRunEnabled ? 'Auto Run enabled' : 'Auto Run disabled'} + + + {autoRunEnabled && ( + + )} + + + {autoRunEnabled ? ( + + + Describe what data to generate for this view. When triggered, an LLM will produce input data matching your schema and populate the preview. + + {}} + mode={autoRunMode} + onModeChange={setAutoRunMode} + model={autoRunModel} + onModelChange={setAutoRunModel} + /> + + {(autoRunSessionId || autoRunMessages.length > 0) && ( + + )} + + ) : ( + + + + Enable Auto Run to generate live data for this view + + + Configure a prompt that describes what data to generate. An LLM will produce input matching your schema and populate the preview automatically. + + + )} + + + + + + + ); +}; + +export default ViewEditor; diff --git a/frontend/src/app/pages/Views/ViewPreview.tsx b/frontend/src/app/pages/Views/ViewPreview.tsx new file mode 100644 index 00000000..bf9c7410 --- /dev/null +++ b/frontend/src/app/pages/Views/ViewPreview.tsx @@ -0,0 +1,163 @@ +import React, { useRef, useEffect, useMemo, forwardRef, useImperativeHandle, useState } from 'react'; +import Box from '@mui/material/Box'; +import { useElementSelection } from '@/app/components/ElementSelectionContext'; +import { useIframeElementSelector } from './useIframeElementSelector'; + +export interface ViewPreviewHandle { + reload: () => void; +} + +interface Props { + /** URL-based serving (multi-file support). Takes priority over frontendCode. */ + serveUrl?: string; + /** Legacy: raw HTML string rendered via srcdoc. */ + frontendCode?: string; + inputData: Record; + backendResult?: Record | null; + style?: React.CSSProperties; +} + +function buildSrcdoc( + frontendCode: string, + inputData: Record, + backendResult: Record | null, +): string { + const inputJson = JSON.stringify(inputData); + const resultJson = JSON.stringify(backendResult); + + const injection = ``; + + if (frontendCode.includes('')) { + return frontendCode.replace('', `${injection}\n`); + } + if (frontendCode.includes(', backendResult: Record | null): string { + const payload = JSON.stringify({ i: inputData, r: backendResult }); + return btoa(unescape(encodeURIComponent(payload))); +} + +const ViewPreview = forwardRef(({ + serveUrl, + frontendCode, + inputData, + backendResult = null, + style, +}, ref) => { + const iframeRef = useRef(null); + const ctx = useElementSelection(); + const [reloadKey, setReloadKey] = useState(0); + + useEffect(() => { + if (ctx && iframeRef.current) { + ctx.iframeRef.current = iframeRef.current; + } + }, [ctx, frontendCode, serveUrl]); + + useIframeElementSelector(iframeRef); + + const iframeSrc = useMemo(() => { + if (!serveUrl) return undefined; + const dataParam = encodeDataParam(inputData, backendResult); + const sep = serveUrl.includes('?') ? '&' : '?'; + return `${serveUrl}${sep}_d=${encodeURIComponent(dataParam)}&_v=${reloadKey}`; + }, [serveUrl, inputData, backendResult, reloadKey]); + + const srcdoc = useMemo(() => { + if (serveUrl || !frontendCode) return undefined; + return buildSrcdoc(frontendCode, inputData, backendResult); + }, [serveUrl, frontendCode, inputData, backendResult]); + + useImperativeHandle(ref, () => ({ + reload: () => { + if (serveUrl) { + setReloadKey(k => k + 1); + } else if (iframeRef.current && srcdoc) { + iframeRef.current.srcdoc = ''; + requestAnimationFrame(() => { + if (iframeRef.current) iframeRef.current.srcdoc = srcdoc; + }); + } + }, + }), [serveUrl, srcdoc]); + + useEffect(() => { + if (iframeRef.current && srcdoc != null) { + iframeRef.current.srcdoc = srcdoc; + } + }, [srcdoc]); + + const hasContent = !!(serveUrl || frontendCode?.trim()); + + if (!hasContent) { + return ( + + No preview available + + ); + } + + const selectActive = ctx?.selectMode ?? false; + + return ( + +