` after it receives a failed `res(connect)`. `GatewayClient.connect()` parses that close into `GatewayResponseError(code, message)` for UI retry policy and user-facing errors.
+- Separately, the proxy may also close with `1011` / `connect failed`; the “connect failed: …” close reason that the UI parses is produced by the browser client, not the proxy.
+- WebSocket close reasons are truncated to 123 UTF-8 bytes in the browser client to avoid protocol errors on long messages.
+
+Error codes used by the proxy include:
+- `studio.gateway_url_missing`
+- `studio.gateway_token_missing`
+- `studio.gateway_url_invalid`
+- `studio.settings_load_failed`
+- `studio.upstream_error`
+- `studio.upstream_closed`
+
+## Reconnects And Retries
+
+There are two layers of retry behavior:
+
+- Transport reconnect (after a successful hello): the vendored browser client reconnects the browser->Studio WebSocket with backoff when it closes, and continues emitting events after reconnect. See `src/lib/gateway/openclaw/GatewayBrowserClient.ts`.
+- Initial connect failure retry: when the initial `connect` handshake fails (for example bad token), `GatewayClient.connect()` tears down the vendored client and returns a rejected promise; `useGatewayConnection()` may schedule a limited re-attempt unless the error code is known non-retryable. See `resolveGatewayAutoRetryDelayMs` in `src/lib/gateway/GatewayClient.ts`.
+
+## Studio Access Gate
+
+When Studio is bound to a public host, `STUDIO_ACCESS_TOKEN` is required. For loopback-only binds, it remains optional. When enabled, Studio enforces a simple access gate:
+- HTTP: blocks `/api/*` routes unless the correct `studio_access` cookie is present.
+- WebSocket: blocks `/api/gateway/ws` upgrades unless the cookie is present.
+
+Files:
+- Gate implementation: `server/access-gate.js`
+- Gate integration for WS upgrades: `server/index.js`
+
+## Streaming: What the Gateway Sends and How Studio Uses It
+
+Studio classifies gateway events by `event` name:
+- `presence`, `heartbeat`: summary refresh triggers
+- `chat`: runtime chat messages (delta/final)
+- `agent`: runtime per-stream deltas (assistant/thinking/tool/lifecycle)
+
+Code:
+- Classification: `src/features/agents/state/runtimeEventBridge.ts`
+- Execution: `src/features/agents/state/gatewayRuntimeEventHandler.ts`
+
+## Live Fields vs Committed Transcript (Why Streaming Can “Look Weird”)
+
+Studio intentionally separates:
+- Live streaming UI: `AgentState.streamText` and `AgentState.thinkingTrace` are updated via `queueLivePatch`, which batches patches and coalesces multiple deltas before they hit React state (`src/app/page.tsx`).
+- Committed transcript: `AgentState.outputLines` is appended via `appendOutput`. These are the lines that become the durable on-screen transcript and are later merged with `chat.history` results (`src/features/agents/state/store.tsx`).
+
+This split is why you can see:
+- “live” assistant output update rapidly at the bottom card during a run
+- then a finalized assistant message (plus tool lines / thinking trace / meta timestamp) appear in the transcript on `final`
+
+### `event: "chat"` payload
+
+Studio treats `chat` events as the canonical “message” stream for transcript completion. Expected fields:
+- `runId`
+- `sessionKey`
+- `state`: `delta | final | aborted | error`
+- `message` (shape varies; Studio extracts text/thinking/tool metadata defensively)
+
+Key behaviors (Studio-side):
+- Ignores user/system roles for transcript append (but uses them for status/summary).
+- User messages shown in the transcript are primarily from local optimistic send and from `chat.history` sync (not from runtime `chat` user-role events).
+- On `final`, appends:
+ - a `[[meta]]{...}` line (timestamp and thinking duration when available)
+ - a `[[trace]]` thinking block when extracted
+ - tool call/result markdown lines when present
+ - the assistant text (if any)
+- If a `final` assistant message arrives without an extractable thinking trace, Studio may request `chat.history` as recovery.
+- `chat.send` is idempotency-keyed upstream and returns a started ack before async completion; this is why history reconciliation can race with runtime events and must be idempotent.
+
+### `event: "agent"` payload
+
+Studio uses `agent` events for live streaming and richer tool/lifecycle updates. Expected fields:
+- `runId`
+- `stream`: `assistant | tool | lifecycle | `
+- `data`: record with `text`/`delta` and stream-specific keys
+
+Stream handling (high-level):
+- `assistant`: merges `data.delta` into a live `streamText` for the UI.
+- reasoning stream (anything that is not `assistant`, `tool`, `lifecycle` and matches hints like `reason`/`think`/`analysis`/`trace`): merged into `thinkingTrace`.
+- `tool`: formats tool call and tool result lines using `[[tool]]` and `[[tool-result]]`.
+- `lifecycle`: start/end/error transitions; if a run reaches `end` without chat final events, Studio may flush the last streamed assistant text as a fallback final transcript entry.
+
+Code:
+- Runtime agent stream merge + append: `src/features/agents/state/gatewayRuntimeEventHandler.ts`
+
+## How Chat UI Renders Streaming
+
+Studio keeps an `outputLines: string[]` transcript per agent, plus live fields like `streamText` and `thinkingTrace`.
+
+Rendering pipeline:
+- `outputLines` contains:
+ - user messages as `> ...`
+ - assistant messages as raw markdown text
+ - tool call/results with prefixes `[[tool]]` and `[[tool-result]]`
+ - optional meta lines `[[meta]]{...}` for timestamps and thinking durations
+ - optional thinking trace lines `[[trace]] ...`
+- The panel derives structured chat items from `outputLines` and (optionally) live streaming state.
+- UI toggles that change rendering:
+ - `showThinkingTraces`: hides/shows `[[trace]]` thinking entries.
+ - `toolCallingEnabled`: when off, tool lines are hidden and some exec tool results may be shown as assistant text.
+
+### Rendering contract
+
+- Assistant markdown renders as assistant markdown. Studio does not wrap normal assistant markdown in a synthetic `Output` container.
+- Tool cards render only from explicit marker lines: `[[tool]]` and `[[tool-result]]`.
+- List-marker visibility comes from chat markdown styles in `src/app/styles/markdown.css`; stream parsing does not invent list bullets.
+
+Files:
+- Chat panel UI: `src/features/agents/components/AgentChatPanel.tsx`
+- Transcript parsing into items: `src/features/agents/components/chatItems.ts`
+- Message extraction helpers (text/thinking/tool parsing): `src/lib/text/message-extract.ts`
+- Media line rewrite (images/audio/video rendered in markdown): `src/lib/text/media-markdown.ts`
+
+## Sending Messages (Browser -> PI via Gateway)
+
+Send path (high level):
+- UI submits a message through `sendChatMessageViaStudio()` which:
+ - Sets agent state to running and clears live streams.
+ - Optionally resets local transcript state for `/new` or `/reset` (local UI behavior).
+ - Optimistically appends the user line (`> ...`) to the transcript.
+ - Ensures session settings are synced once via `sessions.patch` (model/thinking/exec settings) before first send.
+ - Calls `chat.send` with `idempotencyKey = runId` and `deliver: false`.
+
+Stop path:
+- UI calls `chat.abort` to stop an active run.
+
+Files:
+- Send operation: `src/features/agents/operations/chatSendOperation.ts`
+- Session settings sync transport: `src/lib/gateway/GatewayClient.ts`
+- Stop call site: `src/app/page.tsx`
+
+## Post-Connect Side Effects (Local Gateway Only)
+
+After a successful connection, Studio may mutate gateway config when the upstream gateway URL is local:
+- It reads `config.get` and may write `config.set` to ensure `gateway.reload.mode` is `"hot"` for local Studio usage.
+
+File:
+- Reload mode enforcement: `src/lib/gateway/gatewayReloadMode.ts`
+
+## Sequence Gaps (Dropped Events)
+
+Gateway event frames may include `seq`. The vendored browser client tracks `seq` and reports gaps (`expected`, `received`) via `onGap`.
+
+Studio behavior on gap:
+- Logs a warning.
+- Forces a summary snapshot refresh and reconciles running agents.
+
+Files:
+- Gap detection: `src/lib/gateway/openclaw/GatewayBrowserClient.ts`
+- Gap handling: `src/app/page.tsx`
+
+## History Sync (Recovery, Load More)
+
+Studio can fetch history via `chat.history` and merge it into the transcript.
+
+Key points:
+- Studio intentionally treats gateway history as canonical for timestamps/final ordering.
+- History merge is designed to avoid duplicates and reconcile local optimistic sends.
+- History parsing intentionally skips some system-ish content (heartbeat prompts, restart sentinel messages, and UI metadata prefixes). See `buildHistoryLines()` in `src/features/agents/state/runtimeEventBridge.ts`.
+- Transcript v2 can be toggled with `NEXT_PUBLIC_STUDIO_TRANSCRIPT_V2`.
+- Transcript debug logs can be enabled with `NEXT_PUBLIC_STUDIO_TRANSCRIPT_DEBUG`.
+
+Files:
+- History operation: `src/features/agents/operations/historySyncOperation.ts`
+- Transcript merge/sort primitives: `src/features/agents/state/transcript.ts`
+
+## Exec Approvals In Chat (Related To “PI Runs”)
+
+Some runs require exec approval. These are surfaced as in-chat cards and are handled separately from the `chat`/`agent` runtime stream.
+
+Files:
+- Event to pending-card state: `src/features/agents/approvals/execApprovalEvents.ts`
+- Resolve operation: `src/features/agents/approvals/execApprovalResolveOperation.ts`
+- Wiring (subscribe + render): `src/app/page.tsx`, `src/features/agents/components/AgentChatPanel.tsx`
+
+## Media Rendering (Images From Agent Output)
+
+If an agent outputs lines like:
+- `MEDIA: /home/ubuntu/.openclaw/.../image.png`
+
+Studio may render them inline:
+1. UI rewrites eligible `MEDIA:` lines into markdown images (``) but avoids rewriting inside fenced code blocks.
+2. The browser requests `/api/gateway/media`.
+3. The API route reads the image either locally (only under `~/.openclaw`) or over SSH for remote gateways, and returns the bytes with the correct `Content-Type`.
+
+Files:
+- Rewrite helper: `src/lib/text/media-markdown.ts`
+- Media API route: `src/app/api/gateway/media/route.ts`
+- SSH helper + env vars (`OPENCLAW_GATEWAY_SSH_TARGET`, `OPENCLAW_GATEWAY_SSH_USER`): `src/lib/ssh/gateway-host.ts`
+
+## Debugging Checklist (When Chat “Feels Buggy”)
+
+Start with the hop where symptoms appear.
+
+WS bridge / connectivity:
+- Studio server logs (proxy): `server/gateway-proxy.js`
+- Common failures: wrong `ws://` vs `wss://`, missing token, gateway closed, upstream TLS mismatch
+
+Streaming correctness (missing/duplicated output):
+- Event classification + runtime stream merge: `src/features/agents/state/gatewayRuntimeEventHandler.ts`
+- Text/thinking/tool extraction quirks: `src/lib/text/message-extract.ts`
+- UI item derivation and collapsing rules: `src/features/agents/components/chatItems.ts`
+- Dedupe of tool lines per run + closed-run ignore window: `src/features/agents/state/gatewayRuntimeEventHandler.ts`
+
+History and ordering issues:
+- `chat.history` merge logic and dedupe: `src/features/agents/operations/historySyncOperation.ts`
+- Transcript entry ordering/fingerprints: `src/features/agents/state/transcript.ts`
+
+Media not rendering:
+- `MEDIA:` rewrite behavior and code-fence skipping: `src/lib/text/media-markdown.ts`
+- Image fetch route behavior (local vs SSH, allowlisted extensions, size limits): `src/app/api/gateway/media/route.ts`
+
+If you need Gateway-side observability:
+- Capture the exact `connect` settings used by Studio (URL + token are stored server-side in the Studio settings file).
+- Inspect Gateway logs on the Gateway host using your environment’s service/log tooling.
diff --git a/eslint.config.mjs b/eslint.config.mjs
new file mode 100644
index 0000000..c2dc6d4
--- /dev/null
+++ b/eslint.config.mjs
@@ -0,0 +1,29 @@
+import { defineConfig, globalIgnores } from "eslint/config";
+import nextVitals from "eslint-config-next/core-web-vitals";
+import nextTs from "eslint-config-next/typescript";
+import prettier from "eslint-config-prettier/flat";
+
+const eslintConfig = defineConfig([
+ ...nextVitals,
+ ...nextTs,
+ {
+ files: ["server/**/*.js", "scripts/**/*.js"],
+ rules: {
+ "@typescript-eslint/no-require-imports": "off",
+ },
+ },
+ // Override default ignores of eslint-config-next.
+ globalIgnores([
+ // Default ignores of eslint-config-next:
+ ".next/**",
+ "out/**",
+ "build/**",
+ "next-env.d.ts",
+
+ // Vendored third-party code (kept as-is; linting it adds noise).
+ "src/lib/avatars/vendor/**",
+ ]),
+ prettier,
+]);
+
+export default eslintConfig;
diff --git a/get_angles.js b/get_angles.js
new file mode 100644
index 0000000..4400e6a
--- /dev/null
+++ b/get_angles.js
@@ -0,0 +1,27 @@
+async function main() {
+ const THREE = await import("three");
+ const target = new THREE.Vector3(0, 0, 1);
+ const v = new THREE.Vector3(-0.42, 1.32, -1.47).normalize();
+
+ // We want to find a rotation of the earth group (with order XYZ or YXZ).
+ // that brings v to target.
+ // But the earth might have arbitrary rotations.
+ // we want to rotate earth such that earth.localToWorld(v) == target.
+ // so earth.quaternion * v = target.
+ // earth.quaternion = quaternion that rotates v to target.
+ const q = new THREE.Quaternion().setFromUnitVectors(v, target);
+ const e = new THREE.Euler().setFromQuaternion(q, "XYZ");
+ console.log("To point exactly at +Z:");
+ console.log("x:", e.x, "y:", e.y, "z:", e.z);
+
+ // Since the camera is looking slightly down/up, maybe we need to pitch it.
+ const cameraDir = new THREE.Vector3(0, 0.8 - 0.5, -0.5 - 2.05).normalize().negate(); // Vector from surface to camera.
+ console.log("Camera dir:", cameraDir);
+ const q2 = new THREE.Quaternion().setFromUnitVectors(v, cameraDir);
+ const e2 = new THREE.Euler().setFromQuaternion(q2, "XYZ");
+ console.log("To point at camera:");
+ console.log("x:", e2.x, "y:", e2.y, "z:", e2.z);
+}
+
+void main();
+
diff --git a/get_angles2.js b/get_angles2.js
new file mode 100644
index 0000000..e30c9ad
--- /dev/null
+++ b/get_angles2.js
@@ -0,0 +1,22 @@
+async function main() {
+ const THREE = await import("three");
+
+ // Earth rotation order is default XYZ.
+ // The beacon's local position.
+ const beaconLocal = new THREE.Vector3(-0.42, 1.32, -1.47).normalize();
+
+ // The camera's position during dive.
+ const cameraPos = new THREE.Vector3(0, 0.5, 2.05).normalize();
+
+ // We want to rotate beaconLocal to cameraPos.
+ // The required quaternion.
+ const q = new THREE.Quaternion().setFromUnitVectors(beaconLocal, cameraPos);
+
+ // Convert to Euler so we can damp x and y.
+ const e = new THREE.Euler().setFromQuaternion(q, "XYZ");
+
+ console.log("Target Euler:");
+ console.log("x:", e.x, "y:", e.y, "z:", e.z);
+}
+
+void main();
diff --git a/home-screen.png b/home-screen.png
new file mode 100644
index 0000000..3fc5345
Binary files /dev/null and b/home-screen.png differ
diff --git a/make_continents.js b/make_continents.js
new file mode 100644
index 0000000..1418d09
--- /dev/null
+++ b/make_continents.js
@@ -0,0 +1,28 @@
+async function main() {
+ const THREE = await import("three");
+
+ function latLonToVector3(lat, lon, radius) {
+ const phi = (90 - lat) * (Math.PI / 180);
+ const theta = (lon + 90) * (Math.PI / 180);
+ const x = -radius * Math.sin(phi) * Math.cos(theta);
+ const y = radius * Math.cos(phi);
+ const z = radius * Math.sin(phi) * Math.sin(theta);
+ return new THREE.Vector3(x, y, z);
+ }
+
+ const continents = {
+ NA: latLonToVector3(45, -100, 2),
+ NY: latLonToVector3(40.7, -74, 2),
+ SA: latLonToVector3(-15, -60, 2),
+ EU: latLonToVector3(50, 15, 2),
+ AF: latLonToVector3(0, 20, 2),
+ AS: latLonToVector3(40, 90, 2),
+ AU: latLonToVector3(-25, 135, 2),
+ };
+
+ for (const [name, v] of Object.entries(continents)) {
+ console.log(`${name}: [${v.x.toFixed(2)}, ${v.y.toFixed(2)}, ${v.z.toFixed(2)}],`);
+ }
+}
+
+void main();
diff --git a/next.config.ts b/next.config.ts
new file mode 100644
index 0000000..cb651cd
--- /dev/null
+++ b/next.config.ts
@@ -0,0 +1,5 @@
+import type { NextConfig } from "next";
+
+const nextConfig: NextConfig = {};
+
+export default nextConfig;
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..5cc8ff7
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,11180 @@
+{
+ "name": "claw3d",
+ "version": "0.1.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "claw3d",
+ "version": "0.1.0",
+ "license": "MIT",
+ "dependencies": {
+ "@noble/ed25519": "^3.0.0",
+ "@react-three/drei": "^10.7.7",
+ "@react-three/fiber": "^9.5.0",
+ "@vercel/otel": "^2.1.0",
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
+ "lucide-react": "^0.563.0",
+ "next": "16.1.6",
+ "phaser": "^3.90.0",
+ "react": "19.2.3",
+ "react-dom": "19.2.3",
+ "react-markdown": "^10.1.0",
+ "react-mentions-ts": "^5.4.7",
+ "remark-gfm": "^4.0.1",
+ "tailwind-merge": "^3.4.0",
+ "three": "^0.183.2",
+ "ws": "^8.18.3"
+ },
+ "devDependencies": {
+ "@playwright/test": "^1.58.0",
+ "@tailwindcss/postcss": "^4",
+ "@testing-library/jest-dom": "^6.9.1",
+ "@testing-library/react": "^16.3.2",
+ "@types/node": "^20",
+ "@types/react": "^19",
+ "@types/react-dom": "^19",
+ "@types/three": "^0.183.1",
+ "@types/ws": "^8.18.1",
+ "eslint": "^9",
+ "eslint-config-next": "16.1.6",
+ "eslint-config-prettier": "^10.1.8",
+ "jsdom": "^27.4.0",
+ "tailwindcss": "^4",
+ "tw-animate-css": "^1.4.0",
+ "typescript": "^5",
+ "vitest": "^4.0.18"
+ }
+ },
+ "node_modules/@acemir/cssom": {
+ "version": "0.9.31",
+ "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.31.tgz",
+ "integrity": "sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@adobe/css-tools": {
+ "version": "4.4.4",
+ "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz",
+ "integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==",
+ "dev": true,
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@asamuzakjp/css-color": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.1.1.tgz",
+ "integrity": "sha512-B0Hv6G3gWGMn0xKJ0txEi/jM5iFpT3MfDxmhZFb4W047GvytCf1DHQ1D69W3zHI4yWe2aTZAA0JnbMZ7Xc8DuQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@csstools/css-calc": "^2.1.4",
+ "@csstools/css-color-parser": "^3.1.0",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "lru-cache": "^11.2.4"
+ }
+ },
+ "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": {
+ "version": "11.2.5",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.5.tgz",
+ "integrity": "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/@asamuzakjp/dom-selector": {
+ "version": "6.7.6",
+ "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.7.6.tgz",
+ "integrity": "sha512-hBaJER6A9MpdG3WgdlOolHmbOYvSk46y7IQN/1+iqiCuUu6iWdQrs9DGKF8ocqsEqWujWf/V7b7vaDgiUmIvUg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@asamuzakjp/nwsapi": "^2.3.9",
+ "bidi-js": "^1.0.3",
+ "css-tree": "^3.1.0",
+ "is-potential-custom-element-name": "^1.0.1",
+ "lru-cache": "^11.2.4"
+ }
+ },
+ "node_modules/@asamuzakjp/dom-selector/node_modules/lru-cache": {
+ "version": "11.2.5",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.5.tgz",
+ "integrity": "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/@asamuzakjp/nwsapi": {
+ "version": "2.3.9",
+ "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz",
+ "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz",
+ "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.6.tgz",
+ "integrity": "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz",
+ "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.28.6",
+ "@babel/generator": "^7.28.6",
+ "@babel/helper-compilation-targets": "^7.28.6",
+ "@babel/helper-module-transforms": "^7.28.6",
+ "@babel/helpers": "^7.28.6",
+ "@babel/parser": "^7.28.6",
+ "@babel/template": "^7.28.6",
+ "@babel/traverse": "^7.28.6",
+ "@babel/types": "^7.28.6",
+ "@jridgewell/remapping": "^2.3.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz",
+ "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.28.6",
+ "@babel/types": "^7.28.6",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz",
+ "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.28.6",
+ "@babel/helper-validator-option": "^7.27.1",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
+ "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz",
+ "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.28.6",
+ "@babel/types": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz",
+ "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.28.6",
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "@babel/traverse": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
+ "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz",
+ "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.28.6",
+ "@babel/types": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz",
+ "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.6"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz",
+ "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
+ "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.28.6",
+ "@babel/parser": "^7.28.6",
+ "@babel/types": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.6.tgz",
+ "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.28.6",
+ "@babel/generator": "^7.28.6",
+ "@babel/helper-globals": "^7.28.0",
+ "@babel/parser": "^7.28.6",
+ "@babel/template": "^7.28.6",
+ "@babel/types": "^7.28.6",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz",
+ "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@csstools/color-helpers": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz",
+ "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@csstools/css-calc": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz",
+ "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
+ }
+ },
+ "node_modules/@csstools/css-color-parser": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz",
+ "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@csstools/color-helpers": "^5.1.0",
+ "@csstools/css-calc": "^2.1.4"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
+ }
+ },
+ "node_modules/@csstools/css-parser-algorithms": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz",
+ "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@csstools/css-tokenizer": "^3.0.4"
+ }
+ },
+ "node_modules/@csstools/css-syntax-patches-for-csstree": {
+ "version": "1.0.26",
+ "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.26.tgz",
+ "integrity": "sha512-6boXK0KkzT5u5xOgF6TKB+CLq9SOpEGmkZw0g5n9/7yg85wab3UzSxB8TxhLJ31L4SGJ6BCFRw/iftTha1CJXA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0"
+ },
+ "node_modules/@csstools/css-tokenizer": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz",
+ "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@dimforge/rapier3d-compat": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz",
+ "integrity": "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@emnapi/core": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz",
+ "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.1.0",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz",
+ "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz",
+ "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz",
+ "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz",
+ "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz",
+ "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz",
+ "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz",
+ "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz",
+ "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz",
+ "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz",
+ "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz",
+ "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz",
+ "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz",
+ "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz",
+ "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz",
+ "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz",
+ "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz",
+ "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz",
+ "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz",
+ "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz",
+ "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz",
+ "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz",
+ "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz",
+ "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz",
+ "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz",
+ "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz",
+ "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz",
+ "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz",
+ "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.9.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
+ "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.21.1",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
+ "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.7",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
+ "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
+ "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz",
+ "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.1",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.39.2",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz",
+ "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
+ "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
+ "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@exodus/bytes": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.10.0.tgz",
+ "integrity": "sha512-tf8YdcbirXdPnJ+Nd4UN1EXnz+IP2DI45YVEr3vvzcVTOyrApkmIB4zvOQVd3XPr7RXnfBtAx+PXImXOIU0Ajg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+ },
+ "peerDependencies": {
+ "@noble/hashes": "^1.8.0 || ^2.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@noble/hashes": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@humanfs/core": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.7",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
+ "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.1",
+ "@humanwhocodes/retry": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@img/colour": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz",
+ "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@img/sharp-darwin-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
+ "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-darwin-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",
+ "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
+ "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",
+ "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",
+ "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz",
+ "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-ppc64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz",
+ "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-riscv64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz",
+ "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-s390x": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz",
+ "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz",
+ "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz",
+ "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz",
+ "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz",
+ "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz",
+ "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-ppc64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz",
+ "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-ppc64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-riscv64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz",
+ "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-riscv64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-s390x": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz",
+ "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-s390x": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
+ "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",
+ "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
+ "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-wasm32": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",
+ "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==",
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/runtime": "^1.7.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
+ "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-ia32": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",
+ "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",
+ "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@mediapipe/tasks-vision": {
+ "version": "0.10.17",
+ "resolved": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.17.tgz",
+ "integrity": "sha512-CZWV/q6TTe8ta61cZXjfnnHsfWIdFhms03M9T7Cnd5y2mdpylJM0rF1qRq+wsQVRMLz1OYPVEBU9ph2Bx8cxrg==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@monogrid/gainmap-js": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/@monogrid/gainmap-js/-/gainmap-js-3.4.0.tgz",
+ "integrity": "sha512-2Z0FATFHaoYJ8b+Y4y4Hgfn3FRFwuU5zRrk+9dFWp4uGAdHGqVEdP7HP+gLA3X469KXHmfupJaUbKo1b/aDKIg==",
+ "license": "MIT",
+ "dependencies": {
+ "promise-worker-transferable": "^1.0.4"
+ },
+ "peerDependencies": {
+ "three": ">= 0.159.0"
+ }
+ },
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "0.2.12",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz",
+ "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.4.3",
+ "@emnapi/runtime": "^1.4.3",
+ "@tybys/wasm-util": "^0.10.0"
+ }
+ },
+ "node_modules/@next/env": {
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.6.tgz",
+ "integrity": "sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ==",
+ "license": "MIT"
+ },
+ "node_modules/@next/eslint-plugin-next": {
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.1.6.tgz",
+ "integrity": "sha512-/Qq3PTagA6+nYVfryAtQ7/9FEr/6YVyvOtl6rZnGsbReGLf0jZU6gkpr1FuChAQpvV46a78p4cmHOVP8mbfSMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-glob": "3.3.1"
+ }
+ },
+ "node_modules/@next/swc-darwin-arm64": {
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.6.tgz",
+ "integrity": "sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-darwin-x64": {
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.6.tgz",
+ "integrity": "sha512-BLFPYPDO+MNJsiDWbeVzqvYd4NyuRrEYVB5k2N3JfWncuHAy2IVwMAOlVQDFjj+krkWzhY2apvmekMkfQR0CUQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-gnu": {
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.6.tgz",
+ "integrity": "sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-musl": {
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.6.tgz",
+ "integrity": "sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-gnu": {
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.6.tgz",
+ "integrity": "sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-musl": {
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.6.tgz",
+ "integrity": "sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-arm64-msvc": {
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.6.tgz",
+ "integrity": "sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-x64-msvc": {
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.6.tgz",
+ "integrity": "sha512-NRfO39AIrzBnixKbjuo2YiYhB6o9d8v/ymU9m/Xk8cyVk+k7XylniXkHwjs4s70wedVffc6bQNbufk5v0xEm0A==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@noble/ed25519": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@noble/ed25519/-/ed25519-3.0.0.tgz",
+ "integrity": "sha512-QyteqMNm0GLqfa5SoYbSC3+Pvykwpn95Zgth4MFVSMKBB75ELl9tX1LAVsN4c3HXOrakHsF2gL4zWDAYCcsnzg==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "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==",
+ "dev": true,
+ "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==",
+ "dev": true,
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nolyfill/is-core-module": {
+ "version": "1.0.39",
+ "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz",
+ "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.4.0"
+ }
+ },
+ "node_modules/@opentelemetry/api": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
+ "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
+ "license": "Apache-2.0",
+ "peer": true,
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/@opentelemetry/api-logs": {
+ "version": "0.211.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz",
+ "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==",
+ "license": "Apache-2.0",
+ "peer": true,
+ "dependencies": {
+ "@opentelemetry/api": "^1.3.0"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/@opentelemetry/core": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.5.0.tgz",
+ "integrity": "sha512-ka4H8OM6+DlUhSAZpONu0cPBtPPTQKxbxVzC4CzVx5+K4JnroJVBtDzLAMx4/3CDTJXRvVFhpFjtl4SaiTNoyQ==",
+ "license": "Apache-2.0",
+ "peer": true,
+ "dependencies": {
+ "@opentelemetry/semantic-conventions": "^1.29.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.0.0 <1.10.0"
+ }
+ },
+ "node_modules/@opentelemetry/instrumentation": {
+ "version": "0.211.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz",
+ "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==",
+ "license": "Apache-2.0",
+ "peer": true,
+ "dependencies": {
+ "@opentelemetry/api-logs": "0.211.0",
+ "import-in-the-middle": "^2.0.0",
+ "require-in-the-middle": "^8.0.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.3.0"
+ }
+ },
+ "node_modules/@opentelemetry/resources": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.5.0.tgz",
+ "integrity": "sha512-F8W52ApePshpoSrfsSk1H2yJn9aKjCrbpQF1M9Qii0GHzbfVeFUB+rc3X4aggyZD8x9Gu3Slua+s6krmq6Dt8g==",
+ "license": "Apache-2.0",
+ "peer": true,
+ "dependencies": {
+ "@opentelemetry/core": "2.5.0",
+ "@opentelemetry/semantic-conventions": "^1.29.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.3.0 <1.10.0"
+ }
+ },
+ "node_modules/@opentelemetry/sdk-logs": {
+ "version": "0.211.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.211.0.tgz",
+ "integrity": "sha512-O5nPwzgg2JHzo59kpQTPUOTzFi0Nv5LxryG27QoXBciX3zWM3z83g+SNOHhiQVYRWFSxoWn1JM2TGD5iNjOwdA==",
+ "license": "Apache-2.0",
+ "peer": true,
+ "dependencies": {
+ "@opentelemetry/api-logs": "0.211.0",
+ "@opentelemetry/core": "2.5.0",
+ "@opentelemetry/resources": "2.5.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.4.0 <1.10.0"
+ }
+ },
+ "node_modules/@opentelemetry/sdk-metrics": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.5.0.tgz",
+ "integrity": "sha512-BeJLtU+f5Gf905cJX9vXFQorAr6TAfK3SPvTFqP+scfIpDQEJfRaGJWta7sJgP+m4dNtBf9y3yvBKVAZZtJQVA==",
+ "license": "Apache-2.0",
+ "peer": true,
+ "dependencies": {
+ "@opentelemetry/core": "2.5.0",
+ "@opentelemetry/resources": "2.5.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.9.0 <1.10.0"
+ }
+ },
+ "node_modules/@opentelemetry/sdk-trace-base": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.5.0.tgz",
+ "integrity": "sha512-VzRf8LzotASEyNDUxTdaJ9IRJ1/h692WyArDBInf5puLCjxbICD6XkHgpuudis56EndyS7LYFmtTMny6UABNdQ==",
+ "license": "Apache-2.0",
+ "peer": true,
+ "dependencies": {
+ "@opentelemetry/core": "2.5.0",
+ "@opentelemetry/resources": "2.5.0",
+ "@opentelemetry/semantic-conventions": "^1.29.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.3.0 <1.10.0"
+ }
+ },
+ "node_modules/@opentelemetry/semantic-conventions": {
+ "version": "1.39.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.39.0.tgz",
+ "integrity": "sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==",
+ "license": "Apache-2.0",
+ "peer": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@playwright/test": {
+ "version": "1.58.0",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.0.tgz",
+ "integrity": "sha512-fWza+Lpbj6SkQKCrU6si4iu+fD2dD3gxNHFhUPxsfXBPhnv3rRSQVd0NtBUT9Z/RhF/boCBcuUaMUSTRTopjZg==",
+ "devOptional": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "playwright": "1.58.0"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@react-three/drei": {
+ "version": "10.7.7",
+ "resolved": "https://registry.npmjs.org/@react-three/drei/-/drei-10.7.7.tgz",
+ "integrity": "sha512-ff+J5iloR0k4tC++QtD/j9u3w5fzfgFAWDtAGQah9pF2B1YgOq/5JxqY0/aVoQG5r3xSZz0cv5tk2YuBob4xEQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.26.0",
+ "@mediapipe/tasks-vision": "0.10.17",
+ "@monogrid/gainmap-js": "^3.0.6",
+ "@use-gesture/react": "^10.3.1",
+ "camera-controls": "^3.1.0",
+ "cross-env": "^7.0.3",
+ "detect-gpu": "^5.0.56",
+ "glsl-noise": "^0.0.0",
+ "hls.js": "^1.5.17",
+ "maath": "^0.10.8",
+ "meshline": "^3.3.1",
+ "stats-gl": "^2.2.8",
+ "stats.js": "^0.17.0",
+ "suspend-react": "^0.1.3",
+ "three-mesh-bvh": "^0.8.3",
+ "three-stdlib": "^2.35.6",
+ "troika-three-text": "^0.52.4",
+ "tunnel-rat": "^0.1.2",
+ "use-sync-external-store": "^1.4.0",
+ "utility-types": "^3.11.0",
+ "zustand": "^5.0.1"
+ },
+ "peerDependencies": {
+ "@react-three/fiber": "^9.0.0",
+ "react": "^19",
+ "react-dom": "^19",
+ "three": ">=0.159"
+ },
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@react-three/fiber": {
+ "version": "9.5.0",
+ "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-9.5.0.tgz",
+ "integrity": "sha512-FiUzfYW4wB1+PpmsE47UM+mCads7j2+giRBltfwH7SNhah95rqJs3ltEs9V3pP8rYdS0QlNne+9Aj8dS/SiaIA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.17.8",
+ "@types/webxr": "*",
+ "base64-js": "^1.5.1",
+ "buffer": "^6.0.3",
+ "its-fine": "^2.0.0",
+ "react-use-measure": "^2.1.7",
+ "scheduler": "^0.27.0",
+ "suspend-react": "^0.1.3",
+ "use-sync-external-store": "^1.4.0",
+ "zustand": "^5.0.3"
+ },
+ "peerDependencies": {
+ "expo": ">=43.0",
+ "expo-asset": ">=8.4",
+ "expo-file-system": ">=11.0",
+ "expo-gl": ">=11.0",
+ "react": ">=19 <19.3",
+ "react-dom": ">=19 <19.3",
+ "react-native": ">=0.78",
+ "three": ">=0.156"
+ },
+ "peerDependenciesMeta": {
+ "expo": {
+ "optional": true
+ },
+ "expo-asset": {
+ "optional": true
+ },
+ "expo-file-system": {
+ "optional": true
+ },
+ "expo-gl": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ },
+ "react-native": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.57.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.0.tgz",
+ "integrity": "sha512-tPgXB6cDTndIe1ah7u6amCI1T0SsnlOuKgg10Xh3uizJk4e5M1JGaUMk7J4ciuAUcFpbOiNhm2XIjP9ON0dUqA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.57.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.0.tgz",
+ "integrity": "sha512-sa4LyseLLXr1onr97StkU1Nb7fWcg6niokTwEVNOO7awaKaoRObQ54+V/hrF/BP1noMEaaAW6Fg2d/CfLiq3Mg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.57.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.0.tgz",
+ "integrity": "sha512-/NNIj9A7yLjKdmkx5dC2XQ9DmjIECpGpwHoGmA5E1AhU0fuICSqSWScPhN1yLCkEdkCwJIDu2xIeLPs60MNIVg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.57.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.0.tgz",
+ "integrity": "sha512-xoh8abqgPrPYPr7pTYipqnUi1V3em56JzE/HgDgitTqZBZ3yKCWI+7KUkceM6tNweyUKYru1UMi7FC060RyKwA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.57.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.0.tgz",
+ "integrity": "sha512-PCkMh7fNahWSbA0OTUQ2OpYHpjZZr0hPr8lId8twD7a7SeWrvT3xJVyza+dQwXSSq4yEQTMoXgNOfMCsn8584g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.57.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.0.tgz",
+ "integrity": "sha512-1j3stGx+qbhXql4OCDZhnK7b01s6rBKNybfsX+TNrEe9JNq4DLi1yGiR1xW+nL+FNVvI4D02PUnl6gJ/2y6WJA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.57.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.0.tgz",
+ "integrity": "sha512-eyrr5W08Ms9uM0mLcKfM/Uzx7hjhz2bcjv8P2uynfj0yU8GGPdz8iYrBPhiLOZqahoAMB8ZiolRZPbbU2MAi6Q==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.57.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.0.tgz",
+ "integrity": "sha512-Xds90ITXJCNyX9pDhqf85MKWUI4lqjiPAipJ8OLp8xqI2Ehk+TCVhF9rvOoN8xTbcafow3QOThkNnrM33uCFQA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.57.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.0.tgz",
+ "integrity": "sha512-Xws2KA4CLvZmXjy46SQaXSejuKPhwVdaNinldoYfqruZBaJHqVo6hnRa8SDo9z7PBW5x84SH64+izmldCgbezw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.57.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.0.tgz",
+ "integrity": "sha512-hrKXKbX5FdaRJj7lTMusmvKbhMJSGWJ+w++4KmjiDhpTgNlhYobMvKfDoIWecy4O60K6yA4SnztGuNTQF+Lplw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.57.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.0.tgz",
+ "integrity": "sha512-6A+nccfSDGKsPm00d3xKcrsBcbqzCTAukjwWK6rbuAnB2bHaL3r9720HBVZ/no7+FhZLz/U3GwwZZEh6tOSI8Q==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-musl": {
+ "version": "4.57.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.0.tgz",
+ "integrity": "sha512-4P1VyYUe6XAJtQH1Hh99THxr0GKMMwIXsRNOceLrJnaHTDgk1FTcTimDgneRJPvB3LqDQxUmroBclQ1S0cIJwQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.57.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.0.tgz",
+ "integrity": "sha512-8Vv6pLuIZCMcgXre6c3nOPhE0gjz1+nZP6T+hwWjr7sVH8k0jRkH+XnfjjOTglyMBdSKBPPz54/y1gToSKwrSQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-musl": {
+ "version": "4.57.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.0.tgz",
+ "integrity": "sha512-r1te1M0Sm2TBVD/RxBPC6RZVwNqUTwJTA7w+C/IW5v9Ssu6xmxWEi+iJQlpBhtUiT1raJ5b48pI8tBvEjEFnFA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.57.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.0.tgz",
+ "integrity": "sha512-say0uMU/RaPm3CDQLxUUTF2oNWL8ysvHkAjcCzV2znxBr23kFfaxocS9qJm+NdkRhF8wtdEEAJuYcLPhSPbjuQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.57.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.0.tgz",
+ "integrity": "sha512-/MU7/HizQGsnBREtRpcSbSV1zfkoxSTR7wLsRmBPQ8FwUj5sykrP1MyJTvsxP5KBq9SyE6kH8UQQQwa0ASeoQQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.57.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.0.tgz",
+ "integrity": "sha512-Q9eh+gUGILIHEaJf66aF6a414jQbDnn29zeu0eX3dHMuysnhTvsUvZTCAyZ6tJhUjnvzBKE4FtuaYxutxRZpOg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.57.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.0.tgz",
+ "integrity": "sha512-OR5p5yG5OKSxHReWmwvM0P+VTPMwoBS45PXTMYaskKQqybkS3Kmugq1W+YbNWArF8/s7jQScgzXUhArzEQ7x0A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.57.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.0.tgz",
+ "integrity": "sha512-XeatKzo4lHDsVEbm1XDHZlhYZZSQYym6dg2X/Ko0kSFgio+KXLsxwJQprnR48GvdIKDOpqWqssC3iBCjoMcMpw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openbsd-x64": {
+ "version": "4.57.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.0.tgz",
+ "integrity": "sha512-Lu71y78F5qOfYmubYLHPcJm74GZLU6UJ4THkf/a1K7Tz2ycwC2VUbsqbJAXaR6Bx70SRdlVrt2+n5l7F0agTUw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.57.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.0.tgz",
+ "integrity": "sha512-v5xwKDWcu7qhAEcsUubiav7r+48Uk/ENWdr82MBZZRIm7zThSxCIVDfb3ZeRRq9yqk+oIzMdDo6fCcA5DHfMyA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.57.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.0.tgz",
+ "integrity": "sha512-XnaaaSMGSI6Wk8F4KK3QP7GfuuhjGchElsVerCplUuxRIzdvZ7hRBpLR0omCmw+kI2RFJB80nenhOoGXlJ5TfQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.57.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.0.tgz",
+ "integrity": "sha512-3K1lP+3BXY4t4VihLw5MEg6IZD3ojSYzqzBG571W3kNQe4G4CcFpSUQVgurYgib5d+YaCjeFow8QivWp8vuSvA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.57.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.0.tgz",
+ "integrity": "sha512-MDk610P/vJGc5L5ImE4k5s+GZT3en0KoK1MKPXCRgzmksAMk79j4h3k1IerxTNqwDLxsGxStEZVBqG0gIqZqoA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.57.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.0.tgz",
+ "integrity": "sha512-Zv7v6q6aV+VslnpwzqKAmrk5JdVkLUzok2208ZXGipjb+msxBr/fJPZyeEXiFgH7k62Ak0SLIfxQRZQvTuf7rQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rtsao/scc": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
+ "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@standard-schema/spec": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
+ "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@swc/helpers": {
+ "version": "0.5.15",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
+ "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.8.0"
+ }
+ },
+ "node_modules/@tailwindcss/node": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz",
+ "integrity": "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/remapping": "^2.3.4",
+ "enhanced-resolve": "^5.18.3",
+ "jiti": "^2.6.1",
+ "lightningcss": "1.30.2",
+ "magic-string": "^0.30.21",
+ "source-map-js": "^1.2.1",
+ "tailwindcss": "4.1.18"
+ }
+ },
+ "node_modules/@tailwindcss/oxide": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.18.tgz",
+ "integrity": "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ },
+ "optionalDependencies": {
+ "@tailwindcss/oxide-android-arm64": "4.1.18",
+ "@tailwindcss/oxide-darwin-arm64": "4.1.18",
+ "@tailwindcss/oxide-darwin-x64": "4.1.18",
+ "@tailwindcss/oxide-freebsd-x64": "4.1.18",
+ "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18",
+ "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18",
+ "@tailwindcss/oxide-linux-arm64-musl": "4.1.18",
+ "@tailwindcss/oxide-linux-x64-gnu": "4.1.18",
+ "@tailwindcss/oxide-linux-x64-musl": "4.1.18",
+ "@tailwindcss/oxide-wasm32-wasi": "4.1.18",
+ "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18",
+ "@tailwindcss/oxide-win32-x64-msvc": "4.1.18"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-android-arm64": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.18.tgz",
+ "integrity": "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-arm64": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.18.tgz",
+ "integrity": "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-x64": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.18.tgz",
+ "integrity": "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-freebsd-x64": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.18.tgz",
+ "integrity": "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.18.tgz",
+ "integrity": "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.18.tgz",
+ "integrity": "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-musl": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.18.tgz",
+ "integrity": "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-gnu": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.18.tgz",
+ "integrity": "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-musl": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.18.tgz",
+ "integrity": "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-wasm32-wasi": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.18.tgz",
+ "integrity": "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==",
+ "bundleDependencies": [
+ "@napi-rs/wasm-runtime",
+ "@emnapi/core",
+ "@emnapi/runtime",
+ "@tybys/wasm-util",
+ "@emnapi/wasi-threads",
+ "tslib"
+ ],
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.7.1",
+ "@emnapi/runtime": "^1.7.1",
+ "@emnapi/wasi-threads": "^1.1.0",
+ "@napi-rs/wasm-runtime": "^1.1.0",
+ "@tybys/wasm-util": "^0.10.1",
+ "tslib": "^2.4.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.18.tgz",
+ "integrity": "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-x64-msvc": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.18.tgz",
+ "integrity": "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/postcss": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.18.tgz",
+ "integrity": "sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@alloc/quick-lru": "^5.2.0",
+ "@tailwindcss/node": "4.1.18",
+ "@tailwindcss/oxide": "4.1.18",
+ "postcss": "^8.4.41",
+ "tailwindcss": "4.1.18"
+ }
+ },
+ "node_modules/@testing-library/dom": {
+ "version": "10.4.1",
+ "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz",
+ "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==",
+ "dev": true,
+ "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",
+ "dom-accessibility-api": "^0.5.9",
+ "lz-string": "^1.5.0",
+ "picocolors": "1.1.1",
+ "pretty-format": "^27.0.2"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@testing-library/dom/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==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peer": true,
+ "dependencies": {
+ "dequal": "^2.0.3"
+ }
+ },
+ "node_modules/@testing-library/jest-dom": {
+ "version": "6.9.1",
+ "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz",
+ "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@adobe/css-tools": "^4.4.0",
+ "aria-query": "^5.0.0",
+ "css.escape": "^1.5.1",
+ "dom-accessibility-api": "^0.6.3",
+ "picocolors": "^1.1.1",
+ "redent": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=14",
+ "npm": ">=6",
+ "yarn": ">=1"
+ }
+ },
+ "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz",
+ "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@testing-library/react": {
+ "version": "16.3.2",
+ "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.2.tgz",
+ "integrity": "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.12.5"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@testing-library/dom": "^10.0.0",
+ "@types/react": "^18.0.0 || ^19.0.0",
+ "@types/react-dom": "^18.0.0 || ^19.0.0",
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@tweenjs/tween.js": {
+ "version": "23.1.3",
+ "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz",
+ "integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==",
+ "license": "MIT"
+ },
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.10.1",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
+ "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@types/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==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@types/chai": {
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
+ "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/deep-eql": "*",
+ "assertion-error": "^2.0.1"
+ }
+ },
+ "node_modules/@types/debug": {
+ "version": "4.1.12",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
+ "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/ms": "*"
+ }
+ },
+ "node_modules/@types/deep-eql": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
+ "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/draco3d": {
+ "version": "1.4.10",
+ "resolved": "https://registry.npmjs.org/@types/draco3d/-/draco3d-1.4.10.tgz",
+ "integrity": "sha512-AX22jp8Y7wwaBgAixaSvkoG4M/+PlAcm3Qs4OW8yT9DM4xUpWKeFhLueTAyZF39pviAdcDdeJoACapiAceqNcw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "license": "MIT"
+ },
+ "node_modules/@types/estree-jsx": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz",
+ "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "*"
+ }
+ },
+ "node_modules/@types/hast": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
+ "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json5": {
+ "version": "0.0.29",
+ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/mdast": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
+ "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/ms": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
+ "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "20.19.30",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.30.tgz",
+ "integrity": "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/@types/offscreencanvas": {
+ "version": "2019.7.3",
+ "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz",
+ "integrity": "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==",
+ "license": "MIT"
+ },
+ "node_modules/@types/react": {
+ "version": "19.2.9",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.9.tgz",
+ "integrity": "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA==",
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.2.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.2.0"
+ }
+ },
+ "node_modules/@types/react-reconciler": {
+ "version": "0.28.9",
+ "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.28.9.tgz",
+ "integrity": "sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*"
+ }
+ },
+ "node_modules/@types/stats.js": {
+ "version": "0.17.4",
+ "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz",
+ "integrity": "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/three": {
+ "version": "0.183.1",
+ "resolved": "https://registry.npmjs.org/@types/three/-/three-0.183.1.tgz",
+ "integrity": "sha512-f2Pu5Hrepfgavttdye3PsH5RWyY/AvdZQwIVhrc4uNtvF7nOWJacQKcoVJn0S4f0yYbmAE6AR+ve7xDcuYtMGw==",
+ "license": "MIT",
+ "dependencies": {
+ "@dimforge/rapier3d-compat": "~0.12.0",
+ "@tweenjs/tween.js": "~23.1.3",
+ "@types/stats.js": "*",
+ "@types/webxr": ">=0.5.17",
+ "@webgpu/types": "*",
+ "fflate": "~0.8.2",
+ "meshoptimizer": "~1.0.1"
+ }
+ },
+ "node_modules/@types/unist": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
+ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
+ "license": "MIT"
+ },
+ "node_modules/@types/webxr": {
+ "version": "0.5.24",
+ "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz",
+ "integrity": "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/ws": {
+ "version": "8.18.1",
+ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
+ "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.53.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.53.1.tgz",
+ "integrity": "sha512-cFYYFZ+oQFi6hUnBTbLRXfTJiaQtYE3t4O692agbBl+2Zy+eqSKWtPjhPXJu1G7j4RLjKgeJPDdq3EqOwmX5Ag==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.12.2",
+ "@typescript-eslint/scope-manager": "8.53.1",
+ "@typescript-eslint/type-utils": "8.53.1",
+ "@typescript-eslint/utils": "8.53.1",
+ "@typescript-eslint/visitor-keys": "8.53.1",
+ "ignore": "^7.0.5",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^2.4.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.53.1",
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+ "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.53.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.53.1.tgz",
+ "integrity": "sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.53.1",
+ "@typescript-eslint/types": "8.53.1",
+ "@typescript-eslint/typescript-estree": "8.53.1",
+ "@typescript-eslint/visitor-keys": "8.53.1",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/project-service": {
+ "version": "8.53.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.53.1.tgz",
+ "integrity": "sha512-WYC4FB5Ra0xidsmlPb+1SsnaSKPmS3gsjIARwbEkHkoWloQmuzcfypljaJcR78uyLA1h8sHdWWPHSLDI+MtNog==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.53.1",
+ "@typescript-eslint/types": "^8.53.1",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.53.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.53.1.tgz",
+ "integrity": "sha512-Lu23yw1uJMFY8cUeq7JlrizAgeQvWugNQzJp8C3x8Eo5Jw5Q2ykMdiiTB9vBVOOUBysMzmRRmUfwFrZuI2C4SQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.53.1",
+ "@typescript-eslint/visitor-keys": "8.53.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.53.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.53.1.tgz",
+ "integrity": "sha512-qfvLXS6F6b1y43pnf0pPbXJ+YoXIC7HKg0UGZ27uMIemKMKA6XH2DTxsEDdpdN29D+vHV07x/pnlPNVLhdhWiA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.53.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.53.1.tgz",
+ "integrity": "sha512-MOrdtNvyhy0rHyv0ENzub1d4wQYKb2NmIqG7qEqPWFW7Mpy2jzFC3pQ2yKDvirZB7jypm5uGjF2Qqs6OIqu47w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.53.1",
+ "@typescript-eslint/typescript-estree": "8.53.1",
+ "@typescript-eslint/utils": "8.53.1",
+ "debug": "^4.4.3",
+ "ts-api-utils": "^2.4.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.53.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.53.1.tgz",
+ "integrity": "sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.53.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.53.1.tgz",
+ "integrity": "sha512-RGlVipGhQAG4GxV1s34O91cxQ/vWiHJTDHbXRr0li2q/BGg3RR/7NM8QDWgkEgrwQYCvmJV9ichIwyoKCQ+DTg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.53.1",
+ "@typescript-eslint/tsconfig-utils": "8.53.1",
+ "@typescript-eslint/types": "8.53.1",
+ "@typescript-eslint/visitor-keys": "8.53.1",
+ "debug": "^4.4.3",
+ "minimatch": "^9.0.5",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.4.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.53.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.53.1.tgz",
+ "integrity": "sha512-c4bMvGVWW4hv6JmDUEG7fSYlWOl3II2I4ylt0NM+seinYQlZMQIaKaXIIVJWt9Ofh6whrpM+EdDQXKXjNovvrg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.9.1",
+ "@typescript-eslint/scope-manager": "8.53.1",
+ "@typescript-eslint/types": "8.53.1",
+ "@typescript-eslint/typescript-estree": "8.53.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.53.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.53.1.tgz",
+ "integrity": "sha512-oy+wV7xDKFPRyNggmXuZQSBzvoLnpmJs+GhzRhPjrxl2b/jIlyjVokzm47CZCDUdXKr2zd7ZLodPfOBpOPyPlg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.53.1",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
+ "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
+ "license": "ISC"
+ },
+ "node_modules/@unrs/resolver-binding-android-arm-eabi": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz",
+ "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-android-arm64": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz",
+ "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-darwin-arm64": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz",
+ "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-darwin-x64": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz",
+ "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-freebsd-x64": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz",
+ "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz",
+ "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz",
+ "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm64-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz",
+ "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm64-musl": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz",
+ "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz",
+ "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz",
+ "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-riscv64-musl": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz",
+ "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-s390x-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz",
+ "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-x64-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz",
+ "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-x64-musl": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz",
+ "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-wasm32-wasi": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz",
+ "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@napi-rs/wasm-runtime": "^0.2.11"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@unrs/resolver-binding-win32-arm64-msvc": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz",
+ "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-win32-ia32-msvc": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz",
+ "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-win32-x64-msvc": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz",
+ "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@use-gesture/core": {
+ "version": "10.3.1",
+ "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.3.1.tgz",
+ "integrity": "sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==",
+ "license": "MIT"
+ },
+ "node_modules/@use-gesture/react": {
+ "version": "10.3.1",
+ "resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.3.1.tgz",
+ "integrity": "sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==",
+ "license": "MIT",
+ "dependencies": {
+ "@use-gesture/core": "10.3.1"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8.0"
+ }
+ },
+ "node_modules/@vercel/otel": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@vercel/otel/-/otel-2.1.0.tgz",
+ "integrity": "sha512-Zwu2Cu4t46DzBnY1DQSTxZ4MBLVfYsOjnlWuZuLRWnmVPX+SNrVHbs3ssiJ6uvY1J1JJswor4zSn8mHYxzYeBA==",
+ "license": "MIT",
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.9.0 <2.0.0",
+ "@opentelemetry/api-logs": ">=0.200.0 <0.300.0",
+ "@opentelemetry/instrumentation": ">=0.200.0 <0.300.0",
+ "@opentelemetry/resources": ">=2.0.0 <3.0.0",
+ "@opentelemetry/sdk-logs": ">=0.200.0 <0.300.0",
+ "@opentelemetry/sdk-metrics": ">=2.0.0 <3.0.0",
+ "@opentelemetry/sdk-trace-base": ">=2.0.0 <3.0.0"
+ }
+ },
+ "node_modules/@vitest/expect": {
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz",
+ "integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@standard-schema/spec": "^1.0.0",
+ "@types/chai": "^5.2.2",
+ "@vitest/spy": "4.0.18",
+ "@vitest/utils": "4.0.18",
+ "chai": "^6.2.1",
+ "tinyrainbow": "^3.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/mocker": {
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.18.tgz",
+ "integrity": "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/spy": "4.0.18",
+ "estree-walker": "^3.0.3",
+ "magic-string": "^0.30.21"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "msw": "^2.4.9",
+ "vite": "^6.0.0 || ^7.0.0-0"
+ },
+ "peerDependenciesMeta": {
+ "msw": {
+ "optional": true
+ },
+ "vite": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@vitest/pretty-format": {
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz",
+ "integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tinyrainbow": "^3.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/runner": {
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz",
+ "integrity": "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/utils": "4.0.18",
+ "pathe": "^2.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/snapshot": {
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz",
+ "integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "4.0.18",
+ "magic-string": "^0.30.21",
+ "pathe": "^2.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/spy": {
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz",
+ "integrity": "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/utils": {
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz",
+ "integrity": "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "4.0.18",
+ "tinyrainbow": "^3.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@webgpu/types": {
+ "version": "0.1.69",
+ "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.69.tgz",
+ "integrity": "sha512-RPmm6kgRbI8e98zSD3RVACvnuktIja5+yLgDAkTmxLr90BEwdTXRQWNLF3ETTTyH/8mKhznZuN5AveXYFEsMGQ==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/acorn": {
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "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",
+ "peer": true,
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/agent-base": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/aria-query": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
+ "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/array-buffer-byte-length": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
+ "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "is-array-buffer": "^3.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array-includes": {
+ "version": "3.1.9",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz",
+ "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.24.0",
+ "es-object-atoms": "^1.1.1",
+ "get-intrinsic": "^1.3.0",
+ "is-string": "^1.1.1",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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==",
+ "dev": true,
+ "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.6",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz",
+ "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.9",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "es-shim-unscopables": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flat": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz",
+ "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flatmap": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz",
+ "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "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==",
+ "dev": true,
+ "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.4",
+ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz",
+ "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.1",
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "is-array-buffer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/assertion-error": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
+ "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/async-function": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz",
+ "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "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==",
+ "dev": true,
+ "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.11.1",
+ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.1.tgz",
+ "integrity": "sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/bail": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
+ "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "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/baseline-browser-mapping": {
+ "version": "2.9.17",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.17.tgz",
+ "integrity": "sha512-agD0MgJFUP/4nvjqzIB29zRPUuCF7Ge6mEv9s8dHrtYD7QWXRcx75rOADE/d5ah1NI+0vkDl0yorDd5U852IQQ==",
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.js"
+ }
+ },
+ "node_modules/bidi-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
+ "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==",
+ "license": "MIT",
+ "dependencies": {
+ "require-from-string": "^2.0.2"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.1",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
+ "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "baseline-browser-mapping": "^2.9.0",
+ "caniuse-lite": "^1.0.30001759",
+ "electron-to-chromium": "^1.5.263",
+ "node-releases": "^2.0.27",
+ "update-browserslist-db": "^1.2.0"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+ "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+ "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": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.2.1"
+ }
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
+ "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.0",
+ "es-define-property": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/camera-controls": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-3.1.2.tgz",
+ "integrity": "sha512-xkxfpG2ECZ6Ww5/9+kf4mfg1VEYAoe9aDSY+IwF0UEs7qEzwy0aVRfs2grImIECs/PoBtWFrh7RXsQkwG922JA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=22.0.0",
+ "npm": ">=10.5.1"
+ },
+ "peerDependencies": {
+ "three": ">=0.126.1"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001766",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001766.tgz",
+ "integrity": "sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/ccount": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
+ "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/chai": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
+ "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/character-entities": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz",
+ "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-html4": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz",
+ "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-legacy": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
+ "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-reference-invalid": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz",
+ "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/cjs-module-lexer": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz",
+ "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/class-variance-authority": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
+ "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "clsx": "^2.1.1"
+ },
+ "funding": {
+ "url": "https://polar.sh/cva"
+ }
+ },
+ "node_modules/client-only": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
+ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
+ "license": "MIT"
+ },
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/comma-separated-tokens": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
+ "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cross-env": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
+ "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
+ "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.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/css-tree": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz",
+ "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mdn-data": "2.12.2",
+ "source-map-js": "^1.0.1"
+ },
+ "engines": {
+ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cssstyle": {
+ "version": "5.3.7",
+ "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-5.3.7.tgz",
+ "integrity": "sha512-7D2EPVltRrsTkhpQmksIu+LxeWAIEk6wRDMJ1qljlv+CKHJM+cJLlfhWIzNA44eAsHXSNe3+vO6DW1yCYx8SuQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@asamuzakjp/css-color": "^4.1.1",
+ "@csstools/css-syntax-patches-for-csstree": "^1.0.21",
+ "css-tree": "^3.1.0",
+ "lru-cache": "^11.2.4"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/cssstyle/node_modules/lru-cache": {
+ "version": "11.2.5",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.5.tgz",
+ "integrity": "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "license": "MIT"
+ },
+ "node_modules/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==",
+ "dev": true,
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/data-urls": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-6.0.1.tgz",
+ "integrity": "sha512-euIQENZg6x8mj3fO6o9+fOW8MimUI4PpD/fZBhJfeioZVy9TUpM4UY7KjQNVZFlqwJ0UdzRDzkycB997HEq1BQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "whatwg-mimetype": "^5.0.0",
+ "whatwg-url": "^15.1.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/data-urls/node_modules/whatwg-mimetype": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz",
+ "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/data-view-buffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz",
+ "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/data-view-byte-length": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz",
+ "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/inspect-js"
+ }
+ },
+ "node_modules/data-view-byte-offset": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz",
+ "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "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.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decimal.js": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
+ "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/decode-named-character-reference": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz",
+ "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==",
+ "license": "MIT",
+ "dependencies": {
+ "character-entities": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/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==",
+ "dev": true,
+ "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-properties": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+ "dev": true,
+ "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/dequal": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/detect-gpu": {
+ "version": "5.0.70",
+ "resolved": "https://registry.npmjs.org/detect-gpu/-/detect-gpu-5.0.70.tgz",
+ "integrity": "sha512-bqerEP1Ese6nt3rFkwPnGbsUF9a4q+gMmpTVVOEzoCyeCc+y7/RvJnQZJx1JwhgQI5Ntg0Kgat8Uu7XpBqnz1w==",
+ "license": "MIT",
+ "dependencies": {
+ "webgl-constants": "^1.1.1"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "devOptional": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/devlop": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
+ "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
+ "license": "MIT",
+ "dependencies": {
+ "dequal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.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==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/draco3d": {
+ "version": "1.5.7",
+ "resolved": "https://registry.npmjs.org/draco3d/-/draco3d-1.5.7.tgz",
+ "integrity": "sha512-m6WCKt/erDXcw+70IJXnG7M3awwQPAsZvJGX5zY7beBqpELw6RDGkYVU0W43AFxye4pDZ5i2Lbyc/NNGqwjUVQ==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.278",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.278.tgz",
+ "integrity": "sha512-dQ0tM1svDRQOwxnXxm+twlGTjr9Upvt8UFWAgmLsxEzFQxhbti4VwxmMjsDxVC51Zo84swW7FVCXEV+VAkhuPw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/enhanced-resolve": {
+ "version": "5.18.4",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz",
+ "integrity": "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/entities": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
+ "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/es-abstract": {
+ "version": "1.24.1",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz",
+ "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.2",
+ "arraybuffer.prototype.slice": "^1.0.4",
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "data-view-buffer": "^1.0.2",
+ "data-view-byte-length": "^1.0.2",
+ "data-view-byte-offset": "^1.0.1",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "es-set-tostringtag": "^2.1.0",
+ "es-to-primitive": "^1.3.0",
+ "function.prototype.name": "^1.1.8",
+ "get-intrinsic": "^1.3.0",
+ "get-proto": "^1.0.1",
+ "get-symbol-description": "^1.1.0",
+ "globalthis": "^1.0.4",
+ "gopd": "^1.2.0",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "internal-slot": "^1.1.0",
+ "is-array-buffer": "^3.0.5",
+ "is-callable": "^1.2.7",
+ "is-data-view": "^1.0.2",
+ "is-negative-zero": "^2.0.3",
+ "is-regex": "^1.2.1",
+ "is-set": "^2.0.3",
+ "is-shared-array-buffer": "^1.0.4",
+ "is-string": "^1.1.1",
+ "is-typed-array": "^1.1.15",
+ "is-weakref": "^1.1.1",
+ "math-intrinsics": "^1.1.0",
+ "object-inspect": "^1.13.4",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.7",
+ "own-keys": "^1.0.1",
+ "regexp.prototype.flags": "^1.5.4",
+ "safe-array-concat": "^1.1.3",
+ "safe-push-apply": "^1.0.0",
+ "safe-regex-test": "^1.1.0",
+ "set-proto": "^1.0.0",
+ "stop-iteration-iterator": "^1.1.0",
+ "string.prototype.trim": "^1.2.10",
+ "string.prototype.trimend": "^1.0.9",
+ "string.prototype.trimstart": "^1.0.8",
+ "typed-array-buffer": "^1.0.3",
+ "typed-array-byte-length": "^1.0.3",
+ "typed-array-byte-offset": "^1.0.4",
+ "typed-array-length": "^1.0.7",
+ "unbox-primitive": "^1.1.0",
+ "which-typed-array": "^1.1.19"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-iterator-helpers": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.2.tgz",
+ "integrity": "sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.24.1",
+ "es-errors": "^1.3.0",
+ "es-set-tostringtag": "^2.1.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.3.0",
+ "globalthis": "^1.0.4",
+ "gopd": "^1.2.0",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "internal-slot": "^1.1.0",
+ "iterator.prototype": "^1.1.5",
+ "safe-array-concat": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-module-lexer": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
+ "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-shim-unscopables": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz",
+ "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-to-primitive": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz",
+ "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-callable": "^1.2.7",
+ "is-date-object": "^1.0.5",
+ "is-symbol": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz",
+ "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.27.2",
+ "@esbuild/android-arm": "0.27.2",
+ "@esbuild/android-arm64": "0.27.2",
+ "@esbuild/android-x64": "0.27.2",
+ "@esbuild/darwin-arm64": "0.27.2",
+ "@esbuild/darwin-x64": "0.27.2",
+ "@esbuild/freebsd-arm64": "0.27.2",
+ "@esbuild/freebsd-x64": "0.27.2",
+ "@esbuild/linux-arm": "0.27.2",
+ "@esbuild/linux-arm64": "0.27.2",
+ "@esbuild/linux-ia32": "0.27.2",
+ "@esbuild/linux-loong64": "0.27.2",
+ "@esbuild/linux-mips64el": "0.27.2",
+ "@esbuild/linux-ppc64": "0.27.2",
+ "@esbuild/linux-riscv64": "0.27.2",
+ "@esbuild/linux-s390x": "0.27.2",
+ "@esbuild/linux-x64": "0.27.2",
+ "@esbuild/netbsd-arm64": "0.27.2",
+ "@esbuild/netbsd-x64": "0.27.2",
+ "@esbuild/openbsd-arm64": "0.27.2",
+ "@esbuild/openbsd-x64": "0.27.2",
+ "@esbuild/openharmony-arm64": "0.27.2",
+ "@esbuild/sunos-x64": "0.27.2",
+ "@esbuild/win32-arm64": "0.27.2",
+ "@esbuild/win32-ia32": "0.27.2",
+ "@esbuild/win32-x64": "0.27.2"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "9.39.2",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz",
+ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.8.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.21.1",
+ "@eslint/config-helpers": "^0.4.2",
+ "@eslint/core": "^0.17.0",
+ "@eslint/eslintrc": "^3.3.1",
+ "@eslint/js": "9.39.2",
+ "@eslint/plugin-kit": "^0.4.1",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.4.0",
+ "eslint-visitor-keys": "^4.2.1",
+ "espree": "^10.4.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-config-next": {
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.1.6.tgz",
+ "integrity": "sha512-vKq40io2B0XtkkNDYyleATwblNt8xuh3FWp8SpSz3pt7P01OkBFlKsJZ2mWt5WsCySlDQLckb1zMY9yE9Qy0LA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@next/eslint-plugin-next": "16.1.6",
+ "eslint-import-resolver-node": "^0.3.6",
+ "eslint-import-resolver-typescript": "^3.5.2",
+ "eslint-plugin-import": "^2.32.0",
+ "eslint-plugin-jsx-a11y": "^6.10.0",
+ "eslint-plugin-react": "^7.37.0",
+ "eslint-plugin-react-hooks": "^7.0.0",
+ "globals": "16.4.0",
+ "typescript-eslint": "^8.46.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=9.0.0",
+ "typescript": ">=3.3.1"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-config-next/node_modules/globals": {
+ "version": "16.4.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz",
+ "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint-config-prettier": {
+ "version": "10.1.8",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz",
+ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "eslint-config-prettier": "bin/cli.js"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint-config-prettier"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.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==",
+ "dev": true,
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-import-resolver-typescript": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz",
+ "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@nolyfill/is-core-module": "1.0.39",
+ "debug": "^4.4.0",
+ "get-tsconfig": "^4.10.0",
+ "is-bun-module": "^2.0.0",
+ "stable-hash": "^0.0.5",
+ "tinyglobby": "^0.2.13",
+ "unrs-resolver": "^1.6.2"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint-import-resolver-typescript"
+ },
+ "peerDependencies": {
+ "eslint": "*",
+ "eslint-plugin-import": "*",
+ "eslint-plugin-import-x": "*"
+ },
+ "peerDependenciesMeta": {
+ "eslint-plugin-import": {
+ "optional": true
+ },
+ "eslint-plugin-import-x": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-module-utils": {
+ "version": "2.12.1",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz",
+ "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==",
+ "dev": true,
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-plugin-import": {
+ "version": "2.32.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz",
+ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rtsao/scc": "^1.1.0",
+ "array-includes": "^3.1.9",
+ "array.prototype.findlastindex": "^1.2.6",
+ "array.prototype.flat": "^1.3.3",
+ "array.prototype.flatmap": "^1.3.3",
+ "debug": "^3.2.7",
+ "doctrine": "^2.1.0",
+ "eslint-import-resolver-node": "^0.3.9",
+ "eslint-module-utils": "^2.12.1",
+ "hasown": "^2.0.2",
+ "is-core-module": "^2.16.1",
+ "is-glob": "^4.0.3",
+ "minimatch": "^3.1.2",
+ "object.fromentries": "^2.0.8",
+ "object.groupby": "^1.0.3",
+ "object.values": "^1.2.1",
+ "semver": "^6.3.1",
+ "string.prototype.trimend": "^1.0.9",
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "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==",
+ "dev": true,
+ "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-react": {
+ "version": "7.37.5",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz",
+ "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-includes": "^3.1.8",
+ "array.prototype.findlast": "^1.2.5",
+ "array.prototype.flatmap": "^1.3.3",
+ "array.prototype.tosorted": "^1.1.4",
+ "doctrine": "^2.1.0",
+ "es-iterator-helpers": "^1.2.1",
+ "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.9",
+ "object.fromentries": "^2.0.8",
+ "object.values": "^1.2.1",
+ "prop-types": "^15.8.1",
+ "resolve": "^2.0.0-next.5",
+ "semver": "^6.3.1",
+ "string.prototype.matchall": "^4.0.12",
+ "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": "7.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz",
+ "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.24.4",
+ "@babel/parser": "^7.24.4",
+ "hermes-parser": "^0.25.1",
+ "zod": "^3.25.0 || ^4.0.0",
+ "zod-validation-error": "^3.5.0 || ^4.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-react/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==",
+ "dev": true,
+ "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-scope": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
+ "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.15.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
+ "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estree-util-is-identifier-name": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz",
+ "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-walker": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+ "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/eventemitter3": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
+ "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
+ "license": "MIT"
+ },
+ "node_modules/expect-type": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
+ "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "license": "MIT"
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
+ "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==",
+ "dev": true,
+ "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==",
+ "dev": true,
+ "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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fastq": {
+ "version": "1.20.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
+ "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/fflate": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
+ "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
+ "license": "MIT"
+ },
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
+ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/for-each": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
+ "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/function.prototype.name": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz",
+ "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "functions-have-names": "^1.2.3",
+ "hasown": "^2.0.2",
+ "is-callable": "^1.2.7"
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/generator-function": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz",
+ "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/get-symbol-description": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
+ "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-tsconfig": {
+ "version": "4.13.0",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz",
+ "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globalthis": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
+ "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
+ "dev": true,
+ "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/glsl-noise": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/glsl-noise/-/glsl-noise-0.0.0.tgz",
+ "integrity": "sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w==",
+ "license": "MIT"
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/has-bigints": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",
+ "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "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==",
+ "dev": true,
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-proto": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz",
+ "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "dev": true,
+ "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==",
+ "dev": true,
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/hast-util-to-jsx-runtime": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz",
+ "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "mdast-util-mdx-expression": "^2.0.0",
+ "mdast-util-mdx-jsx": "^3.0.0",
+ "mdast-util-mdxjs-esm": "^2.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "style-to-js": "^1.0.0",
+ "unist-util-position": "^5.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-whitespace": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz",
+ "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hermes-estree": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
+ "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/hermes-parser": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz",
+ "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hermes-estree": "0.25.1"
+ }
+ },
+ "node_modules/hls.js": {
+ "version": "1.6.15",
+ "resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.6.15.tgz",
+ "integrity": "sha512-E3a5VwgXimGHwpRGV+WxRTKeSp2DW5DI5MWv34ulL3t5UNmyJWCQ1KmLEHbYzcfThfXG8amBL+fCYPneGHC4VA==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/html-encoding-sniffer": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz",
+ "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@exodus/bytes": "^1.6.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+ }
+ },
+ "node_modules/html-url-attributes": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz",
+ "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/http-proxy-agent": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+ "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.2",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "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": "BSD-3-Clause"
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/immediate": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
+ "license": "MIT"
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/import-in-the-middle": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.5.tgz",
+ "integrity": "sha512-0InH9/4oDCBRzWXhpOqusspLBrVfK1vPvbn9Wxl8DAQ8yyx5fWJRETICSwkiAMaYntjJAMBP1R4B6cQnEUYVEA==",
+ "license": "Apache-2.0",
+ "peer": true,
+ "dependencies": {
+ "acorn": "^8.15.0",
+ "acorn-import-attributes": "^1.9.5",
+ "cjs-module-lexer": "^2.2.0",
+ "module-details-from-path": "^1.0.4"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/inline-style-parser": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz",
+ "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==",
+ "license": "MIT"
+ },
+ "node_modules/internal-slot": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz",
+ "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "hasown": "^2.0.2",
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/is-alphabetical": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
+ "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-alphanumerical": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz",
+ "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==",
+ "license": "MIT",
+ "dependencies": {
+ "is-alphabetical": "^2.0.0",
+ "is-decimal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-array-buffer": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
+ "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-async-function": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
+ "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "async-function": "^1.0.0",
+ "call-bound": "^1.0.3",
+ "get-proto": "^1.0.1",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-bigint": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz",
+ "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-bigints": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-boolean-object": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
+ "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-bun-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz",
+ "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.7.1"
+ }
+ },
+ "node_modules/is-bun-module/node_modules/semver": {
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+ "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
+ "dev": true,
+ "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.2",
+ "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz",
+ "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "get-intrinsic": "^1.2.6",
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-date-object": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz",
+ "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-decimal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz",
+ "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-finalizationregistry": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz",
+ "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-generator-function": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz",
+ "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.4",
+ "generator-function": "^2.0.0",
+ "get-proto": "^1.0.1",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-hexadecimal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz",
+ "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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==",
+ "dev": true,
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-number-object": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz",
+ "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-plain-obj": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
+ "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/is-promise": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz",
+ "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==",
+ "license": "MIT"
+ },
+ "node_modules/is-regex": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
+ "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-shared-array-buffer": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz",
+ "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-string": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz",
+ "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-symbol": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz",
+ "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-symbols": "^1.1.0",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
+ "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakref": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz",
+ "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakset": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz",
+ "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "dev": true,
+ "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/iterator.prototype": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz",
+ "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.6",
+ "get-proto": "^1.0.0",
+ "has-symbols": "^1.1.0",
+ "set-function-name": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/its-fine": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/its-fine/-/its-fine-2.0.0.tgz",
+ "integrity": "sha512-KLViCmWx94zOvpLwSlsx6yOCeMhZYaxrJV87Po5k/FoZzcPSahvK5qJ7fYhS61sZi5ikmh2S3Hz55A2l3U69ng==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/react-reconciler": "^0.28.9"
+ },
+ "peerDependencies": {
+ "react": "^19.0.0"
+ }
+ },
+ "node_modules/jiti": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
+ "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jiti": "lib/jiti-cli.mjs"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsdom": {
+ "version": "27.4.0",
+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-27.4.0.tgz",
+ "integrity": "sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@acemir/cssom": "^0.9.28",
+ "@asamuzakjp/dom-selector": "^6.7.6",
+ "@exodus/bytes": "^1.6.0",
+ "cssstyle": "^5.3.4",
+ "data-urls": "^6.0.0",
+ "decimal.js": "^10.6.0",
+ "html-encoding-sniffer": "^6.0.0",
+ "http-proxy-agent": "^7.0.2",
+ "https-proxy-agent": "^7.0.6",
+ "is-potential-custom-element-name": "^1.0.1",
+ "parse5": "^8.0.0",
+ "saxes": "^6.0.0",
+ "symbol-tree": "^3.2.4",
+ "tough-cookie": "^6.0.0",
+ "w3c-xmlserializer": "^5.0.0",
+ "webidl-conversions": "^8.0.0",
+ "whatwg-mimetype": "^4.0.0",
+ "whatwg-url": "^15.1.0",
+ "ws": "^8.18.3",
+ "xml-name-validator": "^5.0.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+ },
+ "peerDependencies": {
+ "canvas": "^3.0.0"
+ },
+ "peerDependenciesMeta": {
+ "canvas": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/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==",
+ "dev": true,
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "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==",
+ "dev": true,
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "language-subtag-registry": "^0.3.20"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/lie": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+ "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+ "license": "MIT",
+ "dependencies": {
+ "immediate": "~3.0.5"
+ }
+ },
+ "node_modules/lightningcss": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
+ "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-android-arm64": "1.30.2",
+ "lightningcss-darwin-arm64": "1.30.2",
+ "lightningcss-darwin-x64": "1.30.2",
+ "lightningcss-freebsd-x64": "1.30.2",
+ "lightningcss-linux-arm-gnueabihf": "1.30.2",
+ "lightningcss-linux-arm64-gnu": "1.30.2",
+ "lightningcss-linux-arm64-musl": "1.30.2",
+ "lightningcss-linux-x64-gnu": "1.30.2",
+ "lightningcss-linux-x64-musl": "1.30.2",
+ "lightningcss-win32-arm64-msvc": "1.30.2",
+ "lightningcss-win32-x64-msvc": "1.30.2"
+ }
+ },
+ "node_modules/lightningcss-android-arm64": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz",
+ "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-arm64": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz",
+ "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-x64": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz",
+ "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-freebsd-x64": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz",
+ "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz",
+ "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz",
+ "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz",
+ "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz",
+ "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz",
+ "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz",
+ "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz",
+ "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/longest-streak": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
+ "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/lucide-react": {
+ "version": "0.563.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.563.0.tgz",
+ "integrity": "sha512-8dXPB2GI4dI8jV4MgUDGBeLdGk8ekfqVZ0BdLcrRzocGgG75ltNEmWS+gE7uokKF/0oSUuczNDT+g9hFJ23FkA==",
+ "license": "ISC",
+ "peerDependencies": {
+ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "bin": {
+ "lz-string": "bin/bin.js"
+ }
+ },
+ "node_modules/maath": {
+ "version": "0.10.8",
+ "resolved": "https://registry.npmjs.org/maath/-/maath-0.10.8.tgz",
+ "integrity": "sha512-tRvbDF0Pgqz+9XUa4jjfgAQ8/aPKmQdWXilFu2tMy4GWj4NOsx99HlULO4IeREfbO3a0sA145DZYyvXPkybm0g==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/three": ">=0.134.0",
+ "three": ">=0.134.0"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.21",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.5"
+ }
+ },
+ "node_modules/markdown-table": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz",
+ "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/mdast-util-find-and-replace": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz",
+ "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "escape-string-regexp": "^5.0.0",
+ "unist-util-is": "^6.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+ "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/mdast-util-from-markdown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz",
+ "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-to-string": "^4.0.0",
+ "micromark": "^4.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-decode-string": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unist-util-stringify-position": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz",
+ "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==",
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-gfm-autolink-literal": "^2.0.0",
+ "mdast-util-gfm-footnote": "^2.0.0",
+ "mdast-util-gfm-strikethrough": "^2.0.0",
+ "mdast-util-gfm-table": "^2.0.0",
+ "mdast-util-gfm-task-list-item": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-autolink-literal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz",
+ "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "ccount": "^2.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-find-and-replace": "^3.0.0",
+ "micromark-util-character": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-footnote": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz",
+ "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.1.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-strikethrough": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz",
+ "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-table": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz",
+ "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "markdown-table": "^3.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-task-list-item": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz",
+ "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdx-expression": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz",
+ "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdx-jsx": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz",
+ "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "ccount": "^2.0.0",
+ "devlop": "^1.1.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "parse-entities": "^4.0.0",
+ "stringify-entities": "^4.0.0",
+ "unist-util-stringify-position": "^4.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdxjs-esm": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz",
+ "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-phrasing": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz",
+ "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-hast": {
+ "version": "13.2.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz",
+ "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "@ungap/structured-clone": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "trim-lines": "^3.0.0",
+ "unist-util-position": "^5.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-markdown": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz",
+ "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "longest-streak": "^3.0.0",
+ "mdast-util-phrasing": "^4.0.0",
+ "mdast-util-to-string": "^4.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-decode-string": "^2.0.0",
+ "unist-util-visit": "^5.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz",
+ "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdn-data": {
+ "version": "2.12.2",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz",
+ "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==",
+ "dev": true,
+ "license": "CC0-1.0"
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/meshline": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/meshline/-/meshline-3.3.1.tgz",
+ "integrity": "sha512-/TQj+JdZkeSUOl5Mk2J7eLcYTLiQm2IDzmlSvYm7ov15anEcDJ92GHqqazxTSreeNgfnYu24kiEvvv0WlbCdFQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "three": ">=0.137"
+ }
+ },
+ "node_modules/meshoptimizer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-1.0.1.tgz",
+ "integrity": "sha512-Vix+QlA1YYT3FwmBBZ+49cE5y/b+pRrcXKqGpS5ouh33d3lSp2PoTpCw19E0cKDFWalembrHnIaZetf27a+W2g==",
+ "license": "MIT"
+ },
+ "node_modules/micromark": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz",
+ "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@types/debug": "^4.0.0",
+ "debug": "^4.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-core-commonmark": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-combine-extensions": "^2.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-encode": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-subtokenize": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-core-commonmark": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz",
+ "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-factory-destination": "^2.0.0",
+ "micromark-factory-label": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-factory-title": "^2.0.0",
+ "micromark-factory-whitespace": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-html-tag-name": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-subtokenize": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-gfm": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz",
+ "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==",
+ "license": "MIT",
+ "dependencies": {
+ "micromark-extension-gfm-autolink-literal": "^2.0.0",
+ "micromark-extension-gfm-footnote": "^2.0.0",
+ "micromark-extension-gfm-strikethrough": "^2.0.0",
+ "micromark-extension-gfm-table": "^2.0.0",
+ "micromark-extension-gfm-tagfilter": "^2.0.0",
+ "micromark-extension-gfm-task-list-item": "^2.0.0",
+ "micromark-util-combine-extensions": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-autolink-literal": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz",
+ "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==",
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-footnote": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz",
+ "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-core-commonmark": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-strikethrough": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz",
+ "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-table": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz",
+ "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-tagfilter": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz",
+ "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==",
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-task-list-item": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz",
+ "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-factory-destination": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz",
+ "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-label": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz",
+ "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-space": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz",
+ "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-title": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz",
+ "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-whitespace": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz",
+ "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-character": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
+ "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-chunked": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz",
+ "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-classify-character": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz",
+ "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-combine-extensions": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz",
+ "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-decode-numeric-character-reference": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz",
+ "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-decode-string": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz",
+ "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "decode-named-character-reference": "^1.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-encode": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz",
+ "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-html-tag-name": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz",
+ "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-normalize-identifier": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz",
+ "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-resolve-all": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz",
+ "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-sanitize-uri": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz",
+ "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-encode": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-subtokenize": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz",
+ "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-symbol": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
+ "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-types": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz",
+ "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/module-details-from-path": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz",
+ "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "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/napi-postinstall": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz",
+ "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "napi-postinstall": "lib/cli.js"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/napi-postinstall"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/next": {
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/next/-/next-16.1.6.tgz",
+ "integrity": "sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw==",
+ "license": "MIT",
+ "dependencies": {
+ "@next/env": "16.1.6",
+ "@swc/helpers": "0.5.15",
+ "baseline-browser-mapping": "^2.8.3",
+ "caniuse-lite": "^1.0.30001579",
+ "postcss": "8.4.31",
+ "styled-jsx": "5.1.6"
+ },
+ "bin": {
+ "next": "dist/bin/next"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "optionalDependencies": {
+ "@next/swc-darwin-arm64": "16.1.6",
+ "@next/swc-darwin-x64": "16.1.6",
+ "@next/swc-linux-arm64-gnu": "16.1.6",
+ "@next/swc-linux-arm64-musl": "16.1.6",
+ "@next/swc-linux-x64-gnu": "16.1.6",
+ "@next/swc-linux-x64-musl": "16.1.6",
+ "@next/swc-win32-arm64-msvc": "16.1.6",
+ "@next/swc-win32-x64-msvc": "16.1.6",
+ "sharp": "^0.34.4"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.1.0",
+ "@playwright/test": "^1.51.1",
+ "babel-plugin-react-compiler": "*",
+ "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+ "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+ "sass": "^1.3.0"
+ },
+ "peerDependenciesMeta": {
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@playwright/test": {
+ "optional": true
+ },
+ "babel-plugin-react-compiler": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/next/node_modules/postcss": {
+ "version": "8.4.31",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
+ "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
+ "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.6",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.27",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
+ "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
+ "dev": true,
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "dev": true,
+ "license": "MIT",
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.assign": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz",
+ "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0",
+ "has-symbols": "^1.1.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.entries": {
+ "version": "1.1.9",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz",
+ "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.1.1"
+ },
+ "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==",
+ "dev": true,
+ "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.groupby": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz",
+ "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==",
+ "dev": true,
+ "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.1",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz",
+ "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/obug": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz",
+ "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==",
+ "dev": true,
+ "funding": [
+ "https://github.com/sponsors/sxzz",
+ "https://opencollective.com/debug"
+ ],
+ "license": "MIT"
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/own-keys": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz",
+ "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-intrinsic": "^1.2.6",
+ "object-keys": "^1.1.1",
+ "safe-push-apply": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parse-entities": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz",
+ "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "character-entities-legacy": "^3.0.0",
+ "character-reference-invalid": "^2.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "is-alphanumerical": "^2.0.0",
+ "is-decimal": "^2.0.0",
+ "is-hexadecimal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/parse-entities/node_modules/@types/unist": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
+ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
+ "license": "MIT"
+ },
+ "node_modules/parse5": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz",
+ "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "entities": "^6.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/pathe": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
+ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/phaser": {
+ "version": "3.90.0",
+ "resolved": "https://registry.npmjs.org/phaser/-/phaser-3.90.0.tgz",
+ "integrity": "sha512-/cziz/5ZIn02uDkC9RzN8VF9x3Gs3XdFFf9nkiMEQT3p7hQlWuyjy4QWosU802qqno2YSLn2BfqwOKLv/sSVfQ==",
+ "license": "MIT",
+ "dependencies": {
+ "eventemitter3": "^5.0.1"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/playwright": {
+ "version": "1.58.0",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.0.tgz",
+ "integrity": "sha512-2SVA0sbPktiIY/MCOPX8e86ehA/e+tDNq+e5Y8qjKYti2Z/JG7xnronT/TXTIkKbYGWlCbuucZ6dziEgkoEjQQ==",
+ "devOptional": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "playwright-core": "1.58.0"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
+ "node_modules/playwright-core": {
+ "version": "1.58.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.0.tgz",
+ "integrity": "sha512-aaoB1RWrdNi3//rOeKuMiS65UCcgOVljU46At6eFcOFPFHWtd2weHRRow6z/n+Lec0Lvu0k9ZPKJSjPugikirw==",
+ "devOptional": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "playwright-core": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/possible-typed-array-names": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
+ "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.6",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/potpack": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz",
+ "integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==",
+ "license": "ISC"
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/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==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/promise-worker-transferable": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/promise-worker-transferable/-/promise-worker-transferable-1.0.4.tgz",
+ "integrity": "sha512-bN+0ehEnrXfxV2ZQvU2PetO0n4gqBD4ulq3MI1WOPLgr7/Mg9yRQkX5+0v1vagr74ZTsl7XtzlaYDo2EuCeYJw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "is-promise": "^2.1.0",
+ "lie": "^3.0.2"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "node_modules/property-information": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz",
+ "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "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/react": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
+ "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.27.0"
+ },
+ "peerDependencies": {
+ "react": "^19.2.3"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/react-markdown": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz",
+ "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "hast-util-to-jsx-runtime": "^2.0.0",
+ "html-url-attributes": "^3.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "remark-parse": "^11.0.0",
+ "remark-rehype": "^11.0.0",
+ "unified": "^11.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ },
+ "peerDependencies": {
+ "@types/react": ">=18",
+ "react": ">=18"
+ }
+ },
+ "node_modules/react-mentions-ts": {
+ "version": "5.4.7",
+ "resolved": "https://registry.npmjs.org/react-mentions-ts/-/react-mentions-ts-5.4.7.tgz",
+ "integrity": "sha512-bTK6joPmyvLckVf1v7vE2xSSeqvL4ZwuzFvGZpt+IrtdDOdFZjiwTUBo5920kiE6WbH/v1PP81xAo6pQ0NQ0Pg==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=20"
+ },
+ "peerDependencies": {
+ "class-variance-authority": ">=0.6.0",
+ "clsx": ">=2.0.0",
+ "react": ">=19.0.0",
+ "react-dom": ">=19.0.0",
+ "tailwind-merge": ">=3.0.0"
+ }
+ },
+ "node_modules/react-use-measure": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.7.tgz",
+ "integrity": "sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">=16.13",
+ "react-dom": ">=16.13"
+ },
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/redent": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz",
+ "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "indent-string": "^4.0.0",
+ "strip-indent": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/reflect.getprototypeof": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
+ "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.9",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.7",
+ "get-proto": "^1.0.1",
+ "which-builtin-type": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/regexp.prototype.flags": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
+ "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-errors": "^1.3.0",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "set-function-name": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/remark-gfm": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz",
+ "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-gfm": "^3.0.0",
+ "micromark-extension-gfm": "^3.0.0",
+ "remark-parse": "^11.0.0",
+ "remark-stringify": "^11.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-parse": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz",
+ "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-rehype": {
+ "version": "11.1.2",
+ "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz",
+ "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "unified": "^11.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-stringify": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz",
+ "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/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/require-in-the-middle": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz",
+ "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "debug": "^4.3.5",
+ "module-details-from-path": "^1.0.3"
+ },
+ "engines": {
+ "node": ">=9.3.0 || >=8.10.0 <9.0.0"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.22.11",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
+ "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.16.1",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/resolve-pkg-maps": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.57.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.0.tgz",
+ "integrity": "sha512-e5lPJi/aui4TO1LpAXIRLySmwXSE8k3b9zoGfd42p67wzxog4WHjiZF3M2uheQih4DGyc25QEV4yRBbpueNiUA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.8"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.57.0",
+ "@rollup/rollup-android-arm64": "4.57.0",
+ "@rollup/rollup-darwin-arm64": "4.57.0",
+ "@rollup/rollup-darwin-x64": "4.57.0",
+ "@rollup/rollup-freebsd-arm64": "4.57.0",
+ "@rollup/rollup-freebsd-x64": "4.57.0",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.57.0",
+ "@rollup/rollup-linux-arm-musleabihf": "4.57.0",
+ "@rollup/rollup-linux-arm64-gnu": "4.57.0",
+ "@rollup/rollup-linux-arm64-musl": "4.57.0",
+ "@rollup/rollup-linux-loong64-gnu": "4.57.0",
+ "@rollup/rollup-linux-loong64-musl": "4.57.0",
+ "@rollup/rollup-linux-ppc64-gnu": "4.57.0",
+ "@rollup/rollup-linux-ppc64-musl": "4.57.0",
+ "@rollup/rollup-linux-riscv64-gnu": "4.57.0",
+ "@rollup/rollup-linux-riscv64-musl": "4.57.0",
+ "@rollup/rollup-linux-s390x-gnu": "4.57.0",
+ "@rollup/rollup-linux-x64-gnu": "4.57.0",
+ "@rollup/rollup-linux-x64-musl": "4.57.0",
+ "@rollup/rollup-openbsd-x64": "4.57.0",
+ "@rollup/rollup-openharmony-arm64": "4.57.0",
+ "@rollup/rollup-win32-arm64-msvc": "4.57.0",
+ "@rollup/rollup-win32-ia32-msvc": "4.57.0",
+ "@rollup/rollup-win32-x64-gnu": "4.57.0",
+ "@rollup/rollup-win32-x64-msvc": "4.57.0",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "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==",
+ "dev": true,
+ "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.3",
+ "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz",
+ "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "get-intrinsic": "^1.2.6",
+ "has-symbols": "^1.1.0",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">=0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safe-push-apply": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
+ "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safe-regex-test": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
+ "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-regex": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/saxes": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
+ "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "xmlchars": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=v12.22.7"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/set-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==",
+ "dev": true,
+ "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==",
+ "dev": true,
+ "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/set-proto": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz",
+ "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/sharp": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
+ "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@img/colour": "^1.0.0",
+ "detect-libc": "^2.1.2",
+ "semver": "^7.7.3"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-darwin-arm64": "0.34.5",
+ "@img/sharp-darwin-x64": "0.34.5",
+ "@img/sharp-libvips-darwin-arm64": "1.2.4",
+ "@img/sharp-libvips-darwin-x64": "1.2.4",
+ "@img/sharp-libvips-linux-arm": "1.2.4",
+ "@img/sharp-libvips-linux-arm64": "1.2.4",
+ "@img/sharp-libvips-linux-ppc64": "1.2.4",
+ "@img/sharp-libvips-linux-riscv64": "1.2.4",
+ "@img/sharp-libvips-linux-s390x": "1.2.4",
+ "@img/sharp-libvips-linux-x64": "1.2.4",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4",
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4",
+ "@img/sharp-linux-arm": "0.34.5",
+ "@img/sharp-linux-arm64": "0.34.5",
+ "@img/sharp-linux-ppc64": "0.34.5",
+ "@img/sharp-linux-riscv64": "0.34.5",
+ "@img/sharp-linux-s390x": "0.34.5",
+ "@img/sharp-linux-x64": "0.34.5",
+ "@img/sharp-linuxmusl-arm64": "0.34.5",
+ "@img/sharp-linuxmusl-x64": "0.34.5",
+ "@img/sharp-wasm32": "0.34.5",
+ "@img/sharp-win32-arm64": "0.34.5",
+ "@img/sharp-win32-ia32": "0.34.5",
+ "@img/sharp-win32-x64": "0.34.5"
+ }
+ },
+ "node_modules/sharp/node_modules/semver": {
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+ "license": "ISC",
+ "optional": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "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/side-channel": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/siginfo": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
+ "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "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/space-separated-tokens": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
+ "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/stable-hash": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz",
+ "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/stackback": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
+ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/stats-gl": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/stats-gl/-/stats-gl-2.4.2.tgz",
+ "integrity": "sha512-g5O9B0hm9CvnM36+v7SFl39T7hmAlv541tU81ME8YeSb3i1CIP5/QdDeSB3A0la0bKNHpxpwxOVRo2wFTYEosQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/three": "*",
+ "three": "^0.170.0"
+ },
+ "peerDependencies": {
+ "@types/three": "*",
+ "three": "*"
+ }
+ },
+ "node_modules/stats-gl/node_modules/three": {
+ "version": "0.170.0",
+ "resolved": "https://registry.npmjs.org/three/-/three-0.170.0.tgz",
+ "integrity": "sha512-FQK+LEpYc0fBD+J8g6oSEyyNzjp+Q7Ks1C568WWaoMRLW+TkNNWmenWeGgJjV105Gd+p/2ql1ZcjYvNiPZBhuQ==",
+ "license": "MIT"
+ },
+ "node_modules/stats.js": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/stats.js/-/stats.js-0.17.0.tgz",
+ "integrity": "sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw==",
+ "license": "MIT"
+ },
+ "node_modules/std-env": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz",
+ "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/stop-iteration-iterator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
+ "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "internal-slot": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "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==",
+ "dev": true,
+ "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.12",
+ "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz",
+ "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.6",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.6",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "internal-slot": "^1.1.0",
+ "regexp.prototype.flags": "^1.5.3",
+ "set-function-name": "^2.0.2",
+ "side-channel": "^1.1.0"
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ }
+ },
+ "node_modules/string.prototype.trim": {
+ "version": "1.2.10",
+ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz",
+ "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "define-data-property": "^1.1.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-object-atoms": "^1.0.0",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimend": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz",
+ "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "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==",
+ "dev": true,
+ "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-entities": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
+ "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==",
+ "license": "MIT",
+ "dependencies": {
+ "character-entities-html4": "^2.0.0",
+ "character-entities-legacy": "^3.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "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==",
+ "dev": true,
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/style-to-js": {
+ "version": "1.1.21",
+ "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz",
+ "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==",
+ "license": "MIT",
+ "dependencies": {
+ "style-to-object": "1.0.14"
+ }
+ },
+ "node_modules/style-to-object": {
+ "version": "1.0.14",
+ "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz",
+ "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==",
+ "license": "MIT",
+ "dependencies": {
+ "inline-style-parser": "0.2.7"
+ }
+ },
+ "node_modules/styled-jsx": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz",
+ "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==",
+ "license": "MIT",
+ "dependencies": {
+ "client-only": "0.0.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0"
+ },
+ "peerDependenciesMeta": {
+ "@babel/core": {
+ "optional": true
+ },
+ "babel-plugin-macros": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/suspend-react": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/suspend-react/-/suspend-react-0.1.3.tgz",
+ "integrity": "sha512-aqldKgX9aZqpoDp3e8/BZ8Dm7x1pJl+qI3ZKxDN0i/IQTWUwBx/ManmlVJ3wowqbno6c2bmiIfs+Um6LbsjJyQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">=17.0"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tailwind-merge": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz",
+ "integrity": "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/dcastil"
+ }
+ },
+ "node_modules/tailwindcss": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz",
+ "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tapable": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
+ "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/three": {
+ "version": "0.183.2",
+ "resolved": "https://registry.npmjs.org/three/-/three-0.183.2.tgz",
+ "integrity": "sha512-di3BsL2FEQ1PA7Hcvn4fyJOlxRRgFYBpMTcyOgkwJIaDOdJMebEFPA+t98EvjuljDx4hNulAGwF6KIjtwI5jgQ==",
+ "license": "MIT"
+ },
+ "node_modules/three-mesh-bvh": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.8.3.tgz",
+ "integrity": "sha512-4G5lBaF+g2auKX3P0yqx+MJC6oVt6sB5k+CchS6Ob0qvH0YIhuUk1eYr7ktsIpY+albCqE80/FVQGV190PmiAg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "three": ">= 0.159.0"
+ }
+ },
+ "node_modules/three-stdlib": {
+ "version": "2.36.1",
+ "resolved": "https://registry.npmjs.org/three-stdlib/-/three-stdlib-2.36.1.tgz",
+ "integrity": "sha512-XyGQrFmNQ5O/IoKm556ftwKsBg11TIb301MB5dWNicziQBEs2g3gtOYIf7pFiLa0zI2gUwhtCjv9fmjnxKZ1Cg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/draco3d": "^1.4.0",
+ "@types/offscreencanvas": "^2019.6.4",
+ "@types/webxr": "^0.5.2",
+ "draco3d": "^1.4.1",
+ "fflate": "^0.6.9",
+ "potpack": "^1.0.1"
+ },
+ "peerDependencies": {
+ "three": ">=0.128.0"
+ }
+ },
+ "node_modules/three-stdlib/node_modules/fflate": {
+ "version": "0.6.10",
+ "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.6.10.tgz",
+ "integrity": "sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==",
+ "license": "MIT"
+ },
+ "node_modules/tinybench": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
+ "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tinyexec": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz",
+ "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.15",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tinyglobby/node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tinyglobby/node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/tinyrainbow": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz",
+ "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/tldts": {
+ "version": "7.0.19",
+ "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.19.tgz",
+ "integrity": "sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tldts-core": "^7.0.19"
+ },
+ "bin": {
+ "tldts": "bin/cli.js"
+ }
+ },
+ "node_modules/tldts-core": {
+ "version": "7.0.19",
+ "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.19.tgz",
+ "integrity": "sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/tough-cookie": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.0.tgz",
+ "integrity": "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "tldts": "^7.0.5"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz",
+ "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "punycode": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/trim-lines": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
+ "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/troika-three-text": {
+ "version": "0.52.4",
+ "resolved": "https://registry.npmjs.org/troika-three-text/-/troika-three-text-0.52.4.tgz",
+ "integrity": "sha512-V50EwcYGruV5rUZ9F4aNsrytGdKcXKALjEtQXIOBfhVoZU9VAqZNIoGQ3TMiooVqFAbR1w15T+f+8gkzoFzawg==",
+ "license": "MIT",
+ "dependencies": {
+ "bidi-js": "^1.0.2",
+ "troika-three-utils": "^0.52.4",
+ "troika-worker-utils": "^0.52.0",
+ "webgl-sdf-generator": "1.1.1"
+ },
+ "peerDependencies": {
+ "three": ">=0.125.0"
+ }
+ },
+ "node_modules/troika-three-utils": {
+ "version": "0.52.4",
+ "resolved": "https://registry.npmjs.org/troika-three-utils/-/troika-three-utils-0.52.4.tgz",
+ "integrity": "sha512-NORAStSVa/BDiG52Mfudk4j1FG4jC4ILutB3foPnfGbOeIs9+G5vZLa0pnmnaftZUGm4UwSoqEpWdqvC7zms3A==",
+ "license": "MIT",
+ "peerDependencies": {
+ "three": ">=0.125.0"
+ }
+ },
+ "node_modules/troika-worker-utils": {
+ "version": "0.52.0",
+ "resolved": "https://registry.npmjs.org/troika-worker-utils/-/troika-worker-utils-0.52.0.tgz",
+ "integrity": "sha512-W1CpvTHykaPH5brv5VHLfQo9D1OYuo0cSBEUQFFT/nBUzM8iD6Lq2/tgG/f1OelbAS1WtaTPQzE5uM49egnngw==",
+ "license": "MIT"
+ },
+ "node_modules/trough": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz",
+ "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/ts-api-utils": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz",
+ "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
+ "node_modules/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==",
+ "dev": true,
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minimist": "^1.2.0"
+ },
+ "bin": {
+ "json5": "lib/cli.js"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/tunnel-rat": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/tunnel-rat/-/tunnel-rat-0.1.2.tgz",
+ "integrity": "sha512-lR5VHmkPhzdhrM092lI2nACsLO4QubF0/yoOhzX7c+wIpbN1GjHNzCc91QlpxBi+cnx8vVJ+Ur6vL5cEoQPFpQ==",
+ "license": "MIT",
+ "dependencies": {
+ "zustand": "^4.3.2"
+ }
+ },
+ "node_modules/tunnel-rat/node_modules/zustand": {
+ "version": "4.5.7",
+ "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz",
+ "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==",
+ "license": "MIT",
+ "dependencies": {
+ "use-sync-external-store": "^1.2.2"
+ },
+ "engines": {
+ "node": ">=12.7.0"
+ },
+ "peerDependencies": {
+ "@types/react": ">=16.8",
+ "immer": ">=9.0.6",
+ "react": ">=16.8"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "immer": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tw-animate-css": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz",
+ "integrity": "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/Wombosvideo"
+ }
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/typed-array-buffer": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
+ "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/typed-array-byte-length": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz",
+ "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "for-each": "^0.3.3",
+ "gopd": "^1.2.0",
+ "has-proto": "^1.2.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-byte-offset": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz",
+ "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "for-each": "^0.3.3",
+ "gopd": "^1.2.0",
+ "has-proto": "^1.2.0",
+ "is-typed-array": "^1.1.15",
+ "reflect.getprototypeof": "^1.0.9"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-length": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz",
+ "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "is-typed-array": "^1.1.13",
+ "possible-typed-array-names": "^1.0.0",
+ "reflect.getprototypeof": "^1.0.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/typescript-eslint": {
+ "version": "8.53.1",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.53.1.tgz",
+ "integrity": "sha512-gB+EVQfP5RDElh9ittfXlhZJdjSU4jUSTyE2+ia8CYyNvet4ElfaLlAIqDvQV9JPknKx0jQH1racTYe/4LaLSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/eslint-plugin": "8.53.1",
+ "@typescript-eslint/parser": "8.53.1",
+ "@typescript-eslint/typescript-estree": "8.53.1",
+ "@typescript-eslint/utils": "8.53.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/unbox-primitive": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
+ "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-bigints": "^1.0.2",
+ "has-symbols": "^1.1.0",
+ "which-boxed-primitive": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/unified": {
+ "version": "11.0.5",
+ "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz",
+ "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "bail": "^2.0.0",
+ "devlop": "^1.0.0",
+ "extend": "^3.0.0",
+ "is-plain-obj": "^4.0.0",
+ "trough": "^2.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-is": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz",
+ "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-position": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz",
+ "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-stringify-position": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
+ "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz",
+ "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit-parents": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz",
+ "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unrs-resolver": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz",
+ "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "napi-postinstall": "^0.3.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unrs-resolver"
+ },
+ "optionalDependencies": {
+ "@unrs/resolver-binding-android-arm-eabi": "1.11.1",
+ "@unrs/resolver-binding-android-arm64": "1.11.1",
+ "@unrs/resolver-binding-darwin-arm64": "1.11.1",
+ "@unrs/resolver-binding-darwin-x64": "1.11.1",
+ "@unrs/resolver-binding-freebsd-x64": "1.11.1",
+ "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1",
+ "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1",
+ "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1",
+ "@unrs/resolver-binding-linux-arm64-musl": "1.11.1",
+ "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1",
+ "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1",
+ "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1",
+ "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1",
+ "@unrs/resolver-binding-linux-x64-gnu": "1.11.1",
+ "@unrs/resolver-binding-linux-x64-musl": "1.11.1",
+ "@unrs/resolver-binding-wasm32-wasi": "1.11.1",
+ "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1",
+ "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1",
+ "@unrs/resolver-binding-win32-x64-msvc": "1.11.1"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/use-sync-external-store": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
+ "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/utility-types": {
+ "version": "3.11.0",
+ "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz",
+ "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/vfile": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
+ "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile-message": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz",
+ "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-stringify-position": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vite": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
+ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.27.0",
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3",
+ "postcss": "^8.5.6",
+ "rollup": "^4.43.0",
+ "tinyglobby": "^0.2.15"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "lightningcss": "^1.21.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite/node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite/node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/vite/node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/vitest": {
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.18.tgz",
+ "integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/expect": "4.0.18",
+ "@vitest/mocker": "4.0.18",
+ "@vitest/pretty-format": "4.0.18",
+ "@vitest/runner": "4.0.18",
+ "@vitest/snapshot": "4.0.18",
+ "@vitest/spy": "4.0.18",
+ "@vitest/utils": "4.0.18",
+ "es-module-lexer": "^1.7.0",
+ "expect-type": "^1.2.2",
+ "magic-string": "^0.30.21",
+ "obug": "^2.1.1",
+ "pathe": "^2.0.3",
+ "picomatch": "^4.0.3",
+ "std-env": "^3.10.0",
+ "tinybench": "^2.9.0",
+ "tinyexec": "^1.0.2",
+ "tinyglobby": "^0.2.15",
+ "tinyrainbow": "^3.0.3",
+ "vite": "^6.0.0 || ^7.0.0",
+ "why-is-node-running": "^2.3.0"
+ },
+ "bin": {
+ "vitest": "vitest.mjs"
+ },
+ "engines": {
+ "node": "^20.0.0 || ^22.0.0 || >=24.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "@edge-runtime/vm": "*",
+ "@opentelemetry/api": "^1.9.0",
+ "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
+ "@vitest/browser-playwright": "4.0.18",
+ "@vitest/browser-preview": "4.0.18",
+ "@vitest/browser-webdriverio": "4.0.18",
+ "@vitest/ui": "4.0.18",
+ "happy-dom": "*",
+ "jsdom": "*"
+ },
+ "peerDependenciesMeta": {
+ "@edge-runtime/vm": {
+ "optional": true
+ },
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@types/node": {
+ "optional": true
+ },
+ "@vitest/browser-playwright": {
+ "optional": true
+ },
+ "@vitest/browser-preview": {
+ "optional": true
+ },
+ "@vitest/browser-webdriverio": {
+ "optional": true
+ },
+ "@vitest/ui": {
+ "optional": true
+ },
+ "happy-dom": {
+ "optional": true
+ },
+ "jsdom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vitest/node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/w3c-xmlserializer": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
+ "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "xml-name-validator": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/webgl-constants": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/webgl-constants/-/webgl-constants-1.1.1.tgz",
+ "integrity": "sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg=="
+ },
+ "node_modules/webgl-sdf-generator": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/webgl-sdf-generator/-/webgl-sdf-generator-1.1.1.tgz",
+ "integrity": "sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA==",
+ "license": "MIT"
+ },
+ "node_modules/webidl-conversions": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz",
+ "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/whatwg-mimetype": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
+ "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/whatwg-url": {
+ "version": "15.1.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-15.1.0.tgz",
+ "integrity": "sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tr46": "^6.0.0",
+ "webidl-conversions": "^8.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "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.1.1",
+ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz",
+ "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-bigint": "^1.1.0",
+ "is-boolean-object": "^1.2.1",
+ "is-number-object": "^1.1.1",
+ "is-string": "^1.1.1",
+ "is-symbol": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-builtin-type": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz",
+ "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "function.prototype.name": "^1.1.6",
+ "has-tostringtag": "^1.0.2",
+ "is-async-function": "^2.0.0",
+ "is-date-object": "^1.1.0",
+ "is-finalizationregistry": "^1.1.0",
+ "is-generator-function": "^1.0.10",
+ "is-regex": "^1.2.1",
+ "is-weakref": "^1.0.2",
+ "isarray": "^2.0.5",
+ "which-boxed-primitive": "^1.1.0",
+ "which-collection": "^1.0.2",
+ "which-typed-array": "^1.1.16"
+ },
+ "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==",
+ "dev": true,
+ "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.20",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz",
+ "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "for-each": "^0.3.5",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/why-is-node-running": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
+ "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "siginfo": "^2.0.0",
+ "stackback": "0.0.2"
+ },
+ "bin": {
+ "why-is-node-running": "cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ws": {
+ "version": "8.19.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
+ "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
+ "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/xml-name-validator": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
+ "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/xmlchars": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/zod": {
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
+ "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/zod-validation-error": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz",
+ "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.0 || ^4.0.0"
+ }
+ },
+ "node_modules/zustand": {
+ "version": "5.0.11",
+ "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.11.tgz",
+ "integrity": "sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.20.0"
+ },
+ "peerDependencies": {
+ "@types/react": ">=18.0.0",
+ "immer": ">=9.0.6",
+ "react": ">=18.0.0",
+ "use-sync-external-store": ">=1.2.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "immer": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "use-sync-external-store": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/zwitch": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
+ "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..7270432
--- /dev/null
+++ b/package.json
@@ -0,0 +1,57 @@
+{
+ "name": "claw3d",
+ "version": "0.1.0",
+ "private": true,
+ "license": "MIT",
+ "scripts": {
+ "dev": "node server/index.js --dev",
+ "build": "next build",
+ "start": "node server/index.js",
+ "lint": "eslint .",
+ "cleanup:ux-artifacts": "node scripts/cleanup-ux-artifacts.mjs",
+ "sync:gateway-client": "node scripts/sync-openclaw-gateway-client.ts",
+ "studio:setup": "node scripts/studio-setup.js",
+ "smoke:dev-server": "node scripts/smoke-dev-server.mjs",
+ "typecheck": "tsc --noEmit",
+ "test": "vitest",
+ "e2e": "playwright test"
+ },
+ "dependencies": {
+ "@noble/ed25519": "^3.0.0",
+ "@react-three/drei": "^10.7.7",
+ "@react-three/fiber": "^9.5.0",
+ "@vercel/otel": "^2.1.0",
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
+ "lucide-react": "^0.563.0",
+ "next": "16.1.6",
+ "phaser": "^3.90.0",
+ "react": "19.2.3",
+ "react-dom": "19.2.3",
+ "react-markdown": "^10.1.0",
+ "react-mentions-ts": "^5.4.7",
+ "remark-gfm": "^4.0.1",
+ "tailwind-merge": "^3.4.0",
+ "three": "^0.183.2",
+ "ws": "^8.18.3"
+ },
+ "devDependencies": {
+ "@playwright/test": "^1.58.0",
+ "@tailwindcss/postcss": "^4",
+ "@testing-library/jest-dom": "^6.9.1",
+ "@testing-library/react": "^16.3.2",
+ "@types/node": "^20",
+ "@types/react": "^19",
+ "@types/react-dom": "^19",
+ "@types/three": "^0.183.1",
+ "@types/ws": "^8.18.1",
+ "eslint": "^9",
+ "eslint-config-next": "16.1.6",
+ "eslint-config-prettier": "^10.1.8",
+ "jsdom": "^27.4.0",
+ "tailwindcss": "^4",
+ "tw-animate-css": "^1.4.0",
+ "typescript": "^5",
+ "vitest": "^4.0.18"
+ }
+}
diff --git a/playwright.config.ts b/playwright.config.ts
new file mode 100644
index 0000000..fd356b4
--- /dev/null
+++ b/playwright.config.ts
@@ -0,0 +1,18 @@
+import { defineConfig } from "@playwright/test";
+import path from "node:path";
+export default defineConfig({
+ testDir: "./tests/e2e",
+ use: {
+ baseURL: "http://127.0.0.1:3000",
+ },
+ webServer: {
+ command: "npm run dev",
+ port: 3000,
+ reuseExistingServer: !process.env.CI,
+ env: {
+ ...process.env,
+ OPENCLAW_STATE_DIR: path.resolve("./tests/fixtures/openclaw-empty-state"),
+ NEXT_PUBLIC_GATEWAY_URL: "",
+ },
+ },
+});
diff --git a/postcss.config.mjs b/postcss.config.mjs
new file mode 100644
index 0000000..61e3684
--- /dev/null
+++ b/postcss.config.mjs
@@ -0,0 +1,7 @@
+const config = {
+ plugins: {
+ "@tailwindcss/postcss": {},
+ },
+};
+
+export default config;
diff --git a/public/globe.svg b/public/globe.svg
new file mode 100644
index 0000000..567f17b
--- /dev/null
+++ b/public/globe.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/next.svg b/public/next.svg
new file mode 100644
index 0000000..5174b28
--- /dev/null
+++ b/public/next.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/office-assets/backgrounds/office-bg.png b/public/office-assets/backgrounds/office-bg.png
new file mode 100644
index 0000000..ca49ca0
Binary files /dev/null and b/public/office-assets/backgrounds/office-bg.png differ
diff --git a/public/office-assets/models/furniture/bookcaseClosed.glb b/public/office-assets/models/furniture/bookcaseClosed.glb
new file mode 100644
index 0000000..43197a4
Binary files /dev/null and b/public/office-assets/models/furniture/bookcaseClosed.glb differ
diff --git a/public/office-assets/models/furniture/chairDesk.glb b/public/office-assets/models/furniture/chairDesk.glb
new file mode 100644
index 0000000..131101f
Binary files /dev/null and b/public/office-assets/models/furniture/chairDesk.glb differ
diff --git a/public/office-assets/models/furniture/chairModernCushion.glb b/public/office-assets/models/furniture/chairModernCushion.glb
new file mode 100644
index 0000000..a6c18d9
Binary files /dev/null and b/public/office-assets/models/furniture/chairModernCushion.glb differ
diff --git a/public/office-assets/models/furniture/computerScreen.glb b/public/office-assets/models/furniture/computerScreen.glb
new file mode 100644
index 0000000..c509093
Binary files /dev/null and b/public/office-assets/models/furniture/computerScreen.glb differ
diff --git a/public/office-assets/models/furniture/desk.glb b/public/office-assets/models/furniture/desk.glb
new file mode 100644
index 0000000..8ca1870
Binary files /dev/null and b/public/office-assets/models/furniture/desk.glb differ
diff --git a/public/office-assets/models/furniture/deskCorner.glb b/public/office-assets/models/furniture/deskCorner.glb
new file mode 100644
index 0000000..98fb683
Binary files /dev/null and b/public/office-assets/models/furniture/deskCorner.glb differ
diff --git a/public/office-assets/models/furniture/kitchenCabinet.glb b/public/office-assets/models/furniture/kitchenCabinet.glb
new file mode 100644
index 0000000..6292e58
Binary files /dev/null and b/public/office-assets/models/furniture/kitchenCabinet.glb differ
diff --git a/public/office-assets/models/furniture/kitchenCoffeeMachine.glb b/public/office-assets/models/furniture/kitchenCoffeeMachine.glb
new file mode 100644
index 0000000..f10641b
Binary files /dev/null and b/public/office-assets/models/furniture/kitchenCoffeeMachine.glb differ
diff --git a/public/office-assets/models/furniture/kitchenFridgeSmall.glb b/public/office-assets/models/furniture/kitchenFridgeSmall.glb
new file mode 100644
index 0000000..560b1ea
Binary files /dev/null and b/public/office-assets/models/furniture/kitchenFridgeSmall.glb differ
diff --git a/public/office-assets/models/furniture/lampRoundFloor.glb b/public/office-assets/models/furniture/lampRoundFloor.glb
new file mode 100644
index 0000000..69f7c9d
Binary files /dev/null and b/public/office-assets/models/furniture/lampRoundFloor.glb differ
diff --git a/public/office-assets/models/furniture/loungeDesignChair.glb b/public/office-assets/models/furniture/loungeDesignChair.glb
new file mode 100644
index 0000000..50526da
Binary files /dev/null and b/public/office-assets/models/furniture/loungeDesignChair.glb differ
diff --git a/public/office-assets/models/furniture/loungeSofa.glb b/public/office-assets/models/furniture/loungeSofa.glb
new file mode 100644
index 0000000..7761b15
Binary files /dev/null and b/public/office-assets/models/furniture/loungeSofa.glb differ
diff --git a/public/office-assets/models/furniture/plantSmall1.glb b/public/office-assets/models/furniture/plantSmall1.glb
new file mode 100644
index 0000000..6c64dda
Binary files /dev/null and b/public/office-assets/models/furniture/plantSmall1.glb differ
diff --git a/public/office-assets/models/furniture/pottedPlant.glb b/public/office-assets/models/furniture/pottedPlant.glb
new file mode 100644
index 0000000..774d659
Binary files /dev/null and b/public/office-assets/models/furniture/pottedPlant.glb differ
diff --git a/public/office-assets/models/furniture/table.glb b/public/office-assets/models/furniture/table.glb
new file mode 100644
index 0000000..8add1ba
Binary files /dev/null and b/public/office-assets/models/furniture/table.glb differ
diff --git a/public/office-assets/models/furniture/tableCoffee.glb b/public/office-assets/models/furniture/tableCoffee.glb
new file mode 100644
index 0000000..945caf4
Binary files /dev/null and b/public/office-assets/models/furniture/tableCoffee.glb differ
diff --git a/public/office-assets/models/furniture/tableRound.glb b/public/office-assets/models/furniture/tableRound.glb
new file mode 100644
index 0000000..885fdcd
Binary files /dev/null and b/public/office-assets/models/furniture/tableRound.glb differ
diff --git a/readme-image.png b/readme-image.png
new file mode 100644
index 0000000..89f6ae0
Binary files /dev/null and b/readme-image.png differ
diff --git a/scripts/cleanup-ux-artifacts.mjs b/scripts/cleanup-ux-artifacts.mjs
new file mode 100644
index 0000000..ee1484f
--- /dev/null
+++ b/scripts/cleanup-ux-artifacts.mjs
@@ -0,0 +1,88 @@
+import { constants as fsConstants, promises as fs } from "node:fs";
+import { spawnSync } from "node:child_process";
+import os from "node:os";
+import path from "node:path";
+import { fileURLToPath } from "node:url";
+
+const scriptDir = path.dirname(fileURLToPath(import.meta.url));
+const repoRoot = path.resolve(scriptDir, "..");
+const uxAuditDir = path.join(repoRoot, "output", "playwright", "ux-audit");
+const transientFiles = [
+ path.join(repoRoot, ".agent", "ux-audit.md"),
+ path.join(repoRoot, ".agent", "execplan-pending.md"),
+];
+
+async function ensureDir(dir) {
+ await fs.mkdir(dir, { recursive: true });
+}
+
+async function clearDirContents(dir) {
+ await ensureDir(dir);
+ const entries = await fs.readdir(dir);
+ await Promise.all(
+ entries.map((entry) =>
+ fs.rm(path.join(dir, entry), { recursive: true, force: true }),
+ ),
+ );
+}
+
+async function removeIfPresent(filePath) {
+ try {
+ await fs.unlink(filePath);
+ } catch (error) {
+ if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
+ return;
+ }
+ throw error;
+ }
+}
+
+function run(command, args) {
+ return spawnSync(command, args, { encoding: "utf8" });
+}
+
+async function stopPlaywrightSessions() {
+ const codeHome = process.env.CODEX_HOME ?? path.join(os.homedir(), ".codex");
+ const pwcli = path.join(codeHome, "skills", "playwright", "scripts", "playwright_cli.sh");
+ try {
+ await fs.access(pwcli, fsConstants.X_OK);
+ } catch {
+ return;
+ }
+ const result = run(pwcli, ["session-stop-all"]);
+ if (result.status === 0) return;
+ if (result.error) {
+ throw result.error;
+ }
+}
+
+function killPattern(pattern) {
+ const result = run("pkill", ["-f", pattern]);
+ if (result.status === 0 || result.status === 1) return;
+ if (result.error && result.error.code === "ENOENT") return;
+ if (result.error) throw result.error;
+}
+
+function cleanupPlaywrightProcesses() {
+ killPattern("ms-playwright/daemon");
+ killPattern("playwright/cli.js run-mcp-server");
+ killPattern("chrome-headless-shell");
+ killPattern("Google Chrome --headless");
+ killPattern("Chromium --headless");
+}
+
+async function main() {
+ await stopPlaywrightSessions();
+ cleanupPlaywrightProcesses();
+ await clearDirContents(uxAuditDir);
+ for (const transientFile of transientFiles) {
+ await removeIfPresent(transientFile);
+ }
+ console.log("cleanup:ux-artifacts complete");
+}
+
+main().catch((error) => {
+ console.error("cleanup:ux-artifacts failed");
+ console.error(error);
+ process.exit(1);
+});
diff --git a/scripts/smoke-dev-server.mjs b/scripts/smoke-dev-server.mjs
new file mode 100644
index 0000000..da38e4a
--- /dev/null
+++ b/scripts/smoke-dev-server.mjs
@@ -0,0 +1,82 @@
+import { spawn } from "node:child_process";
+import net from "node:net";
+
+const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
+
+const getFreePort = async () => {
+ for (let i = 0; i < 30; i++) {
+ const port = 20000 + Math.floor(Math.random() * 20000);
+ const ok = await new Promise((resolve) => {
+ const server = net.createServer();
+ server.once("error", () => resolve(false));
+ server.listen(port, "127.0.0.1", () => {
+ server.close(() => resolve(true));
+ });
+ });
+ if (ok) return port;
+ }
+ throw new Error("Failed to find a free port for smoke test.");
+};
+
+const main = async () => {
+ const port = await getFreePort();
+ const url = `http://127.0.0.1:${port}/`;
+
+ const child = spawn(process.execPath, ["server/index.js", "--dev"], {
+ env: {
+ ...process.env,
+ HOST: "127.0.0.1",
+ PORT: String(port),
+ },
+ stdio: ["ignore", "pipe", "pipe"],
+ });
+
+ const lines = [];
+ const pushLines = (chunk) => {
+ const text = String(chunk ?? "");
+ for (const line of text.split(/\r?\n/)) {
+ if (!line) continue;
+ lines.push(line);
+ if (lines.length > 80) lines.shift();
+ }
+ };
+ child.stdout.on("data", pushLines);
+ child.stderr.on("data", pushLines);
+
+ const deadline = Date.now() + 60_000;
+ let lastErr = null;
+
+ try {
+ while (Date.now() < deadline) {
+ if (child.exitCode !== null) {
+ throw new Error(`Dev server exited early with code ${child.exitCode}.`);
+ }
+
+ try {
+ const res = await fetch(url, { redirect: "manual" });
+ if (res.status >= 200 && res.status < 500) {
+ process.stdout.write(`OK ${res.status} ${url}\n`);
+ return;
+ }
+ lastErr = new Error(`Unexpected status ${res.status} for ${url}`);
+ } catch (err) {
+ lastErr = err;
+ }
+
+ await sleep(500);
+ }
+
+ throw new Error(
+ `Timed out waiting for dev server to respond at ${url}. Last error: ${lastErr?.message || "unknown"}`
+ );
+ } finally {
+ child.kill("SIGTERM");
+ await Promise.race([new Promise((r) => child.once("exit", r)), sleep(2000)]);
+ }
+};
+
+main().catch((err) => {
+ process.stderr.write(String(err?.stack || err) + "\n");
+ process.exitCode = 1;
+});
+
diff --git a/scripts/studio-setup.js b/scripts/studio-setup.js
new file mode 100644
index 0000000..06b2930
--- /dev/null
+++ b/scripts/studio-setup.js
@@ -0,0 +1,90 @@
+const fs = require("node:fs");
+const path = require("node:path");
+const { execFileSync } = require("node:child_process");
+const readline = require("node:readline/promises");
+
+const { resolveStudioSettingsPath } = require("../server/studio-settings");
+
+const DEFAULT_GATEWAY_URL = "ws://127.0.0.1:18789";
+
+const parseArgs = (argv) => {
+ return {
+ force: argv.includes("--force"),
+ };
+};
+
+const tryReadGatewayTokenFromOpenclawCli = () => {
+ try {
+ const raw = execFileSync("openclaw", ["config", "get", "gateway.auth.token"], {
+ encoding: "utf8",
+ stdio: ["ignore", "pipe", "ignore"],
+ });
+ const token = String(raw ?? "").trim();
+ return token || null;
+ } catch {
+ return null;
+ }
+};
+
+async function main() {
+ const args = parseArgs(process.argv.slice(2));
+
+ const settingsPath = resolveStudioSettingsPath(process.env);
+ const settingsDir = path.dirname(settingsPath);
+
+ if (fs.existsSync(settingsPath) && !args.force) {
+ console.error(
+ `Studio settings already exist at ${settingsPath}. Re-run with --force to overwrite.`
+ );
+ process.exitCode = 1;
+ return;
+ }
+
+ const rl = readline.createInterface({
+ input: process.stdin,
+ output: process.stdout,
+ });
+
+ try {
+ const urlAnswer = await rl.question(
+ `Upstream Gateway URL [${DEFAULT_GATEWAY_URL}]: `
+ );
+ const gatewayUrl = (urlAnswer || DEFAULT_GATEWAY_URL).trim();
+ if (!gatewayUrl) {
+ throw new Error("Gateway URL is required.");
+ }
+
+ const tokenDefault = tryReadGatewayTokenFromOpenclawCli();
+ const tokenPrompt = tokenDefault
+ ? "Upstream Gateway Token [detected from openclaw]: "
+ : "Upstream Gateway Token: ";
+ const tokenAnswer = await rl.question(tokenPrompt);
+ const token = (tokenAnswer || tokenDefault || "").trim();
+ if (!token) {
+ throw new Error(
+ "Gateway token is required. Provide it, or install/openclaw so it can be auto-detected."
+ );
+ }
+
+ fs.mkdirSync(settingsDir, { recursive: true });
+ const next = {
+ version: 1,
+ gateway: {
+ url: gatewayUrl,
+ token,
+ },
+ };
+ fs.writeFileSync(settingsPath, JSON.stringify(next, null, 2), "utf8");
+
+ console.info(`Wrote Studio settings to ${settingsPath}.`);
+ } finally {
+ rl.close();
+ }
+}
+
+main().catch((err) => {
+ const msg = err instanceof Error ? err.message : String(err);
+ console.error(msg);
+ process.exitCode = 1;
+});
+
diff --git a/scripts/sync-openclaw-gateway-client.ts b/scripts/sync-openclaw-gateway-client.ts
new file mode 100644
index 0000000..29bd92e
--- /dev/null
+++ b/scripts/sync-openclaw-gateway-client.ts
@@ -0,0 +1,47 @@
+import fs from "node:fs";
+import path from "node:path";
+
+const repoRoot = process.cwd();
+const requestedSourcePath =
+ process.argv[2]?.trim() ||
+ process.env.OPENCLAW_GATEWAY_CLIENT_SOURCE?.trim() ||
+ process.env.OPENCLAW_UI_PATH?.trim() ||
+ "";
+const sourcePath = requestedSourcePath
+ ? path.resolve(requestedSourcePath)
+ : "";
+const destPath = path.join(
+ repoRoot,
+ "src",
+ "lib",
+ "gateway",
+ "openclaw",
+ "GatewayBrowserClient.ts"
+);
+
+if (!sourcePath) {
+ console.error(
+ "Missing upstream gateway client source path. Provide it as `npm run sync:gateway-client -- /path/to/gateway.ts` or set OPENCLAW_GATEWAY_CLIENT_SOURCE."
+ );
+ process.exit(1);
+}
+
+if (!fs.existsSync(sourcePath)) {
+ console.error(`Missing upstream gateway client at ${sourcePath}.`);
+ process.exit(1);
+}
+
+let contents = fs.readFileSync(sourcePath, "utf8");
+contents = contents
+ .replace(
+ /from "\.\.\/\.\.\/\.\.\/src\/gateway\/protocol\/client-info\.js";/g,
+ 'from "./client-info";'
+ )
+ .replace(
+ /from "\.\.\/\.\.\/\.\.\/src\/gateway\/device-auth\.js";/g,
+ 'from "./device-auth-payload";'
+ );
+
+fs.mkdirSync(path.dirname(destPath), { recursive: true });
+fs.writeFileSync(destPath, contents, "utf8");
+console.log(`Synced gateway client to ${destPath}.`);
diff --git a/server/access-gate.js b/server/access-gate.js
new file mode 100644
index 0000000..ddf4fef
--- /dev/null
+++ b/server/access-gate.js
@@ -0,0 +1,56 @@
+const parseCookies = (header) => {
+ const raw = typeof header === "string" ? header : "";
+ if (!raw.trim()) return {};
+ const out = {};
+ for (const part of raw.split(";")) {
+ const idx = part.indexOf("=");
+ if (idx === -1) continue;
+ const key = part.slice(0, idx).trim();
+ const value = part.slice(idx + 1).trim();
+ if (!key) continue;
+ out[key] = value;
+ }
+ return out;
+};
+
+function createAccessGate(options) {
+ const token = String(options?.token ?? "").trim();
+ const cookieName = String(options?.cookieName ?? "studio_access").trim() || "studio_access";
+
+ const enabled = Boolean(token);
+
+ const isAuthorized = (req) => {
+ if (!enabled) return true;
+ const cookieHeader = req.headers?.cookie;
+ const cookies = parseCookies(cookieHeader);
+ return cookies[cookieName] === token;
+ };
+
+ const handleHttp = (req, res) => {
+ if (!enabled) return false;
+ if (String(req.url || "/").startsWith("/api/")) {
+ if (!isAuthorized(req)) {
+ res.statusCode = 401;
+ res.setHeader("Content-Type", "application/json");
+ res.end(
+ JSON.stringify({
+ error: "Studio access token required. Send the configured Studio access cookie and retry.",
+ })
+ );
+ return true;
+ }
+ }
+
+ return false;
+ };
+
+ const allowUpgrade = (req) => {
+ if (!enabled) return true;
+ return isAuthorized(req);
+ };
+
+ return { enabled, handleHttp, allowUpgrade };
+}
+
+module.exports = { createAccessGate };
+
diff --git a/server/gateway-proxy.js b/server/gateway-proxy.js
new file mode 100644
index 0000000..79f124f
--- /dev/null
+++ b/server/gateway-proxy.js
@@ -0,0 +1,308 @@
+const { WebSocket, WebSocketServer } = require("ws");
+
+const buildErrorResponse = (id, code, message) => {
+ return {
+ type: "res",
+ id,
+ ok: false,
+ error: { code, message },
+ };
+};
+
+const isObject = (value) => Boolean(value && typeof value === "object");
+
+const safeJsonParse = (raw) => {
+ try {
+ return JSON.parse(raw);
+ } catch {
+ return null;
+ }
+};
+
+const resolvePathname = (url) => {
+ const raw = typeof url === "string" ? url : "";
+ const idx = raw.indexOf("?");
+ return (idx === -1 ? raw : raw.slice(0, idx)) || "/";
+};
+
+const injectAuthToken = (params, token) => {
+ const next = isObject(params) ? { ...params } : {};
+ const auth = isObject(next.auth) ? { ...next.auth } : {};
+ auth.token = token;
+ next.auth = auth;
+ return next;
+};
+
+const resolveOriginForUpstream = (upstreamUrl) => {
+ const url = new URL(upstreamUrl);
+ const proto = url.protocol === "wss:" ? "https:" : "http:";
+ const hostname =
+ url.hostname === "127.0.0.1" || url.hostname === "::1" || url.hostname === "0.0.0.0"
+ ? "localhost"
+ : url.hostname;
+ const host = url.port ? `${hostname}:${url.port}` : hostname;
+ return `${proto}//${host}`;
+};
+
+const hasNonEmptyToken = (params) => {
+ const raw = params && isObject(params) && isObject(params.auth) ? params.auth.token : "";
+ return typeof raw === "string" && raw.trim().length > 0;
+};
+
+const hasNonEmptyPassword = (params) => {
+ const raw = params && isObject(params) && isObject(params.auth) ? params.auth.password : "";
+ return typeof raw === "string" && raw.trim().length > 0;
+};
+
+const hasNonEmptyDeviceToken = (params) => {
+ const raw = params && isObject(params) && isObject(params.auth) ? params.auth.deviceToken : "";
+ return typeof raw === "string" && raw.trim().length > 0;
+};
+
+const hasCompleteDeviceAuth = (params) => {
+ const device = params && isObject(params) && isObject(params.device) ? params.device : null;
+ if (!device) {
+ return false;
+ }
+ const id = typeof device.id === "string" ? device.id.trim() : "";
+ const publicKey = typeof device.publicKey === "string" ? device.publicKey.trim() : "";
+ const signature = typeof device.signature === "string" ? device.signature.trim() : "";
+ const nonce = typeof device.nonce === "string" ? device.nonce.trim() : "";
+ const signedAt = device.signedAt;
+ return (
+ id.length > 0 &&
+ publicKey.length > 0 &&
+ signature.length > 0 &&
+ nonce.length > 0 &&
+ Number.isFinite(signedAt) &&
+ signedAt >= 0
+ );
+};
+
+function createGatewayProxy(options) {
+ const {
+ loadUpstreamSettings,
+ allowWs = (req) => resolvePathname(req.url) === "/api/gateway/ws",
+ log = () => {},
+ logError = (msg, err) => console.error(msg, err),
+ } = options || {};
+
+ if (typeof loadUpstreamSettings !== "function") {
+ throw new Error("createGatewayProxy requires loadUpstreamSettings().");
+ }
+
+ const wss = new WebSocketServer({ noServer: true });
+
+ wss.on("connection", (browserWs) => {
+ let upstreamWs = null;
+ let upstreamReady = false;
+ let upstreamUrl = "";
+ let upstreamToken = "";
+ let connectRequestId = null;
+ let connectResponseSent = false;
+ let pendingConnectFrame = null;
+ let pendingUpstreamSetupError = null;
+ let closed = false;
+
+ const closeBoth = (code, reason) => {
+ if (closed) return;
+ closed = true;
+ try {
+ browserWs.close(code, reason);
+ } catch {}
+ try {
+ upstreamWs?.close(code, reason);
+ } catch {}
+ };
+
+ const sendToBrowser = (frame) => {
+ if (browserWs.readyState !== WebSocket.OPEN) return;
+ browserWs.send(JSON.stringify(frame));
+ };
+
+ const sendConnectError = (code, message) => {
+ if (connectRequestId && !connectResponseSent) {
+ connectResponseSent = true;
+ sendToBrowser(buildErrorResponse(connectRequestId, code, message));
+ }
+ closeBoth(1011, "connect failed");
+ };
+
+ const forwardConnectFrame = (frame) => {
+ const browserHasAuth =
+ hasNonEmptyToken(frame.params) ||
+ hasNonEmptyPassword(frame.params) ||
+ hasNonEmptyDeviceToken(frame.params) ||
+ hasCompleteDeviceAuth(frame.params);
+
+ if (!upstreamToken && !browserHasAuth) {
+ sendConnectError(
+ "studio.gateway_token_missing",
+ "Upstream gateway token is not configured on the Studio host."
+ );
+ return;
+ }
+
+ const connectFrame = browserHasAuth
+ ? frame
+ : {
+ ...frame,
+ params: injectAuthToken(frame.params, upstreamToken),
+ };
+ upstreamWs.send(JSON.stringify(connectFrame));
+ };
+
+ const maybeForwardPendingConnect = () => {
+ if (!pendingConnectFrame || !upstreamReady || upstreamWs?.readyState !== WebSocket.OPEN) {
+ return;
+ }
+ const frame = pendingConnectFrame;
+ pendingConnectFrame = null;
+ forwardConnectFrame(frame);
+ };
+
+ const startUpstream = async () => {
+ try {
+ const settings = await loadUpstreamSettings();
+ upstreamUrl = typeof settings?.url === "string" ? settings.url.trim() : "";
+ upstreamToken = typeof settings?.token === "string" ? settings.token.trim() : "";
+ } catch (err) {
+ logError("Failed to load upstream gateway settings.", err);
+ pendingUpstreamSetupError = {
+ code: "studio.settings_load_failed",
+ message: "Failed to load Studio gateway settings.",
+ };
+ return;
+ }
+
+ if (!upstreamUrl) {
+ pendingUpstreamSetupError = {
+ code: "studio.gateway_url_missing",
+ message: "Upstream gateway URL is not configured on the Studio host.",
+ };
+ return;
+ }
+
+ let upstreamOrigin = "";
+ try {
+ upstreamOrigin = resolveOriginForUpstream(upstreamUrl);
+ } catch {
+ pendingUpstreamSetupError = {
+ code: "studio.gateway_url_invalid",
+ message: "Upstream gateway URL is invalid on the Studio host.",
+ };
+ return;
+ }
+
+ upstreamWs = new WebSocket(upstreamUrl, { origin: upstreamOrigin });
+
+ upstreamWs.on("open", () => {
+ upstreamReady = true;
+ maybeForwardPendingConnect();
+ });
+
+ upstreamWs.on("message", (upRaw) => {
+ const upParsed = safeJsonParse(String(upRaw ?? ""));
+ if (upParsed && isObject(upParsed) && upParsed.type === "res") {
+ const resId = typeof upParsed.id === "string" ? upParsed.id : "";
+ if (resId && connectRequestId && resId === connectRequestId) {
+ connectResponseSent = true;
+ }
+ }
+ if (browserWs.readyState === WebSocket.OPEN) {
+ browserWs.send(String(upRaw ?? ""));
+ }
+ });
+
+ upstreamWs.on("close", (ev) => {
+ const reason = typeof ev?.reason === "string" ? ev.reason : "";
+ if (!connectResponseSent && connectRequestId) {
+ sendToBrowser(
+ buildErrorResponse(
+ connectRequestId,
+ "studio.upstream_closed",
+ `Upstream gateway closed (${ev.code}): ${reason}`
+ )
+ );
+ }
+ closeBoth(1012, "upstream closed");
+ });
+
+ upstreamWs.on("error", (err) => {
+ logError("Upstream gateway WebSocket error.", err);
+ sendConnectError(
+ "studio.upstream_error",
+ "Failed to connect to upstream gateway WebSocket."
+ );
+ });
+
+ log("proxy connected");
+ };
+
+ void startUpstream();
+
+ browserWs.on("message", async (raw) => {
+ const parsed = safeJsonParse(String(raw ?? ""));
+ if (!parsed || !isObject(parsed)) {
+ closeBoth(1003, "invalid json");
+ return;
+ }
+
+ if (!connectRequestId) {
+ if (parsed.type !== "req" || parsed.method !== "connect") {
+ closeBoth(1008, "connect required");
+ return;
+ }
+ const id = typeof parsed.id === "string" ? parsed.id : "";
+ if (!id) {
+ closeBoth(1008, "connect id required");
+ return;
+ }
+ connectRequestId = id;
+ if (pendingUpstreamSetupError) {
+ sendConnectError(pendingUpstreamSetupError.code, pendingUpstreamSetupError.message);
+ return;
+ }
+ pendingConnectFrame = parsed;
+ maybeForwardPendingConnect();
+ return;
+ }
+
+ if (!upstreamReady || upstreamWs.readyState !== WebSocket.OPEN) {
+ closeBoth(1013, "upstream not ready");
+ return;
+ }
+
+ if (parsed.type === "req" && parsed.method === "connect" && !connectResponseSent) {
+ pendingConnectFrame = null;
+ forwardConnectFrame(parsed);
+ return;
+ }
+
+ upstreamWs.send(JSON.stringify(parsed));
+ });
+
+ browserWs.on("close", () => {
+ closeBoth(1000, "client closed");
+ });
+
+ browserWs.on("error", (err) => {
+ logError("Browser WebSocket error.", err);
+ closeBoth(1011, "client error");
+ });
+ });
+
+ const handleUpgrade = (req, socket, head) => {
+ if (!allowWs(req)) {
+ socket.destroy();
+ return;
+ }
+ wss.handleUpgrade(req, socket, head, (ws) => {
+ wss.emit("connection", ws, req);
+ });
+ };
+
+ return { wss, handleUpgrade };
+}
+
+module.exports = { createGatewayProxy };
diff --git a/server/index.js b/server/index.js
new file mode 100644
index 0000000..1f689de
--- /dev/null
+++ b/server/index.js
@@ -0,0 +1,130 @@
+const http = require("node:http");
+const next = require("next");
+
+const { createAccessGate } = require("./access-gate");
+const { createGatewayProxy } = require("./gateway-proxy");
+const { assertPublicHostAllowed, resolveHosts } = require("./network-policy");
+const { loadUpstreamGatewaySettings } = require("./studio-settings");
+
+const resolvePort = () => {
+ const raw = process.env.PORT?.trim() || "3000";
+ const port = Number(raw);
+ if (!Number.isFinite(port) || port <= 0) return 3000;
+ return port;
+};
+
+const resolvePathname = (url) => {
+ const raw = typeof url === "string" ? url : "";
+ const idx = raw.indexOf("?");
+ return (idx === -1 ? raw : raw.slice(0, idx)) || "/";
+};
+
+async function main() {
+ const dev = process.argv.includes("--dev");
+ const hostnames = Array.from(new Set(resolveHosts(process.env)));
+ const hostname = hostnames[0] ?? "127.0.0.1";
+ const port = resolvePort();
+ for (const host of hostnames) {
+ assertPublicHostAllowed({
+ host,
+ studioAccessToken: process.env.STUDIO_ACCESS_TOKEN,
+ });
+ }
+
+ const app = next({
+ dev,
+ hostname,
+ port,
+ ...(dev ? { webpack: true } : null),
+ });
+ const handle = app.getRequestHandler();
+
+ const accessGate = createAccessGate({
+ token: process.env.STUDIO_ACCESS_TOKEN,
+ });
+
+ const proxy = createGatewayProxy({
+ loadUpstreamSettings: async () => {
+ const settings = loadUpstreamGatewaySettings(process.env);
+ return { url: settings.url, token: settings.token };
+ },
+ allowWs: (req) => {
+ if (resolvePathname(req.url) !== "/api/gateway/ws") return false;
+ if (!accessGate.allowUpgrade(req)) return false;
+ return true;
+ },
+ });
+
+ await app.prepare();
+ const handleUpgrade = app.getUpgradeHandler();
+ const handleServerUpgrade = (req, socket, head) => {
+ if (resolvePathname(req.url) === "/api/gateway/ws") {
+ proxy.handleUpgrade(req, socket, head);
+ return;
+ }
+ handleUpgrade(req, socket, head);
+ };
+
+ const createServer = () =>
+ http.createServer((req, res) => {
+ if (accessGate.handleHttp(req, res)) return;
+ handle(req, res);
+ });
+
+ const servers = hostnames.map(() => createServer());
+
+ const attachUpgradeHandlers = (server) => {
+ server.on("upgrade", handleServerUpgrade);
+ server.on("newListener", (eventName, listener) => {
+ if (eventName !== "upgrade") return;
+ if (listener === handleServerUpgrade) return;
+ process.nextTick(() => {
+ server.removeListener("upgrade", listener);
+ });
+ });
+ };
+
+ for (const server of servers) {
+ attachUpgradeHandlers(server);
+ }
+
+ const listenOnHost = (server, host) =>
+ new Promise((resolve, reject) => {
+ const onError = (err) => {
+ server.off("error", onError);
+ reject(err);
+ };
+ server.once("error", onError);
+ server.listen(port, host, () => {
+ server.off("error", onError);
+ resolve();
+ });
+ });
+
+ const closeServer = (server) =>
+ new Promise((resolve) => {
+ if (!server.listening) return resolve();
+ server.close(() => resolve());
+ });
+
+ try {
+ await Promise.all(servers.map((server, index) => listenOnHost(server, hostnames[index])));
+ } catch (err) {
+ await Promise.all(servers.map((server) => closeServer(server)));
+ throw err;
+ }
+
+ const hostForBrowser = hostnames.some((value) => value === "127.0.0.1" || value === "::1")
+ ? "localhost"
+ : hostname === "0.0.0.0" || hostname === "::"
+ ? "localhost"
+ : hostname;
+
+ const browserUrl = `http://${hostForBrowser}:${port}`;
+ console.info(`Open in browser: ${browserUrl}`);
+}
+
+main().catch((err) => {
+ console.error(err);
+ process.exitCode = 1;
+});
diff --git a/server/network-policy.js b/server/network-policy.js
new file mode 100644
index 0000000..1bf6457
--- /dev/null
+++ b/server/network-policy.js
@@ -0,0 +1,84 @@
+const net = require("node:net");
+
+const normalizeHost = (host) => {
+ let raw = String(host ?? "").trim().toLowerCase();
+ if (!raw) return "";
+
+ if (raw.startsWith("[")) {
+ const end = raw.indexOf("]");
+ if (end !== -1) {
+ return raw.slice(1, end).trim();
+ }
+ }
+
+ const colonCount = (raw.match(/:/g) || []).length;
+ if (colonCount === 1) {
+ const idx = raw.lastIndexOf(":");
+ const maybePort = raw.slice(idx + 1);
+ if (/^\d+$/.test(maybePort)) {
+ raw = raw.slice(0, idx);
+ }
+ }
+
+ return raw;
+};
+
+const resolveHosts = (env = process.env) => {
+ const host = String(env.HOST ?? "").trim();
+ if (host) return [host];
+ return ["127.0.0.1", "::1"];
+};
+
+const resolveHost = (env = process.env) => {
+ const hosts = resolveHosts(env);
+ return hosts[0] ?? "127.0.0.1";
+};
+
+const isIpv4Loopback = (value) => value.startsWith("127.");
+
+const isIpv6Loopback = (value) => {
+ if (value === "::1" || value === "0:0:0:0:0:0:0:1") return true;
+ if (!value.startsWith("::ffff:")) return false;
+ const mapped = value.slice("::ffff:".length);
+ return net.isIP(mapped) === 4 && isIpv4Loopback(mapped);
+};
+
+const isPublicHost = (host) => {
+ const normalized = normalizeHost(host);
+ if (!normalized) return false;
+
+ if (normalized === "localhost") return false;
+ if (normalized === "0.0.0.0" || normalized === "::") {
+ return true;
+ }
+
+ const ipVersion = net.isIP(normalized);
+ if (ipVersion === 4) {
+ return !isIpv4Loopback(normalized);
+ }
+ if (ipVersion === 6) {
+ return !isIpv6Loopback(normalized);
+ }
+
+ return true;
+};
+
+const assertPublicHostAllowed = ({ host, studioAccessToken }) => {
+ if (!isPublicHost(host)) return;
+
+ const token = String(studioAccessToken ?? "").trim();
+ if (token) return;
+
+ const normalized = normalizeHost(host) || String(host ?? "").trim() || "(unknown)";
+ throw new Error(
+ `Refusing to bind Studio to public host "${normalized}" without STUDIO_ACCESS_TOKEN. ` +
+ "Set STUDIO_ACCESS_TOKEN or bind HOST to 127.0.0.1/::1/localhost."
+ );
+};
+
+module.exports = {
+ resolveHosts,
+ resolveHost,
+ isPublicHost,
+ assertPublicHostAllowed,
+};
diff --git a/server/studio-settings.js b/server/studio-settings.js
new file mode 100644
index 0000000..a36ca0c
--- /dev/null
+++ b/server/studio-settings.js
@@ -0,0 +1,124 @@
+const fs = require("node:fs");
+const os = require("node:os");
+const path = require("node:path");
+
+const LEGACY_STATE_DIRNAMES = [".clawdbot", ".moltbot"];
+const NEW_STATE_DIRNAME = ".openclaw";
+
+const resolveUserPath = (input) => {
+ const trimmed = String(input ?? "").trim();
+ if (!trimmed) return trimmed;
+ if (trimmed.startsWith("~")) {
+ const expanded = trimmed.replace(/^~(?=$|[\\/])/, os.homedir());
+ return path.resolve(expanded);
+ }
+ return path.resolve(trimmed);
+};
+
+const resolveDefaultHomeDir = () => {
+ const home = os.homedir();
+ if (home) {
+ try {
+ if (fs.existsSync(home)) return home;
+ } catch {}
+ }
+ return os.tmpdir();
+};
+
+const resolveStateDir = (env = process.env) => {
+ const override =
+ env.OPENCLAW_STATE_DIR?.trim() ||
+ env.MOLTBOT_STATE_DIR?.trim() ||
+ env.CLAWDBOT_STATE_DIR?.trim();
+ if (override) return resolveUserPath(override);
+
+ const home = resolveDefaultHomeDir();
+ const newDir = path.join(home, NEW_STATE_DIRNAME);
+ const legacyDirs = LEGACY_STATE_DIRNAMES.map((dir) => path.join(home, dir));
+ try {
+ if (fs.existsSync(newDir)) return newDir;
+ } catch {}
+ for (const dir of legacyDirs) {
+ try {
+ if (fs.existsSync(dir)) return dir;
+ } catch {}
+ }
+ return newDir;
+};
+
+const resolveStudioSettingsPath = (env = process.env) => {
+ return path.join(resolveStateDir(env), "claw3d", "settings.json");
+};
+
+const readJsonFile = (filePath) => {
+ if (!fs.existsSync(filePath)) return null;
+ const raw = fs.readFileSync(filePath, "utf8");
+ return JSON.parse(raw);
+};
+
+const DEFAULT_GATEWAY_URL = "ws://localhost:18789";
+const OPENCLAW_CONFIG_FILENAME = "openclaw.json";
+const LOOPBACK_HOSTNAMES = new Set(["localhost", "127.0.0.1", "::1", "0.0.0.0"]);
+
+const isRecord = (value) => Boolean(value && typeof value === "object");
+
+const isLocalGatewayUrl = (value) => {
+ const trimmed = typeof value === "string" ? value.trim() : "";
+ if (!trimmed) return false;
+ try {
+ const parsed = new URL(trimmed);
+ return LOOPBACK_HOSTNAMES.has(parsed.hostname.toLowerCase());
+ } catch {
+ return false;
+ }
+};
+
+const readOpenclawGatewayDefaults = (env = process.env) => {
+ try {
+ const stateDir = resolveStateDir(env);
+ const configPath = path.join(stateDir, OPENCLAW_CONFIG_FILENAME);
+ const parsed = readJsonFile(configPath);
+ if (!isRecord(parsed)) return null;
+ const gateway = isRecord(parsed.gateway) ? parsed.gateway : null;
+ if (!gateway) return null;
+ const auth = isRecord(gateway.auth) ? gateway.auth : null;
+ const token = typeof auth?.token === "string" ? auth.token.trim() : "";
+ const port =
+ typeof gateway.port === "number" && Number.isFinite(gateway.port) ? gateway.port : null;
+ if (!token) return null;
+ const url = port ? `ws://localhost:${port}` : "";
+ if (!url) return null;
+ return { url, token };
+ } catch {
+ return null;
+ }
+};
+
+const loadUpstreamGatewaySettings = (env = process.env) => {
+ const settingsPath = resolveStudioSettingsPath(env);
+ const parsed = readJsonFile(settingsPath);
+ const gateway = parsed && typeof parsed === "object" ? parsed.gateway : null;
+ const url = typeof gateway?.url === "string" ? gateway.url.trim() : "";
+ const token = typeof gateway?.token === "string" ? gateway.token.trim() : "";
+ if (!token && (!url || isLocalGatewayUrl(url))) {
+ const defaults = readOpenclawGatewayDefaults(env);
+ if (defaults) {
+ return {
+ url: url || defaults.url,
+ token: defaults.token,
+ settingsPath,
+ };
+ }
+ }
+ return {
+ url: url || DEFAULT_GATEWAY_URL,
+ token,
+ settingsPath,
+ };
+};
+
+module.exports = {
+ resolveStateDir,
+ resolveStudioSettingsPath,
+ loadUpstreamGatewaySettings,
+};
diff --git a/skills-overview.md b/skills-overview.md
new file mode 100644
index 0000000..04c10b2
--- /dev/null
+++ b/skills-overview.md
@@ -0,0 +1,277 @@
+# Skills in OpenClaw + Claw3D
+
+This document explains skills from first principles, how they work in the OpenClaw runtime, and how Claw3D currently exposes them in UX.
+
+It is intended as design context for rethinking the Skills UX.
+
+## 1) Why skills exist (first principles)
+
+Skills are the mechanism OpenClaw uses to give agents reusable operational know-how without hardcoding that know-how into core runtime logic.
+
+At a product level, a skill is:
+- A unit of capability guidance (`SKILL.md`) that teaches an agent how to perform a job.
+- A gated unit of readiness (only available when required binaries/env/config/OS are satisfied).
+- A portable package format compatible with AgentSkills (`agentskills.io`) so skill content can be authored and shared outside a single product.
+
+Without skills, every workflow instruction would need to live in prompts, app code, or ad hoc user messages. Skills create a middle layer: structured capability packs that are discoverable, filterable, and enforceable.
+
+## 2) AgentSkills.io context
+
+OpenClaw intentionally uses AgentSkills-compatible `SKILL.md` structure and semantics.
+
+Why this matters:
+- Interoperability: skills can move between ecosystems that understand AgentSkills.
+- Community/network effects: external skill ecosystems (for OpenClaw specifically, ClawHub) can be leveraged instead of reinventing proprietary formats.
+- UX consistency: users can reason about “a skill folder with `SKILL.md` + metadata gates” instead of app-specific abstractions.
+
+OpenClaw adds product-specific metadata under `metadata.openclaw` (install specs, gating fields, primary env key, etc.) while keeping the base skill shape compatible.
+
+## 3) Skill object model
+
+A skill is loaded from a directory containing `SKILL.md` with frontmatter.
+
+Minimum frontmatter:
+- `name`
+- `description`
+
+Important optional fields used by OpenClaw:
+- `metadata.openclaw.always`
+- `metadata.openclaw.skillKey`
+- `metadata.openclaw.primaryEnv`
+- `metadata.openclaw.os`
+- `metadata.openclaw.requires.{bins, anyBins, env, config}`
+- `metadata.openclaw.install[]`
+- `user-invocable`
+- `disable-model-invocation`
+- `command-dispatch`, `command-tool`, `command-arg-mode`
+
+In runtime, this becomes a normalized `SkillEntry`:
+- Raw skill (`name`, `description`, `source`, file paths)
+- Parsed frontmatter
+- Resolved OpenClaw metadata
+- Invocation policy flags
+
+## 4) Where skills come from (discovery + precedence)
+
+OpenClaw merges multiple sources into one effective skill set.
+
+Current merge precedence in code (lowest -> highest):
+1. `skills.load.extraDirs` and plugin-contributed skill dirs (`source: openclaw-extra`)
+2. Bundled skills (`openclaw-bundled`)
+3. Managed/global local skills (`~/.openclaw/skills`, `openclaw-managed`)
+4. Personal agents skills (`~/.agents/skills`, `agents-skills-personal`)
+5. Project agents skills (`/.agents/skills`, `agents-skills-project`)
+6. Workspace skills (`/skills`, `openclaw-workspace`)
+
+Name conflicts are resolved by “last writer wins” according to this order.
+
+## 5) Eligibility and gating model
+
+Eligibility is not just “is this skill installed.” It is computed every load/snapshot using:
+- Per-skill disable (`skills.entries..enabled === false`)
+- Bundled allowlist (`skills.allowBundled`) for bundled skills only
+- Runtime requirements:
+ - `requires.bins` (all required)
+ - `requires.anyBins` (at least one)
+ - `requires.env`
+ - `requires.config`
+ - `os`
+- Remote node eligibility (macOS node bin probing can satisfy certain requirements)
+- `always: true` short-circuiting requirement failures
+
+Status output carries:
+- `eligible` / `blocked`
+- structured `missing` reasons
+- `configChecks` with `{ path, satisfied }` (not secret values)
+- install options derived from metadata
+
+## 6) Agent-level filtering semantics
+
+OpenClaw has a separate per-agent skill filter via `agents.list[].skills`:
+- Missing `skills` key: all discovered skills are allowed
+- `skills: []`: no skills allowed
+- `skills: ["a", "b"]`: allowlist mode
+
+This filter is normalized and passed into snapshot generation as `skillFilter`.
+
+In practice this is the key UX distinction:
+- Discovery/readiness is global + workspace-derived.
+- “Can this specific agent use it?” is per-agent allowlist.
+
+## 7) Snapshot + prompt lifecycle
+
+Skills are snapshotted into session state (`skillsSnapshot`) to avoid re-scanning every turn.
+
+Snapshot contains:
+- prebuilt prompt block
+- lightweight skill metadata (`name`, `primaryEnv`, required env names)
+- normalized `skillFilter`
+- resolved skills list
+- version
+
+Lifecycle:
+1. First turn/new session builds snapshot.
+2. File watcher / remote-node events bump snapshot version.
+3. Later turns refresh snapshot only if version is newer.
+4. Prompt injection uses snapshot prompt when present.
+
+Watcher scope includes:
+- workspace `skills/`
+- workspace `.agents/skills`
+- `~/.openclaw/skills`
+- `~/.agents/skills`
+- configured extra dirs
+- plugin skill dirs
+
+Watcher monitors `SKILL.md` patterns (not entire trees) and debounces changes.
+
+## 8) Runtime execution behavior
+
+During an agent run:
+1. Skill env overrides are applied (`skills.entries.*.env` + `apiKey` mapping to `primaryEnv`).
+2. Overrides are sanitized/guarded (dangerous host env keys blocked).
+3. Skills prompt is injected.
+4. Environment is restored after run.
+
+Invocation behavior:
+- `disable-model-invocation: true` keeps skill out of model prompt.
+- `user-invocable: true` exposes slash commands.
+- Optional direct tool dispatch can bypass model routing.
+
+Sandbox nuance:
+- For non-`rw` sandbox workspaces, OpenClaw syncs skills into sandbox workspace (best-effort) so skill files remain accessible.
+
+## 9) Gateway API surface for skills
+
+Core RPC methods:
+- `skills.status` -> returns `SkillStatusReport` for an agent workspace.
+- `skills.install` -> installs dependencies for a skill install option.
+- `skills.update` -> updates `skills.entries.` config (`enabled`, `apiKey`, `env`).
+- `skills.bins` -> aggregates required bins across agent workspaces.
+
+Important scope behavior:
+- `skills.install` is executed against the default agent workspace (not arbitrary selected agent workspace).
+- `skills.update` writes gateway config (`openclaw.json`) and is gateway-wide state mutation.
+
+Security detail:
+- `skills.status` exposes config check satisfaction, not raw secret config values.
+
+## 10) Claw3D UX (current behavior)
+
+### 10.1 Route and navigation model
+
+Studio settings currently live on root route with a query-driven settings mode:
+- Canonical settings state is `/?settingsAgentId=`.
+- `/agents/[agentId]/settings` currently redirects to that query route.
+
+Left nav tabs in settings mode:
+- Behavior
+- Capabilities
+- Skills
+- Automations
+- Advanced
+
+### 10.2 Skills tab data and interactions
+
+When either `Skills` or `System setup` tab is active and connected, Studio:
+1. Calls `skills.status`.
+2. Reads current per-agent allowlist from gateway config (`agents.list[].skills`).
+3. Renders two distinct settings surfaces:
+
+`Skills` tab (agent-scoped):
+- Shows one list focused on “what this agent can use”.
+- Per-skill allow toggle (`Skill ` switch) for agent access only.
+- Simplified status chips (`Ready`, `Setup required`, `Not supported`).
+- Search + status filters for scanning.
+- Non-ready rows provide `Open System Setup` instead of inline setup actions.
+
+`System setup` tab (gateway-scoped):
+- Explicitly states that setup actions affect all agents.
+- Shows setup queue and full readiness details.
+- Per-skill `Configure` modal with setup/lifecycle actions:
+ - install dependencies (`skills.install`)
+ - save API key (`skills.update` with `apiKey`)
+ - global enable/disable (`skills.update` with `enabled`)
+ - remove removable skill directories via Studio remove route
+- Supports transition handoff from agent row to preselected skill setup context.
+
+### 10.3 Mutation wiring from Studio
+
+Per-agent access mutations:
+- `updateGatewayAgentSkillsAllowlist` in Studio writes `config.set` with retry-on-stale-hash behavior.
+- Agent toggles continue to rely on allowlist semantics (`undefined` means all, explicit array means selected-only).
+
+System setup mutations:
+- Install -> `skills.install`
+- API key save -> `skills.update`
+- Remove files -> Studio route `/api/gateway/skills/remove` (local fs or SSH helper)
+
+Removal has strict guards:
+- Only specific sources removable (`openclaw-managed`, `openclaw-workspace`).
+- Must stay inside allowed root.
+- Cannot remove skills root directory.
+- Must look like a real skill dir (`SKILL.md` exists).
+
+### 10.4 Scope warning shown in Studio
+
+Studio computes the default agent id and passes install-scope context into the system setup surface.
+
+Current scope copy behavior:
+- `Skills` tab copy states controls apply to the current agent.
+- `System setup` tab copy states actions apply to all agents.
+- Install target caveat (default-agent workspace behavior) is shown in system setup context and setup modal context, where install actions actually occur.
+
+This keeps scope and install-target warnings accurate while minimizing noise in the agent access flow.
+
+## 11) What recent `.agent/done` plans show
+
+Sorted by most recent creation time in `claw3d/.agent/done`, the latest items are mostly bugfix exec plans (streaming, proxy auth, stale config, cron rollback, etc.).
+
+The most recent plan with explicit skills direction is:
+- `ui-execplan-stuff.md` (2026-02-20 create time), which intentionally scoped skills as coming-soon during that IA pass.
+
+Additional files with incidental skill mentions:
+- `simplify-agent-creation-starter-kits.md`
+- `ux-zero-agent-layout-consolidation.md`
+
+Interpretation:
+- The current Studio code now has a real Skills tab and mutation flow, but the older IA/doc language still contains “coming soon” assumptions in places.
+- For redesign, trust current code behavior over older plan phrasing.
+
+## 12) UX redesign constraints that are not optional
+
+Any redesign should preserve these distinctions:
+
+1. Three separate scopes:
+- Agent allowlist scope (`agents.list[].skills`)
+- Gateway setup scope (`skills.entries.*`, installs)
+- Source/discovery scope (workspace/managed/bundled/extra/plugin)
+
+2. Eligibility vs enablement:
+- A skill can be enabled by allowlist but still blocked by missing requirements.
+- A skill can be eligible but disabled by agent allowlist.
+
+3. Session-snapshot behavior:
+- Skills changes may not appear mid-turn; they apply on next turn/snapshot refresh.
+
+4. Install target caveat:
+- Install currently targets default agent workspace context in gateway path.
+
+5. Security posture:
+- Secret values should never be exposed in status surfaces.
+- Removal must stay bounded to allowed roots and verified skill dirs.
+
+## 13) Practical mental model for reviewing a Skills screenshot
+
+If you hand a screenshot to another LLM for UX feedback, ask it to evaluate on three axes:
+
+1. **Scope clarity**
+- Can a user tell what is per-agent vs gateway-wide?
+
+2. **Readiness clarity**
+- Can a user tell blocked vs eligible and why?
+
+3. **Action safety**
+- Are destructive/setup actions clearly separated from allowlist toggles?
+
+If a design fails any of those axes, users will misconfigure skills even if controls are technically correct.
diff --git a/src/app/[...invalid]/page.tsx b/src/app/[...invalid]/page.tsx
new file mode 100644
index 0000000..d5ed64a
--- /dev/null
+++ b/src/app/[...invalid]/page.tsx
@@ -0,0 +1,5 @@
+import { redirect } from "next/navigation";
+
+export default function InvalidRoutePage() {
+ redirect("/office");
+}
diff --git a/src/app/agents/[agentId]/settings/page.tsx b/src/app/agents/[agentId]/settings/page.tsx
new file mode 100644
index 0000000..a320b15
--- /dev/null
+++ b/src/app/agents/[agentId]/settings/page.tsx
@@ -0,0 +1,10 @@
+import { redirect } from "next/navigation";
+
+export default async function AgentSettingsPage({
+ params,
+}: {
+ params: Promise<{ agentId?: string }> | { agentId?: string };
+}) {
+ await params;
+ redirect("/office");
+}
diff --git a/src/app/agents/page.tsx b/src/app/agents/page.tsx
new file mode 100644
index 0000000..0299d50
--- /dev/null
+++ b/src/app/agents/page.tsx
@@ -0,0 +1,5 @@
+import { redirect } from "next/navigation";
+
+export default function AgentsPage() {
+ redirect("/office");
+}
diff --git a/src/app/api/gateway/agent-state/route.ts b/src/app/api/gateway/agent-state/route.ts
new file mode 100644
index 0000000..d57d514
--- /dev/null
+++ b/src/app/api/gateway/agent-state/route.ts
@@ -0,0 +1,121 @@
+import { NextResponse } from "next/server";
+
+import { restoreAgentStateLocally, trashAgentStateLocally } from "@/lib/agent-state/local";
+import { isLocalGatewayUrl } from "@/lib/gateway/local-gateway";
+import {
+ resolveConfiguredSshTarget,
+ resolveGatewaySshTargetFromGatewayUrl,
+} from "@/lib/ssh/gateway-host";
+import {
+ restoreAgentStateOverSsh,
+ trashAgentStateOverSsh,
+} from "@/lib/ssh/agent-state";
+import { loadStudioSettings } from "@/lib/studio/settings-store";
+
+export const runtime = "nodejs";
+
+type TrashAgentStateRequest = {
+ agentId: string;
+};
+
+type RestoreAgentStateRequest = {
+ agentId: string;
+ trashDir: string;
+};
+
+const isSafeAgentId = (value: string) => /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}$/.test(value);
+
+const resolveAgentStateSshTarget = (): string | null => {
+ const configured = resolveConfiguredSshTarget(process.env);
+ if (configured) return configured;
+ const settings = loadStudioSettings();
+ const gatewayUrl = settings.gateway?.url ?? "";
+ if (isLocalGatewayUrl(gatewayUrl)) return null;
+ return resolveGatewaySshTargetFromGatewayUrl(gatewayUrl, process.env);
+};
+
+export async function POST(request: Request) {
+ try {
+ const body = (await request.json()) as unknown;
+ if (!body || typeof body !== "object") {
+ return NextResponse.json({ error: "Invalid request payload." }, { status: 400 });
+ }
+ const { agentId } = body as Partial;
+ const trimmed = typeof agentId === "string" ? agentId.trim() : "";
+ if (!trimmed) {
+ return NextResponse.json({ error: "agentId is required." }, { status: 400 });
+ }
+ if (!isSafeAgentId(trimmed)) {
+ return NextResponse.json({ error: `Invalid agentId: ${trimmed}` }, { status: 400 });
+ }
+
+ const sshTarget = resolveAgentStateSshTarget();
+ const result = sshTarget
+ ? trashAgentStateOverSsh({ sshTarget, agentId: trimmed })
+ : trashAgentStateLocally({ agentId: trimmed });
+ return NextResponse.json({ result });
+ } catch (err) {
+ const message =
+ err instanceof Error ? err.message : "Failed to trash agent workspace/state.";
+ console.error(message);
+ const status =
+ message.includes("Invalid request payload") ||
+ message.includes("agentId is required") ||
+ message.includes("trashDir is required") ||
+ message.includes("Invalid agentId") ||
+ message.includes("Gateway URL is missing") ||
+ message.includes("Invalid gateway URL") ||
+ message.includes("require OPENCLAW_GATEWAY_SSH_TARGET")
+ ? 400
+ : 500;
+ return NextResponse.json({ error: message }, { status });
+ }
+}
+
+export async function PUT(request: Request) {
+ try {
+ const body = (await request.json()) as unknown;
+ if (!body || typeof body !== "object") {
+ return NextResponse.json({ error: "Invalid request payload." }, { status: 400 });
+ }
+ const { agentId, trashDir } = body as Partial;
+ const trimmedAgent = typeof agentId === "string" ? agentId.trim() : "";
+ const trimmedTrash = typeof trashDir === "string" ? trashDir.trim() : "";
+ if (!trimmedAgent) {
+ return NextResponse.json({ error: "agentId is required." }, { status: 400 });
+ }
+ if (!trimmedTrash) {
+ return NextResponse.json({ error: "trashDir is required." }, { status: 400 });
+ }
+ if (!isSafeAgentId(trimmedAgent)) {
+ return NextResponse.json({ error: `Invalid agentId: ${trimmedAgent}` }, { status: 400 });
+ }
+
+ const sshTarget = resolveAgentStateSshTarget();
+ const result = sshTarget
+ ? restoreAgentStateOverSsh({
+ sshTarget,
+ agentId: trimmedAgent,
+ trashDir: trimmedTrash,
+ })
+ : restoreAgentStateLocally({
+ agentId: trimmedAgent,
+ trashDir: trimmedTrash,
+ });
+ return NextResponse.json({ result });
+ } catch (err) {
+ const message = err instanceof Error ? err.message : "Failed to restore agent state.";
+ console.error(message);
+ const status =
+ message.includes("Invalid request payload") ||
+ message.includes("agentId is required") ||
+ message.includes("trashDir is required") ||
+ message.includes("Invalid agentId") ||
+ message.includes("Gateway URL is missing") ||
+ message.includes("Invalid gateway URL") ||
+ message.includes("require OPENCLAW_GATEWAY_SSH_TARGET")
+ ? 400
+ : 500;
+ return NextResponse.json({ error: message }, { status });
+ }
+}
diff --git a/src/app/api/gateway/media/route.ts b/src/app/api/gateway/media/route.ts
new file mode 100644
index 0000000..bd48e1c
--- /dev/null
+++ b/src/app/api/gateway/media/route.ts
@@ -0,0 +1,216 @@
+import { NextResponse } from "next/server";
+
+import { isLocalGatewayUrl } from "@/lib/gateway/local-gateway";
+import {
+ resolveConfiguredSshTarget,
+ resolveGatewaySshTargetFromGatewayUrl,
+ runSshJson,
+} from "@/lib/ssh/gateway-host";
+import { loadStudioSettings } from "@/lib/studio/settings-store";
+import * as fs from "node:fs/promises";
+import * as os from "node:os";
+import * as path from "node:path";
+
+export const runtime = "nodejs";
+
+const MAX_MEDIA_BYTES = 25 * 1024 * 1024;
+
+const MIME_BY_EXT: Record = {
+ ".png": "image/png",
+ ".jpg": "image/jpeg",
+ ".jpeg": "image/jpeg",
+ ".gif": "image/gif",
+ ".webp": "image/webp",
+};
+
+const expandTildeLocal = (value: string): string => {
+ const trimmed = value.trim();
+ if (trimmed === "~") return os.homedir();
+ if (trimmed.startsWith("~/")) return path.join(os.homedir(), trimmed.slice(2));
+ return trimmed;
+};
+
+const validateRawMediaPath = (raw: string): { trimmed: string; mime: string } => {
+ const trimmed = raw.trim();
+ if (!trimmed) throw new Error("path is required");
+ if (trimmed.length > 4096) throw new Error("path too long");
+ if (/[^\S\r\n]*[\0\r\n]/.test(trimmed)) throw new Error("path contains invalid characters");
+
+ const ext = path.extname(trimmed).toLowerCase();
+ const mime = MIME_BY_EXT[ext];
+ if (!mime) throw new Error(`Unsupported media extension: ${ext || "(none)"}`);
+
+ return { trimmed, mime };
+};
+
+const resolveAndValidateLocalMediaPath = (raw: string): { resolved: string; mime: string } => {
+ const { trimmed, mime } = validateRawMediaPath(raw);
+
+ const expanded = expandTildeLocal(trimmed);
+ if (!path.isAbsolute(expanded)) {
+ throw new Error("path must be absolute or start with ~/");
+ }
+
+ const resolved = path.resolve(expanded);
+
+ const allowedRoot = path.join(os.homedir(), ".openclaw");
+ const allowedPrefix = `${allowedRoot}${path.sep}`;
+ if (!(resolved === allowedRoot || resolved.startsWith(allowedPrefix))) {
+ throw new Error(`Refusing to read media outside ${allowedRoot}`);
+ }
+
+ return { resolved, mime };
+};
+
+const validateRemoteMediaPath = (raw: string): { remotePath: string; mime: string } => {
+ const { trimmed, mime } = validateRawMediaPath(raw);
+
+ if (!(trimmed.startsWith("/") || trimmed === "~" || trimmed.startsWith("~/"))) {
+ throw new Error("path must be absolute or start with ~/");
+ }
+
+ // Remote side enforces ~/.openclaw; this guard lets Studio on macOS request
+ // /home/ubuntu/.openclaw/... without tripping local homedir checks.
+ const normalized = trimmed.replaceAll("\\\\", "/");
+ const inOpenclaw =
+ normalized === "~/.openclaw" ||
+ normalized.startsWith("~/.openclaw/") ||
+ normalized.includes("/.openclaw/");
+ if (!inOpenclaw) {
+ throw new Error("Refusing to read remote media outside ~/.openclaw");
+ }
+
+ return { remotePath: trimmed, mime };
+};
+
+const readLocalMedia = async (resolvedPath: string): Promise<{ bytes: Buffer; size: number }> => {
+ const stat = await fs.stat(resolvedPath);
+ if (!stat.isFile()) {
+ throw new Error("path is not a file");
+ }
+ if (stat.size > MAX_MEDIA_BYTES) {
+ throw new Error(`media file too large (${stat.size} bytes)`);
+ }
+ const buf = await fs.readFile(resolvedPath);
+ return { bytes: buf, size: stat.size };
+};
+
+const REMOTE_READ_SCRIPT = `
+set -euo pipefail
+
+python3 - "$1" <<'PY'
+import base64
+import json
+import mimetypes
+import os
+import pathlib
+import sys
+
+raw = sys.argv[1].strip()
+if not raw:
+ print(json.dumps({"error": "path is required"}))
+ raise SystemExit(2)
+
+p = pathlib.Path(os.path.expanduser(raw))
+try:
+ resolved = p.resolve(strict=True)
+except FileNotFoundError:
+ print(json.dumps({"error": f"file not found: {raw}"}))
+ raise SystemExit(3)
+
+home = pathlib.Path.home().resolve()
+allowed = (home / ".openclaw").resolve()
+if resolved != allowed and allowed not in resolved.parents:
+ print(json.dumps({"error": f"Refusing to read media outside {allowed}"}))
+ raise SystemExit(4)
+
+ext = resolved.suffix.lower()
+mime = {
+ ".png": "image/png",
+ ".jpg": "image/jpeg",
+ ".jpeg": "image/jpeg",
+ ".gif": "image/gif",
+ ".webp": "image/webp",
+}.get(ext) or (mimetypes.guess_type(str(resolved))[0] or "")
+
+if not mime.startswith("image/"):
+ print(json.dumps({"error": f"Unsupported media extension: {ext or '(none)'}"}))
+ raise SystemExit(5)
+
+size = resolved.stat().st_size
+max_bytes = ${MAX_MEDIA_BYTES}
+if size > max_bytes:
+ print(json.dumps({"error": f"media file too large ({size} bytes)"}))
+ raise SystemExit(6)
+
+data = base64.b64encode(resolved.read_bytes()).decode("ascii")
+print(json.dumps({"ok": True, "mime": mime, "size": size, "data": data}))
+PY
+`;
+
+const resolveSshTarget = (): string | null => {
+ const settings = loadStudioSettings();
+ const gatewayUrl = settings.gateway?.url ?? "";
+ if (isLocalGatewayUrl(gatewayUrl)) return null;
+ const configured = resolveConfiguredSshTarget(process.env);
+ if (configured) return configured;
+ return resolveGatewaySshTargetFromGatewayUrl(gatewayUrl, process.env);
+};
+
+export async function GET(request: Request) {
+ try {
+ const { searchParams } = new URL(request.url);
+ const rawPath = (searchParams.get("path") ?? "").trim();
+
+ const sshTarget = resolveSshTarget();
+
+ if (!sshTarget) {
+ const { resolved, mime } = resolveAndValidateLocalMediaPath(rawPath);
+ const { bytes, size } = await readLocalMedia(resolved);
+ const body = new Blob([Uint8Array.from(bytes)], { type: mime });
+ return new Response(body, {
+ headers: {
+ "Content-Type": mime,
+ "Content-Length": String(size),
+ "Cache-Control": "no-store",
+ },
+ });
+ }
+
+ const { remotePath, mime } = validateRemoteMediaPath(rawPath);
+
+ const payload = runSshJson({
+ sshTarget,
+ argv: ["bash", "-s", "--", remotePath],
+ label: "gateway media read",
+ input: REMOTE_READ_SCRIPT,
+ fallbackMessage: `Failed to fetch media over ssh (${sshTarget})`,
+ maxBuffer: Math.ceil(MAX_MEDIA_BYTES * 1.6),
+ }) as {
+ ok?: boolean;
+ data?: string;
+ mime?: string;
+ size?: number;
+ };
+
+ const b64 = payload.data ?? "";
+ if (!b64) {
+ throw new Error("Remote media fetch returned empty data");
+ }
+
+ const buf = Buffer.from(b64, "base64");
+ const responseMime = payload.mime || mime;
+ const body = new Blob([Uint8Array.from(buf)], { type: responseMime });
+
+ return new Response(body, {
+ headers: {
+ "Content-Type": responseMime,
+ "Content-Length": String(buf.length),
+ "Cache-Control": "no-store",
+ },
+ });
+ } catch (err) {
+ const message = err instanceof Error ? err.message : "Failed to fetch media";
+ return NextResponse.json({ error: message }, { status: 400 });
+ }
+}
diff --git a/src/app/api/gateway/skills/remove/route.ts b/src/app/api/gateway/skills/remove/route.ts
new file mode 100644
index 0000000..0fe047c
--- /dev/null
+++ b/src/app/api/gateway/skills/remove/route.ts
@@ -0,0 +1,89 @@
+import { NextResponse } from "next/server";
+
+import { isLocalGatewayUrl } from "@/lib/gateway/local-gateway";
+import { removeSkillLocally } from "@/lib/skills/remove-local";
+import type { RemovableSkillSource, SkillRemoveRequest } from "@/lib/skills/types";
+import {
+ resolveConfiguredSshTarget,
+ resolveGatewaySshTargetFromGatewayUrl,
+} from "@/lib/ssh/gateway-host";
+import { removeSkillOverSsh } from "@/lib/ssh/skills-remove";
+import { loadStudioSettings } from "@/lib/studio/settings-store";
+
+export const runtime = "nodejs";
+
+const REMOVABLE_SOURCES = new Set([
+ "openclaw-managed",
+ "openclaw-workspace",
+]);
+
+const normalizeRequired = (value: unknown, field: string): string => {
+ if (typeof value !== "string") {
+ throw new Error(`${field} is required.`);
+ }
+ const trimmed = value.trim();
+ if (!trimmed) {
+ throw new Error(`${field} is required.`);
+ }
+ return trimmed;
+};
+
+const resolveSkillRemovalSshTarget = (): string | null => {
+ const configured = resolveConfiguredSshTarget(process.env);
+ if (configured) return configured;
+ const settings = loadStudioSettings();
+ const gatewayUrl = settings.gateway?.url ?? "";
+ if (isLocalGatewayUrl(gatewayUrl)) return null;
+ return resolveGatewaySshTargetFromGatewayUrl(gatewayUrl, process.env);
+};
+
+const normalizeRemoveRequest = (body: unknown): SkillRemoveRequest => {
+ if (!body || typeof body !== "object") {
+ throw new Error("Invalid request payload.");
+ }
+
+ const record = body as Partial>;
+ const sourceRaw = normalizeRequired(record.source, "source");
+ if (!REMOVABLE_SOURCES.has(sourceRaw as RemovableSkillSource)) {
+ throw new Error(`Unsupported skill source for removal: ${sourceRaw}`);
+ }
+
+ return {
+ skillKey: normalizeRequired(record.skillKey, "skillKey"),
+ source: sourceRaw as RemovableSkillSource,
+ baseDir: normalizeRequired(record.baseDir, "baseDir"),
+ workspaceDir: normalizeRequired(record.workspaceDir, "workspaceDir"),
+ managedSkillsDir: normalizeRequired(record.managedSkillsDir, "managedSkillsDir"),
+ };
+};
+
+export async function POST(request: Request) {
+ try {
+ const body = (await request.json()) as unknown;
+ const removeRequest = normalizeRemoveRequest(body);
+
+ const sshTarget = resolveSkillRemovalSshTarget();
+ const result = sshTarget
+ ? removeSkillOverSsh({ sshTarget, request: removeRequest })
+ : removeSkillLocally(removeRequest);
+
+ return NextResponse.json({ result });
+ } catch (err) {
+ const message = err instanceof Error ? err.message : "Failed to remove skill.";
+ const status =
+ message.includes("required") ||
+ message.includes("Invalid request payload") ||
+ message.includes("Unsupported skill source") ||
+ message.includes("Refusing to remove") ||
+ message.includes("not a directory") ||
+ message.includes("Gateway URL is missing") ||
+ message.includes("Invalid gateway URL") ||
+ message.includes("require OPENCLAW_GATEWAY_SSH_TARGET")
+ ? 400
+ : 500;
+ if (status >= 500) {
+ console.error(message);
+ }
+ return NextResponse.json({ error: message }, { status });
+ }
+}
diff --git a/src/app/api/office/browser-preview/route.ts b/src/app/api/office/browser-preview/route.ts
new file mode 100644
index 0000000..0a3de41
--- /dev/null
+++ b/src/app/api/office/browser-preview/route.ts
@@ -0,0 +1,194 @@
+import { NextResponse } from "next/server";
+
+import {
+ normalizeBrowserPreviewUrl,
+ resolveBrowserControlBaseUrl,
+} from "@/lib/office/browserPreview";
+import { validateBrowserPreviewTarget } from "@/lib/security/urlSafety";
+import { loadStudioSettings } from "@/lib/studio/settings-store";
+
+export const runtime = "nodejs";
+
+type BrowserTab = {
+ targetId: string;
+ url: string;
+};
+
+type BrowserTabsResponse = {
+ running?: boolean;
+ tabs?: BrowserTab[];
+};
+
+type BrowserOpenResponse = {
+ targetId?: string;
+ url?: string;
+};
+
+type BrowserScreenshotResponse = {
+ ok?: boolean;
+ path?: string;
+ targetId?: string;
+ url?: string;
+};
+
+const DEFAULT_CAPTURE_WAIT_MS = 1_500;
+
+const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
+
+const buildBrowserHeaders = (token: string | null): HeadersInit => {
+ if (!token) return { "Content-Type": "application/json" };
+ return {
+ Authorization: `Bearer ${token}`,
+ "Content-Type": "application/json",
+ };
+};
+
+const parseBrowserError = async (response: Response): Promise => {
+ try {
+ const payload = (await response.json()) as { error?: string; message?: string };
+ return payload.error?.trim() || payload.message?.trim() || response.statusText || "Browser request failed";
+ } catch {
+ return response.statusText || "Browser request failed";
+ }
+};
+
+const browserRequest = async (
+ baseUrl: string,
+ pathname: string,
+ init: RequestInit,
+ token: string | null,
+): Promise => {
+ const response = await fetch(`${baseUrl}${pathname}`, {
+ ...init,
+ headers: {
+ ...buildBrowserHeaders(token),
+ ...(init.headers ?? {}),
+ },
+ cache: "no-store",
+ });
+ if (!response.ok) {
+ throw new Error(await parseBrowserError(response));
+ }
+ return (await response.json()) as T;
+};
+
+const samePreviewTarget = (left: string, right: string): boolean => {
+ return normalizeBrowserPreviewUrl(left) === normalizeBrowserPreviewUrl(right);
+};
+
+const ensurePreviewTab = async (
+ baseUrl: string,
+ token: string | null,
+ browserUrl: string,
+): Promise<{ targetId: string; resolvedUrl: string }> => {
+ const tabsPayload = await browserRequest(baseUrl, "/tabs", { method: "GET" }, token);
+
+ if (tabsPayload.running === false) {
+ await browserRequest(baseUrl, "/start", { method: "POST" }, token);
+ await sleep(400);
+ }
+
+ const tabs = Array.isArray(tabsPayload.tabs) ? tabsPayload.tabs : [];
+ const exactMatch = tabs.find((tab) => samePreviewTarget(tab.url, browserUrl));
+ if (exactMatch?.targetId) {
+ await browserRequest(baseUrl, "/tabs/focus", {
+ method: "POST",
+ body: JSON.stringify({ targetId: exactMatch.targetId }),
+ }, token);
+ return { targetId: exactMatch.targetId, resolvedUrl: exactMatch.url || browserUrl };
+ }
+
+ const reusableTab = tabs[0];
+ if (reusableTab?.targetId) {
+ await browserRequest(baseUrl, "/tabs/focus", {
+ method: "POST",
+ body: JSON.stringify({ targetId: reusableTab.targetId }),
+ }, token);
+ await browserRequest(baseUrl, "/navigate", {
+ method: "POST",
+ body: JSON.stringify({ targetId: reusableTab.targetId, url: browserUrl }),
+ }, token);
+ return { targetId: reusableTab.targetId, resolvedUrl: browserUrl };
+ }
+
+ const opened = await browserRequest(baseUrl, "/tabs/open", {
+ method: "POST",
+ body: JSON.stringify({ url: browserUrl }),
+ }, token);
+ if (!opened.targetId) {
+ throw new Error("Browser preview did not return a target tab.");
+ }
+ return { targetId: opened.targetId, resolvedUrl: opened.url || browserUrl };
+};
+
+export async function GET(request: Request) {
+ try {
+ const { searchParams } = new URL(request.url);
+ const rawUrl = (searchParams.get("url") ?? "").trim();
+ if (!rawUrl) {
+ return NextResponse.json({ error: "url is required" }, { status: 400 });
+ }
+
+ let browserUrl: string;
+ try {
+ browserUrl = validateBrowserPreviewTarget(rawUrl).toString();
+ } catch {
+ return NextResponse.json(
+ { error: "url must be an absolute public http(s) URL" },
+ { status: 400 }
+ );
+ }
+
+ const waitMsRaw = Number(searchParams.get("waitMs") ?? "");
+ const waitMs =
+ Number.isFinite(waitMsRaw) && waitMsRaw >= 0
+ ? Math.min(Math.round(waitMsRaw), 8_000)
+ : DEFAULT_CAPTURE_WAIT_MS;
+
+ const settings = loadStudioSettings();
+ const gatewayUrl = settings.gateway?.url?.trim() ?? "";
+ const controlBaseUrl = resolveBrowserControlBaseUrl(gatewayUrl);
+ if (!controlBaseUrl) {
+ return NextResponse.json(
+ { error: "Browser screenshot preview only works when Studio is connected to a local gateway." },
+ { status: 501 },
+ );
+ }
+
+ const token = settings.gateway?.token?.trim() || null;
+ const { targetId, resolvedUrl } = await ensurePreviewTab(controlBaseUrl, token, browserUrl);
+
+ if (waitMs > 0) {
+ await sleep(waitMs);
+ }
+
+ const screenshot = await browserRequest(controlBaseUrl, "/screenshot", {
+ method: "POST",
+ body: JSON.stringify({ targetId, type: "png" }),
+ }, token);
+
+ if (!screenshot.path?.trim()) {
+ throw new Error("Browser screenshot did not return a media path.");
+ }
+
+ const mediaUrl = `/api/gateway/media?path=${encodeURIComponent(screenshot.path)}`;
+ return NextResponse.json(
+ {
+ ok: true,
+ browserUrl: screenshot.url || resolvedUrl,
+ imagePath: screenshot.path,
+ mediaUrl,
+ targetId: screenshot.targetId || targetId,
+ capturedAt: Date.now(),
+ },
+ {
+ headers: {
+ "Cache-Control": "no-store",
+ },
+ },
+ );
+ } catch (err) {
+ const message = err instanceof Error ? err.message : "Failed to build browser preview";
+ return NextResponse.json({ error: message }, { status: 400 });
+ }
+}
diff --git a/src/app/api/office/call/route.ts b/src/app/api/office/call/route.ts
new file mode 100644
index 0000000..09a0dd6
--- /dev/null
+++ b/src/app/api/office/call/route.ts
@@ -0,0 +1,56 @@
+import { NextResponse } from "next/server";
+
+import { buildMockPhoneCallScenario } from "@/lib/office/call/mock";
+
+export const runtime = "nodejs";
+
+type PhoneCallRequestBody = {
+ callee?: string;
+ message?: string | null;
+};
+
+const MAX_CALLEE_CHARS = 120;
+const MAX_MESSAGE_CHARS = 1_000;
+
+const normalizeText = (value: string | null | undefined): string =>
+ (value ?? "").replace(/\s+/g, " ").trim();
+
+export async function POST(request: Request) {
+ try {
+ const body = (await request.json()) as PhoneCallRequestBody;
+ const callee = normalizeText(body.callee);
+ const message = normalizeText(body.message);
+
+ if (!callee) {
+ return NextResponse.json({ error: "callee is required." }, { status: 400 });
+ }
+ if (callee.length > MAX_CALLEE_CHARS) {
+ return NextResponse.json(
+ { error: `callee exceeds ${MAX_CALLEE_CHARS} characters.` },
+ { status: 400 },
+ );
+ }
+ if (message.length > MAX_MESSAGE_CHARS) {
+ return NextResponse.json(
+ { error: `message exceeds ${MAX_MESSAGE_CHARS} characters.` },
+ { status: 400 },
+ );
+ }
+
+ // TODO: Create Claw3D voice and text skill.
+ const scenario = buildMockPhoneCallScenario({
+ callee,
+ message: message || null,
+ voiceAvailable: Boolean(process.env.ELEVENLABS_API_KEY?.trim()),
+ });
+
+ return NextResponse.json(
+ { scenario },
+ { headers: { "Cache-Control": "no-store" } },
+ );
+ } catch (error) {
+ const message =
+ error instanceof Error ? error.message : "Failed to prepare the mock phone call.";
+ return NextResponse.json({ error: message }, { status: 500 });
+ }
+}
diff --git a/src/app/api/office/github/route.ts b/src/app/api/office/github/route.ts
new file mode 100644
index 0000000..f25ff2e
--- /dev/null
+++ b/src/app/api/office/github/route.ts
@@ -0,0 +1,120 @@
+import { NextResponse } from "next/server";
+
+import {
+ loadGitHubDashboard,
+ loadGitHubPullRequestDetail,
+ submitGitHubInlineComment,
+ submitGitHubPullRequestReview,
+ type GitHubInlineCommentSide,
+ type GitHubReviewAction,
+} from "@/lib/office/github";
+
+export const runtime = "nodejs";
+
+type ReviewRequestBody = {
+ repo?: string;
+ number?: number;
+ action?: GitHubReviewAction;
+ body?: string | null;
+ path?: string;
+ line?: number;
+ side?: GitHubInlineCommentSide;
+ commitId?: string | null;
+};
+
+const parsePullRequestNumber = (value: string | null): number | null => {
+ if (!value) return null;
+ const parsed = Number(value);
+ if (!Number.isFinite(parsed) || parsed <= 0) return null;
+ return Math.round(parsed);
+};
+
+export async function GET(request: Request) {
+ try {
+ const { searchParams } = new URL(request.url);
+ const repo = (searchParams.get("repo") ?? "").trim();
+ const number = parsePullRequestNumber(searchParams.get("number"));
+ if (repo && number) {
+ return NextResponse.json(loadGitHubPullRequestDetail({ repo, number }), {
+ headers: { "Cache-Control": "no-store" },
+ });
+ }
+
+ return NextResponse.json(loadGitHubDashboard(), {
+ headers: { "Cache-Control": "no-store" },
+ });
+ } catch (error) {
+ const message =
+ error instanceof Error ? error.message : "Failed to load GitHub server room data.";
+ return NextResponse.json({ error: message }, { status: 400 });
+ }
+}
+
+export async function POST(request: Request) {
+ try {
+ const body = (await request.json()) as ReviewRequestBody;
+ const repo = typeof body.repo === "string" ? body.repo.trim() : "";
+ const number =
+ typeof body.number === "number" && Number.isFinite(body.number)
+ ? Math.round(body.number)
+ : null;
+ const action = typeof body.action === "string" ? body.action : null;
+ const path = typeof body.path === "string" ? body.path.trim() : "";
+ const line =
+ typeof body.line === "number" && Number.isFinite(body.line)
+ ? Math.round(body.line)
+ : null;
+ const side = body.side === "LEFT" || body.side === "RIGHT" ? body.side : null;
+ const commentBody = typeof body.body === "string" ? body.body : null;
+
+ if (action) {
+ if (!repo || !number) {
+ return NextResponse.json(
+ { error: "repo, number, and action are required." },
+ { status: 400 },
+ );
+ }
+
+ if (!["APPROVE", "COMMENT", "REQUEST_CHANGES"].includes(action)) {
+ return NextResponse.json(
+ { error: "Unsupported review action." },
+ { status: 400 },
+ );
+ }
+
+ const result = submitGitHubPullRequestReview({
+ repo,
+ number,
+ action,
+ body: commentBody,
+ });
+ return NextResponse.json(result, {
+ headers: { "Cache-Control": "no-store" },
+ });
+ }
+
+ if (!repo || !number || !path || !line || !side || !commentBody?.trim()) {
+ return NextResponse.json(
+ { error: "repo, number, path, line, side, and body are required." },
+ { status: 400 },
+ );
+ }
+
+ const result = submitGitHubInlineComment({
+ repo,
+ number,
+ path,
+ line,
+ side,
+ body: commentBody,
+ commitId: typeof body.commitId === "string" ? body.commitId : null,
+ });
+ return NextResponse.json(result, {
+ headers: { "Cache-Control": "no-store" },
+ });
+ } catch (error) {
+ const message =
+ error instanceof Error ? error.message : "Failed to submit GitHub review.";
+ return NextResponse.json({ error: message }, { status: 400 });
+ }
+}
diff --git a/src/app/api/office/presence/route.ts b/src/app/api/office/presence/route.ts
new file mode 100644
index 0000000..28250bd
--- /dev/null
+++ b/src/app/api/office/presence/route.ts
@@ -0,0 +1,17 @@
+import { NextResponse } from "next/server";
+
+import { loadOfficePresenceSnapshot } from "@/lib/office/presence";
+
+export const runtime = "nodejs";
+
+export async function GET(request: Request) {
+ try {
+ const url = new URL(request.url);
+ const workspaceId = url.searchParams.get("workspaceId")?.trim() || "default";
+ const snapshot = loadOfficePresenceSnapshot(workspaceId);
+ return NextResponse.json(snapshot);
+ } catch (error) {
+ const message = error instanceof Error ? error.message : "Failed to load office presence.";
+ return NextResponse.json({ error: message }, { status: 500 });
+ }
+}
diff --git a/src/app/api/office/publish/route.ts b/src/app/api/office/publish/route.ts
new file mode 100644
index 0000000..0dcb95c
--- /dev/null
+++ b/src/app/api/office/publish/route.ts
@@ -0,0 +1,38 @@
+import { NextResponse } from "next/server";
+
+import { listOfficeVersions, publishOfficeVersion } from "@/lib/office/store";
+
+export const runtime = "nodejs";
+
+const asString = (value: unknown) => (typeof value === "string" ? value.trim() : "");
+
+export async function PUT(request: Request) {
+ try {
+ const body = (await request.json()) as Record;
+ const workspaceId = asString(body.workspaceId) || "default";
+ const officeId = asString(body.officeId);
+ const officeVersionId = asString(body.officeVersionId);
+ const publishedBy = asString(body.publishedBy) || "studio";
+ if (!workspaceId || !officeId) {
+ return NextResponse.json({ error: "workspaceId and officeId are required." }, { status: 400 });
+ }
+ let selectedVersionId = officeVersionId;
+ if (!selectedVersionId) {
+ const versions = listOfficeVersions(workspaceId, officeId);
+ selectedVersionId = versions[0]?.id ?? "";
+ }
+ if (!selectedVersionId) {
+ return NextResponse.json({ error: "No office version available to publish." }, { status: 400 });
+ }
+ const published = publishOfficeVersion({
+ workspaceId,
+ officeId,
+ officeVersionId: selectedVersionId,
+ publishedBy,
+ });
+ return NextResponse.json({ published });
+ } catch (error) {
+ const message = error instanceof Error ? error.message : "Failed to publish office version.";
+ return NextResponse.json({ error: message }, { status: 500 });
+ }
+}
diff --git a/src/app/api/office/route.ts b/src/app/api/office/route.ts
new file mode 100644
index 0000000..5f8108c
--- /dev/null
+++ b/src/app/api/office/route.ts
@@ -0,0 +1,88 @@
+import { NextResponse } from "next/server";
+
+import { createEmptyOfficeMap, normalizeOfficeMap, type OfficeMap } from "@/lib/office/schema";
+import {
+ getPublishedOffice,
+ getPublishedOfficeMap,
+ listOfficesForWorkspace,
+ listOfficeVersions,
+ saveOfficeVersion,
+ upsertOffice,
+} from "@/lib/office/store";
+
+export const runtime = "nodejs";
+
+const asString = (value: unknown) => (typeof value === "string" ? value.trim() : "");
+
+export async function GET(request: Request) {
+ try {
+ const url = new URL(request.url);
+ const workspaceId = asString(url.searchParams.get("workspaceId")) || "default";
+ const officeId = asString(url.searchParams.get("officeId"));
+ const offices = listOfficesForWorkspace(workspaceId);
+ const published = getPublishedOffice(workspaceId);
+ const publishedMap = getPublishedOfficeMap(workspaceId);
+ const versions = officeId ? listOfficeVersions(workspaceId, officeId) : [];
+ return NextResponse.json({
+ workspaceId,
+ offices,
+ versions,
+ published,
+ publishedMap,
+ });
+ } catch (error) {
+ const message = error instanceof Error ? error.message : "Failed to load office data.";
+ return NextResponse.json({ error: message }, { status: 500 });
+ }
+}
+
+export async function PUT(request: Request) {
+ try {
+ const body = (await request.json()) as Record;
+ const action = asString(body.action);
+ if (action === "upsertOffice") {
+ const workspaceId = asString(body.workspaceId) || "default";
+ const officeId = asString(body.officeId);
+ const name = asString(body.name);
+ if (!officeId || !name) {
+ return NextResponse.json({ error: "officeId and name are required." }, { status: 400 });
+ }
+ const office = upsertOffice({
+ workspaceId,
+ officeId,
+ name,
+ });
+ return NextResponse.json({ office });
+ }
+ if (action === "saveVersion") {
+ const workspaceId = asString(body.workspaceId) || "default";
+ const officeId = asString(body.officeId);
+ const versionId = asString(body.versionId);
+ const createdBy = asString(body.createdBy) || "studio";
+ if (!officeId || !versionId) {
+ return NextResponse.json({ error: "officeId and versionId are required." }, { status: 400 });
+ }
+ const incomingMap = body.map as OfficeMap | undefined;
+ const fallback = createEmptyOfficeMap({
+ workspaceId,
+ officeVersionId: versionId,
+ width: 1600,
+ height: 900,
+ });
+ const map = normalizeOfficeMap(incomingMap, fallback);
+ const record = saveOfficeVersion({
+ workspaceId,
+ officeId,
+ versionId,
+ createdBy,
+ notes: asString(body.notes),
+ map,
+ });
+ return NextResponse.json({ version: record });
+ }
+ return NextResponse.json({ error: "Unsupported office action." }, { status: 400 });
+ } catch (error) {
+ const message = error instanceof Error ? error.message : "Failed to save office data.";
+ return NextResponse.json({ error: message }, { status: 500 });
+ }
+}
diff --git a/src/app/api/office/standup/config/route.ts b/src/app/api/office/standup/config/route.ts
new file mode 100644
index 0000000..4474135
--- /dev/null
+++ b/src/app/api/office/standup/config/route.ts
@@ -0,0 +1,79 @@
+import { NextResponse } from "next/server";
+
+import {
+ applyStudioSettingsPatch,
+ loadStudioSettings,
+} from "@/lib/studio/settings-store";
+import {
+ resolveStandupPreference,
+ sanitizeStandupPreference,
+ type StudioStandupPreferencePatch,
+} from "@/lib/studio/settings";
+import { validateJiraBaseUrl } from "@/lib/security/urlSafety";
+
+export const runtime = "nodejs";
+
+const readGatewayUrl = (request: Request) => {
+ const url = new URL(request.url);
+ return (url.searchParams.get("gatewayUrl") ?? "").trim();
+};
+
+export async function GET(request: Request) {
+ try {
+ const gatewayUrl = readGatewayUrl(request);
+ if (!gatewayUrl) {
+ return NextResponse.json(
+ { error: "gatewayUrl is required." },
+ { status: 400 }
+ );
+ }
+ const settings = loadStudioSettings();
+ const config = resolveStandupPreference(settings, gatewayUrl);
+ return NextResponse.json({ gatewayUrl, config: sanitizeStandupPreference(config) });
+ } catch (error) {
+ const message =
+ error instanceof Error ? error.message : "Failed to load standup config.";
+ return NextResponse.json({ error: message }, { status: 500 });
+ }
+}
+
+export async function PUT(request: Request) {
+ try {
+ const body = (await request.json()) as {
+ gatewayUrl?: string;
+ config?: StudioStandupPreferencePatch;
+ };
+ const gatewayUrl = typeof body.gatewayUrl === "string" ? body.gatewayUrl.trim() : "";
+ if (!gatewayUrl) {
+ return NextResponse.json(
+ { error: "gatewayUrl is required." },
+ { status: 400 }
+ );
+ }
+ if (!body.config || typeof body.config !== "object") {
+ return NextResponse.json({ error: "config is required." }, { status: 400 });
+ }
+ if (body.config.jira?.baseUrl?.trim()) {
+ validateJiraBaseUrl(body.config.jira.baseUrl);
+ }
+ const settings = applyStudioSettingsPatch({
+ standup: {
+ [gatewayUrl]: body.config,
+ },
+ });
+ return NextResponse.json({
+ gatewayUrl,
+ config: sanitizeStandupPreference(resolveStandupPreference(settings, gatewayUrl)),
+ });
+ } catch (error) {
+ const message =
+ error instanceof Error ? error.message : "Failed to save standup config.";
+ const status =
+ message.includes("gatewayUrl is required") ||
+ message.includes("config is required") ||
+ message.includes("Jira base URL")
+ ? 400
+ : 500;
+ return NextResponse.json({ error: message }, { status });
+ }
+}
diff --git a/src/app/api/office/standup/meeting/route.ts b/src/app/api/office/standup/meeting/route.ts
new file mode 100644
index 0000000..30e61e9
--- /dev/null
+++ b/src/app/api/office/standup/meeting/route.ts
@@ -0,0 +1,66 @@
+import { NextResponse } from "next/server";
+
+import {
+ advanceStandupMeeting,
+ startStandupSpeaker,
+ updateStandupArrivals,
+} from "@/lib/office/standup/service";
+import {
+ loadActiveStandupMeeting,
+ updateStandupMeeting,
+} from "@/lib/office/standup/store";
+
+export const runtime = "nodejs";
+
+export async function GET() {
+ try {
+ return NextResponse.json(
+ { meeting: loadActiveStandupMeeting() },
+ { headers: { "Cache-Control": "no-store" } }
+ );
+ } catch (error) {
+ const message =
+ error instanceof Error ? error.message : "Failed to load standup meeting.";
+ return NextResponse.json({ error: message }, { status: 500 });
+ }
+}
+
+export async function PUT(request: Request) {
+ try {
+ const body = (await request.json()) as {
+ action?: "arrivals" | "start" | "advance" | "complete";
+ arrivedAgentIds?: string[];
+ speakerAgentId?: string | null;
+ };
+ const action = typeof body.action === "string" ? body.action : "";
+ if (!action) {
+ return NextResponse.json({ error: "action is required." }, { status: 400 });
+ }
+ const store = updateStandupMeeting((meeting) => {
+ if (!meeting) return null;
+ if (action === "arrivals") {
+ return updateStandupArrivals(meeting, body.arrivedAgentIds ?? []);
+ }
+ if (action === "start") {
+ const speakerAgentId =
+ typeof body.speakerAgentId === "string" ? body.speakerAgentId.trim() : null;
+ return startStandupSpeaker(meeting, speakerAgentId);
+ }
+ if (action === "advance") {
+ return advanceStandupMeeting(meeting);
+ }
+ if (action === "complete") {
+ return startStandupSpeaker(meeting, null);
+ }
+ return meeting;
+ });
+ return NextResponse.json(
+ { meeting: store.activeMeeting },
+ { headers: { "Cache-Control": "no-store" } }
+ );
+ } catch (error) {
+ const message =
+ error instanceof Error ? error.message : "Failed to update standup meeting.";
+ return NextResponse.json({ error: message }, { status: 500 });
+ }
+}
diff --git a/src/app/api/office/standup/run/route.ts b/src/app/api/office/standup/run/route.ts
new file mode 100644
index 0000000..0bfcfa9
--- /dev/null
+++ b/src/app/api/office/standup/run/route.ts
@@ -0,0 +1,61 @@
+import { NextResponse } from "next/server";
+
+import { buildStandupMeeting } from "@/lib/office/standup/service";
+import { saveStandupMeeting } from "@/lib/office/standup/store";
+import type { StandupAgentSnapshot, StandupTriggerKind } from "@/lib/office/standup/types";
+import {
+ applyStudioSettingsPatch,
+ loadStudioSettings,
+} from "@/lib/studio/settings-store";
+import { resolveStandupPreference } from "@/lib/studio/settings";
+
+export const runtime = "nodejs";
+
+export async function POST(request: Request) {
+ try {
+ const body = (await request.json()) as {
+ gatewayUrl?: string;
+ agents?: StandupAgentSnapshot[];
+ trigger?: StandupTriggerKind;
+ scheduledFor?: string | null;
+ };
+ const gatewayUrl =
+ typeof body.gatewayUrl === "string" ? body.gatewayUrl.trim() : "";
+ if (!gatewayUrl) {
+ return NextResponse.json(
+ { error: "gatewayUrl is required." },
+ { status: 400 }
+ );
+ }
+ const settings = loadStudioSettings();
+ const config = resolveStandupPreference(settings, gatewayUrl);
+ const trigger = body.trigger === "scheduled" ? "scheduled" : "manual";
+ const meeting = await buildStandupMeeting({
+ config,
+ agents: Array.isArray(body.agents) ? body.agents : [],
+ trigger,
+ scheduledFor:
+ typeof body.scheduledFor === "string" ? body.scheduledFor : null,
+ });
+ saveStandupMeeting(meeting);
+ if (trigger === "scheduled") {
+ applyStudioSettingsPatch({
+ standup: {
+ [gatewayUrl]: {
+ schedule: {
+ lastAutoRunAt: new Date().toISOString(),
+ },
+ },
+ },
+ });
+ }
+ return NextResponse.json(
+ { meeting },
+ { headers: { "Cache-Control": "no-store" } }
+ );
+ } catch (error) {
+ const message =
+ error instanceof Error ? error.message : "Failed to start standup meeting.";
+ return NextResponse.json({ error: message }, { status: 500 });
+ }
+}
diff --git a/src/app/api/office/text/route.ts b/src/app/api/office/text/route.ts
new file mode 100644
index 0000000..5a9ca28
--- /dev/null
+++ b/src/app/api/office/text/route.ts
@@ -0,0 +1,55 @@
+import { NextResponse } from "next/server";
+
+import { buildMockTextMessageScenario } from "@/lib/office/text/mock";
+
+export const runtime = "nodejs";
+
+type TextMessageRequestBody = {
+ recipient?: string;
+ message?: string | null;
+};
+
+const MAX_RECIPIENT_CHARS = 120;
+const MAX_MESSAGE_CHARS = 1_000;
+
+const normalizeText = (value: string | null | undefined): string =>
+ (value ?? "").replace(/\s+/g, " ").trim();
+
+export async function POST(request: Request) {
+ try {
+ const body = (await request.json()) as TextMessageRequestBody;
+ const recipient = normalizeText(body.recipient);
+ const message = normalizeText(body.message);
+
+ if (!recipient) {
+ return NextResponse.json({ error: "recipient is required." }, { status: 400 });
+ }
+ if (recipient.length > MAX_RECIPIENT_CHARS) {
+ return NextResponse.json(
+ { error: `recipient exceeds ${MAX_RECIPIENT_CHARS} characters.` },
+ { status: 400 },
+ );
+ }
+ if (message.length > MAX_MESSAGE_CHARS) {
+ return NextResponse.json(
+ { error: `message exceeds ${MAX_MESSAGE_CHARS} characters.` },
+ { status: 400 },
+ );
+ }
+
+ // TODO: Create Claw3D voice and text skill.
+ const scenario = buildMockTextMessageScenario({
+ recipient,
+ message: message || null,
+ });
+
+ return NextResponse.json(
+ { scenario },
+ { headers: { "Cache-Control": "no-store" } },
+ );
+ } catch (error) {
+ const message =
+ error instanceof Error ? error.message : "Failed to prepare the mock text message.";
+ return NextResponse.json({ error: message }, { status: 500 });
+ }
+}
diff --git a/src/app/api/office/voice/reply/route.ts b/src/app/api/office/voice/reply/route.ts
new file mode 100644
index 0000000..6c2f16c
--- /dev/null
+++ b/src/app/api/office/voice/reply/route.ts
@@ -0,0 +1,47 @@
+import { NextResponse } from "next/server";
+import { synthesizeVoiceReply, type VoiceReplyProvider } from "@/lib/voiceReply/provider";
+
+export const runtime = "nodejs";
+
+type VoiceReplyRequestBody = {
+ text?: string;
+ provider?: VoiceReplyProvider;
+ voiceId?: string | null;
+ speed?: number;
+};
+
+const MAX_REPLY_CHARS = 5_000;
+
+export async function POST(request: Request) {
+ try {
+ const body = (await request.json()) as VoiceReplyRequestBody;
+ const text = typeof body.text === "string" ? body.text.replace(/\s+/g, " ").trim() : "";
+ if (!text) {
+ return NextResponse.json({ error: "Voice reply text is required." }, { status: 400 });
+ }
+ if (text.length > MAX_REPLY_CHARS) {
+ return NextResponse.json(
+ { error: `Voice reply text exceeds ${MAX_REPLY_CHARS} characters.` },
+ { status: 400 }
+ );
+ }
+ const response = await synthesizeVoiceReply({
+ text,
+ provider: body.provider,
+ voiceId: body.voiceId,
+ speed: body.speed,
+ });
+ return new Response(response.body, {
+ status: 200,
+ headers: {
+ "Cache-Control": "no-store",
+ "Content-Type": response.headers.get("content-type") ?? "audio/mpeg",
+ },
+ });
+ } catch (error) {
+ const message =
+ error instanceof Error ? error.message : "Failed to synthesize the voice reply.";
+ const status = message.includes("Missing ELEVENLABS_API_KEY") ? 503 : 500;
+ return NextResponse.json({ error: message }, { status });
+ }
+}
diff --git a/src/app/api/office/voice/transcribe/route.ts b/src/app/api/office/voice/transcribe/route.ts
new file mode 100644
index 0000000..afc730c
--- /dev/null
+++ b/src/app/api/office/voice/transcribe/route.ts
@@ -0,0 +1,46 @@
+import { NextResponse } from "next/server";
+
+import { transcribeVoiceWithOpenClaw } from "@/lib/openclaw/voiceTranscription";
+
+export const runtime = "nodejs";
+
+const MAX_VOICE_UPLOAD_BYTES = 20 * 1024 * 1024;
+
+export async function POST(request: Request) {
+ try {
+ const formData = await request.formData();
+ const audio = formData.get("audio");
+ if (!(audio instanceof File)) {
+ return NextResponse.json({ error: "audio file is required." }, { status: 400 });
+ }
+
+ const arrayBuffer = await audio.arrayBuffer();
+ const byteLength = arrayBuffer.byteLength;
+ if (byteLength <= 0) {
+ return NextResponse.json({ error: "Audio upload is empty." }, { status: 400 });
+ }
+ if (byteLength > MAX_VOICE_UPLOAD_BYTES) {
+ return NextResponse.json(
+ { error: `Audio upload exceeds the ${MAX_VOICE_UPLOAD_BYTES} byte limit.` },
+ { status: 400 },
+ );
+ }
+
+ const result = await transcribeVoiceWithOpenClaw({
+ buffer: Buffer.from(arrayBuffer),
+ fileName: audio.name,
+ mimeType: audio.type,
+ });
+
+ return NextResponse.json({
+ transcript: result.transcript,
+ provider: result.provider,
+ model: result.model,
+ decision: result.decision,
+ ignored: result.ignored,
+ });
+ } catch (error) {
+ const message = error instanceof Error ? error.message : "Failed to transcribe audio.";
+ return NextResponse.json({ error: message }, { status: 500 });
+ }
+}
diff --git a/src/app/api/path-suggestions/route.ts b/src/app/api/path-suggestions/route.ts
new file mode 100644
index 0000000..697066d
--- /dev/null
+++ b/src/app/api/path-suggestions/route.ts
@@ -0,0 +1,121 @@
+import { NextResponse } from "next/server";
+
+import fs from "node:fs";
+import os from "node:os";
+import path from "node:path";
+
+import { resolveUserPath } from "@/lib/clawdbot/paths";
+
+export const runtime = "nodejs";
+
+type PathAutocompleteEntry = {
+ name: string;
+ fullPath: string;
+ displayPath: string;
+ isDirectory: boolean;
+};
+
+type PathAutocompleteResult = {
+ query: string;
+ directory: string;
+ entries: PathAutocompleteEntry[];
+};
+
+type PathAutocompleteOptions = {
+ query: string;
+ maxResults?: number;
+ homedir?: () => string;
+};
+
+const normalizeQuery = (query: string): string => {
+ const trimmed = query.trim();
+ if (!trimmed) {
+ throw new Error("Query is required.");
+ }
+ if (trimmed === "~") {
+ return "~/";
+ }
+ if (trimmed.startsWith("~")) {
+ return trimmed;
+ }
+ const withoutLeading = trimmed.replace(/^[\\/]+/, "");
+ return `~/${withoutLeading}`;
+};
+
+const isWithinHome = (target: string, home: string): boolean => {
+ const relative = path.relative(home, target);
+ if (!relative) return true;
+ return !relative.startsWith("..") && !path.isAbsolute(relative);
+};
+
+const listPathAutocompleteEntries = ({
+ query,
+ maxResults = 10,
+ homedir = os.homedir,
+}: PathAutocompleteOptions): PathAutocompleteResult => {
+ const normalized = normalizeQuery(query);
+ const resolvedHome = path.resolve(homedir());
+ const resolvedQuery = resolveUserPath(normalized, homedir);
+ if (!isWithinHome(resolvedQuery, resolvedHome)) {
+ throw new Error("Path must stay within the home directory.");
+ }
+
+ const endsWithSlash = normalized.endsWith("/") || normalized.endsWith(path.sep);
+ const directoryPath = endsWithSlash ? resolvedQuery : path.dirname(resolvedQuery);
+ const prefix = endsWithSlash ? "" : path.basename(resolvedQuery);
+
+ if (!isWithinHome(directoryPath, resolvedHome)) {
+ throw new Error("Path must stay within the home directory.");
+ }
+ if (!fs.existsSync(directoryPath)) {
+ throw new Error(`Directory does not exist: ${directoryPath}`);
+ }
+ const stat = fs.statSync(directoryPath);
+ if (!stat.isDirectory()) {
+ throw new Error(`Path is not a directory: ${directoryPath}`);
+ }
+
+ const limit = Number.isFinite(maxResults) && maxResults > 0 ? Math.floor(maxResults) : 10;
+
+ const entries = fs
+ .readdirSync(directoryPath, { withFileTypes: true })
+ .filter((entry) => !entry.name.startsWith("."))
+ .filter((entry) => entry.name.startsWith(prefix))
+ .map((entry) => {
+ const fullPath = path.join(directoryPath, entry.name);
+ const relative = path.relative(resolvedHome, fullPath);
+ const normalizedRelative = relative.split(path.sep).join("/");
+ const displayBase = `~/${normalizedRelative}`;
+ return {
+ name: entry.name,
+ fullPath,
+ displayPath: entry.isDirectory() ? `${displayBase}/` : displayBase,
+ isDirectory: entry.isDirectory(),
+ } satisfies PathAutocompleteEntry;
+ })
+ .sort((a, b) => {
+ if (a.isDirectory !== b.isDirectory) {
+ return a.isDirectory ? -1 : 1;
+ }
+ return a.name.localeCompare(b.name);
+ })
+ .slice(0, limit);
+
+ return { query: normalized, directory: directoryPath, entries };
+};
+
+export async function GET(request: Request) {
+ try {
+ const { searchParams } = new URL(request.url);
+ const rawQuery = searchParams.get("q");
+ const query = rawQuery && rawQuery.trim() ? rawQuery.trim() : "~/";
+ const result = listPathAutocompleteEntries({ query, maxResults: 10 });
+ return NextResponse.json(result);
+ } catch (err) {
+ const message =
+ err instanceof Error ? err.message : "Failed to list path suggestions.";
+ console.error(message);
+ const status = message.includes("does not exist") ? 404 : 400;
+ return NextResponse.json({ error: message }, { status });
+ }
+}
diff --git a/src/app/api/studio/route.ts b/src/app/api/studio/route.ts
new file mode 100644
index 0000000..51a5730
--- /dev/null
+++ b/src/app/api/studio/route.ts
@@ -0,0 +1,53 @@
+import { NextResponse } from "next/server";
+
+import {
+ sanitizeStudioGatewaySettings,
+ sanitizeStudioSettings,
+ type StudioSettingsPatch,
+} from "@/lib/studio/settings";
+import {
+ applyStudioSettingsPatch,
+ loadLocalGatewayDefaults,
+ loadStudioSettings,
+} from "@/lib/studio/settings-store";
+
+export const runtime = "nodejs";
+
+const isPatch = (value: unknown): value is StudioSettingsPatch =>
+ Boolean(value && typeof value === "object");
+
+export async function GET() {
+ try {
+ const settings = loadStudioSettings();
+ const localGatewayDefaults = loadLocalGatewayDefaults();
+ return NextResponse.json(
+ {
+ settings: sanitizeStudioSettings(settings),
+ localGatewayDefaults: sanitizeStudioGatewaySettings(localGatewayDefaults),
+ },
+ { headers: { "Cache-Control": "no-store" } }
+ );
+ } catch (err) {
+ const message = err instanceof Error ? err.message : "Failed to load studio settings.";
+ console.error(message);
+ return NextResponse.json({ error: message }, { status: 500 });
+ }
+}
+
+export async function PUT(request: Request) {
+ try {
+ const body = (await request.json()) as unknown;
+ if (!isPatch(body)) {
+ return NextResponse.json({ error: "Invalid settings payload." }, { status: 400 });
+ }
+ const settings = applyStudioSettingsPatch(body);
+ return NextResponse.json(
+ { settings: sanitizeStudioSettings(settings) },
+ { headers: { "Cache-Control": "no-store" } }
+ );
+ } catch (err) {
+ const message = err instanceof Error ? err.message : "Failed to save studio settings.";
+ console.error(message);
+ return NextResponse.json({ error: message }, { status: 500 });
+ }
+}
diff --git a/src/app/favicon.ico b/src/app/favicon.ico
new file mode 100644
index 0000000..718d6fe
Binary files /dev/null and b/src/app/favicon.ico differ
diff --git a/src/app/globals.css b/src/app/globals.css
new file mode 100644
index 0000000..182bb04
--- /dev/null
+++ b/src/app/globals.css
@@ -0,0 +1,1355 @@
+@import "tailwindcss";
+@import "tw-animate-css";
+@import "../../node_modules/react-mentions-ts/styles/tailwind.css";
+@import "./styles/markdown.css";
+
+@custom-variant dark (&:is(.dark *));
+
+:root {
+ --neutral-tint-rgb: rgb(90 132 154);
+ --neutral-tint: #5a849a;
+ --background: #f7f9fc;
+ --foreground: rgb(27 33 43);
+ --card: #ffffff;
+ --card-foreground: rgb(27 33 43);
+ --popover: #ffffff;
+ --popover-foreground: rgb(27 33 43);
+ --primary: oklch(0.56 0.1 214);
+ --primary-foreground: oklch(0.98 0.004 230);
+ --secondary: color-mix(in srgb, var(--neutral-tint-rgb) 14%, transparent);
+ --secondary-foreground: rgb(35 43 54);
+ --muted: color-mix(in srgb, var(--neutral-tint-rgb) 11%, transparent);
+ --muted-foreground: rgb(87 98 114 / 0.9);
+ --accent: color-mix(in srgb, var(--neutral-tint-rgb) 14%, transparent);
+ --accent-foreground: rgb(35 43 54);
+ --destructive: oklch(0.58 0.22 22);
+ --destructive-foreground: oklch(0.99 0.004 90);
+ --border: color-mix(in srgb, var(--neutral-tint-rgb) 20%, transparent);
+ --input: color-mix(in srgb, var(--neutral-tint-rgb) 13%, transparent);
+ --ring: oklch(0.56 0.1 214);
+ --chart-1: oklch(0.58 0.07 220);
+ --chart-2: oklch(0.66 0.08 170);
+ --chart-3: oklch(0.62 0.07 250);
+ --chart-4: oklch(0.71 0.06 205);
+ --chart-5: oklch(0.5 0.06 240);
+ --sidebar: rgb(255 255 255 / 0.92);
+ --sidebar-foreground: var(--foreground);
+ --sidebar-primary: oklch(0.56 0.1 214);
+ --sidebar-primary-foreground: oklch(0.98 0.004 230);
+ --sidebar-accent: color-mix(in srgb, var(--neutral-tint-rgb) 14%, transparent);
+ --sidebar-accent-foreground: var(--accent-foreground);
+ --sidebar-border: color-mix(in srgb, var(--neutral-tint-rgb) 16%, transparent);
+ --sidebar-ring: oklch(0.56 0.1 214);
+ --sidebar-card-bg: color-mix(in srgb, var(--neutral-tint-rgb) 6%, transparent);
+ --sidebar-card-border: color-mix(in srgb, var(--neutral-tint-rgb) 20%, transparent);
+ --sidebar-input-bg: color-mix(in srgb, var(--neutral-tint-rgb) 9%, transparent);
+ --sidebar-input-border: color-mix(in srgb, var(--neutral-tint-rgb) 26%, transparent);
+ --sidebar-control-off: color-mix(in srgb, var(--neutral-tint-rgb) 12%, transparent);
+ --sidebar-control-on: color-mix(in oklch, var(--primary) 74%, var(--surface-2));
+ --radius: 1rem;
+ --radius-small: 6px;
+ --radius-card: 14px;
+ --radius-container: 20px;
+ --font-display: "Bebas Neue", sans-serif;
+ --font-sans: "IBM Plex Sans", sans-serif;
+ --font-mono: "IBM Plex Mono", monospace;
+ --shadow-color: oklch(0.19 0.01 255);
+ --shadow-opacity: 0.16;
+ --shadow-blur: 28px;
+ --shadow-spread: -6px;
+ --shadow-offset-x: 0px;
+ --shadow-offset-y: 10px;
+ --letter-spacing: 0em;
+ --spacing: 0.25rem;
+ --space-1: 4px;
+ --space-2: 8px;
+ --space-3: 12px;
+ --space-4: 16px;
+ --space-6: 24px;
+ --space-8: 32px;
+ --space-12: 48px;
+ --tracking-normal: 0.005em;
+ --panel: color-mix(in oklch, var(--surface-1) 96%, white 4%);
+ --panel-border: color-mix(in srgb, var(--neutral-tint-rgb) 20%, transparent);
+ --surface-0: var(--background);
+ --surface-1: var(--card);
+ --surface-2: color-mix(in srgb, var(--neutral-tint-rgb) 8%, transparent);
+ --surface-3: color-mix(in srgb, var(--neutral-tint-rgb) 14%, transparent);
+ --surface-selected: color-mix(in srgb, var(--neutral-tint-rgb) 20%, transparent);
+ --surface-selected-border: color-mix(in srgb, var(--neutral-tint-rgb) 34%, transparent);
+ --elev-overlay-1: rgb(255 255 255 / 0.05);
+ --elev-overlay-2: rgb(255 255 255 / 0.08);
+ --elev-overlay-3: rgb(255 255 255 / 0.12);
+ --elev-edge-1: color-mix(in oklch, var(--border) 70%, transparent);
+ --elev-edge-2: color-mix(in oklch, var(--border) 84%, transparent);
+ --plane-workspace-bg: color-mix(in oklch, var(--surface-1) 92%, var(--surface-0) 8%);
+ --plane-workspace-border: color-mix(in oklch, var(--panel-border) 88%, var(--surface-0));
+ --plane-sidepanel-bg: color-mix(in oklch, var(--surface-2) 12%, var(--surface-1));
+ --plane-sidepanel-border: color-mix(in oklch, var(--panel-border) 94%, var(--surface-2));
+ --plane-card-bg: color-mix(in oklch, var(--sidebar-card-bg) 92%, var(--surface-1));
+ --plane-card-border: color-mix(in oklch, var(--sidebar-card-border) 92%, var(--surface-2));
+ --chat-surface-bg: color-mix(in oklch, var(--surface-1) 92%, white 8%);
+ --chat-assistant-bg: color-mix(in oklch, var(--surface-2) 92%, var(--surface-1));
+ --chat-assistant-border: color-mix(in oklch, var(--border) 70%, transparent);
+ --chat-user-bg: color-mix(in srgb, var(--neutral-tint-rgb) 8%, transparent);
+ --chat-user-border: color-mix(in oklch, var(--border) 74%, transparent);
+ --chat-user-header-bg: color-mix(in srgb, var(--neutral-tint-rgb) 12%, transparent);
+ --action-bg: color-mix(in oklch, var(--primary) 92%, white 8%);
+ --action-bg-hover: color-mix(in oklch, var(--primary) 88%, white 12%);
+ --action-border: color-mix(in oklch, var(--primary) 52%, var(--border));
+ --action-fg: var(--primary-foreground);
+ --danger-soft-bg: color-mix(in oklch, var(--destructive) 13%, transparent);
+ --danger-soft-border: color-mix(in oklch, var(--destructive) 34%, transparent);
+ --danger-soft-fg: color-mix(in oklch, var(--destructive) 78%, black 22%);
+ --status-idle-bg: color-mix(in oklch, var(--surface-3) 86%, transparent);
+ --status-idle-border: color-mix(in oklch, var(--border) 74%, transparent);
+ --status-idle-fg: color-mix(in oklch, var(--foreground) 72%, var(--muted-foreground) 28%);
+ --status-running-bg: color-mix(in oklch, #1ea465 16%, transparent);
+ --status-running-border: color-mix(in oklch, #1b8f59 38%, transparent);
+ --status-running-fg: color-mix(in oklch, #0e6a40 72%, black 28%);
+ --status-error-bg: var(--danger-soft-bg);
+ --status-error-border: var(--danger-soft-border);
+ --status-error-fg: var(--danger-soft-fg);
+ --status-connecting-bg: color-mix(in oklch, #b97811 16%, transparent);
+ --status-connecting-border: color-mix(in oklch, #9f660f 36%, transparent);
+ --status-connecting-fg: color-mix(in oklch, #6a4100 78%, black 22%);
+ --status-connected-bg: color-mix(in oklch, #21888f 18%, transparent);
+ --status-connected-border: color-mix(in oklch, #1b7277 38%, transparent);
+ --status-connected-fg: color-mix(in oklch, #15585c 78%, black 22%);
+ --status-disconnected-bg: var(--status-idle-bg);
+ --status-disconnected-border: var(--status-idle-border);
+ --status-disconnected-fg: var(--status-idle-fg);
+ --status-approval-bg: color-mix(in oklch, #a77113 16%, transparent);
+ --status-approval-border: color-mix(in oklch, #8f6011 34%, transparent);
+ --status-approval-fg: color-mix(in oklch, #6a4504 82%, black 18%);
+ --command-bg: color-mix(in oklch, #081018 88%, var(--surface-1));
+ --command-border: color-mix(in oklch, #344d5e 62%, transparent);
+ --command-fg: rgb(234 242 248 / 0.98);
+ --command-copy-bg: color-mix(in oklch, #182838 82%, var(--surface-1));
+ --command-copy-hover-bg: color-mix(in oklch, #243c52 78%, var(--surface-1));
+ --command-copy-border: color-mix(in oklch, #3d6178 62%, transparent);
+ --model-control-border: color-mix(in oklch, var(--action-bg) 34%, var(--sidebar-input-border));
+}
+
+.dark {
+ --radius: 1rem;
+ --neutral-tint-rgb: rgb(108 146 176);
+ --neutral-tint: #6c92b0;
+ --background: #0b1118;
+ --foreground: rgb(246 250 255 / 0.97);
+ --card: #121a24;
+ --card-foreground: rgb(246 250 255 / 0.97);
+ --popover: #121a24;
+ --popover-foreground: rgb(246 250 255 / 0.94);
+ --primary: #22a8cc;
+ --primary-foreground: rgb(247 252 255 / 0.98);
+ --secondary: color-mix(in srgb, var(--neutral-tint-rgb) 14%, transparent);
+ --secondary-foreground: rgb(246 250 255 / 0.95);
+ --muted: color-mix(in srgb, var(--neutral-tint-rgb) 12%, transparent);
+ --muted-foreground: rgb(224 236 248 / 0.78);
+ --accent: color-mix(in srgb, var(--neutral-tint-rgb) 20%, transparent);
+ --accent-foreground: rgb(246 250 255 / 0.94);
+ --destructive: oklch(0.62 0.21 22);
+ --destructive-foreground: rgb(255 255 255 / 0.96);
+ --border: color-mix(in srgb, var(--neutral-tint-rgb) 30%, transparent);
+ --input: color-mix(in srgb, var(--neutral-tint-rgb) 22%, transparent);
+ --ring: #30bce2;
+ --chart-1: oklch(0.69 0.09 218);
+ --chart-2: oklch(0.7 0.08 170);
+ --chart-3: oklch(0.68 0.08 255);
+ --chart-4: oklch(0.7 0.07 205);
+ --chart-5: oklch(0.62 0.07 240);
+ --sidebar: #0f1721;
+ --sidebar-foreground: rgb(246 250 255 / 0.94);
+ --sidebar-primary: #22a8cc;
+ --sidebar-primary-foreground: rgb(247 252 255 / 0.98);
+ --sidebar-accent: color-mix(in srgb, var(--neutral-tint-rgb) 20%, transparent);
+ --sidebar-accent-foreground: rgb(246 250 255 / 0.94);
+ --sidebar-border: color-mix(in srgb, var(--neutral-tint-rgb) 24%, transparent);
+ --sidebar-ring: #30bce2;
+ --sidebar-card-bg: color-mix(in oklch, var(--surface-2) 88%, var(--surface-1));
+ --sidebar-card-border: color-mix(in srgb, var(--neutral-tint-rgb) 36%, transparent);
+ --sidebar-input-bg: color-mix(in oklch, var(--surface-2) 84%, var(--surface-1));
+ --sidebar-input-border: color-mix(in srgb, var(--neutral-tint-rgb) 40%, transparent);
+ --sidebar-control-off: color-mix(in oklch, var(--surface-2) 88%, var(--surface-1));
+ --sidebar-control-on: color-mix(in oklch, var(--primary) 82%, var(--surface-2));
+ --panel: color-mix(in oklch, var(--surface-1) 96%, black 4%);
+ --panel-border: color-mix(in srgb, var(--neutral-tint-rgb) 36%, transparent);
+ --shadow-color: oklch(0.08 0.012 250);
+ --shadow-opacity: 0.24;
+ --surface-0: #0b1118;
+ --surface-1: #121a24;
+ --surface-2: #1a2533;
+ --surface-3: #223142;
+ --surface-selected: #26384c;
+ --surface-selected-border: #3c5a76;
+ --elev-overlay-1: rgb(255 255 255 / 0.03);
+ --elev-overlay-2: rgb(255 255 255 / 0.06);
+ --elev-overlay-3: rgb(255 255 255 / 0.09);
+ --elev-edge-1: color-mix(in oklch, var(--surface-selected-border) 42%, var(--border));
+ --elev-edge-2: color-mix(in oklch, var(--surface-selected-border) 64%, var(--border));
+ --plane-workspace-bg: color-mix(in oklch, var(--surface-1) 94%, var(--surface-0) 6%);
+ --plane-workspace-border: color-mix(in oklch, var(--panel-border) 88%, var(--surface-0));
+ --plane-sidepanel-bg: color-mix(in oklch, var(--surface-2) 20%, var(--surface-1));
+ --plane-sidepanel-border: color-mix(in oklch, var(--panel-border) 96%, var(--surface-2));
+ --plane-card-bg: color-mix(in oklch, var(--surface-2) 76%, var(--surface-1));
+ --plane-card-border: color-mix(in oklch, var(--surface-selected-border) 44%, var(--border));
+ --chat-surface-bg: color-mix(in oklch, var(--surface-1) 88%, black 12%);
+ --chat-assistant-bg: color-mix(in oklch, var(--surface-2) 90%, var(--surface-1));
+ --chat-assistant-border: color-mix(in oklch, var(--border) 74%, var(--surface-selected-border) 26%);
+ --chat-user-bg: color-mix(in oklch, var(--surface-1) 86%, var(--surface-0));
+ --chat-user-border: color-mix(in oklch, var(--border) 82%, transparent);
+ --chat-user-header-bg: color-mix(in oklch, var(--surface-2) 72%, var(--surface-1));
+ --action-bg: color-mix(in oklch, var(--primary) 88%, white 12%);
+ --action-bg-hover: color-mix(in oklch, var(--primary) 84%, white 16%);
+ --action-border: color-mix(in oklch, var(--primary) 68%, var(--border));
+ --action-fg: var(--primary-foreground);
+ --danger-soft-bg: color-mix(in oklch, var(--destructive) 18%, transparent);
+ --danger-soft-border: color-mix(in oklch, var(--destructive) 42%, transparent);
+ --danger-soft-fg: color-mix(in oklch, var(--destructive) 82%, white 18%);
+ --status-idle-bg: color-mix(in oklch, var(--surface-3) 66%, var(--surface-2));
+ --status-idle-border: color-mix(in oklch, var(--surface-selected-border) 78%, var(--border));
+ --status-idle-fg: color-mix(in oklch, var(--foreground) 92%, var(--muted-foreground) 8%);
+ --status-running-bg: color-mix(in oklch, #20aa68 22%, transparent);
+ --status-running-border: color-mix(in oklch, #2ec57a 46%, transparent);
+ --status-running-fg: color-mix(in oklch, #7be3ad 68%, white 32%);
+ --status-error-bg: var(--danger-soft-bg);
+ --status-error-border: var(--danger-soft-border);
+ --status-error-fg: var(--danger-soft-fg);
+ --status-connecting-bg: color-mix(in oklch, #b97811 24%, transparent);
+ --status-connecting-border: color-mix(in oklch, #cf8a14 50%, transparent);
+ --status-connecting-fg: color-mix(in oklch, #f5cb86 66%, white 34%);
+ --status-connected-bg: color-mix(in oklch, #239199 24%, transparent);
+ --status-connected-border: color-mix(in oklch, #27a8b0 52%, transparent);
+ --status-connected-fg: color-mix(in oklch, #9be5e8 68%, white 32%);
+ --status-disconnected-bg: var(--status-idle-bg);
+ --status-disconnected-border: var(--status-idle-border);
+ --status-disconnected-fg: var(--status-idle-fg);
+ --status-approval-bg: color-mix(in oklch, #a77113 26%, transparent);
+ --status-approval-border: color-mix(in oklch, #c68518 48%, transparent);
+ --status-approval-fg: color-mix(in oklch, #f2c986 68%, white 32%);
+ --command-bg: color-mix(in oklch, #060c12 90%, var(--surface-1));
+ --command-border: color-mix(in oklch, #325066 68%, transparent);
+ --command-fg: rgb(236 243 248 / 0.96);
+ --command-copy-bg: color-mix(in oklch, #1b2d3f 86%, var(--surface-1));
+ --command-copy-hover-bg: color-mix(in oklch, #254056 82%, var(--surface-1));
+ --command-copy-border: color-mix(in oklch, #406882 66%, transparent);
+ --model-control-border: color-mix(in oklch, var(--action-bg) 48%, var(--sidebar-input-border));
+}
+
+@theme inline {
+ --color-background: var(--background);
+ --color-foreground: var(--foreground);
+ --color-card: var(--card);
+ --color-card-foreground: var(--card-foreground);
+ --color-popover: var(--popover);
+ --color-popover-foreground: var(--popover-foreground);
+ --color-primary: var(--primary);
+ --color-primary-foreground: var(--primary-foreground);
+ --color-secondary: var(--secondary);
+ --color-secondary-foreground: var(--secondary-foreground);
+ --color-muted: var(--muted);
+ --color-muted-foreground: var(--muted-foreground);
+ --color-accent: var(--accent);
+ --color-accent-foreground: var(--accent-foreground);
+ --color-destructive: var(--destructive);
+ --color-destructive-foreground: var(--destructive-foreground);
+ --color-border: var(--border);
+ --color-input: var(--input);
+ --color-ring: var(--ring);
+ --color-chart-1: var(--chart-1);
+ --color-chart-2: var(--chart-2);
+ --color-chart-3: var(--chart-3);
+ --color-chart-4: var(--chart-4);
+ --color-chart-5: var(--chart-5);
+ --color-sidebar: var(--sidebar);
+ --color-sidebar-foreground: var(--sidebar-foreground);
+ --color-sidebar-primary: var(--sidebar-primary);
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
+ --color-sidebar-accent: var(--sidebar-accent);
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
+ --color-sidebar-border: var(--sidebar-border);
+ --color-sidebar-ring: var(--sidebar-ring);
+ --color-shadow-color: var(--shadow-color);
+ --color-surface-0: var(--surface-0);
+ --color-surface-1: var(--surface-1);
+ --color-surface-2: var(--surface-2);
+ --color-surface-3: var(--surface-3);
+ --radius-sm: var(--radius-small);
+ --radius-md: var(--radius-small);
+ --radius-lg: var(--radius-card);
+ --radius-xl: var(--radius-container);
+ --radius-2xl: var(--radius-container);
+ --radius-3xl: var(--radius-container);
+ --radius-4xl: var(--radius-container);
+ --font-sans: var(--font-sans);
+ --font-mono: var(--font-mono);
+ --font-serif: "Baskervville", serif;
+ --tracking-tighter: calc(var(--tracking-normal) - 0.05em);
+ --tracking-tight: calc(var(--tracking-normal) - 0.025em);
+ --tracking-wide: calc(var(--tracking-normal) + 0.025em);
+ --tracking-wider: calc(var(--tracking-normal) + 0.05em);
+ --tracking-widest: calc(var(--tracking-normal) + 0.1em);
+ --tracking-normal: var(--tracking-normal);
+ --shadow-2xs: 0 1px 1px color-mix(in oklch, var(--shadow-color) 10%, transparent);
+ --shadow-xs: 0 2px 6px color-mix(in oklch, var(--shadow-color) 16%, transparent);
+ --shadow-sm: 0 7px 18px -9px color-mix(in oklch, var(--shadow-color) 35%, transparent);
+ --shadow: 0 10px 24px -14px color-mix(in oklch, var(--shadow-color) 42%, transparent);
+ --shadow-md: 0 14px 36px -17px color-mix(in oklch, var(--shadow-color) 48%, transparent);
+ --shadow-lg: 0 24px 60px -26px color-mix(in oklch, var(--shadow-color) 58%, transparent);
+ --shadow-xl: 0 36px 90px -38px color-mix(in oklch, var(--shadow-color) 66%, transparent);
+ --shadow-2xl: 0 44px 120px -45px color-mix(in oklch, var(--shadow-color) 78%, transparent);
+}
+
+@layer base {
+ html {
+ font-size: 112%;
+ }
+
+ * {
+ @apply border-border outline-ring/50;
+ box-sizing: border-box;
+ }
+
+ body {
+ @apply bg-background text-foreground;
+ min-height: 100vh;
+ margin: 0;
+ letter-spacing: var(--tracking-normal);
+ font-family: var(--font-sans), sans-serif;
+ position: relative;
+ overflow: hidden;
+ }
+
+ body::before {
+ content: "";
+ position: fixed;
+ inset: -30%;
+ z-index: -2;
+ background:
+ radial-gradient(circle at 16% 18%, color-mix(in oklch, var(--border) 24%, transparent), transparent 42%),
+ radial-gradient(circle at 84% 8%, color-mix(in oklch, var(--muted) 58%, transparent), transparent 34%),
+ linear-gradient(148deg, color-mix(in oklch, var(--background) 94%, white) 0%, var(--background) 62%, color-mix(in oklch, var(--background) 90%, black) 100%);
+ transform: rotate(-2deg) scale(1.04);
+ }
+
+ body::after {
+ content: "";
+ position: fixed;
+ inset: 0;
+ z-index: -1;
+ background-image:
+ linear-gradient(transparent 95%, color-mix(in oklch, var(--border) 58%, transparent) 96%),
+ linear-gradient(90deg, transparent 95%, color-mix(in oklch, var(--border) 52%, transparent) 96%);
+ background-size: 30px 30px;
+ opacity: 0.14;
+ pointer-events: none;
+ }
+
+ .dark body::before {
+ inset: 0;
+ transform: none;
+ background: var(--background);
+ }
+
+ .dark body::after {
+ background-image: none;
+ opacity: 0;
+ }
+}
+
+button:not(:disabled),
+input[type="button"]:not(:disabled),
+input[type="submit"]:not(:disabled),
+input[type="reset"]:not(:disabled),
+summary,
+[role="button"]:not([aria-disabled="true"]) {
+ cursor: pointer;
+}
+
+* {
+ scrollbar-width: thin;
+ scrollbar-color: color-mix(in oklch, var(--foreground) 11%, var(--border)) transparent;
+}
+
+.dark * {
+ scrollbar-color: color-mix(in oklch, var(--foreground) 34%, var(--border)) transparent;
+}
+
+*::-webkit-scrollbar {
+ width: 8px;
+ height: 8px;
+}
+
+*::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+*::-webkit-scrollbar-thumb {
+ background: color-mix(in oklch, var(--foreground) 11%, var(--border));
+ border-radius: 999px;
+ border: 2px solid transparent;
+}
+
+*::-webkit-scrollbar-thumb:hover {
+ background: color-mix(in oklch, var(--foreground) 17%, var(--border));
+}
+
+.dark *::-webkit-scrollbar-thumb {
+ background: color-mix(in oklch, var(--foreground) 36%, var(--border));
+}
+
+.dark *::-webkit-scrollbar-thumb:hover {
+ background: color-mix(in oklch, var(--foreground) 46%, var(--border));
+}
+
+textarea {
+ scrollbar-gutter: auto;
+}
+
+textarea::-webkit-scrollbar {
+ width: 0;
+ height: 0;
+}
+
+textarea:hover::-webkit-scrollbar,
+textarea:focus-visible::-webkit-scrollbar,
+textarea:focus::-webkit-scrollbar {
+ width: 10px;
+ height: 10px;
+}
+
+.glass-panel {
+ border: 0;
+ background: var(--panel);
+ backdrop-filter: none;
+ border-radius: var(--radius-container);
+ box-shadow: var(--shadow-xs);
+}
+
+.fade-up {
+ animation: fadeUp 540ms cubic-bezier(0.2, 0.74, 0.2, 1) both;
+}
+
+.fade-up-delay {
+ animation: fadeUp 540ms cubic-bezier(0.2, 0.74, 0.2, 1) 120ms both;
+}
+
+.agent-avatar-selected {
+ box-shadow: 0 0 0 2px color-mix(in oklch, var(--primary) 36%, transparent);
+}
+
+.agent-name-selected {
+ border-color: color-mix(in oklch, var(--primary) 40%, var(--border));
+ box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--primary) 16%, transparent);
+}
+
+.agent-inspect-panel {
+ position: relative;
+ height: 100%;
+ width: 100%;
+ border-radius: var(--radius-container);
+ background: transparent;
+ color: var(--sidebar-foreground);
+ border: 0;
+ box-shadow: none;
+ overflow-y: auto;
+ scrollbar-width: thin;
+ scrollbar-color: color-mix(in oklch, var(--border) 60%, transparent) transparent;
+}
+
+.sidebar-shell {
+ box-shadow: var(--shadow-xs);
+ background: color-mix(in oklch, var(--panel) 84%, white 16%);
+}
+
+.dark .sidebar-shell {
+ box-shadow: var(--shadow-2xs);
+ background: var(--plane-sidepanel-bg);
+}
+
+.sidebar-section {
+ padding-block: 1.5rem;
+}
+
+.sidebar-section:first-of-type {
+ padding-top: 1rem;
+}
+
+.sidebar-section + .sidebar-section {
+ margin-top: 2rem;
+}
+
+.sidebar-section-title {
+ font-size: 1rem;
+ line-height: 1.2;
+ font-weight: 600;
+ color: color-mix(in oklch, var(--foreground) 96%, var(--muted-foreground) 4%);
+}
+
+.sidebar-copy {
+ font-size: 0.82rem;
+ line-height: 1.55;
+}
+
+.sidebar-card {
+ border: 1px solid var(--sidebar-card-border);
+ border-radius: var(--radius-card);
+ background: var(--sidebar-card-bg);
+ box-shadow: 0 1px 2px color-mix(in oklch, var(--foreground) 5%, transparent);
+}
+
+.sidebar-input {
+ border: 1px solid var(--sidebar-input-border);
+ background: var(--sidebar-input-bg);
+ transition:
+ border-color 180ms ease,
+ box-shadow 180ms ease,
+ background-color 180ms ease;
+}
+
+.sidebar-input:focus,
+.sidebar-input:focus-visible {
+ border-color: color-mix(in oklch, var(--ring) 72%, var(--sidebar-input-border));
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring) 22%, transparent) !important;
+}
+
+.ui-switch {
+ position: relative;
+ display: inline-flex;
+ height: 32px;
+ width: 54px;
+ align-items: center;
+ border-radius: var(--radius-small);
+ border: 0;
+ background: var(--sidebar-control-off);
+ box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--foreground) 8%, transparent);
+ transition:
+ background-color 180ms ease,
+ box-shadow 180ms ease;
+}
+
+.ui-switch--on {
+ background: var(--sidebar-control-on);
+ box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--sidebar-control-on) 56%, transparent);
+}
+
+.ui-switch-thumb {
+ height: 26px;
+ width: 26px;
+ border-radius: calc(var(--radius-small) - 2px);
+ background: var(--primary-foreground);
+ box-shadow: 0 2px 5px color-mix(in oklch, black 20%, transparent);
+ transform: translateX(3px);
+ transition: transform 180ms ease;
+}
+
+.ui-switch--on .ui-switch-thumb {
+ transform: translateX(25px);
+}
+
+.sidebar-btn-primary {
+ display: inline-flex;
+ min-height: 40px;
+ align-items: center;
+ justify-content: center;
+ border-radius: calc(var(--radius) + 0.16rem);
+ border: 1px solid var(--action-border);
+ background: var(--action-bg);
+ color: var(--action-fg);
+ transition: filter 180ms ease;
+}
+
+.sidebar-btn-primary:hover {
+ filter: brightness(1.04);
+}
+
+.sidebar-btn-ghost {
+ display: inline-flex;
+ min-height: 36px;
+ align-items: center;
+ justify-content: center;
+ border-radius: calc(var(--radius) + 0.08rem);
+ border: 1px solid color-mix(in oklch, var(--border) 92%, transparent);
+ background: color-mix(in oklch, var(--surface-1) 82%, transparent);
+ color: color-mix(in oklch, var(--foreground) 76%, var(--muted-foreground) 24%);
+ transition:
+ border-color 180ms ease,
+ background-color 180ms ease;
+}
+
+.sidebar-btn-ghost:hover {
+ border-color: color-mix(in oklch, var(--border) 76%, var(--foreground) 24%);
+ background: color-mix(in oklch, var(--surface-2) 88%, transparent);
+}
+
+.sidebar-btn-utility {
+ display: inline-flex;
+ min-height: 34px;
+ align-items: center;
+ justify-content: center;
+ border: 0;
+ background: transparent;
+ color: color-mix(in oklch, var(--muted-foreground) 78%, var(--foreground) 22%);
+ transition: color 180ms ease;
+}
+
+.sidebar-btn-utility:hover {
+ color: color-mix(in oklch, var(--foreground) 72%, var(--muted-foreground) 28%);
+ text-decoration: underline;
+ text-underline-offset: 2px;
+}
+
+.ui-panel {
+ border: 1px solid var(--panel-border);
+ border-radius: var(--radius-card);
+ background: var(--panel);
+ box-shadow: var(--shadow-sm);
+}
+
+.dark .ui-panel {
+ border-color: var(--plane-workspace-border);
+ background: var(--plane-workspace-bg);
+ box-shadow: var(--shadow-2xs);
+}
+
+.ui-depth-workspace {
+ background: var(--plane-workspace-bg);
+}
+
+.dark .ui-depth-workspace {
+ border-color: var(--plane-workspace-border);
+ background: var(--plane-workspace-bg);
+ box-shadow:
+ inset 0 1px 0 var(--elev-overlay-1),
+ var(--shadow-2xs);
+}
+
+.ui-depth-sidepanel {
+ background: var(--plane-sidepanel-bg);
+}
+
+.dark .ui-depth-sidepanel {
+ border-color: var(--plane-sidepanel-border);
+ background: var(--plane-sidepanel-bg);
+ box-shadow:
+ 0 0 0 1px color-mix(in oklch, var(--elev-edge-1) 72%, transparent),
+ inset 0 1px 0 var(--elev-overlay-1),
+ var(--shadow-xs);
+}
+
+.ui-section,
+.sidebar-section {
+ padding-block: 1.5rem;
+}
+
+.ui-section:first-of-type,
+.sidebar-section:first-of-type {
+ padding-top: 1rem;
+}
+
+.ui-section + .ui-section,
+.sidebar-section + .sidebar-section {
+ margin-top: 1.5rem;
+}
+
+.sidebar-section + .sidebar-section {
+ border-top: 1px solid color-mix(in oklch, var(--border) 66%, transparent);
+ padding-top: 1.35rem;
+}
+
+.ui-section-title,
+.sidebar-section-title {
+ font-size: 1rem;
+ line-height: 1.2;
+ font-weight: 600;
+ letter-spacing: -0.01em;
+ color: color-mix(in oklch, var(--foreground) 96%, var(--muted-foreground) 4%);
+}
+
+.sidebar-section-title {
+ display: inline-flex;
+ align-items: center;
+ min-height: 1.7rem;
+ border: 1px solid color-mix(in oklch, var(--border) 72%, transparent);
+ border-radius: calc(var(--radius-small) - 2px);
+ padding: 0.12rem 0.5rem;
+ background: color-mix(in oklch, var(--surface-2) 86%, transparent);
+}
+
+.ui-card,
+.sidebar-card {
+ border: 1px solid var(--sidebar-card-border);
+ border-radius: var(--radius-card);
+ background: var(--sidebar-card-bg);
+ box-shadow: 0 1px 2px color-mix(in oklch, var(--foreground) 5%, transparent);
+}
+
+.dark .ui-card,
+.dark .sidebar-card {
+ border-color: var(--plane-card-border);
+ background: var(--plane-card-bg);
+ box-shadow:
+ inset 0 1px 0 var(--elev-overlay-1),
+ 0 1px 2px color-mix(in oklch, var(--foreground) 10%, transparent);
+}
+
+.ui-menu-popover {
+ background: var(--popover);
+}
+
+.ui-card-selected {
+ border-color: var(--surface-selected-border);
+ background: var(--surface-selected);
+ box-shadow:
+ inset 0 0 0 1px color-mix(in oklch, var(--primary) 38%, transparent),
+ 0 1px 2px color-mix(in oklch, var(--foreground) 7%, transparent);
+}
+
+.dark .ui-card-selected {
+ border-color: color-mix(in oklch, var(--primary) 32%, var(--plane-card-border));
+ background: color-mix(in oklch, var(--surface-selected) 78%, var(--plane-card-bg));
+ box-shadow:
+ inset 0 0 0 1px color-mix(in oklch, var(--primary) 48%, transparent),
+ inset 0 1px 0 var(--elev-overlay-2),
+ 0 1px 2px color-mix(in oklch, var(--foreground) 10%, transparent);
+}
+
+.ui-card-select-indicator {
+ pointer-events: none;
+ position: absolute;
+ left: 0;
+ top: 0.7rem;
+ bottom: 0.7rem;
+ width: 5px;
+ border-radius: 0 9999px 9999px 0;
+ background: linear-gradient(
+ 180deg,
+ color-mix(in oklch, var(--primary) 96%, white 4%) 0%,
+ color-mix(in oklch, var(--primary) 84%, var(--surface-selected-border)) 55%,
+ color-mix(in oklch, var(--primary) 68%, var(--surface-selected-border)) 100%
+ );
+ transition: opacity 180ms ease;
+}
+
+.ui-input,
+.sidebar-input {
+ border: 1px solid var(--sidebar-input-border);
+ background: var(--sidebar-input-bg);
+ color: color-mix(in oklch, white 92%, var(--foreground) 8%);
+ transition:
+ border-color 180ms ease,
+ box-shadow 180ms ease,
+ background-color 180ms ease;
+}
+
+.ui-input::placeholder,
+.sidebar-input::placeholder {
+ color: color-mix(in oklch, white 58%, var(--muted-foreground) 42%);
+}
+
+.ui-input:focus,
+.ui-input:focus-visible,
+.sidebar-input:focus,
+.sidebar-input:focus-visible {
+ border-color: color-mix(in oklch, var(--ring) 72%, var(--sidebar-input-border));
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring) 22%, transparent) !important;
+}
+
+.ui-btn-primary,
+.sidebar-btn-primary {
+ display: inline-flex;
+ min-height: 40px;
+ align-items: center;
+ justify-content: center;
+ border-radius: var(--radius-small);
+ border: 1px solid var(--action-border);
+ background: var(--action-bg);
+ color: var(--action-fg);
+ transition: filter 180ms ease;
+}
+
+.ui-btn-primary:hover,
+.sidebar-btn-primary:hover {
+ background: var(--action-bg-hover);
+ filter: none;
+}
+
+.ui-btn-send {
+ border-radius: var(--radius-small);
+ background: color-mix(in oklch, var(--action-bg) 92%, var(--surface-1));
+}
+
+.ui-btn-send:hover {
+ background: color-mix(in oklch, var(--action-bg-hover) 92%, var(--surface-1));
+}
+
+.ui-btn-secondary {
+ display: inline-flex;
+ min-height: 36px;
+ align-items: center;
+ justify-content: center;
+ border-radius: var(--radius-small);
+ border: 1px solid color-mix(in oklch, var(--border) 82%, transparent);
+ background: color-mix(in oklch, var(--surface-1) 92%, transparent);
+ color: color-mix(in oklch, var(--foreground) 74%, var(--muted-foreground) 26%);
+ transition:
+ border-color 180ms ease,
+ background-color 180ms ease;
+}
+
+.ui-btn-secondary:hover {
+ border-color: color-mix(in oklch, var(--border) 62%, var(--foreground) 38%);
+ background: color-mix(in oklch, var(--surface-2) 90%, transparent);
+}
+
+.ui-btn-ghost,
+.sidebar-btn-ghost {
+ display: inline-flex;
+ min-height: 36px;
+ align-items: center;
+ justify-content: center;
+ border-radius: var(--radius-small);
+ border: 0;
+ background: color-mix(in oklch, var(--surface-1) 82%, transparent);
+ color: color-mix(in oklch, var(--foreground) 76%, var(--muted-foreground) 24%);
+ transition:
+ border-color 180ms ease,
+ background-color 180ms ease;
+}
+
+.ui-btn-ghost:hover,
+.sidebar-btn-ghost:hover {
+ background: color-mix(in oklch, var(--surface-2) 96%, transparent);
+ box-shadow: var(--shadow-2xs);
+}
+
+.ui-btn-icon {
+ --ui-btn-icon-size: 2.25rem;
+ display: inline-flex;
+ height: var(--ui-btn-icon-size);
+ width: var(--ui-btn-icon-size);
+ align-items: center;
+ justify-content: center;
+ border-radius: var(--radius-small);
+ border: 0;
+ background: color-mix(in oklch, var(--surface-3) 92%, transparent);
+ color: color-mix(in oklch, var(--foreground) 72%, var(--muted-foreground) 28%);
+ transition:
+ border-color 180ms ease,
+ background-color 180ms ease,
+ color 180ms ease;
+}
+
+.ui-btn-icon-sm {
+ --ui-btn-icon-size: 1.95rem;
+}
+
+.ui-btn-icon-xs {
+ --ui-btn-icon-size: 1.6rem;
+}
+
+.agent-avatar-shuffle-btn {
+ border: 1px solid color-mix(in oklch, var(--border) 74%, transparent);
+ background: color-mix(in oklch, var(--surface-1) 92%, var(--surface-2));
+ color: color-mix(in oklch, var(--foreground) 80%, var(--muted-foreground) 20%);
+ box-shadow: 0 1px 2px color-mix(in oklch, var(--foreground) 14%, transparent);
+ opacity: 0.92;
+}
+
+.ui-btn-icon.agent-avatar-shuffle-btn:hover,
+.ui-btn-icon.agent-avatar-shuffle-btn:focus-visible {
+ opacity: 1;
+ background: color-mix(in oklch, var(--surface-1) 98%, var(--surface-2));
+ color: color-mix(in oklch, var(--foreground) 92%, var(--muted-foreground) 8%);
+ box-shadow: 0 1px 3px color-mix(in oklch, var(--foreground) 18%, transparent);
+}
+
+.ui-topbar {
+ border-bottom: 1px solid color-mix(in oklch, var(--border) 72%, transparent);
+ border-radius: 0;
+ background: color-mix(in oklch, var(--surface-1) 96%, var(--surface-0));
+ box-shadow: none;
+}
+
+.dark .ui-topbar {
+ border-bottom-color: color-mix(in oklch, var(--border) 88%, var(--surface-selected-border));
+ background: color-mix(in oklch, var(--surface-1) 94%, var(--surface-0));
+}
+
+.ui-topbar .ui-btn-icon {
+ --ui-btn-icon-size: 1.55rem;
+ background: transparent;
+ box-shadow: none;
+}
+
+.ui-topbar .ui-btn-icon:hover {
+ background: color-mix(in oklch, var(--surface-2) 72%, transparent);
+ box-shadow: none;
+}
+
+.ui-btn-icon:hover {
+ background: color-mix(in oklch, var(--surface-2) 98%, transparent);
+ box-shadow: var(--shadow-2xs);
+ color: color-mix(in oklch, var(--foreground) 88%, var(--muted-foreground) 12%);
+}
+
+.ui-segment {
+ display: grid;
+ gap: 0.45rem;
+ border: 1px solid color-mix(in oklch, var(--border) 78%, transparent);
+ border-radius: var(--radius-small);
+ padding: 0.2rem;
+ background: color-mix(in oklch, var(--surface-2) 78%, transparent);
+ box-shadow: none;
+}
+
+.ui-segment-command-mode {
+ gap: 0.46rem;
+ padding: 0.32rem;
+}
+
+.dark .ui-segment-command-mode {
+ border-color: color-mix(in oklch, var(--border) 94%, transparent);
+ background: color-mix(in oklch, var(--surface-2) 92%, var(--surface-1));
+ box-shadow: inset 0 1px 0 var(--elev-overlay-1);
+}
+
+.ui-segment-item {
+ border: 1px solid transparent;
+ border-radius: calc(var(--radius-small) - 2px);
+ background: color-mix(in oklch, var(--surface-1) 88%, var(--surface-2));
+ color: color-mix(in oklch, var(--foreground) 64%, var(--muted-foreground) 36%);
+ font-size: 12px;
+ font-weight: 500;
+ letter-spacing: 0.02em;
+ box-shadow: none;
+ transition:
+ background-color 180ms ease,
+ box-shadow 180ms ease,
+ color 180ms ease;
+}
+
+.ui-segment-item[data-active="true"],
+.ui-segment-item[aria-pressed="true"] {
+ background: color-mix(in oklch, var(--surface-selected) 88%, var(--surface-1));
+ border: 1px solid var(--surface-selected-border);
+ color: color-mix(in oklch, var(--foreground) 90%, var(--muted-foreground) 10%);
+ box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--primary) 24%, transparent);
+}
+
+.ui-segment-fleet-filter {
+ padding-left: 0;
+ padding-right: 0;
+ border: 0;
+ background: transparent;
+ box-shadow: none;
+}
+
+.ui-segment-fleet-filter .ui-segment-item {
+ border: 0;
+ box-shadow: none;
+}
+
+.ui-segment-fleet-filter .ui-segment-item[data-active="true"],
+.ui-segment-fleet-filter .ui-segment-item[aria-pressed="true"] {
+ border: 0;
+ box-shadow: none;
+}
+
+.ui-segment-command-mode .ui-segment-item {
+ min-height: 2.125rem;
+ border-color: color-mix(in oklch, var(--border) 68%, transparent);
+ background: color-mix(in oklch, var(--surface-1) 86%, var(--surface-2));
+ color: color-mix(in oklch, var(--foreground) 74%, var(--muted-foreground) 26%);
+}
+
+.dark .ui-segment-command-mode .ui-segment-item {
+ border-color: color-mix(in oklch, var(--border) 78%, transparent);
+ background: color-mix(in oklch, var(--surface-1) 90%, var(--surface-2));
+ color: color-mix(in oklch, var(--foreground) 80%, var(--muted-foreground) 20%);
+}
+
+.ui-segment-command-mode .ui-segment-item[data-active="true"],
+.ui-segment-command-mode .ui-segment-item[aria-pressed="true"] {
+ color: color-mix(in oklch, var(--foreground) 96%, var(--muted-foreground) 4%);
+ border-color: color-mix(in oklch, var(--primary) 64%, var(--surface-selected-border));
+ background: color-mix(in oklch, var(--primary) 24%, var(--surface-selected));
+ box-shadow:
+ inset 0 1px 0 var(--elev-overlay-2),
+ inset 0 0 0 1px color-mix(in oklch, var(--primary) 42%, transparent);
+}
+
+.ui-selected {
+ background: var(--surface-selected);
+ border: 1px solid var(--surface-selected-border);
+ box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--primary) 24%, transparent);
+}
+
+.ui-chat-scroll {
+ border: 1px solid color-mix(in oklch, var(--border) 72%, transparent);
+ border-radius: var(--radius-card);
+ background: var(--chat-surface-bg);
+ --foreground: #ffffff;
+}
+
+.dark .ui-chat-scroll {
+ border-color: color-mix(in oklch, var(--plane-workspace-border) 90%, var(--border));
+ background: color-mix(in oklch, var(--surface-1) 95%, var(--surface-0));
+}
+
+.ui-chat-scroll-borderless {
+ border: 0 !important;
+ border-radius: 0;
+ background: transparent;
+}
+
+.dark .ui-chat-scroll-borderless {
+ border: 0 !important;
+ background: transparent;
+}
+
+.ui-chat-user-card {
+ border: 1px solid var(--chat-user-border);
+ background: var(--chat-user-bg);
+ box-shadow: 0 1px 3px color-mix(in oklch, var(--foreground) 8%, transparent);
+}
+
+.ui-chat-assistant-card {
+ border: 1px solid var(--chat-assistant-border);
+ border-radius: var(--radius-small);
+ background: var(--chat-assistant-bg);
+ padding: 0.92rem 1.08rem;
+ box-shadow: inset 0 1px 0 var(--elev-overlay-1);
+}
+
+.dark .ui-chat-assistant-card {
+ padding: 1rem 1.2rem;
+ box-shadow:
+ inset 0 1px 0 var(--elev-overlay-2),
+ 0 1px 2px color-mix(in oklch, var(--foreground) 10%, transparent);
+}
+
+.ui-chat-thinking {
+ border: 1px solid color-mix(in oklch, var(--border) 48%, transparent);
+ background: color-mix(in oklch, var(--surface-1) 92%, transparent);
+ color: color-mix(in oklch, var(--muted-foreground) 86%, var(--foreground) 14%);
+}
+
+.ui-settings-row {
+ border: 1px solid color-mix(in oklch, var(--border) 56%, transparent);
+ border-radius: var(--radius-small);
+ background: color-mix(in oklch, var(--surface-2) 78%, transparent);
+}
+
+.ui-chip {
+ display: inline-flex;
+ align-items: center;
+ border-radius: var(--radius-small);
+ border: 1px solid var(--status-idle-border);
+ background: var(--status-idle-bg);
+ box-shadow: inset 0 1px 0 var(--elev-overlay-1);
+ color: var(--status-idle-fg);
+}
+
+.ui-scroll {
+ scrollbar-width: thin;
+ scrollbar-color: color-mix(in oklch, var(--border) 60%, transparent) transparent;
+}
+
+.dark .ui-scroll {
+ scrollbar-color: color-mix(in oklch, var(--foreground) 36%, var(--border)) transparent;
+}
+
+.ui-scroll::-webkit-scrollbar {
+ width: 8px;
+ height: 8px;
+}
+
+.ui-scroll::-webkit-scrollbar-thumb {
+ border-radius: 9999px;
+ background: color-mix(in oklch, var(--border) 58%, transparent);
+}
+
+.dark .ui-scroll::-webkit-scrollbar-thumb {
+ background: color-mix(in oklch, var(--foreground) 34%, var(--border));
+}
+
+.dark .ui-scroll::-webkit-scrollbar-thumb:hover {
+ background: color-mix(in oklch, var(--foreground) 44%, var(--border));
+}
+
+.lucide {
+ stroke-width: 1.75;
+}
+
+.agent-inspect-panel .text-\[10px\] {
+ font-size: 11px;
+}
+
+.agent-inspect-panel .text-\[11px\] {
+ font-size: 12px;
+}
+
+[data-agent-panel] .text-\[10px\] {
+ font-size: 11px;
+}
+
+[data-agent-panel] .text-\[11px\] {
+ font-size: 12px;
+}
+
+.console-title {
+ font-family: var(--font-display), sans-serif;
+ letter-spacing: 0.04em;
+ text-transform: uppercase;
+}
+
+.type-page-title {
+ font-size: 23px;
+ font-weight: 500;
+ line-height: 1.16;
+ letter-spacing: 0.005em;
+}
+
+.type-agent-name {
+ font-size: 16px;
+ font-weight: 500;
+ line-height: 1.2;
+}
+
+.type-secondary-heading {
+ font-size: 15px;
+ font-weight: 500;
+ line-height: 1.3;
+}
+
+.type-body {
+ font-size: 14px;
+ font-weight: 400;
+ line-height: 1.68;
+}
+
+.type-meta {
+ font-size: 12.5px;
+ font-weight: 400;
+ line-height: 1.35;
+ letter-spacing: 0.01em;
+}
+
+.ui-badge {
+ display: inline-flex;
+ align-items: center;
+ border-radius: var(--radius-small);
+ padding: 0.18rem 0.58rem;
+ font-family: var(--font-mono), monospace;
+ font-size: 12px;
+ font-weight: 400;
+ letter-spacing: 0.045em;
+ line-height: 1.1;
+}
+
+.ui-control-important {
+ border-color: var(--model-control-border);
+ background: color-mix(in oklch, var(--surface-1) 80%, var(--surface-2));
+ box-shadow: 0 1px 2px color-mix(in oklch, var(--foreground) 8%, transparent);
+}
+
+.ui-alert-danger {
+ border: 1px solid var(--danger-soft-border);
+ background: var(--danger-soft-bg);
+ color: var(--danger-soft-fg);
+}
+
+.ui-text-danger {
+ color: var(--danger-soft-fg);
+}
+
+.ui-text-success {
+ color: var(--status-running-fg);
+}
+
+.ui-badge-status-idle {
+ border: 1px solid var(--status-idle-border);
+ background: var(--status-idle-bg);
+ color: var(--status-idle-fg);
+ box-shadow: inset 0 1px 0 var(--elev-overlay-1);
+}
+
+.ui-badge-status-running {
+ border: 1px solid var(--status-running-border);
+ background: var(--status-running-bg);
+ color: var(--status-running-fg);
+}
+
+.ui-badge-status-error {
+ border: 1px solid var(--status-error-border);
+ background: var(--status-error-bg);
+ color: var(--status-error-fg);
+}
+
+.ui-badge-status-connecting {
+ border: 1px solid var(--status-connecting-border);
+ background: var(--status-connecting-bg);
+ color: var(--status-connecting-fg);
+}
+
+.ui-badge-status-connected {
+ border: 1px solid var(--status-connected-border);
+ background: var(--status-connected-bg);
+ color: var(--status-connected-fg);
+}
+
+.ui-badge-status-disconnected {
+ border: 1px solid var(--status-disconnected-border);
+ background: var(--status-disconnected-bg);
+ color: var(--status-disconnected-fg);
+}
+
+.ui-badge-approval {
+ border: 1px solid var(--status-approval-border);
+ background: var(--status-approval-bg);
+ color: var(--status-approval-fg);
+}
+
+.ui-btn-danger {
+ border: 1px solid var(--danger-soft-border);
+ background: var(--danger-soft-bg);
+ color: var(--danger-soft-fg);
+}
+
+.ui-btn-danger:hover {
+ background: color-mix(in oklch, var(--destructive) 20%, transparent);
+}
+
+.ui-btn-icon-danger {
+ color: var(--danger-soft-fg);
+}
+
+.ui-btn-icon-danger:hover {
+ background: color-mix(in oklch, var(--destructive) 18%, transparent);
+ color: var(--danger-soft-fg);
+}
+
+.ui-dot-status-disconnected,
+.ui-dot-status-connecting,
+.ui-dot-status-connected {
+ border-radius: 9999px;
+}
+
+.ui-dot-status-disconnected {
+ background: var(--status-disconnected-fg);
+}
+
+.ui-dot-status-connecting {
+ background: var(--status-connecting-fg);
+}
+
+.ui-dot-status-connected {
+ background: var(--status-connected-fg);
+}
+
+.ui-command-surface {
+ border: 1px solid var(--command-border);
+ background: var(--command-bg);
+}
+
+.ui-command-surface code {
+ color: var(--command-fg);
+}
+
+.ui-command-copy {
+ border: 1px solid var(--command-copy-border);
+ background: var(--command-copy-bg);
+ color: var(--command-fg);
+}
+
+.ui-command-copy:hover {
+ background: var(--command-copy-hover-bg);
+}
+
+.status-ping {
+ opacity: 0.9;
+}
+
+.typing-dots {
+ display: inline-flex;
+ align-items: center;
+ gap: 3px;
+}
+
+.typing-dots > span {
+ width: 4px;
+ height: 4px;
+ border-radius: 9999px;
+ background: currentColor;
+ opacity: 0.28;
+ animation: typingPulse 960ms ease-in-out infinite;
+}
+
+.typing-dots > span:nth-child(2) {
+ animation-delay: 160ms;
+}
+
+.typing-dots > span:nth-child(3) {
+ animation-delay: 320ms;
+}
+
+:where(button, input, textarea, select, [role="button"], summary):focus-visible {
+ outline: 2px solid var(--ring);
+ outline-offset: 2px;
+ box-shadow: none !important;
+}
+
+.agent-rename-input:focus,
+.agent-rename-input:focus-visible {
+ outline: none !important;
+ outline-offset: 0;
+ border-color: color-mix(in oklch, var(--ring) 62%, var(--sidebar-input-border));
+ box-shadow: 0 0 0 2px color-mix(in oklch, var(--ring) 20%, transparent) !important;
+}
+
+.agent-rename-control:focus-visible {
+ outline: none !important;
+ outline-offset: 0;
+ box-shadow: 0 0 0 2px color-mix(in oklch, var(--ring) 20%, transparent) !important;
+}
+
+.chat-composer-input {
+ color: #ffffff;
+}
+
+.type-agent-name {
+ color: #ffffff;
+}
+
+.chat-composer-input:focus,
+.chat-composer-input:focus-visible {
+ outline: none !important;
+ outline-offset: 0 !important;
+ box-shadow: none !important;
+}
+
+@keyframes fadeUp {
+ from {
+ opacity: 0;
+ transform: translateY(14px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@keyframes typingPulse {
+ 0%,
+ 80%,
+ 100% {
+ opacity: 0.28;
+ transform: translateY(0);
+ }
+ 40% {
+ opacity: 0.95;
+ transform: translateY(-1px);
+ }
+}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
new file mode 100644
index 0000000..42ea6cd
--- /dev/null
+++ b/src/app/layout.tsx
@@ -0,0 +1,48 @@
+import type { Metadata } from "next";
+import { Bebas_Neue, IBM_Plex_Mono, IBM_Plex_Sans } from "next/font/google";
+import "./globals.css";
+
+export const metadata: Metadata = {
+ title: "Claw3D",
+ description: "Focused operator studio for the OpenClaw gateway.",
+};
+
+const display = Bebas_Neue({
+ variable: "--font-display",
+ weight: "400",
+ subsets: ["latin"],
+});
+
+const sans = IBM_Plex_Sans({
+ variable: "--font-sans",
+ weight: ["400", "500", "600", "700"],
+ subsets: ["latin"],
+});
+
+const mono = IBM_Plex_Mono({
+ variable: "--font-mono",
+ weight: ["400", "500", "600"],
+ subsets: ["latin"],
+});
+
+export default function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+
+
+
+
+
+ {children}
+
+
+ );
+}
diff --git a/src/app/office/builder/page.tsx b/src/app/office/builder/page.tsx
new file mode 100644
index 0000000..c94cbb5
--- /dev/null
+++ b/src/app/office/builder/page.tsx
@@ -0,0 +1,21 @@
+import { OfficeBuilderPanel } from "@/features/office/components/OfficeBuilderPanel";
+import { createStarterOfficeMap, normalizeOfficeMap } from "@/lib/office/schema";
+import { getPublishedOfficeMap } from "@/lib/office/store";
+
+const WORKSPACE_ID = "default";
+const OFFICE_ID = "hq";
+
+export default function OfficeBuilderPage() {
+ const fallback = createStarterOfficeMap({
+ workspaceId: WORKSPACE_ID,
+ officeVersionId: "builder-draft",
+ width: 1600,
+ height: 900,
+ });
+ const map = normalizeOfficeMap(getPublishedOfficeMap(WORKSPACE_ID), fallback);
+ return (
+
+
+
+ );
+}
diff --git a/src/app/office/page.tsx b/src/app/office/page.tsx
new file mode 100644
index 0000000..2be82e8
--- /dev/null
+++ b/src/app/office/page.tsx
@@ -0,0 +1,13 @@
+import { Suspense } from "react";
+import { AgentStoreProvider } from "@/features/agents/state/store";
+import { OfficeScreen } from "@/features/office/screens/OfficeScreen";
+
+export default function OfficePage() {
+ return (
+
+
+
+
+
+ );
+}
diff --git a/src/app/page.tsx b/src/app/page.tsx
new file mode 100644
index 0000000..f886929
--- /dev/null
+++ b/src/app/page.tsx
@@ -0,0 +1,5 @@
+import { redirect } from "next/navigation";
+
+export default function Home() {
+ redirect("/office");
+}
diff --git a/src/app/styles/markdown.css b/src/app/styles/markdown.css
new file mode 100644
index 0000000..6281e9a
--- /dev/null
+++ b/src/app/styles/markdown.css
@@ -0,0 +1,196 @@
+.agent-markdown {
+ display: block;
+ white-space: normal;
+ word-break: break-word;
+ line-height: 1.7;
+}
+
+.agent-markdown p {
+ margin: 0;
+}
+
+.agent-markdown p + p,
+.agent-markdown ul,
+.agent-markdown ol,
+.agent-markdown pre,
+.agent-markdown blockquote,
+.agent-markdown table {
+ margin-top: 1rem;
+}
+
+.agent-markdown h1,
+.agent-markdown h2,
+.agent-markdown h3,
+.agent-markdown h4,
+.agent-markdown h5,
+.agent-markdown h6 {
+ margin: 1.1rem 0 0.5rem 0;
+ font-weight: 700;
+ letter-spacing: 0.01em;
+}
+
+.agent-markdown :is(h1, h2, h3, h4, h5, h6):first-child {
+ margin-top: 0;
+}
+
+.agent-markdown h1 + p,
+.agent-markdown h2 + p,
+.agent-markdown h3 + p,
+.agent-markdown h4 + p,
+.agent-markdown h5 + p,
+.agent-markdown h6 + p,
+.agent-markdown h1 + ul,
+.agent-markdown h2 + ul,
+.agent-markdown h3 + ul,
+.agent-markdown h4 + ul,
+.agent-markdown h5 + ul,
+.agent-markdown h6 + ul,
+.agent-markdown h1 + ol,
+.agent-markdown h2 + ol,
+.agent-markdown h3 + ol,
+.agent-markdown h4 + ol,
+.agent-markdown h5 + ol,
+.agent-markdown h6 + ol {
+ margin-top: 0.25rem;
+}
+
+.agent-markdown ul,
+.agent-markdown ol {
+ padding-left: 1.1rem;
+}
+
+.agent-markdown ul {
+ list-style-type: disc;
+}
+
+.agent-markdown ol {
+ list-style-type: decimal;
+}
+
+.agent-markdown ul ul {
+ list-style-type: circle;
+ padding-left: 1.2rem;
+}
+
+.agent-markdown ul ul ul {
+ list-style-type: square;
+}
+
+.agent-markdown ol ol {
+ list-style-type: lower-alpha;
+ padding-left: 1.3rem;
+}
+
+.agent-markdown ol ol ol {
+ list-style-type: lower-roman;
+}
+
+.agent-markdown ul ol {
+ list-style-type: decimal;
+ padding-left: 1.3rem;
+}
+
+.agent-markdown ol ul {
+ list-style-type: disc;
+ padding-left: 1.2rem;
+}
+
+.agent-markdown li {
+ margin-top: 0.25rem;
+}
+
+.agent-markdown li > p {
+ margin: 0;
+}
+
+.agent-markdown code {
+ font-family: var(--font-mono), ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
+ "Liberation Mono", "Courier New", monospace;
+ font-size: 0.82em;
+ overflow-wrap: anywhere;
+ word-break: break-word;
+ background: color-mix(in oklch, var(--surface-3) 92%, transparent);
+ padding: 0.14rem 0.28rem;
+ border-radius: 0.3125rem;
+ border: 1px solid color-mix(in oklch, var(--surface-selected-border) 88%, transparent);
+}
+
+.agent-markdown pre {
+ background: color-mix(in oklch, var(--surface-2) 94%, transparent);
+ border-radius: 0.4rem;
+ padding: 0.75rem 0.85rem;
+ border: 1px solid color-mix(in oklch, var(--surface-selected-border) 82%, transparent);
+ overflow-x: auto;
+ line-height: 1.64;
+}
+
+.agent-markdown pre code {
+ background: transparent;
+ padding: 0;
+ font-size: 0.85em;
+ border: 0;
+ border-radius: 0;
+}
+
+.agent-tool-markdown pre {
+ max-height: 70px;
+ overflow: auto;
+ padding: 0.5rem 0.6rem;
+}
+
+.agent-markdown blockquote {
+ border-left: 3px solid var(--surface-selected-border);
+ padding-left: 0.7rem;
+ color: var(--muted-foreground);
+}
+
+.agent-markdown table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 0.85em;
+}
+
+.agent-markdown th,
+.agent-markdown td {
+ border: 1px solid var(--border);
+ padding: 0.45rem 0.65rem;
+ text-align: left;
+}
+
+.agent-markdown a {
+ color: color-mix(in oklch, var(--action-bg) 68%, var(--foreground));
+ text-decoration: underline;
+ text-decoration-thickness: 1px;
+ text-underline-offset: 2px;
+}
+
+.agent-markdown img {
+ max-width: 100%;
+ height: auto;
+ display: block;
+ border-radius: 0.5rem;
+ border: 1px solid var(--border);
+}
+
+.dark .agent-markdown code {
+ background: color-mix(in oklch, var(--surface-1) 80%, var(--surface-0));
+ border-color: color-mix(in oklch, var(--chat-assistant-border) 88%, var(--surface-selected-border));
+}
+
+.dark .agent-markdown pre {
+ background: color-mix(in oklch, var(--surface-0) 82%, var(--surface-1));
+ border-color: color-mix(in oklch, var(--chat-assistant-border) 86%, transparent);
+ box-shadow: inset 0 1px 0 var(--elev-overlay-1);
+}
+
+.dark .ui-chat-assistant-card .agent-markdown :is(p + p, ul, ol, pre, blockquote, table) {
+ margin-top: 1.35rem;
+}
+
+.dark .ui-chat-assistant-card .agent-markdown :is(h1, h2, h3, h4, h5, h6) {
+ margin: 1.35rem 0 0.68rem 0;
+}
+
+.dark .ui-chat-assistant-card .agent-markdown li + li {
+ margin-top: 0.42rem;
+}
diff --git a/src/components/theme-toggle.tsx b/src/components/theme-toggle.tsx
new file mode 100644
index 0000000..4601c76
--- /dev/null
+++ b/src/components/theme-toggle.tsx
@@ -0,0 +1,57 @@
+"use client";
+
+import { useEffect, useState } from "react";
+import { Moon, Sun } from "lucide-react";
+
+const THEME_STORAGE_KEY = "theme";
+
+type ThemeMode = "light" | "dark";
+
+const getPreferredTheme = (): ThemeMode => {
+ if (typeof window === "undefined") return "light";
+ const stored = window.localStorage.getItem(THEME_STORAGE_KEY);
+ if (stored === "light" || stored === "dark") return stored;
+ const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
+ return prefersDark ? "dark" : "light";
+};
+
+const applyTheme = (mode: ThemeMode) => {
+ if (typeof document === "undefined") return;
+ document.documentElement.classList.toggle("dark", mode === "dark");
+};
+
+export const ThemeToggle = () => {
+ // Keep SSR + initial hydration stable ("light") to avoid markup mismatch.
+ const [theme, setTheme] = useState("light");
+
+ useEffect(() => {
+ const preferred = getPreferredTheme();
+ // eslint-disable-next-line react-hooks/set-state-in-effect
+ setTheme(preferred);
+ applyTheme(preferred);
+ }, []);
+
+ const toggleTheme = () => {
+ setTheme((current) => {
+ const next: ThemeMode = current === "dark" ? "light" : "dark";
+ if (typeof window !== "undefined") {
+ window.localStorage.setItem(THEME_STORAGE_KEY, next);
+ }
+ applyTheme(next);
+ return next;
+ });
+ };
+
+ const isDark = theme === "dark";
+
+ return (
+
+ );
+};
diff --git a/src/features/agents/approvals/execApprovalControlLoopWorkflow.ts b/src/features/agents/approvals/execApprovalControlLoopWorkflow.ts
new file mode 100644
index 0000000..b8b77f9
--- /dev/null
+++ b/src/features/agents/approvals/execApprovalControlLoopWorkflow.ts
@@ -0,0 +1,239 @@
+import type { PendingExecApproval } from "@/features/agents/approvals/types";
+import {
+ applyApprovalIngressEffects,
+ deriveAwaitingUserInputPatches,
+ derivePendingApprovalPruneDelayMs,
+ prunePendingApprovalState,
+ resolveApprovalAutoResumeDispatch,
+ resolveApprovalAutoResumePreflight,
+ type ApprovalPendingState,
+ type AwaitingUserInputPatch,
+} from "@/features/agents/approvals/execApprovalRuntimeCoordinator";
+import { shouldPauseRunForPendingExecApproval } from "@/features/agents/approvals/execApprovalPausePolicy";
+import {
+ resolveGatewayEventIngressDecision,
+ type CronTranscriptIntent,
+} from "@/features/agents/state/gatewayEventIngressWorkflow";
+import type { AgentState } from "@/features/agents/state/store";
+import type { EventFrame } from "@/lib/gateway/GatewayClient";
+
+export type ExecApprovalPendingSnapshot = ApprovalPendingState;
+
+export type ExecApprovalIngressCommand =
+ | { kind: "replacePendingState"; pendingState: ApprovalPendingState }
+ | {
+ kind: "pauseRunForApproval";
+ approval: PendingExecApproval;
+ preferredAgentId: string | null;
+ }
+ | { kind: "markActivity"; agentId: string }
+ | { kind: "recordCronDedupeKey"; dedupeKey: string }
+ | { kind: "appendCronTranscript"; intent: CronTranscriptIntent };
+
+export type PauseRunIntent =
+ | { kind: "skip"; reason: string }
+ | { kind: "pause"; agentId: string; sessionKey: string; runId: string };
+
+export type AutoResumeIntent =
+ | { kind: "skip"; reason: string }
+ | { kind: "resume"; targetAgentId: string; pausedRunId: string; sessionKey: string };
+
+const resolvePauseTargetAgent = (params: {
+ approval: PendingExecApproval;
+ preferredAgentId: string | null | undefined;
+ agents: AgentState[];
+}): AgentState | null => {
+ const preferredAgentId = params.preferredAgentId?.trim() ?? "";
+ if (preferredAgentId) {
+ const match =
+ params.agents.find((agent) => agent.agentId === preferredAgentId) ?? null;
+ if (match) return match;
+ }
+
+ const approvalSessionKey = params.approval.sessionKey?.trim() ?? "";
+ if (!approvalSessionKey) return null;
+
+ return (
+ params.agents.find((agent) => agent.sessionKey.trim() === approvalSessionKey) ??
+ null
+ );
+};
+
+export const planPausedRunMapCleanup = (params: {
+ pausedRunIdByAgentId: ReadonlyMap;
+ agents: AgentState[];
+}): string[] => {
+ const staleAgentIds: string[] = [];
+ for (const [agentId, trackedRunId] of params.pausedRunIdByAgentId.entries()) {
+ const trackedAgent = params.agents.find((agent) => agent.agentId === agentId) ?? null;
+ const currentRunId = trackedAgent?.runId?.trim() ?? "";
+ if (!currentRunId || currentRunId !== trackedRunId) {
+ staleAgentIds.push(agentId);
+ }
+ }
+ return staleAgentIds;
+};
+
+export const planPauseRunIntent = (params: {
+ approval: PendingExecApproval;
+ preferredAgentId?: string | null;
+ agents: AgentState[];
+ pausedRunIdByAgentId: ReadonlyMap;
+}): PauseRunIntent => {
+ const agent = resolvePauseTargetAgent({
+ approval: params.approval,
+ preferredAgentId: params.preferredAgentId,
+ agents: params.agents,
+ });
+ if (!agent) {
+ return { kind: "skip", reason: "missing-agent" };
+ }
+
+ const runId = agent.runId?.trim() ?? "";
+ if (!runId) {
+ return { kind: "skip", reason: "missing-run-id" };
+ }
+
+ const pausedRunId = params.pausedRunIdByAgentId.get(agent.agentId) ?? null;
+ const shouldPause = shouldPauseRunForPendingExecApproval({
+ agent,
+ approval: params.approval,
+ pausedRunId,
+ });
+ if (!shouldPause) {
+ return { kind: "skip", reason: "pause-policy-denied" };
+ }
+
+ const sessionKey = agent.sessionKey.trim();
+ if (!sessionKey) {
+ return { kind: "skip", reason: "missing-session-key" };
+ }
+
+ return {
+ kind: "pause",
+ agentId: agent.agentId,
+ sessionKey,
+ runId,
+ };
+};
+
+export const planAutoResumeIntent = (params: {
+ approval: PendingExecApproval;
+ targetAgentId: string;
+ pendingState: ApprovalPendingState;
+ pausedRunIdByAgentId: ReadonlyMap;
+ agents: AgentState[];
+}): AutoResumeIntent => {
+ const preflight = resolveApprovalAutoResumePreflight({
+ approval: params.approval,
+ targetAgentId: params.targetAgentId,
+ pendingState: params.pendingState,
+ pausedRunIdByAgentId: params.pausedRunIdByAgentId,
+ });
+
+ if (preflight.kind !== "resume") {
+ return { kind: "skip", reason: preflight.reason };
+ }
+
+ const dispatchIntent = resolveApprovalAutoResumeDispatch({
+ targetAgentId: preflight.targetAgentId,
+ pausedRunId: preflight.pausedRunId,
+ agents: params.agents,
+ });
+
+ if (dispatchIntent.kind !== "resume") {
+ return { kind: "skip", reason: dispatchIntent.reason };
+ }
+
+ return {
+ kind: "resume",
+ targetAgentId: dispatchIntent.targetAgentId,
+ pausedRunId: dispatchIntent.pausedRunId,
+ sessionKey: dispatchIntent.sessionKey,
+ };
+};
+
+export const planIngressCommands = (params: {
+ event: EventFrame;
+ agents: AgentState[];
+ pendingState: ApprovalPendingState;
+ pausedRunIdByAgentId: ReadonlyMap;
+ seenCronDedupeKeys: ReadonlySet;
+ nowMs: number;
+}): ExecApprovalIngressCommand[] => {
+ const ingressDecision = resolveGatewayEventIngressDecision({
+ event: params.event,
+ agents: params.agents,
+ seenCronDedupeKeys: params.seenCronDedupeKeys,
+ nowMs: params.nowMs,
+ });
+
+ const approvalIngress = applyApprovalIngressEffects({
+ pendingState: params.pendingState,
+ approvalEffects: ingressDecision.approvalEffects,
+ agents: params.agents,
+ pausedRunIdByAgentId: params.pausedRunIdByAgentId,
+ });
+
+ const commands: ExecApprovalIngressCommand[] = [];
+ if (
+ approvalIngress.pendingState.approvalsByAgentId !== params.pendingState.approvalsByAgentId ||
+ approvalIngress.pendingState.unscopedApprovals !== params.pendingState.unscopedApprovals
+ ) {
+ commands.push({
+ kind: "replacePendingState",
+ pendingState: approvalIngress.pendingState,
+ });
+ }
+
+ for (const pauseRequest of approvalIngress.pauseRequests) {
+ commands.push({
+ kind: "pauseRunForApproval",
+ approval: pauseRequest.approval,
+ preferredAgentId: pauseRequest.preferredAgentId,
+ });
+ }
+
+ for (const agentId of approvalIngress.markActivityAgentIds) {
+ commands.push({ kind: "markActivity", agentId });
+ }
+
+ if (ingressDecision.cronDedupeKeyToRecord) {
+ commands.push({
+ kind: "recordCronDedupeKey",
+ dedupeKey: ingressDecision.cronDedupeKeyToRecord,
+ });
+ }
+
+ if (ingressDecision.cronTranscriptIntent) {
+ commands.push({
+ kind: "appendCronTranscript",
+ intent: ingressDecision.cronTranscriptIntent,
+ });
+ }
+
+ return commands;
+};
+
+export const planPendingPruneDelay = (params: {
+ pendingState: ApprovalPendingState;
+ nowMs: number;
+ graceMs: number;
+}): number | null => {
+ return derivePendingApprovalPruneDelayMs(params);
+};
+
+export const planPrunedPendingState = (params: {
+ pendingState: ApprovalPendingState;
+ nowMs: number;
+ graceMs: number;
+}): ApprovalPendingState => {
+ return prunePendingApprovalState(params).pendingState;
+};
+
+export const planAwaitingUserInputPatches = (params: {
+ agents: AgentState[];
+ approvalsByAgentId: Record;
+}): AwaitingUserInputPatch[] => {
+ return deriveAwaitingUserInputPatches(params);
+};
diff --git a/src/features/agents/approvals/execApprovalEvents.ts b/src/features/agents/approvals/execApprovalEvents.ts
new file mode 100644
index 0000000..f9115ee
--- /dev/null
+++ b/src/features/agents/approvals/execApprovalEvents.ts
@@ -0,0 +1,106 @@
+import type { AgentState } from "@/features/agents/state/store";
+import type { EventFrame } from "@/lib/gateway/GatewayClient";
+import type { ExecApprovalDecision } from "@/features/agents/approvals/types";
+
+type RequestedPayload = {
+ id: string;
+ request: {
+ command: string;
+ cwd: string | null;
+ host: string | null;
+ security: string | null;
+ ask: string | null;
+ agentId: string | null;
+ resolvedPath: string | null;
+ sessionKey: string | null;
+ };
+ createdAtMs: number;
+ expiresAtMs: number;
+};
+
+type ResolvedPayload = {
+ id: string;
+ decision: ExecApprovalDecision;
+ resolvedBy: string | null;
+ ts: number;
+};
+
+const asRecord = (value: unknown): Record | null =>
+ value && typeof value === "object" && !Array.isArray(value)
+ ? (value as Record)
+ : null;
+
+const asNonEmptyString = (value: unknown): string | null => {
+ if (typeof value !== "string") return null;
+ const trimmed = value.trim();
+ return trimmed.length > 0 ? trimmed : null;
+};
+
+const asOptionalString = (value: unknown): string | null =>
+ typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
+
+const asPositiveTimestamp = (value: unknown): number | null =>
+ typeof value === "number" && Number.isFinite(value) && value > 0 ? value : null;
+
+export const parseExecApprovalRequested = (event: EventFrame): RequestedPayload | null => {
+ if (event.type !== "event" || event.event !== "exec.approval.requested") return null;
+ const payload = asRecord(event.payload);
+ if (!payload) return null;
+ const id = asNonEmptyString(payload.id);
+ const request = asRecord(payload.request);
+ const createdAtMs = asPositiveTimestamp(payload.createdAtMs);
+ const expiresAtMs = asPositiveTimestamp(payload.expiresAtMs);
+ if (!id || !request || !createdAtMs || !expiresAtMs) return null;
+ const command = asNonEmptyString(request.command);
+ if (!command) return null;
+ return {
+ id,
+ request: {
+ command,
+ cwd: asOptionalString(request.cwd),
+ host: asOptionalString(request.host),
+ security: asOptionalString(request.security),
+ ask: asOptionalString(request.ask),
+ agentId: asOptionalString(request.agentId),
+ resolvedPath: asOptionalString(request.resolvedPath),
+ sessionKey: asOptionalString(request.sessionKey),
+ },
+ createdAtMs,
+ expiresAtMs,
+ };
+};
+
+export const parseExecApprovalResolved = (event: EventFrame): ResolvedPayload | null => {
+ if (event.type !== "event" || event.event !== "exec.approval.resolved") return null;
+ const payload = asRecord(event.payload);
+ if (!payload) return null;
+ const id = asNonEmptyString(payload.id);
+ const decisionRaw = asNonEmptyString(payload.decision);
+ const ts = asPositiveTimestamp(payload.ts);
+ if (!id || !decisionRaw || !ts) return null;
+ if (decisionRaw !== "allow-once" && decisionRaw !== "allow-always" && decisionRaw !== "deny") {
+ return null;
+ }
+ return {
+ id,
+ decision: decisionRaw,
+ resolvedBy: asOptionalString(payload.resolvedBy),
+ ts,
+ };
+};
+
+export const resolveExecApprovalAgentId = (params: {
+ requested: RequestedPayload;
+ agents: AgentState[];
+}): string | null => {
+ const requestedAgentId = params.requested.request.agentId;
+ if (requestedAgentId) {
+ return requestedAgentId;
+ }
+ const requestedSessionKey = params.requested.request.sessionKey;
+ if (!requestedSessionKey) return null;
+ const matchedBySession = params.agents.find(
+ (agent) => agent.sessionKey.trim() === requestedSessionKey
+ );
+ return matchedBySession?.agentId ?? null;
+};
diff --git a/src/features/agents/approvals/execApprovalLifecycleWorkflow.ts b/src/features/agents/approvals/execApprovalLifecycleWorkflow.ts
new file mode 100644
index 0000000..2899cb2
--- /dev/null
+++ b/src/features/agents/approvals/execApprovalLifecycleWorkflow.ts
@@ -0,0 +1,127 @@
+import type { ExecApprovalDecision, PendingExecApproval } from "@/features/agents/approvals/types";
+import {
+ parseExecApprovalRequested,
+ parseExecApprovalResolved,
+ resolveExecApprovalAgentId,
+} from "@/features/agents/approvals/execApprovalEvents";
+import type { AgentState } from "@/features/agents/state/store";
+import type { EventFrame } from "@/lib/gateway/GatewayClient";
+import { GatewayResponseError } from "@/lib/gateway/errors";
+
+export type ExecApprovalEventEffects = {
+ scopedUpserts: Array<{ agentId: string; approval: PendingExecApproval }>;
+ unscopedUpserts: PendingExecApproval[];
+ removals: string[];
+ markActivityAgentIds: string[];
+};
+
+export type ExecApprovalFollowUpIntent = {
+ shouldSend: boolean;
+ agentId: string | null;
+ sessionKey: string | null;
+ message: string | null;
+};
+
+const EMPTY_EVENT_EFFECTS: ExecApprovalEventEffects = {
+ scopedUpserts: [],
+ unscopedUpserts: [],
+ removals: [],
+ markActivityAgentIds: [],
+};
+
+const NO_FOLLOW_UP_INTENT: ExecApprovalFollowUpIntent = {
+ shouldSend: false,
+ agentId: null,
+ sessionKey: null,
+ message: null,
+};
+
+export const resolveExecApprovalEventEffects = (params: {
+ event: EventFrame;
+ agents: AgentState[];
+}): ExecApprovalEventEffects | null => {
+ const requested = parseExecApprovalRequested(params.event);
+ if (requested) {
+ const resolvedAgentId = resolveExecApprovalAgentId({
+ requested,
+ agents: params.agents,
+ });
+ const approval: PendingExecApproval = {
+ id: requested.id,
+ agentId: resolvedAgentId,
+ sessionKey: requested.request.sessionKey,
+ command: requested.request.command,
+ cwd: requested.request.cwd,
+ host: requested.request.host,
+ security: requested.request.security,
+ ask: requested.request.ask,
+ resolvedPath: requested.request.resolvedPath,
+ createdAtMs: requested.createdAtMs,
+ expiresAtMs: requested.expiresAtMs,
+ resolving: false,
+ error: null,
+ };
+ if (!resolvedAgentId) {
+ return {
+ ...EMPTY_EVENT_EFFECTS,
+ unscopedUpserts: [approval],
+ };
+ }
+ return {
+ ...EMPTY_EVENT_EFFECTS,
+ scopedUpserts: [{ agentId: resolvedAgentId, approval }],
+ markActivityAgentIds: [resolvedAgentId],
+ };
+ }
+
+ const resolved = parseExecApprovalResolved(params.event);
+ if (!resolved) {
+ return null;
+ }
+ return {
+ ...EMPTY_EVENT_EFFECTS,
+ removals: [resolved.id],
+ };
+};
+
+export const resolveExecApprovalFollowUpIntent = (params: {
+ decision: ExecApprovalDecision;
+ approval: PendingExecApproval | null;
+ agents: AgentState[];
+ followUpMessage: string;
+}): ExecApprovalFollowUpIntent => {
+ if (params.decision !== "allow-once" && params.decision !== "allow-always") {
+ return NO_FOLLOW_UP_INTENT;
+ }
+ if (!params.approval) {
+ return NO_FOLLOW_UP_INTENT;
+ }
+ const scopedAgentId = params.approval.agentId?.trim() ?? "";
+ const sessionAgentId =
+ params.approval.sessionKey?.trim()
+ ? (params.agents.find(
+ (agent) => agent.sessionKey.trim() === params.approval?.sessionKey?.trim()
+ )?.agentId ?? "")
+ : "";
+ const targetAgentId = scopedAgentId || sessionAgentId;
+ if (!targetAgentId) {
+ return NO_FOLLOW_UP_INTENT;
+ }
+ const targetSessionKey =
+ params.approval.sessionKey?.trim() ||
+ params.agents.find((agent) => agent.agentId === targetAgentId)?.sessionKey?.trim() ||
+ "";
+ const followUpMessage = params.followUpMessage.trim();
+ if (!targetSessionKey || !followUpMessage) {
+ return NO_FOLLOW_UP_INTENT;
+ }
+ return {
+ shouldSend: true,
+ agentId: targetAgentId,
+ sessionKey: targetSessionKey,
+ message: followUpMessage,
+ };
+};
+
+export const shouldTreatExecApprovalResolveErrorAsUnknownId = (error: unknown): boolean =>
+ error instanceof GatewayResponseError && /unknown approval id/i.test(error.message);
diff --git a/src/features/agents/approvals/execApprovalPausePolicy.ts b/src/features/agents/approvals/execApprovalPausePolicy.ts
new file mode 100644
index 0000000..d63db47
--- /dev/null
+++ b/src/features/agents/approvals/execApprovalPausePolicy.ts
@@ -0,0 +1,32 @@
+import type { PendingExecApproval } from "@/features/agents/approvals/types";
+import type { AgentState } from "@/features/agents/state/store";
+
+const normalizeExecAsk = (
+ value: string | null | undefined
+): "off" | "on-miss" | "always" | null => {
+ if (typeof value !== "string") return null;
+ const normalized = value.trim().toLowerCase();
+ if (normalized === "off" || normalized === "on-miss" || normalized === "always") {
+ return normalized;
+ }
+ return null;
+};
+
+export const shouldPauseRunForPendingExecApproval = (params: {
+ agent: AgentState | null;
+ approval: PendingExecApproval;
+ pausedRunId: string | null;
+}): boolean => {
+ const agent = params.agent;
+ if (!agent) return false;
+ if (agent.status !== "running") return false;
+
+ const runId = agent.runId?.trim() ?? "";
+ if (!runId) return false;
+ if (params.pausedRunId === runId) return false;
+
+ const approvalAsk = normalizeExecAsk(params.approval.ask);
+ const agentAsk = normalizeExecAsk(agent.sessionExecAsk);
+ const effectiveAsk = approvalAsk ?? agentAsk;
+ return effectiveAsk === "always";
+};
diff --git a/src/features/agents/approvals/execApprovalResolveOperation.ts b/src/features/agents/approvals/execApprovalResolveOperation.ts
new file mode 100644
index 0000000..7463370
--- /dev/null
+++ b/src/features/agents/approvals/execApprovalResolveOperation.ts
@@ -0,0 +1,155 @@
+import type { AgentState } from "@/features/agents/state/store";
+import type { ExecApprovalDecision, PendingExecApproval } from "@/features/agents/approvals/types";
+import {
+ removePendingApprovalEverywhere,
+ updatePendingApprovalById,
+} from "@/features/agents/approvals/pendingStore";
+import { shouldTreatExecApprovalResolveErrorAsUnknownId } from "@/features/agents/approvals/execApprovalLifecycleWorkflow";
+
+type GatewayClientLike = {
+ call: (method: string, params: unknown) => Promise;
+};
+
+type SetState = (next: T | ((current: T) => T)) => void;
+
+export const resolveExecApprovalViaStudio = async (params: {
+ client: GatewayClientLike;
+ approvalId: string;
+ decision: ExecApprovalDecision;
+ getAgents: () => AgentState[];
+ getLatestAgent: (agentId: string) => AgentState | null;
+ getPendingState: () => {
+ approvalsByAgentId: Record;
+ unscopedApprovals: PendingExecApproval[];
+ };
+ setPendingExecApprovalsByAgentId: SetState>;
+ setUnscopedPendingExecApprovals: SetState;
+ requestHistoryRefresh: (agentId: string) => Promise | void;
+ onAllowResolved?: (params: {
+ approval: PendingExecApproval;
+ targetAgentId: string;
+ }) => Promise | void;
+ onAllowed?: (params: { approval: PendingExecApproval; targetAgentId: string }) => Promise | void;
+ isDisconnectLikeError: (error: unknown) => boolean;
+ shouldTreatUnknownId?: (error: unknown) => boolean;
+ logWarn?: (message: string, error: unknown) => void;
+}): Promise => {
+ const id = params.approvalId.trim();
+ if (!id) return;
+
+ const resolvePendingApproval = (
+ approvalId: string,
+ state: {
+ approvalsByAgentId: Record;
+ unscopedApprovals: PendingExecApproval[];
+ }
+ ): PendingExecApproval | null => {
+ for (const approvals of Object.values(state.approvalsByAgentId)) {
+ const found = approvals.find((approval) => approval.id === approvalId);
+ if (found) return found;
+ }
+ return state.unscopedApprovals.find((approval) => approval.id === approvalId) ?? null;
+ };
+
+ const resolveApprovalTargetAgentId = (approval: PendingExecApproval | null): string | null => {
+ if (!approval) return null;
+ const scopedAgentId = approval.agentId?.trim() ?? "";
+ if (scopedAgentId) return scopedAgentId;
+ const scopedSessionKey = approval.sessionKey?.trim() ?? "";
+ if (!scopedSessionKey) return null;
+ const matched = params
+ .getAgents()
+ .find((agent) => agent.sessionKey.trim() === scopedSessionKey);
+ return matched?.agentId ?? null;
+ };
+
+ const snapshot = params.getPendingState();
+ const approval = resolvePendingApproval(id, snapshot);
+
+ const removeLocalApproval = (approvalId: string) => {
+ params.setPendingExecApprovalsByAgentId((current) => {
+ return removePendingApprovalEverywhere({
+ approvalsByAgentId: current,
+ unscopedApprovals: [],
+ approvalId,
+ }).approvalsByAgentId;
+ });
+ params.setUnscopedPendingExecApprovals((current) => {
+ return removePendingApprovalEverywhere({
+ approvalsByAgentId: {},
+ unscopedApprovals: current,
+ approvalId,
+ }).unscopedApprovals;
+ });
+ };
+
+ const setLocalApprovalState = (resolving: boolean, error: string | null) => {
+ params.setPendingExecApprovalsByAgentId((current) => {
+ let changed = false;
+ const next: Record = {};
+ for (const [agentId, approvals] of Object.entries(current)) {
+ const updated = updatePendingApprovalById(approvals, id, (approval) => ({
+ ...approval,
+ resolving,
+ error,
+ }));
+ if (updated !== approvals) {
+ changed = true;
+ }
+ if (updated.length > 0) {
+ next[agentId] = updated;
+ }
+ }
+ return changed ? next : current;
+ });
+ params.setUnscopedPendingExecApprovals((current) =>
+ updatePendingApprovalById(current, id, (approval) => ({
+ ...approval,
+ resolving,
+ error,
+ }))
+ );
+ };
+
+ setLocalApprovalState(true, null);
+
+ try {
+ await params.client.call("exec.approval.resolve", { id, decision: params.decision });
+ removeLocalApproval(id);
+
+ if (params.decision !== "allow-once" && params.decision !== "allow-always") {
+ return;
+ }
+
+ if (!approval) return;
+ const targetAgentId = resolveApprovalTargetAgentId(approval);
+ if (!targetAgentId) return;
+ await params.onAllowResolved?.({ approval, targetAgentId });
+
+ const latest = params.getLatestAgent(targetAgentId);
+ const activeRunId = latest?.runId?.trim() ?? "";
+ if (activeRunId) {
+ try {
+ await params.client.call("agent.wait", { runId: activeRunId, timeoutMs: 15_000 });
+ } catch (waitError) {
+ if (!params.isDisconnectLikeError(waitError)) {
+ (params.logWarn ?? ((message, error) => console.warn(message, error)))(
+ "Failed to wait for run after exec approval resolve.",
+ waitError
+ );
+ }
+ }
+ }
+
+ await params.requestHistoryRefresh(targetAgentId);
+ await params.onAllowed?.({ approval, targetAgentId });
+ } catch (err) {
+ const shouldTreatUnknownId = params.shouldTreatUnknownId ?? shouldTreatExecApprovalResolveErrorAsUnknownId;
+ if (shouldTreatUnknownId(err)) {
+ removeLocalApproval(id);
+ return;
+ }
+ const message = err instanceof Error ? err.message : "Failed to resolve exec approval.";
+ setLocalApprovalState(false, message);
+ }
+};
diff --git a/src/features/agents/approvals/execApprovalRunControlOperation.ts b/src/features/agents/approvals/execApprovalRunControlOperation.ts
new file mode 100644
index 0000000..e3aa76a
--- /dev/null
+++ b/src/features/agents/approvals/execApprovalRunControlOperation.ts
@@ -0,0 +1,288 @@
+import type {
+ ExecApprovalDecision,
+ PendingExecApproval,
+} from "@/features/agents/approvals/types";
+import type {
+ ExecApprovalIngressCommand,
+ ExecApprovalPendingSnapshot,
+} from "@/features/agents/approvals/execApprovalControlLoopWorkflow";
+import { resolveExecApprovalViaStudio } from "@/features/agents/approvals/execApprovalResolveOperation";
+import {
+ planApprovalIngressRunControl,
+ planAutoResumeRunControl,
+ planPauseRunControl,
+} from "@/features/agents/approvals/execApprovalRunControlWorkflow";
+import { sendChatMessageViaStudio } from "@/features/agents/operations/chatSendOperation";
+import type { AgentState } from "@/features/agents/state/store";
+import type { EventFrame } from "@/lib/gateway/GatewayClient";
+import { EXEC_APPROVAL_AUTO_RESUME_MARKER } from "@/lib/text/message-extract";
+
+type GatewayClientLike = {
+ call: (method: string, params: unknown) => Promise;
+};
+
+type RunControlDispatchAction =
+ | { type: "updateAgent"; agentId: string; patch: Partial }
+ | { type: "appendOutput"; agentId: string; line: string; transcript?: Record }
+ | { type: "markActivity"; agentId: string; at?: number };
+
+type RunControlDispatch = (action: RunControlDispatchAction) => void;
+
+type SetState = (next: T | ((current: T) => T)) => void;
+
+const AUTO_RESUME_FOLLOW_UP_MESSAGE = `${EXEC_APPROVAL_AUTO_RESUME_MARKER}\nContinue where you left off and finish the task.`;
+export const EXEC_APPROVAL_AUTO_RESUME_WAIT_TIMEOUT_MS = 3_000;
+
+export async function runPauseRunForExecApprovalOperation(params: {
+ status: string;
+ client: GatewayClientLike;
+ approval: PendingExecApproval;
+ preferredAgentId?: string | null;
+ getAgents: () => AgentState[];
+ pausedRunIdByAgentId: Map;
+ isDisconnectLikeError: (error: unknown) => boolean;
+ logWarn?: (message: string, error: unknown) => void;
+}): Promise {
+ if (params.status !== "connected") return;
+
+ const plan = planPauseRunControl({
+ approval: params.approval,
+ preferredAgentId: params.preferredAgentId ?? null,
+ agents: params.getAgents(),
+ pausedRunIdByAgentId: params.pausedRunIdByAgentId,
+ });
+ for (const agentId of plan.stalePausedAgentIds) {
+ params.pausedRunIdByAgentId.delete(agentId);
+ }
+ if (plan.pauseIntent.kind !== "pause") {
+ return;
+ }
+
+ params.pausedRunIdByAgentId.set(plan.pauseIntent.agentId, plan.pauseIntent.runId);
+ try {
+ await params.client.call("chat.abort", {
+ sessionKey: plan.pauseIntent.sessionKey,
+ });
+ } catch (error) {
+ params.pausedRunIdByAgentId.delete(plan.pauseIntent.agentId);
+ if (!params.isDisconnectLikeError(error)) {
+ (params.logWarn ?? ((message, err) => console.warn(message, err)))(
+ "Failed to pause run for pending exec approval.",
+ error
+ );
+ }
+ }
+}
+
+export async function runExecApprovalAutoResumeOperation(params: {
+ client: GatewayClientLike;
+ dispatch: RunControlDispatch;
+ approval: PendingExecApproval;
+ targetAgentId: string;
+ getAgents: () => AgentState[];
+ getPendingState: () => ExecApprovalPendingSnapshot;
+ pausedRunIdByAgentId: Map;
+ isDisconnectLikeError: (error: unknown) => boolean;
+ logWarn?: (message: string, error: unknown) => void;
+ clearRunTracking?: (runId: string) => void;
+ sendChatMessage?: typeof sendChatMessageViaStudio;
+ now?: () => number;
+}): Promise {
+ const sendChatMessage = params.sendChatMessage ?? sendChatMessageViaStudio;
+ const pendingState = params.getPendingState();
+ const prePlan = planAutoResumeRunControl({
+ approval: params.approval,
+ targetAgentId: params.targetAgentId,
+ pendingState,
+ pausedRunIdByAgentId: params.pausedRunIdByAgentId,
+ agents: params.getAgents(),
+ });
+ if (prePlan.preWaitIntent.kind !== "resume") {
+ return;
+ }
+
+ const preWaitIntent = prePlan.preWaitIntent;
+ params.pausedRunIdByAgentId.delete(preWaitIntent.targetAgentId);
+ params.dispatch({
+ type: "updateAgent",
+ agentId: preWaitIntent.targetAgentId,
+ patch: {
+ status: "running",
+ runId: preWaitIntent.pausedRunId,
+ lastActivityAt: (params.now ?? (() => Date.now()))(),
+ },
+ });
+
+ try {
+ await params.client.call("agent.wait", {
+ runId: preWaitIntent.pausedRunId,
+ timeoutMs: EXEC_APPROVAL_AUTO_RESUME_WAIT_TIMEOUT_MS,
+ });
+ } catch (error) {
+ if (!params.isDisconnectLikeError(error)) {
+ (params.logWarn ?? ((message, err) => console.warn(message, err)))(
+ "Failed waiting for paused run before auto-resume.",
+ error
+ );
+ }
+ }
+
+ const postPlan = planAutoResumeRunControl({
+ approval: params.approval,
+ targetAgentId: preWaitIntent.targetAgentId,
+ pendingState,
+ pausedRunIdByAgentId: new Map([[preWaitIntent.targetAgentId, preWaitIntent.pausedRunId]]),
+ agents: params.getAgents(),
+ });
+ if (postPlan.postWaitIntent.kind !== "resume") {
+ return;
+ }
+
+ await sendChatMessage({
+ client: params.client,
+ dispatch: params.dispatch,
+ getAgent: (agentId) => params.getAgents().find((entry) => entry.agentId === agentId) ?? null,
+ agentId: postPlan.postWaitIntent.targetAgentId,
+ sessionKey: postPlan.postWaitIntent.sessionKey,
+ message: AUTO_RESUME_FOLLOW_UP_MESSAGE,
+ clearRunTracking: params.clearRunTracking,
+ echoUserMessage: false,
+ });
+}
+
+export async function runResolveExecApprovalOperation(params: {
+ client: GatewayClientLike;
+ approvalId: string;
+ decision: ExecApprovalDecision;
+ getAgents: () => AgentState[];
+ getPendingState: () => ExecApprovalPendingSnapshot;
+ setPendingExecApprovalsByAgentId: SetState>;
+ setUnscopedPendingExecApprovals: SetState;
+ requestHistoryRefresh: (agentId: string) => Promise | void;
+ pausedRunIdByAgentId: Map;
+ dispatch: RunControlDispatch;
+ isDisconnectLikeError: (error: unknown) => boolean;
+ logWarn?: (message: string, error: unknown) => void;
+ clearRunTracking?: (runId: string) => void;
+ resolveExecApproval?: typeof resolveExecApprovalViaStudio;
+ runAutoResume?: typeof runExecApprovalAutoResumeOperation;
+}): Promise {
+ const resolveExecApproval = params.resolveExecApproval ?? resolveExecApprovalViaStudio;
+ const runAutoResume = params.runAutoResume ?? runExecApprovalAutoResumeOperation;
+
+ await resolveExecApproval({
+ client: params.client,
+ approvalId: params.approvalId,
+ decision: params.decision,
+ getAgents: params.getAgents,
+ getLatestAgent: (agentId) =>
+ params.getAgents().find((entry) => entry.agentId === agentId) ?? null,
+ getPendingState: params.getPendingState,
+ setPendingExecApprovalsByAgentId: params.setPendingExecApprovalsByAgentId,
+ setUnscopedPendingExecApprovals: params.setUnscopedPendingExecApprovals,
+ requestHistoryRefresh: params.requestHistoryRefresh,
+ onAllowed: async ({ approval, targetAgentId }) => {
+ await runAutoResume({
+ client: params.client,
+ dispatch: params.dispatch,
+ approval,
+ targetAgentId,
+ getAgents: params.getAgents,
+ getPendingState: params.getPendingState,
+ pausedRunIdByAgentId: params.pausedRunIdByAgentId,
+ isDisconnectLikeError: params.isDisconnectLikeError,
+ logWarn: params.logWarn,
+ clearRunTracking: params.clearRunTracking,
+ });
+ },
+ isDisconnectLikeError: params.isDisconnectLikeError,
+ logWarn: params.logWarn,
+ });
+}
+
+export function executeExecApprovalIngressCommands(params: {
+ commands: ExecApprovalIngressCommand[];
+ replacePendingState: (nextPendingState: ExecApprovalPendingSnapshot) => void;
+ pauseRunForApproval: (
+ approval: PendingExecApproval,
+ preferredAgentId: string | null
+ ) => Promise | void;
+ dispatch: RunControlDispatch;
+ recordCronDedupeKey: (dedupeKey: string) => void;
+}): void {
+ for (const command of params.commands) {
+ if (command.kind === "replacePendingState") {
+ params.replacePendingState(command.pendingState);
+ continue;
+ }
+ if (command.kind === "pauseRunForApproval") {
+ void params.pauseRunForApproval(command.approval, command.preferredAgentId);
+ continue;
+ }
+ if (command.kind === "markActivity") {
+ params.dispatch({
+ type: "markActivity",
+ agentId: command.agentId,
+ });
+ continue;
+ }
+ if (command.kind === "recordCronDedupeKey") {
+ params.recordCronDedupeKey(command.dedupeKey);
+ continue;
+ }
+
+ const intent = command.intent;
+ params.dispatch({
+ type: "appendOutput",
+ agentId: intent.agentId,
+ line: intent.line,
+ transcript: {
+ source: "runtime-agent",
+ role: "assistant",
+ kind: "assistant",
+ sessionKey: intent.sessionKey,
+ timestampMs: intent.timestampMs,
+ entryId: intent.dedupeKey,
+ confirmed: true,
+ },
+ });
+ params.dispatch({
+ type: "markActivity",
+ agentId: intent.agentId,
+ at: intent.activityAtMs ?? undefined,
+ });
+ }
+}
+
+export function runGatewayEventIngressOperation(params: {
+ event: EventFrame;
+ getAgents: () => AgentState[];
+ getPendingState: () => ExecApprovalPendingSnapshot;
+ pausedRunIdByAgentId: ReadonlyMap;
+ seenCronDedupeKeys: ReadonlySet;
+ nowMs: number;
+ replacePendingState: (nextPendingState: ExecApprovalPendingSnapshot) => void;
+ pauseRunForApproval: (
+ approval: PendingExecApproval,
+ preferredAgentId: string | null
+ ) => Promise | void;
+ dispatch: RunControlDispatch;
+ recordCronDedupeKey: (dedupeKey: string) => void;
+}): ExecApprovalIngressCommand[] {
+ const commands = planApprovalIngressRunControl({
+ event: params.event,
+ agents: params.getAgents(),
+ pendingState: params.getPendingState(),
+ pausedRunIdByAgentId: params.pausedRunIdByAgentId,
+ seenCronDedupeKeys: params.seenCronDedupeKeys,
+ nowMs: params.nowMs,
+ });
+ executeExecApprovalIngressCommands({
+ commands,
+ replacePendingState: params.replacePendingState,
+ pauseRunForApproval: params.pauseRunForApproval,
+ dispatch: params.dispatch,
+ recordCronDedupeKey: params.recordCronDedupeKey,
+ });
+ return commands;
+}
diff --git a/src/features/agents/approvals/execApprovalRunControlWorkflow.ts b/src/features/agents/approvals/execApprovalRunControlWorkflow.ts
new file mode 100644
index 0000000..8e43887
--- /dev/null
+++ b/src/features/agents/approvals/execApprovalRunControlWorkflow.ts
@@ -0,0 +1,95 @@
+import type { PendingExecApproval } from "@/features/agents/approvals/types";
+import {
+ planAutoResumeIntent,
+ planIngressCommands,
+ planPausedRunMapCleanup,
+ planPauseRunIntent,
+ type ExecApprovalIngressCommand,
+ type ExecApprovalPendingSnapshot,
+} from "@/features/agents/approvals/execApprovalControlLoopWorkflow";
+import type { AgentState } from "@/features/agents/state/store";
+
+type GatewayEventFrame = Parameters[0]["event"];
+
+export type PauseRunControlPlan = {
+ stalePausedAgentIds: string[];
+ pauseIntent: ReturnType;
+};
+
+export type AutoResumeRunControlPlan = {
+ preWaitIntent: ReturnType;
+ postWaitIntent: ReturnType;
+};
+
+export function planPauseRunControl(params: {
+ approval: PendingExecApproval;
+ preferredAgentId: string | null;
+ agents: AgentState[];
+ pausedRunIdByAgentId: ReadonlyMap;
+}): PauseRunControlPlan {
+ return {
+ stalePausedAgentIds: planPausedRunMapCleanup({
+ pausedRunIdByAgentId: params.pausedRunIdByAgentId,
+ agents: params.agents,
+ }),
+ pauseIntent: planPauseRunIntent({
+ approval: params.approval,
+ preferredAgentId: params.preferredAgentId,
+ agents: params.agents,
+ pausedRunIdByAgentId: params.pausedRunIdByAgentId,
+ }),
+ };
+}
+
+export function planAutoResumeRunControl(params: {
+ approval: PendingExecApproval;
+ targetAgentId: string;
+ pendingState: ExecApprovalPendingSnapshot;
+ pausedRunIdByAgentId: ReadonlyMap;
+ agents: AgentState[];
+}): AutoResumeRunControlPlan {
+ const preWaitIntent = planAutoResumeIntent({
+ approval: params.approval,
+ targetAgentId: params.targetAgentId,
+ pendingState: params.pendingState,
+ pausedRunIdByAgentId: params.pausedRunIdByAgentId,
+ agents: params.agents,
+ });
+ if (preWaitIntent.kind !== "resume") {
+ return {
+ preWaitIntent,
+ postWaitIntent: preWaitIntent,
+ };
+ }
+
+ return {
+ preWaitIntent,
+ postWaitIntent: planAutoResumeIntent({
+ approval: params.approval,
+ targetAgentId: preWaitIntent.targetAgentId,
+ pendingState: params.pendingState,
+ pausedRunIdByAgentId: new Map([
+ [preWaitIntent.targetAgentId, preWaitIntent.pausedRunId],
+ ]),
+ agents: params.agents,
+ }),
+ };
+}
+
+export function planApprovalIngressRunControl(params: {
+ event: GatewayEventFrame;
+ agents: AgentState[];
+ pendingState: ExecApprovalPendingSnapshot;
+ pausedRunIdByAgentId: ReadonlyMap;
+ seenCronDedupeKeys: ReadonlySet;
+ nowMs: number;
+}): ExecApprovalIngressCommand[] {
+ return planIngressCommands({
+ event: params.event,
+ agents: params.agents,
+ pendingState: params.pendingState,
+ pausedRunIdByAgentId: params.pausedRunIdByAgentId,
+ seenCronDedupeKeys: params.seenCronDedupeKeys,
+ nowMs: params.nowMs,
+ });
+}
diff --git a/src/features/agents/approvals/execApprovalRuntimeCoordinator.ts b/src/features/agents/approvals/execApprovalRuntimeCoordinator.ts
new file mode 100644
index 0000000..3d44f1d
--- /dev/null
+++ b/src/features/agents/approvals/execApprovalRuntimeCoordinator.ts
@@ -0,0 +1,300 @@
+import type { ExecApprovalEventEffects } from "@/features/agents/approvals/execApprovalLifecycleWorkflow";
+import { shouldPauseRunForPendingExecApproval } from "@/features/agents/approvals/execApprovalPausePolicy";
+import type { PendingExecApproval } from "@/features/agents/approvals/types";
+import {
+ nextPendingApprovalPruneDelayMs,
+ pruneExpiredPendingApprovals,
+ pruneExpiredPendingApprovalsMap,
+ removePendingApprovalById,
+ removePendingApprovalByIdMap,
+ removePendingApprovalEverywhere,
+ upsertPendingApproval,
+} from "@/features/agents/approvals/pendingStore";
+import type { AgentState } from "@/features/agents/state/store";
+
+export type ApprovalPendingState = {
+ approvalsByAgentId: Record;
+ unscopedApprovals: PendingExecApproval[];
+};
+
+export type ApprovalPauseRequest = {
+ approval: PendingExecApproval;
+ preferredAgentId: string | null;
+};
+
+export type ApprovalIngressResult = {
+ pendingState: ApprovalPendingState;
+ pauseRequests: ApprovalPauseRequest[];
+ markActivityAgentIds: string[];
+};
+
+export type AwaitingUserInputPatch = {
+ agentId: string;
+ awaitingUserInput: boolean;
+};
+
+export type AutoResumePreflightIntent =
+ | { kind: "skip"; reason: "missing-paused-run" | "blocking-pending-approvals" }
+ | { kind: "resume"; targetAgentId: string; pausedRunId: string };
+
+export type AutoResumeDispatchIntent =
+ | { kind: "skip"; reason: "missing-paused-run" | "missing-agent" | "run-replaced" | "missing-session-key" }
+ | { kind: "resume"; targetAgentId: string; pausedRunId: string; sessionKey: string };
+
+const resolveAgentForPauseRequest = (params: {
+ approval: PendingExecApproval;
+ preferredAgentId: string | null;
+ agents: AgentState[];
+}): AgentState | null => {
+ const preferredAgentId = params.preferredAgentId?.trim() ?? "";
+ if (preferredAgentId) {
+ const match = params.agents.find((agent) => agent.agentId === preferredAgentId) ?? null;
+ if (match) return match;
+ }
+ const approvalSessionKey = params.approval.sessionKey?.trim() ?? "";
+ if (!approvalSessionKey) return null;
+ return (
+ params.agents.find((agent) => agent.sessionKey.trim() === approvalSessionKey) ?? null
+ );
+};
+
+const shouldQueuePauseRequest = (params: {
+ approval: PendingExecApproval;
+ preferredAgentId: string | null;
+ agents: AgentState[];
+ pausedRunIdByAgentId: ReadonlyMap;
+}): boolean => {
+ const agent = resolveAgentForPauseRequest(params);
+ if (!agent) return false;
+ const pausedRunId = params.pausedRunIdByAgentId.get(agent.agentId) ?? null;
+ return shouldPauseRunForPendingExecApproval({
+ agent,
+ approval: params.approval,
+ pausedRunId,
+ });
+};
+
+export const applyApprovalIngressEffects = (params: {
+ pendingState: ApprovalPendingState;
+ approvalEffects: ExecApprovalEventEffects | null;
+ agents: AgentState[];
+ pausedRunIdByAgentId: ReadonlyMap;
+}): ApprovalIngressResult => {
+ const effects = params.approvalEffects;
+ if (!effects) {
+ return {
+ pendingState: params.pendingState,
+ pauseRequests: [],
+ markActivityAgentIds: [],
+ };
+ }
+
+ let approvalsByAgentId = params.pendingState.approvalsByAgentId;
+ let unscopedApprovals = params.pendingState.unscopedApprovals;
+ const pauseRequests: ApprovalPauseRequest[] = [];
+
+ for (const approvalId of effects.removals) {
+ const removed = removePendingApprovalEverywhere({
+ approvalsByAgentId,
+ unscopedApprovals,
+ approvalId,
+ });
+ approvalsByAgentId = removed.approvalsByAgentId;
+ unscopedApprovals = removed.unscopedApprovals;
+ }
+
+ for (const scopedUpsert of effects.scopedUpserts) {
+ approvalsByAgentId = removePendingApprovalByIdMap(
+ approvalsByAgentId,
+ scopedUpsert.approval.id
+ );
+ const existing = approvalsByAgentId[scopedUpsert.agentId] ?? [];
+ const upserted = upsertPendingApproval(existing, scopedUpsert.approval);
+ if (upserted !== existing) {
+ approvalsByAgentId = {
+ ...approvalsByAgentId,
+ [scopedUpsert.agentId]: upserted,
+ };
+ }
+ unscopedApprovals = removePendingApprovalById(
+ unscopedApprovals,
+ scopedUpsert.approval.id
+ );
+ if (
+ shouldQueuePauseRequest({
+ approval: scopedUpsert.approval,
+ preferredAgentId: scopedUpsert.agentId,
+ agents: params.agents,
+ pausedRunIdByAgentId: params.pausedRunIdByAgentId,
+ })
+ ) {
+ pauseRequests.push({
+ approval: scopedUpsert.approval,
+ preferredAgentId: scopedUpsert.agentId,
+ });
+ }
+ }
+
+ for (const unscopedUpsert of effects.unscopedUpserts) {
+ approvalsByAgentId = removePendingApprovalByIdMap(
+ approvalsByAgentId,
+ unscopedUpsert.id
+ );
+ const withoutExisting = removePendingApprovalById(
+ unscopedApprovals,
+ unscopedUpsert.id
+ );
+ unscopedApprovals = upsertPendingApproval(withoutExisting, unscopedUpsert);
+ if (
+ shouldQueuePauseRequest({
+ approval: unscopedUpsert,
+ preferredAgentId: null,
+ agents: params.agents,
+ pausedRunIdByAgentId: params.pausedRunIdByAgentId,
+ })
+ ) {
+ pauseRequests.push({
+ approval: unscopedUpsert,
+ preferredAgentId: null,
+ });
+ }
+ }
+
+ return {
+ pendingState: {
+ approvalsByAgentId,
+ unscopedApprovals,
+ },
+ pauseRequests,
+ markActivityAgentIds: effects.markActivityAgentIds,
+ };
+};
+
+export const deriveAwaitingUserInputPatches = (params: {
+ agents: AgentState[];
+ approvalsByAgentId: Record;
+}): AwaitingUserInputPatch[] => {
+ const pendingCountsByAgentId = new Map();
+ for (const [agentId, approvals] of Object.entries(params.approvalsByAgentId)) {
+ if (approvals.length <= 0) continue;
+ pendingCountsByAgentId.set(agentId, approvals.length);
+ }
+
+ const patches: AwaitingUserInputPatch[] = [];
+ for (const agent of params.agents) {
+ const awaitingUserInput = (pendingCountsByAgentId.get(agent.agentId) ?? 0) > 0;
+ if (agent.awaitingUserInput === awaitingUserInput) continue;
+ patches.push({
+ agentId: agent.agentId,
+ awaitingUserInput,
+ });
+ }
+ return patches;
+};
+
+export const derivePendingApprovalPruneDelayMs = (params: {
+ pendingState: ApprovalPendingState;
+ nowMs: number;
+ graceMs: number;
+}): number | null => {
+ return nextPendingApprovalPruneDelayMs({
+ approvalsByAgentId: params.pendingState.approvalsByAgentId,
+ unscopedApprovals: params.pendingState.unscopedApprovals,
+ nowMs: params.nowMs,
+ graceMs: params.graceMs,
+ });
+};
+
+export const prunePendingApprovalState = (params: {
+ pendingState: ApprovalPendingState;
+ nowMs: number;
+ graceMs: number;
+}): { pendingState: ApprovalPendingState } => {
+ return {
+ pendingState: {
+ approvalsByAgentId: pruneExpiredPendingApprovalsMap(
+ params.pendingState.approvalsByAgentId,
+ {
+ nowMs: params.nowMs,
+ graceMs: params.graceMs,
+ }
+ ),
+ unscopedApprovals: pruneExpiredPendingApprovals(
+ params.pendingState.unscopedApprovals,
+ {
+ nowMs: params.nowMs,
+ graceMs: params.graceMs,
+ }
+ ),
+ },
+ };
+};
+
+export const resolveApprovalAutoResumePreflight = (params: {
+ approval: PendingExecApproval;
+ targetAgentId: string;
+ pendingState: ApprovalPendingState;
+ pausedRunIdByAgentId: ReadonlyMap;
+}): AutoResumePreflightIntent => {
+ const pausedRunId = params.pausedRunIdByAgentId.get(params.targetAgentId)?.trim() ?? "";
+ if (!pausedRunId) {
+ return { kind: "skip", reason: "missing-paused-run" };
+ }
+
+ const scopedPending = (
+ params.pendingState.approvalsByAgentId[params.targetAgentId] ?? []
+ ).some((pendingApproval) => pendingApproval.id !== params.approval.id);
+
+ const targetSessionKey = params.approval.sessionKey?.trim() ?? "";
+ const unscopedPending = params.pendingState.unscopedApprovals.some((pendingApproval) => {
+ if (pendingApproval.id === params.approval.id) return false;
+ const pendingAgentId = pendingApproval.agentId?.trim() ?? "";
+ if (pendingAgentId && pendingAgentId === params.targetAgentId) return true;
+ if (!targetSessionKey) return false;
+ return (pendingApproval.sessionKey?.trim() ?? "") === targetSessionKey;
+ });
+
+ if (scopedPending || unscopedPending) {
+ return { kind: "skip", reason: "blocking-pending-approvals" };
+ }
+
+ return {
+ kind: "resume",
+ targetAgentId: params.targetAgentId,
+ pausedRunId,
+ };
+};
+
+export const resolveApprovalAutoResumeDispatch = (params: {
+ targetAgentId: string;
+ pausedRunId: string;
+ agents: AgentState[];
+}): AutoResumeDispatchIntent => {
+ const pausedRunId = params.pausedRunId.trim();
+ if (!pausedRunId) {
+ return { kind: "skip", reason: "missing-paused-run" };
+ }
+
+ const latest =
+ params.agents.find((agent) => agent.agentId === params.targetAgentId) ?? null;
+ if (!latest) {
+ return { kind: "skip", reason: "missing-agent" };
+ }
+
+ const latestRunId = latest.runId?.trim() ?? "";
+ if (latest.status === "running" && latestRunId && latestRunId !== pausedRunId) {
+ return { kind: "skip", reason: "run-replaced" };
+ }
+
+ const sessionKey = latest.sessionKey.trim();
+ if (!sessionKey) {
+ return { kind: "skip", reason: "missing-session-key" };
+ }
+
+ return {
+ kind: "resume",
+ targetAgentId: params.targetAgentId,
+ pausedRunId,
+ sessionKey,
+ };
+};
diff --git a/src/features/agents/approvals/pendingStore.ts b/src/features/agents/approvals/pendingStore.ts
new file mode 100644
index 0000000..bb94b19
--- /dev/null
+++ b/src/features/agents/approvals/pendingStore.ts
@@ -0,0 +1,155 @@
+import type { PendingExecApproval } from "@/features/agents/approvals/types";
+
+export const upsertPendingApproval = (
+ approvals: PendingExecApproval[],
+ nextApproval: PendingExecApproval
+): PendingExecApproval[] => {
+ const index = approvals.findIndex((entry) => entry.id === nextApproval.id);
+ if (index < 0) {
+ return [nextApproval, ...approvals];
+ }
+ const next = [...approvals];
+ next[index] = nextApproval;
+ return next;
+};
+
+export const mergePendingApprovalsForFocusedAgent = (params: {
+ scopedApprovals: PendingExecApproval[];
+ unscopedApprovals: PendingExecApproval[];
+}): PendingExecApproval[] => {
+ if (params.scopedApprovals.length === 0) return params.unscopedApprovals;
+ if (params.unscopedApprovals.length === 0) return params.scopedApprovals;
+ const merged = [...params.unscopedApprovals];
+ const seen = new Map();
+ for (let index = 0; index < merged.length; index += 1) {
+ seen.set(merged[index]!.id, index);
+ }
+ for (const approval of params.scopedApprovals) {
+ const existingIndex = seen.get(approval.id);
+ if (existingIndex === undefined) {
+ seen.set(approval.id, merged.length);
+ merged.push(approval);
+ continue;
+ }
+ merged[existingIndex] = approval;
+ }
+ return merged;
+};
+
+export const updatePendingApprovalById = (
+ approvals: PendingExecApproval[],
+ approvalId: string,
+ updater: (approval: PendingExecApproval) => PendingExecApproval
+): PendingExecApproval[] => {
+ let changed = false;
+ const next = approvals.map((approval) => {
+ if (approval.id !== approvalId) return approval;
+ changed = true;
+ return updater(approval);
+ });
+ return changed ? next : approvals;
+};
+
+export const removePendingApprovalById = (
+ approvals: PendingExecApproval[],
+ approvalId: string
+): PendingExecApproval[] => approvals.filter((approval) => approval.id !== approvalId);
+
+export const removePendingApprovalEverywhere = (params: {
+ approvalsByAgentId: Record;
+ unscopedApprovals: PendingExecApproval[];
+ approvalId: string;
+}): {
+ approvalsByAgentId: Record;
+ unscopedApprovals: PendingExecApproval[];
+} => {
+ const hasScoped = Object.values(params.approvalsByAgentId).some((approvals) =>
+ approvals.some((approval) => approval.id === params.approvalId)
+ );
+ const hasUnscoped = params.unscopedApprovals.some(
+ (approval) => approval.id === params.approvalId
+ );
+ if (!hasScoped && !hasUnscoped) {
+ return {
+ approvalsByAgentId: params.approvalsByAgentId,
+ unscopedApprovals: params.unscopedApprovals,
+ };
+ }
+ return {
+ approvalsByAgentId: hasScoped
+ ? removePendingApprovalByIdMap(params.approvalsByAgentId, params.approvalId)
+ : params.approvalsByAgentId,
+ unscopedApprovals: hasUnscoped
+ ? removePendingApprovalById(params.unscopedApprovals, params.approvalId)
+ : params.unscopedApprovals,
+ };
+};
+
+export const removePendingApprovalByIdMap = (
+ approvalsByAgentId: Record,
+ approvalId: string
+): Record => {
+ let changed = false;
+ const next: Record = {};
+ for (const [agentId, approvals] of Object.entries(approvalsByAgentId)) {
+ const filtered = removePendingApprovalById(approvals, approvalId);
+ if (filtered.length !== approvals.length) {
+ changed = true;
+ }
+ if (filtered.length > 0) {
+ next[agentId] = filtered;
+ }
+ }
+ return changed ? next : approvalsByAgentId;
+};
+
+export const pruneExpiredPendingApprovals = (
+ approvals: PendingExecApproval[],
+ params: { nowMs: number; graceMs: number }
+): PendingExecApproval[] => {
+ const cutoff = params.nowMs - params.graceMs;
+ return approvals.filter((approval) => approval.expiresAtMs >= cutoff);
+};
+
+export const pruneExpiredPendingApprovalsMap = (
+ approvalsByAgentId: Record,
+ params: { nowMs: number; graceMs: number }
+): Record => {
+ let changed = false;
+ const next: Record = {};
+ for (const [agentId, approvals] of Object.entries(approvalsByAgentId)) {
+ const filtered = pruneExpiredPendingApprovals(approvals, params);
+ if (filtered.length !== approvals.length) {
+ changed = true;
+ }
+ if (filtered.length > 0) {
+ next[agentId] = filtered;
+ }
+ }
+ return changed ? next : approvalsByAgentId;
+};
+
+export const nextPendingApprovalPruneDelayMs = (params: {
+ approvalsByAgentId: Record;
+ unscopedApprovals: PendingExecApproval[];
+ nowMs: number;
+ graceMs: number;
+}): number | null => {
+ let earliestExpiresMs = Number.POSITIVE_INFINITY;
+ for (const approvals of Object.values(params.approvalsByAgentId)) {
+ for (const approval of approvals) {
+ if (approval.expiresAtMs < earliestExpiresMs) {
+ earliestExpiresMs = approval.expiresAtMs;
+ }
+ }
+ }
+ for (const approval of params.unscopedApprovals) {
+ if (approval.expiresAtMs < earliestExpiresMs) {
+ earliestExpiresMs = approval.expiresAtMs;
+ }
+ }
+ if (!Number.isFinite(earliestExpiresMs)) {
+ return null;
+ }
+ return Math.max(0, earliestExpiresMs + params.graceMs - params.nowMs);
+};
diff --git a/src/features/agents/approvals/types.ts b/src/features/agents/approvals/types.ts
new file mode 100644
index 0000000..78eb9f7
--- /dev/null
+++ b/src/features/agents/approvals/types.ts
@@ -0,0 +1,17 @@
+export type ExecApprovalDecision = "allow-once" | "allow-always" | "deny";
+
+export type PendingExecApproval = {
+ id: string;
+ agentId: string | null;
+ sessionKey: string | null;
+ command: string;
+ cwd: string | null;
+ host: string | null;
+ security: string | null;
+ ask: string | null;
+ resolvedPath: string | null;
+ createdAtMs: number;
+ expiresAtMs: number;
+ resolving: boolean;
+ error: string | null;
+};
diff --git a/src/features/agents/components/AgentAvatar.tsx b/src/features/agents/components/AgentAvatar.tsx
new file mode 100644
index 0000000..2232123
--- /dev/null
+++ b/src/features/agents/components/AgentAvatar.tsx
@@ -0,0 +1,43 @@
+import Image from "next/image";
+import { useMemo } from "react";
+
+import { buildAvatarDataUrl } from "@/lib/avatars/multiavatar";
+
+type AgentAvatarProps = {
+ seed: string;
+ name: string;
+ avatarUrl?: string | null;
+ size?: number;
+ isSelected?: boolean;
+};
+
+export const AgentAvatar = ({
+ seed,
+ name,
+ avatarUrl,
+ size = 112,
+ isSelected = false,
+}: AgentAvatarProps) => {
+ const src = useMemo(() => {
+ const trimmed = avatarUrl?.trim();
+ if (trimmed) return trimmed;
+ return buildAvatarDataUrl(seed);
+ }, [avatarUrl, seed]);
+
+ return (
+
+
+
+ );
+};
diff --git a/src/features/agents/components/AgentChatPanel.tsx b/src/features/agents/components/AgentChatPanel.tsx
new file mode 100644
index 0000000..b76eb6b
--- /dev/null
+++ b/src/features/agents/components/AgentChatPanel.tsx
@@ -0,0 +1,1655 @@
+import {
+ memo,
+ useCallback,
+ useEffect,
+ useMemo,
+ useRef,
+ useState,
+ type ChangeEvent,
+ type KeyboardEvent,
+ type MutableRefObject,
+ type ReactNode,
+} from "react";
+import type { AgentState as AgentRecord } from "@/features/agents/state/store";
+import ReactMarkdown from "react-markdown";
+import remarkGfm from "remark-gfm";
+import { Check, ChevronRight, Clock, Cog, Mic, Pencil, Shuffle, Square, Trash2, X } from "lucide-react";
+import type { GatewayModelChoice } from "@/lib/gateway/models";
+import { rewriteMediaLinesToMarkdown } from "@/lib/text/media-markdown";
+import { normalizeAssistantDisplayText } from "@/lib/text/assistantText";
+import { isNearBottom } from "@/lib/dom";
+import { useVoiceRecorder, type VoiceRecorderState, type VoiceSendPayload } from "@/hooks/useVoiceRecorder";
+import { AgentAvatar } from "./AgentAvatar";
+import type {
+ ExecApprovalDecision,
+ PendingExecApproval,
+} from "@/features/agents/approvals/types";
+import {
+ buildAgentChatRenderBlocks,
+ buildFinalAgentChatItems,
+ summarizeToolLabel,
+ type AssistantTraceEvent,
+ type AgentChatItem,
+} from "./chatItems";
+
+const formatChatTimestamp = (timestampMs: number): string => {
+ return new Intl.DateTimeFormat(undefined, {
+ hour: "2-digit",
+ minute: "2-digit",
+ hour12: true,
+ }).format(new Date(timestampMs));
+};
+
+const formatDurationLabel = (durationMs: number): string => {
+ const seconds = durationMs / 1000;
+ if (!Number.isFinite(seconds) || seconds <= 0) return "0.0s";
+ if (seconds < 10) return `${seconds.toFixed(1)}s`;
+ return `${Math.round(seconds)}s`;
+};
+
+const SPINE_LEFT = "left-[15px]";
+const ASSISTANT_GUTTER_CLASS = "pl-[44px]";
+const ASSISTANT_MAX_WIDTH_DEFAULT_CLASS = "max-w-[68ch]";
+const ASSISTANT_MAX_WIDTH_EXPANDED_CLASS = "max-w-[1120px]";
+const CHAT_TOP_THRESHOLD_PX = 8;
+const EMPTY_CHAT_INTRO_MESSAGES = [
+ "How can I help you today?",
+ "What should we accomplish today?",
+ "Ready when you are. What do you want to tackle?",
+ "What are we working on today?",
+ "I'm here and ready. What's the plan?",
+];
+
+const stableStringHash = (value: string): number => {
+ let hash = 0;
+ for (let i = 0; i < value.length; i += 1) {
+ hash = (hash * 31 + value.charCodeAt(i)) >>> 0;
+ }
+ return hash;
+};
+
+const resolveEmptyChatIntroMessage = (agentId: string, sessionEpoch: number | undefined): string => {
+ if (EMPTY_CHAT_INTRO_MESSAGES.length === 0) return "How can I help you today?";
+ const normalizedEpoch =
+ typeof sessionEpoch === "number" && Number.isFinite(sessionEpoch)
+ ? Math.max(0, Math.trunc(sessionEpoch))
+ : 0;
+ const offset = stableStringHash(agentId) % EMPTY_CHAT_INTRO_MESSAGES.length;
+ const index = (offset + normalizedEpoch) % EMPTY_CHAT_INTRO_MESSAGES.length;
+ return EMPTY_CHAT_INTRO_MESSAGES[index];
+};
+
+const looksLikePath = (value: string): boolean => {
+ if (!value) return false;
+ if (/(^|[\s(])(?:[A-Za-z]:\\|~\/|\/)/.test(value)) return true;
+ if (/(^|[\s(])(src|app|packages|components)\//.test(value)) return true;
+ if (/(^|[\s(])[\w.-]+\.(ts|tsx|js|jsx|json|md|py|go|rs|java|kt|rb|sh|yaml|yml)\b/.test(value)) {
+ return true;
+ }
+ return false;
+};
+
+const isStructuredMarkdown = (text: string): boolean => {
+ if (!text) return false;
+ if (/```/.test(text)) return true;
+ if (/^\s*#{1,6}\s+/m.test(text)) return true;
+ if (/^\s*[-*+]\s+/m.test(text)) return true;
+ if (/^\s*\d+\.\s+/m.test(text)) return true;
+ if (/^\s*\|.+\|\s*$/m.test(text)) return true;
+ if (looksLikePath(text) && text.split("\n").filter(Boolean).length >= 3) return true;
+ return false;
+};
+
+const resolveAssistantMaxWidthClass = (text: string | null | undefined): string => {
+ const value = (text ?? "").trim();
+ if (!value) return ASSISTANT_MAX_WIDTH_DEFAULT_CLASS;
+ if (isStructuredMarkdown(value)) return ASSISTANT_MAX_WIDTH_EXPANDED_CLASS;
+ const nonEmptyLines = value.split("\n").filter((line) => line.trim().length > 0);
+ const shortLineCount = nonEmptyLines.filter((line) => line.trim().length <= 44).length;
+ if (nonEmptyLines.length >= 10 && shortLineCount / Math.max(1, nonEmptyLines.length) >= 0.65) {
+ return ASSISTANT_MAX_WIDTH_EXPANDED_CLASS;
+ }
+ return ASSISTANT_MAX_WIDTH_DEFAULT_CLASS;
+};
+
+type AgentChatPanelProps = {
+ agent: AgentRecord;
+ isSelected: boolean;
+ canSend: boolean;
+ models: GatewayModelChoice[];
+ stopBusy: boolean;
+ stopDisabledReason?: string | null;
+ onLoadMoreHistory: () => void;
+ onOpenSettings: () => void;
+ onRename?: (name: string) => Promise;
+ onNewSession?: () => Promise | void;
+ onModelChange: (value: string | null) => void;
+ onThinkingChange: (value: string | null) => void;
+ onToolCallingToggle?: (enabled: boolean) => void;
+ onThinkingTracesToggle?: (enabled: boolean) => void;
+ onDraftChange: (value: string) => void;
+ onSend: (message: string) => void;
+ onRemoveQueuedMessage?: (index: number) => void;
+ onStopRun: () => void;
+ onAvatarShuffle: () => void;
+ pendingExecApprovals?: PendingExecApproval[];
+ onResolveExecApproval?: (id: string, decision: ExecApprovalDecision) => void;
+ onVoiceSend?: (payload: VoiceSendPayload) => Promise;
+};
+
+const formatApprovalExpiry = (timestampMs: number): string => {
+ if (!Number.isFinite(timestampMs) || timestampMs <= 0) return "Unknown";
+ return new Intl.DateTimeFormat(undefined, {
+ month: "short",
+ day: "numeric",
+ hour: "2-digit",
+ minute: "2-digit",
+ }).format(new Date(timestampMs));
+};
+
+const ExecApprovalCard = memo(function ExecApprovalCard({
+ approval,
+ onResolve,
+}: {
+ approval: PendingExecApproval;
+ onResolve?: (id: string, decision: ExecApprovalDecision) => void;
+}) {
+ const disabled = approval.resolving || !onResolve;
+ return (
+
+
+ Exec approval required
+
+
+ {approval.command}
+
+
+ Host: {approval.host ?? "unknown"}
+ Expires: {formatApprovalExpiry(approval.expiresAtMs)}
+ {approval.cwd ? CWD: {approval.cwd} : null}
+
+ {approval.error ? (
+
+ {approval.error}
+
+ ) : null}
+
+
+
+
+
+
+ );
+});
+
+const ToolCallDetails = memo(function ToolCallDetails({
+ line,
+ className,
+}: {
+ line: string;
+ className?: string;
+}) {
+ const { summaryText, body, inlineOnly } = summarizeToolLabel(line);
+ const [open, setOpen] = useState(false);
+ const resolvedClassName =
+ className ??
+ `w-full ${ASSISTANT_MAX_WIDTH_EXPANDED_CLASS} ${ASSISTANT_GUTTER_CLASS} self-start rounded-md bg-surface-3 px-2 py-1 text-[10px] text-muted-foreground shadow-2xs`;
+ if (inlineOnly) {
+ return (
+
+ {summaryText}
+
+ );
+ }
+ return (
+
+ {
+ event.preventDefault();
+ setOpen((current) => !current);
+ }}
+ >
+ {summaryText}
+
+ {open && body ? (
+
+
+ {rewriteMediaLinesToMarkdown(body)}
+
+
+ ) : null}
+
+ );
+});
+
+const ThinkingDetailsRow = memo(function ThinkingDetailsRow({
+ events,
+ thinkingText,
+ toolLines = [],
+ durationMs,
+ showTyping,
+}: {
+ events?: AssistantTraceEvent[];
+ thinkingText?: string | null;
+ toolLines?: string[];
+ durationMs?: number;
+ showTyping?: boolean;
+}) {
+ const [open, setOpen] = useState(false);
+ const traceEvents = (() => {
+ if (events && events.length > 0) return events;
+ const normalizedThinkingText = thinkingText?.trim() ?? "";
+ const next: AssistantTraceEvent[] = [];
+ if (normalizedThinkingText) {
+ next.push({ kind: "thinking", text: normalizedThinkingText });
+ }
+ for (const line of toolLines) {
+ next.push({ kind: "tool", text: line });
+ }
+ return next;
+ })();
+ if (traceEvents.length === 0) return null;
+ return (
+
+ {
+ event.preventDefault();
+ setOpen((current) => !current);
+ }}
+ >
+
+
+
+ Thinking (internal)
+
+ {typeof durationMs === "number" ? (
+
+
+ {formatDurationLabel(durationMs)}
+
+ ) : null}
+ {showTyping ? (
+
+ ) : null}
+
+
+ {open ? (
+
+ {traceEvents.map((event, index) =>
+ event.kind === "thinking" ? (
+
+ {event.text}
+
+ ) : (
+
+ )
+ )}
+
+ ) : null}
+
+ );
+});
+
+const UserMessageCard = memo(function UserMessageCard({
+ text,
+ timestampMs,
+}: {
+ text: string;
+ timestampMs?: number;
+}) {
+ return (
+
+
+
+ You
+
+ {typeof timestampMs === "number" ? (
+
+ ) : null}
+
+
+ {text}
+
+
+ );
+});
+
+const AssistantMessageCard = memo(function AssistantMessageCard({
+ avatarSeed,
+ avatarUrl,
+ name,
+ timestampMs,
+ thinkingEvents,
+ thinkingText,
+ thinkingToolLines,
+ thinkingDurationMs,
+ contentText,
+ streaming,
+}: {
+ avatarSeed: string;
+ avatarUrl: string | null;
+ name: string;
+ timestampMs?: number;
+ thinkingEvents?: AssistantTraceEvent[];
+ thinkingText?: string | null;
+ thinkingToolLines?: string[];
+ thinkingDurationMs?: number;
+ contentText?: string | null;
+ streaming?: boolean;
+}) {
+ const resolvedTimestamp = typeof timestampMs === "number" ? timestampMs : null;
+ const hasThinking = Boolean(
+ (thinkingEvents?.length ?? 0) > 0 ||
+ thinkingText?.trim() ||
+ (thinkingToolLines?.length ?? 0) > 0
+ );
+ const widthClass = hasThinking
+ ? ASSISTANT_MAX_WIDTH_EXPANDED_CLASS
+ : resolveAssistantMaxWidthClass(contentText);
+ const hasContent = Boolean(contentText?.trim());
+ const compactStreamingIndicator = Boolean(streaming && !hasThinking && !hasContent);
+
+ return (
+
+
+
+
+
+
+
+ {name}
+
+ {resolvedTimestamp !== null ? (
+
+ ) : null}
+
+
+ {compactStreamingIndicator ? (
+
+
+ Thinking
+
+
+
+ ) : (
+
+ {streaming && !hasThinking ? (
+
+
+ Thinking
+
+
+
+ ) : null}
+
+ {hasThinking ? (
+
+ ) : null}
+
+ {contentText ? (
+
+ {streaming ? (
+ (() => {
+ if (!contentText.includes("MEDIA:")) {
+ return (
+
+ {contentText}
+
+ );
+ }
+ const rewritten = rewriteMediaLinesToMarkdown(contentText);
+ if (!rewritten.includes(") {
+ return (
+
+ {contentText}
+
+ );
+ }
+ return (
+
+ {rewritten}
+
+ );
+ })()
+ ) : (
+
+
+ {rewriteMediaLinesToMarkdown(contentText)}
+
+
+ )}
+
+ ) : null}
+
+ )}
+
+
+ );
+});
+
+const AssistantIntroCard = memo(function AssistantIntroCard({
+ avatarSeed,
+ avatarUrl,
+ name,
+ title,
+}: {
+ avatarSeed: string;
+ avatarUrl: string | null;
+ name: string;
+ title: string;
+}) {
+ return (
+
+
+
+
+
+
+
+ {name}
+
+
+
+ {title}
+
+ Try describing a task, bug, or question to get started.
+
+
+
+
+ );
+});
+
+const AgentChatFinalItems = memo(function AgentChatFinalItems({
+ agentId,
+ name,
+ avatarSeed,
+ avatarUrl,
+ chatItems,
+ running,
+ runStartedAt,
+}: {
+ agentId: string;
+ name: string;
+ avatarSeed: string;
+ avatarUrl: string | null;
+ chatItems: AgentChatItem[];
+ running: boolean;
+ runStartedAt: number | null;
+}) {
+ const blocks = buildAgentChatRenderBlocks(chatItems);
+
+ return (
+ <>
+ {blocks.map((block, index) => {
+ if (block.kind === "user") {
+ return (
+
+ );
+ }
+ const streaming = running && index === blocks.length - 1 && !block.text;
+ return (
+
+ );
+ })}
+ >
+ );
+});
+
+const AgentChatTranscript = memo(function AgentChatTranscript({
+ agentId,
+ name,
+ avatarSeed,
+ avatarUrl,
+ status,
+ historyMaybeTruncated,
+ historyFetchedCount,
+ historyFetchLimit,
+ onLoadMoreHistory,
+ chatItems,
+ liveThinkingText,
+ liveAssistantText,
+ showTypingIndicator,
+ outputLineCount,
+ liveAssistantCharCount,
+ liveThinkingCharCount,
+ runStartedAt,
+ scrollToBottomNextOutputRef,
+ pendingExecApprovals,
+ onResolveExecApproval,
+ emptyStateTitle,
+}: {
+ agentId: string;
+ name: string;
+ avatarSeed: string;
+ avatarUrl: string | null;
+ status: AgentRecord["status"];
+ historyMaybeTruncated: boolean;
+ historyFetchedCount: number | null;
+ historyFetchLimit: number | null;
+ onLoadMoreHistory: () => void;
+ chatItems: AgentChatItem[];
+ liveThinkingText: string;
+ liveAssistantText: string;
+ showTypingIndicator: boolean;
+ outputLineCount: number;
+ liveAssistantCharCount: number;
+ liveThinkingCharCount: number;
+ runStartedAt: number | null;
+ scrollToBottomNextOutputRef: MutableRefObject;
+ pendingExecApprovals: PendingExecApproval[];
+ onResolveExecApproval?: (id: string, decision: ExecApprovalDecision) => void;
+ emptyStateTitle: string;
+}) {
+ const chatRef = useRef(null);
+ const chatBottomRef = useRef(null);
+ const scrollFrameRef = useRef(null);
+ const pinnedRef = useRef(true);
+ const [isPinned, setIsPinned] = useState(true);
+ const [isAtTop, setIsAtTop] = useState(false);
+ const [nowMs, setNowMs] = useState(null);
+
+ const scrollChatToBottom = useCallback(() => {
+ if (!chatRef.current) return;
+ if (chatBottomRef.current) {
+ chatBottomRef.current.scrollIntoView({ block: "end" });
+ return;
+ }
+ chatRef.current.scrollTop = chatRef.current.scrollHeight;
+ }, []);
+
+ const setPinned = useCallback((nextPinned: boolean) => {
+ if (pinnedRef.current === nextPinned) return;
+ pinnedRef.current = nextPinned;
+ setIsPinned(nextPinned);
+ }, []);
+
+ const updatePinnedFromScroll = useCallback(() => {
+ const el = chatRef.current;
+ if (!el) return;
+ const nextAtTop = el.scrollTop <= CHAT_TOP_THRESHOLD_PX;
+ setIsAtTop((current) => (current === nextAtTop ? current : nextAtTop));
+ setPinned(
+ isNearBottom(
+ {
+ scrollTop: el.scrollTop,
+ scrollHeight: el.scrollHeight,
+ clientHeight: el.clientHeight,
+ },
+ 48
+ )
+ );
+ }, [setPinned]);
+
+ const scheduleScrollToBottom = useCallback(() => {
+ if (scrollFrameRef.current !== null) return;
+ scrollFrameRef.current = requestAnimationFrame(() => {
+ scrollFrameRef.current = null;
+ scrollChatToBottom();
+ });
+ }, [scrollChatToBottom]);
+
+ useEffect(() => {
+ updatePinnedFromScroll();
+ }, [updatePinnedFromScroll]);
+
+ const showJumpToLatest =
+ !isPinned && (outputLineCount > 0 || liveAssistantCharCount > 0 || liveThinkingCharCount > 0);
+
+ useEffect(() => {
+ const shouldForceScroll = scrollToBottomNextOutputRef.current;
+ if (shouldForceScroll) {
+ scrollToBottomNextOutputRef.current = false;
+ scheduleScrollToBottom();
+ return;
+ }
+
+ if (pinnedRef.current) {
+ scheduleScrollToBottom();
+ return;
+ }
+ }, [
+ liveAssistantCharCount,
+ liveThinkingCharCount,
+ outputLineCount,
+ pendingExecApprovals.length,
+ scheduleScrollToBottom,
+ scrollToBottomNextOutputRef,
+ ]);
+
+ useEffect(() => {
+ return () => {
+ if (scrollFrameRef.current !== null) {
+ cancelAnimationFrame(scrollFrameRef.current);
+ scrollFrameRef.current = null;
+ }
+ };
+ }, []);
+
+ const showLiveAssistantCard =
+ status === "running" && Boolean(liveThinkingText || liveAssistantText || showTypingIndicator);
+ const hasApprovals = pendingExecApprovals.length > 0;
+ const hasTranscriptContent = chatItems.length > 0 || hasApprovals;
+
+ useEffect(() => {
+ if (status !== "running" || typeof runStartedAt !== "number" || !showLiveAssistantCard) {
+ return;
+ }
+
+ const timeoutId = window.setTimeout(() => {
+ setNowMs(Date.now());
+ }, 0);
+ const intervalId = window.setInterval(() => setNowMs(Date.now()), 250);
+
+ return () => {
+ window.clearTimeout(timeoutId);
+ window.clearInterval(intervalId);
+ };
+ }, [runStartedAt, showLiveAssistantCard, status]);
+
+ return (
+
+ updatePinnedFromScroll()}
+ onWheel={(event) => {
+ event.stopPropagation();
+ }}
+ onWheelCapture={(event) => {
+ event.stopPropagation();
+ }}
+ >
+
+
+ {historyMaybeTruncated && isAtTop ? (
+
+
+ Showing most recent {typeof historyFetchedCount === "number" ? historyFetchedCount : "?"} messages
+ {typeof historyFetchLimit === "number" ? ` (limit ${historyFetchLimit})` : ""}
+
+
+
+ ) : null}
+ {!hasTranscriptContent ? (
+
+ ) : (
+ <>
+
+ {showLiveAssistantCard ? (
+
+ ) : null}
+ {pendingExecApprovals.map((approval) => (
+
+ ))}
+
+ >
+ )}
+
+
+
+ {showJumpToLatest ? (
+
+ ) : null}
+
+ );
+});
+
+const noopToggle = () => {};
+const InlineHoverTooltip = ({
+ text,
+ children,
+}: {
+ text: string;
+ children: ReactNode;
+}) => {
+ return (
+
+ {children}
+
+ {text}
+
+
+ );
+};
+
+const AgentChatComposer = memo(function AgentChatComposer({
+ value,
+ onChange,
+ onKeyDown,
+ onSend,
+ onVoiceToggle,
+ onStop,
+ canSend,
+ stopBusy,
+ stopDisabledReason,
+ running,
+ sendDisabled,
+ voiceEnabled,
+ voiceSupported,
+ voiceState,
+ voiceError,
+ queuedMessages,
+ onRemoveQueuedMessage,
+ inputRef,
+ modelOptions,
+ modelValue,
+ allowThinking,
+ thinkingValue,
+ onModelChange,
+ onThinkingChange,
+ toolCallingEnabled,
+ showThinkingTraces,
+ onToolCallingToggle,
+ onThinkingTracesToggle,
+}: {
+ value: string;
+ onChange: (event: ChangeEvent) => void;
+ onKeyDown: (event: KeyboardEvent) => void;
+ onSend: () => void;
+ onVoiceToggle?: () => void;
+ onStop: () => void;
+ canSend: boolean;
+ stopBusy: boolean;
+ stopDisabledReason?: string | null;
+ running: boolean;
+ sendDisabled: boolean;
+ voiceEnabled: boolean;
+ voiceSupported: boolean;
+ voiceState: VoiceRecorderState;
+ voiceError?: string | null;
+ queuedMessages: string[];
+ onRemoveQueuedMessage?: (index: number) => void;
+ inputRef: (el: HTMLTextAreaElement | HTMLInputElement | null) => void;
+ modelOptions: { value: string; label: string }[];
+ modelValue: string;
+ allowThinking: boolean;
+ thinkingValue: string;
+ onModelChange: (value: string | null) => void;
+ onThinkingChange: (value: string | null) => void;
+ toolCallingEnabled: boolean;
+ showThinkingTraces: boolean;
+ onToolCallingToggle: (enabled: boolean) => void;
+ onThinkingTracesToggle: (enabled: boolean) => void;
+}) {
+ const stopReason = stopDisabledReason?.trim() ?? "";
+ const stopDisabled = !canSend || stopBusy || Boolean(stopReason);
+ const stopAriaLabel = stopReason ? `Stop unavailable: ${stopReason}` : "Stop";
+ const voiceBusy = voiceState === "requesting" || voiceState === "transcribing";
+ const voiceRecording = voiceState === "recording";
+ const voiceDisabled = voiceRecording ? false : !voiceEnabled || !voiceSupported || !canSend || voiceBusy;
+ const voiceLabel =
+ voiceState === "recording"
+ ? "Stop"
+ : voiceState === "transcribing"
+ ? "..."
+ : voiceState === "requesting"
+ ? "Mic..."
+ : "Mic";
+ const voiceStatusText =
+ voiceState === "recording"
+ ? "Recording. Tap stop to send."
+ : voiceState === "transcribing"
+ ? "Transcribing your voice note."
+ : voiceState === "requesting"
+ ? "Requesting microphone access."
+ : !voiceSupported && voiceEnabled
+ ? "This browser does not support microphone recording."
+ : null;
+ const modelSelectedLabel = useMemo(() => {
+ if (modelOptions.length === 0) return "No models found";
+ return modelOptions.find((option) => option.value === modelValue)?.label ?? modelValue;
+ }, [modelOptions, modelValue]);
+ const modelSelectWidthCh = Math.max(11, Math.min(44, modelSelectedLabel.length + 6));
+ const thinkingSelectedLabel = useMemo(() => {
+ switch (thinkingValue) {
+ case "off":
+ return "Off";
+ case "minimal":
+ return "Minimal";
+ case "low":
+ return "Low";
+ case "medium":
+ return "Medium";
+ case "high":
+ return "High";
+ case "xhigh":
+ return "XHigh";
+ default:
+ return "Default";
+ }
+ }, [thinkingValue]);
+ const thinkingSelectWidthCh = Math.max(9, Math.min(22, thinkingSelectedLabel.length + 6));
+ return (
+ <>
+
+
+
+
+
+ {allowThinking ? (
+
+
+
+ ) : null}
+
+
+ Show
+
+
+
+
+
+ {queuedMessages.length > 0 ? (
+
+
+ {queuedMessages.map((queuedMessage, index) => (
+
+
+ Queued
+
+
+ {queuedMessage}
+
+
+
+ ))}
+
+ {running ? (
+
+ ) : null}
+
+
+ ) : null}
+ {voiceStatusText || voiceError ? (
+
+ {voiceError ?? voiceStatusText}
+
+ ) : null}
+
+
+ {voiceEnabled ? (
+
+ ) : null}
+ {running ? (
+
+
+
+ ) : null}
+
+
+
+ >
+ );
+});
+
+export const AgentChatPanel = ({
+ agent,
+ isSelected,
+ canSend,
+ models,
+ stopBusy,
+ stopDisabledReason = null,
+ onLoadMoreHistory,
+ onOpenSettings,
+ onRename,
+ onNewSession,
+ onModelChange,
+ onThinkingChange,
+ onToolCallingToggle = noopToggle,
+ onThinkingTracesToggle = noopToggle,
+ onDraftChange,
+ onSend,
+ onRemoveQueuedMessage,
+ onStopRun,
+ onAvatarShuffle,
+ pendingExecApprovals = [],
+ onResolveExecApproval,
+ onVoiceSend,
+}: AgentChatPanelProps) => {
+ const [draftValue, setDraftValue] = useState(agent.draft);
+ const [newSessionBusy, setNewSessionBusy] = useState(false);
+ const [renameEditing, setRenameEditing] = useState(false);
+ const [renameSaving, setRenameSaving] = useState(false);
+ const [renameDraft, setRenameDraft] = useState(agent.name);
+ const [renameError, setRenameError] = useState(null);
+ const draftRef = useRef(null);
+ const renameInputRef = useRef(null);
+ const renameEditorRef = useRef(null);
+ const scrollToBottomNextOutputRef = useRef(false);
+ const plainDraftRef = useRef(agent.draft);
+ const draftIdentityRef = useRef<{ agentId: string; sessionKey: string }>({
+ agentId: agent.agentId,
+ sessionKey: agent.sessionKey,
+ });
+ const pendingResizeFrameRef = useRef(null);
+ const {
+ state: voiceState,
+ error: voiceError,
+ supported: voiceSupported,
+ toggle: toggleVoiceRecording,
+ } = useVoiceRecorder({
+ enabled: Boolean(onVoiceSend),
+ onVoiceSend,
+ });
+
+ const resizeDraft = useCallback(() => {
+ const el = draftRef.current;
+ if (!el) return;
+ el.style.height = "auto";
+ el.style.height = `${el.scrollHeight}px`;
+ el.style.overflowY = el.scrollHeight > el.clientHeight ? "auto" : "hidden";
+ }, []);
+
+ const handleDraftRef = useCallback((el: HTMLTextAreaElement | HTMLInputElement | null) => {
+ draftRef.current = el instanceof HTMLTextAreaElement ? el : null;
+ }, []);
+
+ useEffect(() => {
+ const previousIdentity = draftIdentityRef.current;
+ const identityChanged =
+ previousIdentity.agentId !== agent.agentId ||
+ previousIdentity.sessionKey !== agent.sessionKey;
+ if (identityChanged) {
+ draftIdentityRef.current = {
+ agentId: agent.agentId,
+ sessionKey: agent.sessionKey,
+ };
+ plainDraftRef.current = agent.draft;
+ setDraftValue(agent.draft);
+ return;
+ }
+ if (agent.draft === plainDraftRef.current) return;
+ if (agent.draft.length !== 0) return;
+ plainDraftRef.current = "";
+ setDraftValue("");
+ }, [agent.agentId, agent.draft, agent.sessionKey]);
+
+ useEffect(() => {
+ setRenameEditing(false);
+ setRenameSaving(false);
+ setRenameError(null);
+ setRenameDraft(agent.name);
+ }, [agent.agentId, agent.name]);
+
+ useEffect(() => {
+ if (!renameEditing) return;
+ const frameId = requestAnimationFrame(() => {
+ renameInputRef.current?.focus();
+ renameInputRef.current?.select();
+ });
+ return () => {
+ cancelAnimationFrame(frameId);
+ };
+ }, [renameEditing]);
+
+ useEffect(() => {
+ if (pendingResizeFrameRef.current !== null) {
+ cancelAnimationFrame(pendingResizeFrameRef.current);
+ }
+ pendingResizeFrameRef.current = requestAnimationFrame(() => {
+ pendingResizeFrameRef.current = null;
+ resizeDraft();
+ });
+ return () => {
+ if (pendingResizeFrameRef.current !== null) {
+ cancelAnimationFrame(pendingResizeFrameRef.current);
+ pendingResizeFrameRef.current = null;
+ }
+ };
+ }, [resizeDraft, draftValue]);
+
+ const handleSend = useCallback(
+ (message: string) => {
+ if (!canSend) return;
+ const trimmed = message.trim();
+ if (!trimmed) return;
+ plainDraftRef.current = "";
+ setDraftValue("");
+ onDraftChange("");
+ scrollToBottomNextOutputRef.current = true;
+ onSend(trimmed);
+ },
+ [canSend, onDraftChange, onSend]
+ );
+
+ const chatItems = useMemo(
+ () =>
+ buildFinalAgentChatItems({
+ outputLines: agent.outputLines,
+ showThinkingTraces: agent.showThinkingTraces,
+ toolCallingEnabled: agent.toolCallingEnabled,
+ }),
+ [agent.outputLines, agent.showThinkingTraces, agent.toolCallingEnabled]
+ );
+ const running = agent.status === "running";
+ const renderBlocks = useMemo(() => buildAgentChatRenderBlocks(chatItems), [chatItems]);
+ const hasActiveStreamingTailInTranscript =
+ running && renderBlocks.length > 0 && !renderBlocks[renderBlocks.length - 1].text;
+ const liveAssistantText =
+ running && agent.streamText ? normalizeAssistantDisplayText(agent.streamText) : "";
+ const liveThinkingText =
+ running && agent.showThinkingTraces && agent.thinkingTrace ? agent.thinkingTrace.trim() : "";
+ const hasVisibleLiveThinking = Boolean(liveThinkingText.trim());
+ const showTypingIndicator =
+ running &&
+ !hasVisibleLiveThinking &&
+ !liveAssistantText &&
+ !hasActiveStreamingTailInTranscript;
+
+ const modelOptions = useMemo(
+ () =>
+ models.map((entry) => {
+ const key = `${entry.provider}/${entry.id}`;
+ const alias = typeof entry.name === "string" ? entry.name.trim() : "";
+ return {
+ value: key,
+ label: !alias || alias === key ? key : alias,
+ reasoning: entry.reasoning,
+ };
+ }),
+ [models]
+ );
+ const modelValue = agent.model ?? "";
+ const modelOptionsWithFallback =
+ modelValue && !modelOptions.some((option) => option.value === modelValue)
+ ? [{ value: modelValue, label: modelValue, reasoning: undefined }, ...modelOptions]
+ : modelOptions;
+ const selectedModel = modelOptionsWithFallback.find((option) => option.value === modelValue);
+ const allowThinking = selectedModel?.reasoning !== false;
+
+ const avatarSeed = agent.avatarSeed ?? agent.agentId;
+ const emptyStateTitle = useMemo(
+ () => resolveEmptyChatIntroMessage(agent.agentId, agent.sessionEpoch),
+ [agent.agentId, agent.sessionEpoch]
+ );
+ const sendDisabled = !canSend || !draftValue.trim();
+
+ const handleComposerChange = useCallback(
+ (event: ChangeEvent) => {
+ const value = event.target.value;
+ plainDraftRef.current = value;
+ setDraftValue(value);
+ onDraftChange(value);
+ },
+ [onDraftChange]
+ );
+
+ const handleComposerKeyDown = useCallback(
+ (event: KeyboardEvent) => {
+ if (event.nativeEvent.isComposing || event.nativeEvent.keyCode === 229) return;
+ if (event.key !== "Enter" || event.shiftKey) return;
+ if (event.defaultPrevented) return;
+ event.preventDefault();
+ handleSend(draftValue);
+ },
+ [draftValue, handleSend]
+ );
+
+ const handleComposerSend = useCallback(() => {
+ handleSend(draftValue);
+ }, [draftValue, handleSend]);
+
+ const handleVoiceToggle = useCallback(
+ () => {
+ if (!canSend && voiceState !== "recording") return;
+ void toggleVoiceRecording();
+ },
+ [canSend, toggleVoiceRecording, voiceState],
+ );
+
+ const beginRename = useCallback(() => {
+ if (!onRename) return;
+ setRenameEditing(true);
+ setRenameDraft(agent.name);
+ setRenameError(null);
+ }, [agent.name, onRename]);
+
+ const cancelRename = useCallback(() => {
+ if (renameSaving) return;
+ setRenameEditing(false);
+ setRenameDraft(agent.name);
+ setRenameError(null);
+ }, [agent.name, renameSaving]);
+
+ useEffect(() => {
+ if (!renameEditing) return;
+ const handlePointerDown = (event: PointerEvent) => {
+ const target = event.target;
+ if (!(target instanceof Node)) return;
+ if (renameEditorRef.current?.contains(target)) return;
+ cancelRename();
+ };
+ document.addEventListener("pointerdown", handlePointerDown, true);
+ return () => {
+ document.removeEventListener("pointerdown", handlePointerDown, true);
+ };
+ }, [cancelRename, renameEditing]);
+
+ const submitRename = useCallback(async () => {
+ if (!onRename || renameSaving) return;
+ const nextName = renameDraft.trim();
+ const currentName = agent.name.trim();
+ if (!nextName) {
+ setRenameError("Agent name is required.");
+ return;
+ }
+ if (nextName === currentName) {
+ setRenameEditing(false);
+ setRenameError(null);
+ setRenameDraft(agent.name);
+ return;
+ }
+ setRenameSaving(true);
+ setRenameError(null);
+ try {
+ const ok = await onRename(nextName);
+ if (!ok) {
+ setRenameError("Failed to rename agent.");
+ return;
+ }
+ setRenameEditing(false);
+ setRenameDraft(nextName);
+ } finally {
+ setRenameSaving(false);
+ }
+ }, [agent.name, onRename, renameDraft, renameSaving]);
+
+ const handleRenameInputKeyDown = useCallback(
+ (event: KeyboardEvent) => {
+ if (event.key === "Enter") {
+ event.preventDefault();
+ void submitRename();
+ return;
+ }
+ if (event.key === "Escape") {
+ event.preventDefault();
+ cancelRename();
+ }
+ },
+ [cancelRename, submitRename]
+ );
+
+ const handleNewSession = useCallback(async () => {
+ if (!onNewSession || newSessionBusy || !canSend) return;
+ setNewSessionBusy(true);
+ try {
+ await onNewSession();
+ } finally {
+ setNewSessionBusy(false);
+ }
+ }, [canSend, newSessionBusy, onNewSession]);
+
+ const newSessionDisabled = newSessionBusy || !canSend || !onNewSession;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ {renameEditing ? (
+
+ {
+ setRenameDraft(event.target.value);
+ if (renameError) setRenameError(null);
+ }}
+ onKeyDown={handleRenameInputKeyDown}
+ />
+
+
+
+ ) : (
+
+
+ {agent.name}
+
+ {onRename ? (
+
+ ) : null}
+
+ )}
+
+
+ {renameError ? (
+ {renameError}
+ ) : null}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ({
+ value: option.value,
+ label: option.label,
+ }))}
+ modelValue={modelValue}
+ allowThinking={allowThinking}
+ thinkingValue={agent.thinkingLevel ?? ""}
+ onModelChange={onModelChange}
+ onThinkingChange={onThinkingChange}
+ toolCallingEnabled={agent.toolCallingEnabled}
+ showThinkingTraces={agent.showThinkingTraces}
+ onToolCallingToggle={onToolCallingToggle}
+ onThinkingTracesToggle={onThinkingTracesToggle}
+ />
+
+
+
+ );
+};
diff --git a/src/features/agents/components/AgentCreateModal.tsx b/src/features/agents/components/AgentCreateModal.tsx
new file mode 100644
index 0000000..b7a490b
--- /dev/null
+++ b/src/features/agents/components/AgentCreateModal.tsx
@@ -0,0 +1,157 @@
+"use client";
+
+import { useState } from "react";
+import { Shuffle } from "lucide-react";
+import type { AgentCreateModalSubmitPayload } from "@/features/agents/creation/types";
+import { AgentAvatar } from "@/features/agents/components/AgentAvatar";
+import { randomUUID } from "@/lib/uuid";
+
+type AgentCreateModalProps = {
+ open: boolean;
+ suggestedName: string;
+ busy?: boolean;
+ submitError?: string | null;
+ onClose: () => void;
+ onSubmit: (payload: AgentCreateModalSubmitPayload) => Promise | void;
+};
+
+const fieldClassName =
+ "ui-input w-full rounded-md px-3 py-2 text-xs text-foreground outline-none";
+const labelClassName =
+ "font-mono text-[11px] font-semibold tracking-[0.05em] text-muted-foreground";
+
+const resolveInitialName = (suggestedName: string): string => {
+ const trimmed = suggestedName.trim();
+ if (!trimmed) return "New Agent";
+ return trimmed;
+};
+
+const AgentCreateModalContent = ({
+ suggestedName,
+ busy,
+ submitError,
+ onClose,
+ onSubmit,
+}: Omit) => {
+ const [name, setName] = useState(() => resolveInitialName(suggestedName));
+ const [avatarSeed, setAvatarSeed] = useState(() => randomUUID());
+
+ const canSubmit = name.trim().length > 0;
+
+ const handleSubmit = () => {
+ if (!canSubmit || busy) return;
+ const trimmedName = name.trim();
+ if (!trimmedName) return;
+ void onSubmit({ name: trimmedName, avatarSeed });
+ };
+
+ return (
+
+
+
+ );
+};
+
+export const AgentCreateModal = ({
+ open,
+ suggestedName,
+ busy = false,
+ submitError = null,
+ onClose,
+ onSubmit,
+}: AgentCreateModalProps) => {
+ if (!open) return null;
+ return (
+
+ );
+};
diff --git a/src/features/agents/components/AgentInspectPanels.tsx b/src/features/agents/components/AgentInspectPanels.tsx
new file mode 100644
index 0000000..38b4d2f
--- /dev/null
+++ b/src/features/agents/components/AgentInspectPanels.tsx
@@ -0,0 +1,7 @@
+"use client";
+
+export { AgentBrainPanel, type AgentBrainPanelProps } from "@/features/agents/components/inspect/AgentBrainPanel";
+export {
+ AgentSettingsPanel,
+ type AgentSettingsPanelProps,
+} from "@/features/agents/components/inspect/AgentSettingsPanel";
diff --git a/src/features/agents/components/AgentSkillsPanel.tsx b/src/features/agents/components/AgentSkillsPanel.tsx
new file mode 100644
index 0000000..3e3fa39
--- /dev/null
+++ b/src/features/agents/components/AgentSkillsPanel.tsx
@@ -0,0 +1,255 @@
+"use client";
+
+import { useMemo, useState } from "react";
+
+import type { SkillStatusReport } from "@/lib/skills/types";
+import {
+ buildAgentSkillsAllowlistSet,
+ buildSkillMissingDetails,
+ deriveAgentSkillDisplayState,
+ deriveAgentSkillsAccessMode,
+ deriveSkillReadinessState,
+ type AgentSkillDisplayState,
+} from "@/lib/skills/presentation";
+
+type SkillRowFilter = "all" | AgentSkillDisplayState;
+
+type AgentSkillsPanelProps = {
+ skillsReport?: SkillStatusReport | null;
+ skillsLoading?: boolean;
+ skillsError?: string | null;
+ skillsBusy?: boolean;
+ skillsBusyKey?: string | null;
+ skillsAllowlist?: string[] | undefined;
+ onSetSkillEnabled: (skillName: string, enabled: boolean) => Promise | void;
+ onOpenSystemSetup: (skillKey?: string) => void;
+};
+
+const FILTERS: Array<{ id: SkillRowFilter; label: string }> = [
+ { id: "all", label: "All" },
+ { id: "ready", label: "Ready" },
+ { id: "setup-required", label: "Setup required" },
+ { id: "not-supported", label: "Not supported" },
+];
+
+const DISPLAY_LABELS: Record = {
+ ready: "Ready",
+ "setup-required": "Setup required",
+ "not-supported": "Not supported",
+};
+
+const DISPLAY_CLASSES: Record = {
+ ready: "ui-badge-status-running",
+ "setup-required": "ui-badge-status-error",
+ "not-supported": "ui-badge-status-error",
+};
+
+const resolveHint = (
+ skill: SkillStatusReport["skills"][number],
+ displayState: AgentSkillDisplayState
+): string | null => {
+ if (displayState === "ready") {
+ return null;
+ }
+ if (displayState === "not-supported") {
+ if (skill.blockedByAllowlist) {
+ return "Blocked by bundled skills policy.";
+ }
+ return buildSkillMissingDetails(skill).find((line) => line.startsWith("Requires OS:")) ?? "Not supported.";
+ }
+ const readiness = deriveSkillReadinessState(skill);
+ if (readiness === "disabled-globally") {
+ return "Disabled globally. Enable it in System setup.";
+ }
+ return buildSkillMissingDetails(skill)[0] ?? "Requires setup in System setup.";
+};
+
+export const AgentSkillsPanel = ({
+ skillsReport = null,
+ skillsLoading = false,
+ skillsError = null,
+ skillsBusy = false,
+ skillsBusyKey = null,
+ skillsAllowlist,
+ onSetSkillEnabled,
+ onOpenSystemSetup,
+}: AgentSkillsPanelProps) => {
+ const [skillsFilter, setSkillsFilter] = useState("");
+ const [rowFilter, setRowFilter] = useState("all");
+
+ const skillEntries = useMemo(() => skillsReport?.skills ?? [], [skillsReport]);
+ const accessMode = deriveAgentSkillsAccessMode(skillsAllowlist);
+ const allowlistSet = useMemo(() => buildAgentSkillsAllowlistSet(skillsAllowlist), [skillsAllowlist]);
+ const anySkillBusy = skillsBusy || Boolean(skillsBusyKey);
+
+ const rows = useMemo(() => {
+ return skillEntries.map((skill) => {
+ const normalizedName = skill.name.trim();
+ const allowed =
+ accessMode === "all" ? true : accessMode === "none" ? false : allowlistSet.has(normalizedName);
+ const readiness = deriveSkillReadinessState(skill);
+ return {
+ skill,
+ allowed,
+ displayState: deriveAgentSkillDisplayState(readiness),
+ };
+ });
+ }, [accessMode, allowlistSet, skillEntries]);
+
+ const searchedRows = useMemo(() => {
+ const query = skillsFilter.trim().toLowerCase();
+ if (!query) {
+ return rows;
+ }
+ return rows.filter((entry) =>
+ [entry.skill.name, entry.skill.description, entry.skill.source, entry.skill.skillKey]
+ .join(" ")
+ .toLowerCase()
+ .includes(query)
+ );
+ }, [rows, skillsFilter]);
+
+ const filteredRows = useMemo(() => {
+ if (rowFilter === "all") {
+ return searchedRows;
+ }
+ return searchedRows.filter((entry) => entry.displayState === rowFilter);
+ }, [rowFilter, searchedRows]);
+
+ const filterCounts = useMemo(
+ () =>
+ searchedRows.reduce(
+ (counts, entry) => {
+ counts.all += 1;
+ counts[entry.displayState] += 1;
+ return counts;
+ },
+ {
+ all: 0,
+ ready: 0,
+ "setup-required": 0,
+ "not-supported": 0,
+ } satisfies Record
+ ),
+ [searchedRows]
+ );
+
+ const enabledCount = useMemo(
+ () => rows.reduce((count, entry) => count + (entry.allowed ? 1 : 0), 0),
+ [rows]
+ );
+
+ return (
+
+
+ Skills
+
+ {enabledCount}/{skillEntries.length}
+
+
+ Skill access controls apply to this agent.
+ {accessMode === "selected" ? (
+
+ This agent is using selected skills only.
+
+ ) : null}
+
+ setSkillsFilter(event.target.value)}
+ placeholder="Search skills"
+ className="w-full rounded-md border border-border/60 bg-surface-1 px-3 py-2 text-[11px] text-foreground outline-none transition focus:border-border"
+ aria-label="Search skills"
+ />
+
+
+ {FILTERS.map((filter) => {
+ const selected = rowFilter === filter.id;
+ return (
+
+ );
+ })}
+
+ {skillsLoading ? Loading skills... : null}
+ {!skillsLoading && skillsError ? (
+ {skillsError}
+ ) : null}
+ {!skillsLoading && !skillsError && filteredRows.length === 0 ? (
+ No matching skills.
+ ) : null}
+ {!skillsLoading && !skillsError && filteredRows.length > 0 ? (
+
+ {filteredRows.map((entry) => {
+ const statusLabel = DISPLAY_LABELS[entry.displayState];
+ const statusClassName = DISPLAY_CLASSES[entry.displayState];
+ const canConfigureInSystem = entry.displayState === "setup-required";
+ const switchDisabled = anySkillBusy || entry.displayState === "not-supported";
+ return (
+
+
+
+ {entry.skill.name}
+
+ {entry.skill.source}
+
+
+ {statusLabel}
+
+
+ {entry.skill.description}
+ {entry.displayState !== "ready" ? (
+
+ {resolveHint(entry.skill, entry.displayState)}
+
+ ) : null}
+
+
+
+ {canConfigureInSystem ? (
+
+ ) : null}
+
+
+ );
+ })}
+
+ ) : null}
+
+ );
+};
diff --git a/src/features/agents/components/AgentSkillsSetupModal.tsx b/src/features/agents/components/AgentSkillsSetupModal.tsx
new file mode 100644
index 0000000..a250aa5
--- /dev/null
+++ b/src/features/agents/components/AgentSkillsSetupModal.tsx
@@ -0,0 +1,235 @@
+"use client";
+
+import { useEffect } from "react";
+
+import type { SkillStatusEntry } from "@/lib/skills/types";
+import {
+ buildSkillMissingDetails,
+ canRemoveSkill,
+ deriveSkillReadinessState,
+ resolvePreferredInstallOption,
+} from "@/lib/skills/presentation";
+
+type SkillSetupMessage = { kind: "success" | "error"; message: string };
+
+type AgentSkillsSetupModalProps = {
+ skill: SkillStatusEntry | null;
+ skillsBusy: boolean;
+ skillsBusyKey: string | null;
+ skillMessage: SkillSetupMessage | null;
+ apiKeyDraft: string;
+ defaultAgentScopeWarning?: string | null;
+ onClose: () => void;
+ onInstallSkill: (skillKey: string, name: string, installId: string) => Promise | void;
+ onSetSkillGlobalEnabled: (skillKey: string, enabled: boolean) => Promise | void;
+ onRemoveSkill: (
+ skill: { skillKey: string; source: string; baseDir: string }
+ ) => Promise | void;
+ onSkillApiKeyChange: (skillKey: string, value: string) => Promise | void;
+ onSaveSkillApiKey: (skillKey: string) => Promise | void;
+};
+
+const READINESS_LABELS = {
+ ready: "Ready",
+ "needs-setup": "Needs setup",
+ unavailable: "Unavailable",
+ "disabled-globally": "Disabled globally",
+} as const;
+
+const READINESS_CLASSES = {
+ ready: "ui-badge-status-running",
+ "needs-setup": "ui-badge-status-error",
+ unavailable: "ui-badge-status-error",
+ "disabled-globally": "ui-badge-status-error",
+} as const;
+
+export const AgentSkillsSetupModal = ({
+ skill,
+ skillsBusy,
+ skillsBusyKey,
+ skillMessage,
+ apiKeyDraft,
+ defaultAgentScopeWarning = null,
+ onClose,
+ onInstallSkill,
+ onSetSkillGlobalEnabled,
+ onRemoveSkill,
+ onSkillApiKeyChange,
+ onSaveSkillApiKey,
+}: AgentSkillsSetupModalProps) => {
+ useEffect(() => {
+ if (!skill) {
+ return;
+ }
+ const handleKeyDown = (event: KeyboardEvent) => {
+ if (event.key !== "Escape") {
+ return;
+ }
+ event.preventDefault();
+ onClose();
+ };
+ window.addEventListener("keydown", handleKeyDown);
+ return () => {
+ window.removeEventListener("keydown", handleKeyDown);
+ };
+ }, [onClose, skill]);
+
+ if (!skill) {
+ return null;
+ }
+
+ const readiness = deriveSkillReadinessState(skill);
+ const readinessLabel = READINESS_LABELS[readiness];
+ const readinessClassName = READINESS_CLASSES[readiness];
+ const missingDetails = buildSkillMissingDetails(skill);
+ const installOption = resolvePreferredInstallOption(skill);
+ const canDeleteSkill = canRemoveSkill(skill);
+ const busyForSkill = skillsBusyKey === skill.skillKey;
+ const anySkillBusy = skillsBusy || Boolean(skillsBusyKey);
+ const trimmedApiKey = apiKeyDraft.trim();
+
+ return (
+
+ event.stopPropagation()}
+ >
+
+
+
+ System setup
+
+
+ {skill.name}
+
+ {readinessLabel}
+
+
+
+ Changes affect all agents on this gateway.
+
+
+
+
+
+ {defaultAgentScopeWarning ? (
+
+ {defaultAgentScopeWarning}
+
+ ) : null}
+ {skill.description}
+ {skill.blockedByAllowlist ? (
+
+ Blocked by bundled skills policy (`skills.allowBundled`).
+
+ ) : null}
+ {missingDetails.map((line) => (
+
+ {line}
+
+ ))}
+ {skillMessage ? (
+
+ {skillMessage.message}
+
+ ) : null}
+
+ {installOption ? (
+
+ ) : null}
+
+ {skill.primaryEnv ? (
+ <>
+ {
+ void onSkillApiKeyChange(skill.skillKey, event.target.value);
+ }}
+ disabled={anySkillBusy}
+ className="w-full rounded-md border border-border/60 bg-surface-1 px-3 py-2 text-[10px] text-foreground outline-none transition focus:border-border"
+ placeholder={`Set ${skill.primaryEnv}`}
+ aria-label={`API key for ${skill.name}`}
+ />
+
+ >
+ ) : null}
+ {canDeleteSkill ? (
+
+ ) : null}
+
+
+
+
+ );
+};
diff --git a/src/features/agents/components/ConnectionPanel.tsx b/src/features/agents/components/ConnectionPanel.tsx
new file mode 100644
index 0000000..33714c0
--- /dev/null
+++ b/src/features/agents/components/ConnectionPanel.tsx
@@ -0,0 +1,94 @@
+import type { GatewayStatus } from "@/lib/gateway/GatewayClient";
+import { X } from "lucide-react";
+import { resolveGatewayStatusBadgeClass, resolveGatewayStatusLabel } from "./colorSemantics";
+
+type ConnectionPanelProps = {
+ gatewayUrl: string;
+ token: string;
+ status: GatewayStatus;
+ error: string | null;
+ onGatewayUrlChange: (value: string) => void;
+ onTokenChange: (value: string) => void;
+ onConnect: () => void;
+ onDisconnect: () => void;
+ onClose?: () => void;
+};
+
+export const ConnectionPanel = ({
+ gatewayUrl,
+ token,
+ status,
+ error,
+ onGatewayUrlChange,
+ onTokenChange,
+ onConnect,
+ onDisconnect,
+ onClose,
+}: ConnectionPanelProps) => {
+ const isConnected = status === "connected";
+ const isConnecting = status === "connecting";
+
+ return (
+
+
+
+
+ {resolveGatewayStatusLabel(status)}
+
+
+
+ {onClose ? (
+
+ ) : null}
+
+
+
+
+
+ {error ? (
+
+ {error}
+
+ ) : null}
+
+ );
+};
diff --git a/src/features/agents/components/EmptyStatePanel.tsx b/src/features/agents/components/EmptyStatePanel.tsx
new file mode 100644
index 0000000..0668e02
--- /dev/null
+++ b/src/features/agents/components/EmptyStatePanel.tsx
@@ -0,0 +1,58 @@
+import { clsx, type ClassValue } from "clsx";
+import { twMerge } from "tailwind-merge";
+
+const cn = (...inputs: ClassValue[]) => twMerge(clsx(inputs));
+
+type EmptyStatePanelProps = {
+ title: string;
+ label?: string;
+ description?: string;
+ detail?: string;
+ fillHeight?: boolean;
+ compact?: boolean;
+ className?: string;
+};
+
+export const EmptyStatePanel = ({
+ title,
+ label,
+ description,
+ detail,
+ fillHeight = false,
+ compact = false,
+ className,
+}: EmptyStatePanelProps) => {
+ return (
+
+ {label ? (
+
+ {label}
+
+ ) : null}
+
+ {title}
+
+ {description ? (
+
+ {description}
+
+ ) : null}
+ {detail ? (
+
+ {detail}
+
+ ) : null}
+
+ );
+};
diff --git a/src/features/agents/components/FleetSidebar.tsx b/src/features/agents/components/FleetSidebar.tsx
new file mode 100644
index 0000000..ff3f48b
--- /dev/null
+++ b/src/features/agents/components/FleetSidebar.tsx
@@ -0,0 +1,173 @@
+import type { AgentState, FocusFilter } from "@/features/agents/state/store";
+import { useLayoutEffect, useMemo, useRef } from "react";
+import { AgentAvatar } from "./AgentAvatar";
+import {
+ NEEDS_APPROVAL_BADGE_CLASS,
+ resolveAgentStatusBadgeClass,
+ resolveAgentStatusLabel,
+} from "./colorSemantics";
+import { EmptyStatePanel } from "./EmptyStatePanel";
+
+type FleetSidebarProps = {
+ agents: AgentState[];
+ selectedAgentId: string | null;
+ filter: FocusFilter;
+ onFilterChange: (next: FocusFilter) => void;
+ onSelectAgent: (agentId: string) => void;
+ onCreateAgent: () => void;
+ createDisabled?: boolean;
+ createBusy?: boolean;
+};
+
+const FILTER_OPTIONS: Array<{ value: FocusFilter; label: string; testId: string }> = [
+ { value: "all", label: "All", testId: "fleet-filter-all" },
+ { value: "running", label: "Running", testId: "fleet-filter-running" },
+ { value: "approvals", label: "Approvals", testId: "fleet-filter-approvals" },
+];
+
+export const FleetSidebar = ({
+ agents,
+ selectedAgentId,
+ filter,
+ onFilterChange,
+ onSelectAgent,
+ onCreateAgent,
+ createDisabled = false,
+ createBusy = false,
+}: FleetSidebarProps) => {
+ const rowRefs = useRef