diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 887dffc..c178f05 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -5,14 +5,14 @@ }, "metadata": { "description": "PlugBrain — local project and code memory for AI agents", - "version": "0.3.1" + "version": "0.5.0-dev.1" }, "plugins": [ { "name": "plugbrain", "source": "./", "description": "Ask the brain first: AST code intelligence, Obsidian-style notes and agent coordination for this repository, served locally over MCP.", - "version": "0.3.1", + "version": "0.5.0-dev.1", "license": "MIT", "category": "productivity", "keywords": [ diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index b4ca1ef..b3abb56 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "plugbrain", - "version": "0.3.1", + "version": "0.5.0-dev.1", "description": "Local project and code memory for AI agents: AST code intelligence, Obsidian-style notes and multi-agent coordination, asked through MCP.", "author": { "name": "litbitrim" diff --git a/CHANGELOG.md b/CHANGELOG.md index 74439c7..23ecde5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to PlugBrain are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/) and the project adheres to [Semantic Versioning](https://semver.org/). +## [0.5.0-dev.1] — 2026-09-30 (development candidate) + +This branch carries the local PlugBrain 0.5 source candidate onto the public +repository history for review and cross-platform verification. It is not an +installed or published release. See `release/RELEASE-NOTES-v0.5.0-dev.1.md` +for the included work and the open acceptance gates. + ## [0.3.1] — 2026-09-26 The `v0.3.0` tag exists, but no 0.3.0 release was published: its cross-platform diff --git a/README.md b/README.md index d8ed5e7..5a4be34 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ plugbrain swarm release codex-1 --task # The integrator looks, approves, and sends the next hint plugbrain swarm board --git plugbrain swarm approve codex-1 --note "Reviewed. Commit it." -plugbrain swarm send codex-1 --subject "Next" --body "Rebase on main after your commit" +plugbrain swarm send codex-1 --subject "Next" --body "Rebase on main after your commit" --from integrator ``` A turn ends in one of four states: `needs-task` (done, give me work), @@ -127,7 +127,9 @@ plugbrain serve # the local daemon: web UI and API on http://127.0.0.1:4310 `plugbrain setup --all` (or `claude`, `codex`, `cursor`, `windsurf`, `hermes`, `agy`, `opencode`) adds PlugBrain to an MCP client later. It backs up the client's config first; `--undo` restores it. The data directory defaults to -`~/.plugbrain`; set `PLUGBRAIN_HOME` to choose another one. +`~/.plugbrain`; set `PLUGBRAIN_HOME` to choose another one. One page per client, +with the file it writes, how to check it took effect and how to remove it again: +[docs/agents/](docs/agents/). ### Claude Code plugin diff --git a/bench/m18-gold-30-draft.md b/bench/m18-gold-30-draft.md new file mode 100644 index 0000000..1505998 --- /dev/null +++ b/bench/m18-gold-30-draft.md @@ -0,0 +1,68 @@ +# M18: 30 Goldfragen, Entwurf vom 30.09.2026 + +**Status: Messplan, kein Paritätsnachweis.** Dieser Korpus ersetzt für die nächste +Messung die alte `goldfragen.json`-Auswahl mit 28 Brain- und zwei PlugEngine-Fällen. +Jede Frage muss mit der rohen Antwort von Brain und GitNexus gegen denselben +Quellstand bewertet werden. Ein `found`-Status allein ist keine richtige Antwort. + +## Eingefrorene Kandidaten vor Messbeginn + +| Kürzel | Checkout | beobachteter Commit | +| --- | --- | --- | +| H | `Code/PlugHarness--int-orch` | `9f90dcf` | +| B | `Code/PlugBoard--dag` | `9165be2` | +| S | `Code/PlugPT-Shell--suite` | `7cd326b` | + +Die drei Commits sind Vorschläge für die Messung. Vor dem Lauf müssen Branch, +HEAD, Dirty-Vektor, Brain-Auswahl und GitNexus-Index für **denselben** Stand +nachgewiesen werden. Die `changes`-Fragen verwenden eingefrorene synthetische +Diffs; sie ändern keine dieser Checkouts. + +## Fragen und Quell-Oracle + +Pfade sind relativ zum jeweiligen Checkout. Zeilen und Bezeichner sind ein +Startpunkt für das Oracle und werden beim Einfrieren des Korpus erneut geprüft. + +| ID | Art | Frage und Anker | Zu bewerten | +| --- | --- | --- | --- | +| H01 | query | Wo wird HTTP-Inferenz-Dispatch erzeugt? `packages/plug/gateway/src/server.ts:95#createPlugGateway` | Exakte Definition statt namensähnlicher Operator-Funktion. | +| H02 | context | Welche Prüfungen und Admission-Writes gehören zu `packages/plug/gateway/src/governor.ts:121#Governor.admit`? | Ledger, harte Limits, Parallelität und Cooldown nur soweit quellenbelegt. | +| H03 | impact | Wer hängt von `packages/plug/gateway/src/route-registry.ts:314#materializeRoutes` ab? | Operator-Nutzung um `server.ts:377,390`; Tests getrennt ausweisen. | +| H04 | changes | Hunk in `packages/plug/operator/src/native-admission.ts:62#admitNativeDeployment`. | Exaktes Symbol und Checkout; keine fremden Checkouts. | +| H05 | Cypher | Finde `packages/plug/operator/src/brain-core.ts:123#callBrainCore`. | Scoped Function-Identität und gegen AST geprüfte Anzahl. | +| H06 | query | Wer wählt die installierte Brain-Bindung? `packages/plug/operator/src/brain-core.ts:86#resolvePlugBrainCoreBinding` | Definition von `ensureBrainCore` unterscheiden. | +| H07 | context | Was ruft `packages/plug/operator/src/brain-core.ts:194#ensureBrainCore` auf, und wer ruft es? | `brainCoreIsHealthy`, `brainCoreServiceSpec`, Operator-Server um Zeile 1091. | +| H08 | impact | Wer ruft `packages/plug/gateway/src/ledger.ts:365#RequestLedger.reserveTokens`? | Konkreter Aufruf in `gateway/src/budgets.ts:137`, keine Modul-Import-Inflation. | +| H09 | query | Wo ist Board-Agent-Spawn implementiert? `packages/plug/operator/src/board-agent-spawn.ts:202#spawnBoardResourceAgents` | Exakte Quelle statt generischer Spawn-Helfer. | +| H10 | context | Was prüft `packages/plug/gateway/src/route-registry.ts:768#acquireRouteForAgent`? | `resolveBoundRoute` und belegte Zweige; keine erfundenen Runtime-Caller. | +| B01 | query | Wo liest Board Planet/Galaxy? `src/main/brain/register.ts:145#readGalaxy` | Main-Prozess-Definition statt Renderer-Treffer. | +| B02 | context | Wie registriert und indiziert `src/main/brain/register.ts:187#ensureWorkspace`? | `/api/workspaces`, `/api/reindex`, Handler um Zeile 349, Fehlerpfad. | +| B03 | impact | Welcher Startpfad nutzt `src/main/agents/register.ts:84#registerAgentLaneHandlers`? | `src/main/index.ts:281`; Tests von Runtime trennen. | +| B04 | changes | Hunk in `src/renderer/shell/spawn.ts:152#spawnTerminal`. | Symbol, Zeile, Revision und Checkout exakt zuordnen. | +| B05 | Cypher | Finde `src/renderer/fleet/FleetSurface.tsx:31#FleetSurface`. | Scoped Function/Komponente; Anzahl unabhängig aus AST prüfen. | +| B06 | query | Wo ist das Brain-Workspace-Panel? `src/renderer/brain/BrainWorkspacePanel.tsx:54#BrainWorkspacePanel` | Exakte Komponente und Checkout. | +| B07 | context | Was ruft `src/main/agents/harness-operator-client.ts:529#HarnessOperatorClient.sessionSend`? | `/v1/session/send` und belegter Registry-Consumer. | +| B08 | impact | Wer ruft `src/renderer/terminal/launch.ts:167#startFreebuffInSession`? | Swarm um Zeile 201 und AgentSurface um 606/612; keine anderen Launches. | +| B09 | query | Wo wird City/Mesh gerendert? `src/renderer/brain/CityMeshPanel.tsx:41#CityMeshPanel` | Panel von Core-Projektion unterscheiden. | +| B10 | context | Welchen Endpunkt benutzt `src/main/brain/register.ts:267#attachAgent`? | `/api/agent/attach`, Handler um Zeile 352, Fehler nicht als Erfolg melden. | +| S01 | query | Wo werden Desktop-HostBridge-Operationen gemappt? `apps/desktop/electron/plug-host-bridge.ts:126#routePlugHostBridgeInvocation` | Exakte Map, keine erfundene freie Fetch-Route. | +| S02 | context | Was ruft `apps/desktop/electron/plug-host-bridge.ts:225#createPlugHostBridgeMainClient`? | Mapper um Zeile 357, Identität und Transport; keine Secrets. | +| S03 | impact | Wer registriert `apps/desktop/electron/plug-host-bridge.ts:392#registerPlugHostBridgeIpc`? | `apps/desktop/electron/main.ts:386`; Tests getrennt. | +| S04 | changes | Hunk in `apps/desktop/electron/plug-host-bridge.ts:409#isTrustedPlugDeskRendererUrl`. | Trust-Funktion im richtigen Checkout. | +| S05 | Cypher | Finde `hermes_cli/commands.py:414#resolve_command`. | Python-Definition und AST-Anzahl; Parser-Lücke ehrlich melden. | +| S06 | query | Wo startet PlugDesk-Rendering? `apps/desktop/src/plugdesk/plugdesk-root.tsx:1251#PlugDeskBootstrap` | Definition und Referenzen in `apps/desktop/src/main.tsx`. | +| S07 | context | Wer nutzt `hermes_cli/commands.py:619#gateway_help_lines`? | Direkte Aufrufe in `hermes_cli/slash_exec.py:165,204`. | +| S08 | impact | Welche Caller hängen von `agent/skill_commands.py:655#get_skill_commands` ab? | Belegte CLI- und Slash-Exec-Caller; Dynamik als unaufgelöst. | +| S09 | context | Wo wird `hermes_cli/auth.py:205#normalize_actual_base_url` genutzt? | Referenzen in auth, runtime_provider und auxiliary_client; keine Credential-Inhalte. | +| S10 | query | Wo wird `/skill`-Payload gebaut? `agent/skill_commands.py:766#build_skill_invocation_message` | Exakte Definition statt Helper um Zeile 862. | + +## Messregel + +Für jeden Fall: Frage, eingefrorener Quellanker, erwartete konkrete Antwort, +Brain- und GitNexus-Rohantwort, richtige/falsche/unaufgelöste Behauptungen, +Latenz und Index-Revision speichern. Precision und Recall auf derselben +bewerteten Faktenmenge rechnen. Zusätzlich die kritischen Identitäts-, +Revisions- und Restorefälle des Masters prüfen. M18 bleibt offen, bis die +30 Fragen mit beiden Werkzeugen auf derselben Revision ausgeführt sind, +Brain mindestens so viele korrekt beantwortet, Precision **und** Recall +jeweils mindestens 95 % betragen und p95 höchstens 2 s ist. diff --git a/desktop/PlugBrain.nsi b/desktop/PlugBrain.nsi index 2a17c58..a9a87f0 100644 --- a/desktop/PlugBrain.nsi +++ b/desktop/PlugBrain.nsi @@ -1,7 +1,9 @@ Unicode true !define PRODUCT_NAME "PlugBrain" -!define PRODUCT_VERSION "0.3.1" +!ifndef PRODUCT_VERSION + !error "PRODUCT_VERSION must be supplied by the packager" +!endif !define PRODUCT_PUBLISHER "PLUG" Name "${PRODUCT_NAME} ${PRODUCT_VERSION}" diff --git a/docs/agent-protocol.md b/docs/agent-protocol.md index 8ff0f71..bfce61e 100644 --- a/docs/agent-protocol.md +++ b/docs/agent-protocol.md @@ -6,18 +6,28 @@ agent follows on every turn. Paste the block below into your project's (Claude Code), and give every agent its own id. Commands are the `plugbrain swarm` CLI, so any agent that can run a shell -command can take part. See [cli.md](cli.md#swarm) for every option. +command can take part. See [cli.md](cli.md#swarm) for every option, and +[agents/](agents/README.md) for teaching a client to reach PlugBrain in the +first place. ## Before the first turn Register each agent once. The account label tells the board which quota the -agent spends; one key or subscription can carry several agents. +agent spends. Set `--quota-pool` to the provider project/account limit shared by +its agents; different keys in that project must use the same pool name. ```bash plugbrain swarm register codex-1 --surface other --account openai --model gpt-6-luna plugbrain swarm register claude-1 --surface claude-code --account anthropic +plugbrain swarm register nv01 --surface freebuff --account nvidia:key-01 --key nvidia-01 --quota-pool nvidia:project-a +plugbrain swarm register nv02 --surface freebuff --account nvidia:key-02 --key nvidia-02 --quota-pool nvidia:project-a ``` +Configure the shared pool once with `plugbrain swarm quota-pool set nvidia:project-a +--max-concurrent 2 --rpm 30`. Supervised runs reserve an attempt atomically and +settle it after the worker exits. Provider-reported usage is only recorded when +available; missing usage stays unknown. + Surfaces are `claude-code`, `codex-app`, `freebuff`, `agy`, `native` and `other`. ## Block for AGENTS.md / CLAUDE.md @@ -34,21 +44,29 @@ You are one agent in a team coordinated by PlugBrain. Your agent id is ``. 2. **Before you write:** claim the paths you will change: `plugbrain swarm claim ... --task `. If the claim is refused, another agent owns the path. Message that agent - (`plugbrain swarm send --subject "..." --body "..."`) instead of - writing anyway. + (`plugbrain swarm send --subject "..." --body "..." --from `) instead of + writing anyway. Set `PLUGBRAIN_AGENT=` once in your shell and every + message is signed with your id; a message without a sender is refused. 3. **Before tests, builds, installs or new worktrees:** run `plugbrain swarm admit test|build|install|worktree`. Exit code 5 means the machine has no room. Do not start the work; end the turn as `blocked`. -4. **End of every turn,** with exactly one state: +4. **Hand in completed work:** run + `plugbrain swarm deliver --path [--repo ]`. + The evidence must be a non-empty file inside the workspace. The receipt stores + its SHA-256, your agent id, attempt, and source revision when available. Review + tasks (title starts with `R-` or `--review`) also need a verdict and reviewed commit hash. +5. **End of every turn,** with exactly one state: `plugbrain swarm turn end --state --summary ""` - `needs-task`: you are done and need new work - `awaiting-commit`: a tested change is ready; wait for approval - `blocked`: a real blocker, named in the summary - `paused`: you were told to stop Then release your claims: `plugbrain swarm release --task `. -5. **Commit only after approval.** Approval arrives as a message with the + Ending with `needs-task` without a delivery leaves the task claimed and visible + under `plugbrain swarm board --next`. +6. **Commit only after approval.** Approval arrives as a message with the subject "Commit freigegeben". Commit, then end the turn with `needs-task`. -6. **Report limits, never secrets.** If your tool shows a quota, report the +7. **Report limits, never secrets.** If your tool shows a quota, report the number: `plugbrain swarm quota percent|credits|requests|rpm|tokens`. Never put keys or tokens into commands, summaries, messages or notes. ``` @@ -61,8 +79,10 @@ role. | Task | Command | | --- | --- | | See every agent, its turn, task, claims and worktree | `plugbrain swarm board --git` | -| Queue work, optionally for one agent | `plugbrain swarm enqueue "" --body "<brief>" [--to <id>]` | -| Send a message | `plugbrain swarm send <id> --subject "<s>" --body "<b>"` | +| Queue work, optionally for one agent, optionally after another task | `plugbrain swarm enqueue "<title>" --body "<brief>" [--to <id>] [--after <taskId>]` | +| Send a message | `plugbrain swarm send <id> --subject "<s>" --body "<b>" --from integrator` | +| See who is still, and release arrivals | `plugbrain swarm watchdog` | +| Reviewer list and automatic review routing | `plugbrain swarm review-pool set <id>... && plugbrain swarm review-pool auto on` | | Let an agent commit | `plugbrain swarm approve <id> [--note "<n>"]` | | Machine room and quotas | `plugbrain swarm resources` | | Take an agent off the board | `plugbrain swarm retire <id> --note "<reason>"` | diff --git a/docs/agents/README.md b/docs/agents/README.md new file mode 100644 index 0000000..ee33545 --- /dev/null +++ b/docs/agents/README.md @@ -0,0 +1,68 @@ +# Agent setup + +One page per client: what it needs, the one command that configures it, what +lands in which file, how to check that it worked, and how to take it back out. + +| Client | Page | Config file (under your home) | Format, map key | +| --- | --- | --- | --- | +| Claude Code | [claude-code.md](claude-code.md) | `.claude.json` | JSON, `mcpServers` | +| Codex | [codex.md](codex.md) | `.codex/config.toml` | TOML, `mcp_servers` | +| Cursor | [cursor.md](cursor.md) | `.cursor/mcp.json` | JSON, `mcpServers` | +| Windsurf | [windsurf.md](windsurf.md) | `.codeium/windsurf/mcp_config.json` | JSON, `mcpServers` | +| OpenCode | [opencode.md](opencode.md) | `.config/opencode/opencode.json` | JSON, `mcp` | +| Hermes | [hermes.md](hermes.md) | `.hermes/config.yaml` | YAML, `mcp_servers` | +| AGY | [agy.md](agy.md) | `.gemini/antigravity/mcp_config.json` | JSON, `mcpServers` | + +The seven ids, paths, formats and map keys are `CLIENTS` in +[`src/setup/clients.ts`](../../src/setup/clients.ts). + +## What every setup does + +- **One command.** `plugbrain setup <id>`, or `plugbrain setup --all` for every + client that is installed. An unknown id is refused (exit code 1) and the known + ones are listed. +- **One entry, nothing else.** It writes a server named `plugbrain` into the + client's own config file and changes no other entry's content. +- **No ids, no tokens.** The entry is only *the node binary*, *the PlugBrain CLI* + and the argument `mcp`. +- **The workspace comes from the folder.** PlugBrain takes the project from the + directory the client starts it in: `PLUGBRAIN_WORKSPACE` if set, else the + registered root that contains the folder, else the folder's git root + ([`src/setup/workspace-from-cwd.ts`](../../src/setup/workspace-from-cwd.ts)). + So the same entry works in every project, and no workspace id has to be + written anywhere. +- **A backup before the first real change.** A copy named + `<file>.plugbrain-backup-<stamp>` lands next to the file. If setup created the + file, the backup is an empty marker instead, so `--undo` can tell a creation + from an update. +- **Idempotent.** A second run that would write the same entry writes nothing and + takes no backup. +- **`--dry-run` writes nothing at all** — no file, no backup — and prints only + the changed lines. +- **`--undo` takes it back out.** For a file setup created it removes just the + `plugbrain` entry (and the file, when nothing of yours remains); for an + existing file it restores the newest backup. Either way the state it replaces + is kept as `<file>.plugbrain-undobackup-<stamp>` first, so edits you made after + setup are not lost. +- **A client that is not installed is skipped**, and the report says how many + were. Nothing is written for them. +- **Errors stay local.** A client whose config cannot be parsed is reported as an + `error`; the other clients are still configured. +- `PLUGBRAIN_CONFIG_HOME` moves where the client config files are looked for. + That is not `PLUGBRAIN_HOME`, which is the brain's own data directory. + +Run `plugbrain setup --dry-run` first if you want to see the change before it +happens. + +## After setup: the per-turn rules + +Setup teaches a client how to *reach* PlugBrain. What an agent should do on every +turn — check in, claim paths, admit work, end the turn with a state — is the +agent protocol block in [agent-protocol.md](../agent-protocol.md). Paste it into +the project's `AGENTS.md` or `CLAUDE.md`, and give every agent its own id. + +## See also + +- [`plugbrain setup`](../cli.md#setup) — the command and its flags +- [MCP tools](../mcp-tools.md) — what an agent can call once the server runs +- [agent-protocol.md](../agent-protocol.md) — the turn rules diff --git a/docs/agents/agy.md b/docs/agents/agy.md new file mode 100644 index 0000000..626cd58 --- /dev/null +++ b/docs/agents/agy.md @@ -0,0 +1,74 @@ +# AGY + +AGY keeps its MCP servers in a `mcpServers` map in +`~/.gemini/antigravity/mcp_config.json`. PlugBrain is added there as one more +server. + +## Prerequisite + +- PlugBrain, either the installed product or a checkout. A checkout entry runs + the TypeScript CLI directly and therefore needs Node.js 24 or newer + (the `engines` field in `package.json`). +- The `~/.gemini/antigravity` folder must exist; `mcp_config.json` inside it may + be missing. Detection counts the folder as installed (`resolveClientTargets` + in `src/setup/clients.ts`). + +## Setup + +```bash +plugbrain setup agy +``` + +## What is written + +In `~/.gemini/antigravity/mcp_config.json`: + +```json +{ + "mcpServers": { + "plugbrain": { + "command": "<node>", + "args": ["<path to the PlugBrain CLI>", "mcp"] + } + } +} +``` + +- `command` is the node binary that ran the setup; `args` ends in `mcp`. + From a checkout the CLI is still a `.ts` file, so `args` starts with + `--experimental-strip-types` (`buildEntry` in `src/setup/clients.ts`). +- Every other server in the map is kept. The file is re-serialised with + two-space indentation, so its layout may change even where the content does + not. +- If the file is not valid JSON, setup refuses to rewrite it and reports an + error instead. + +## Check + +1. `plugbrain setup agy --dry-run` prints `unchanged` for AGY when the entry is + already there — that is the cheap proof it was written as intended. +2. Start AGY inside your project and ask it to call a PlugBrain tool, for example + `search` with a word from your code, or `swarm_board` for the fleet view. The + workspace comes from the folder AGY starts PlugBrain in, so it must be a + registered and indexed project (`plugbrain init` does both). +3. If no tool answers, run `plugbrain mcp` by hand in that folder: it speaks + stdio, and a folder with no workspace gets a clear refusal rather than a + crash. + +## Undo + +```bash +plugbrain setup agy --undo +``` + +Restores the newest `<file>.plugbrain-backup-<stamp>` next to the file. Your +current state is kept as `<file>.plugbrain-undobackup-<stamp>` first. If setup +created the file, undo removes only the `plugbrain` entry and deletes the file +when nothing of yours is left in it. + +## Agent protocol + +The per-turn rules belong in the project's `AGENTS.md`: +[agent-protocol.md](../agent-protocol.md). When an AGY tab joins the fleet, its +surface name is `agy` +([agent-protocol.md](../agent-protocol.md#before-the-first-turn)). diff --git a/docs/agents/claude-code.md b/docs/agents/claude-code.md new file mode 100644 index 0000000..61f76a4 --- /dev/null +++ b/docs/agents/claude-code.md @@ -0,0 +1,87 @@ +# Claude Code + +Claude Code keeps its MCP servers in a `mcpServers` map in `~/.claude.json`. +PlugBrain is added there as one more server; the optional plugin below is a +second, independent way in. + +## Prerequisite + +- PlugBrain, either the installed product or a checkout. A checkout entry runs + the TypeScript CLI directly and therefore needs Node.js 24 or newer + (the `engines` field in `package.json`). +- `~/.claude.json` must exist. Claude Code writes it on first start, so start + Claude Code once before running setup. + Claude Code is the one client whose detection looks at the file only — an + existing folder is not enough, because its file sits directly in the home + directory (`resolveClientTargets` in `src/setup/clients.ts`). + +## Setup + +```bash +plugbrain setup claude +``` + +## Optional: the plugin + +The repository also ships a Claude Code plugin. The plugin starts the brain when +a session needs it and gives Claude a skill that asks the brain first: context +before edits, impact before renames, notes before re-deriving a decision. + +``` +/plugin marketplace add litbitrim/plugbrain +/plugin install plugbrain@plugbrain +``` + +## What is written + +In `~/.claude.json`: + +```json +{ + "mcpServers": { + "plugbrain": { + "command": "<node>", + "args": ["<path to the PlugBrain CLI>", "mcp"] + } + } +} +``` + +- `command` is the node binary that ran the setup; `args` ends in `mcp`. + From a checkout the CLI is still a `.ts` file, so `args` starts with + `--experimental-strip-types` (`buildEntry` in `src/setup/clients.ts`). +- Every other server in the map is kept. The file is re-serialised with + two-space indentation, so its layout may change even where the content does + not. +- If the file is not valid JSON, setup refuses to rewrite it and reports an + error instead. + +## Check + +1. `plugbrain setup claude --dry-run` prints `unchanged` for Claude Code when + the entry is already there — that is the cheap proof it was written as + intended. +2. Start Claude Code inside your project and ask it to call a PlugBrain tool, + for example `search` with a word from your code, or `swarm_board` for the + fleet view. The workspace comes from the folder Claude Code starts PlugBrain + in, so it must be a registered and indexed project (`plugbrain init` does + both). +3. If no tool answers, run `plugbrain mcp` by hand in that folder: it speaks + stdio, and a folder with no workspace gets a clear refusal rather than a + crash. + +## Undo + +```bash +plugbrain setup claude --undo +``` + +Restores the newest `<file>.plugbrain-backup-<stamp>` next to the file. Your +current state is kept as `<file>.plugbrain-undobackup-<stamp>` first. If setup +created the file, undo removes only the `plugbrain` entry and deletes the file +when nothing of yours is left in it. + +## Agent protocol + +Setup only teaches Claude Code how to reach PlugBrain. The per-turn rules for +the agent go into `CLAUDE.md`: [agent-protocol.md](../agent-protocol.md). diff --git a/docs/agents/codex.md b/docs/agents/codex.md new file mode 100644 index 0000000..c2c41d5 --- /dev/null +++ b/docs/agents/codex.md @@ -0,0 +1,65 @@ +# Codex + +Codex keeps its MCP servers in the `mcp_servers` table of +`~/.codex/config.toml`. PlugBrain is added as one TOML table named `plugbrain`. + +## Prerequisite + +- PlugBrain, either the installed product or a checkout. A checkout entry runs + the TypeScript CLI directly and therefore needs Node.js 24 or newer + (the `engines` field in `package.json`). +- The `~/.codex` folder must exist; `config.toml` inside it may be missing. + Detection counts the folder as installed (`resolveClientTargets` in + `src/setup/clients.ts`). + +## Setup + +```bash +plugbrain setup codex +``` + +## What is written + +In `~/.codex/config.toml`: + +```toml +[mcp_servers.plugbrain] +command = "<node>" +args = ["<path to the PlugBrain CLI>", "mcp"] +``` + +- `command` is the node binary that ran the setup; `args` ends in `mcp`. + From a checkout the CLI is still a `.ts` file, so `args` starts with + `--experimental-strip-types` (`buildEntry` in `src/setup/clients.ts`). +- The file is edited in place: if the table exists it is replaced, otherwise the + block is appended. Comments and the layout of your other tables survive + (`renderToml`). +- A file that does not exist yet is created with only this block in it. + +## Check + +1. `plugbrain setup codex --dry-run` prints `unchanged` for Codex when the entry + is already there — that is the cheap proof it was written as intended. +2. Start Codex inside your project and ask it to call a PlugBrain tool, for + example `search` with a word from your code, or `swarm_board` for the fleet + view. The workspace comes from the folder Codex starts PlugBrain in, so it + must be a registered and indexed project (`plugbrain init` does both). +3. If no tool answers, run `plugbrain mcp` by hand in that folder: it speaks + stdio, and a folder with no workspace gets a clear refusal rather than a + crash. + +## Undo + +```bash +plugbrain setup codex --undo +``` + +Restores the newest `<file>.plugbrain-backup-<stamp>` next to the file. Your +current state is kept as `<file>.plugbrain-undobackup-<stamp>` first. If setup +created the file, undo removes only the `plugbrain` entry and deletes the file +when nothing of yours is left in it. + +## Agent protocol + +Codex reads a project's `AGENTS.md`, which is also where the PlugBrain turn +rules belong: [agent-protocol.md](../agent-protocol.md). diff --git a/docs/agents/cursor.md b/docs/agents/cursor.md new file mode 100644 index 0000000..16835a5 --- /dev/null +++ b/docs/agents/cursor.md @@ -0,0 +1,71 @@ +# Cursor + +Cursor keeps its MCP servers in a `mcpServers` map in +`~/.cursor/mcp.json`. PlugBrain is added there as one more server. + +## Prerequisite + +- PlugBrain, either the installed product or a checkout. A checkout entry runs + the TypeScript CLI directly and therefore needs Node.js 24 or newer + (the `engines` field in `package.json`). +- The `~/.cursor` folder must exist; `mcp.json` inside it may be missing. + Detection counts the folder as installed (`resolveClientTargets` in + `src/setup/clients.ts`). + +## Setup + +```bash +plugbrain setup cursor +``` + +## What is written + +In `~/.cursor/mcp.json`: + +```json +{ + "mcpServers": { + "plugbrain": { + "command": "<node>", + "args": ["<path to the PlugBrain CLI>", "mcp"] + } + } +} +``` + +- `command` is the node binary that ran the setup; `args` ends in `mcp`. + From a checkout the CLI is still a `.ts` file, so `args` starts with + `--experimental-strip-types` (`buildEntry` in `src/setup/clients.ts`). +- Every other server in the map is kept. The file is re-serialised with + two-space indentation, so its layout may change even where the content does + not. +- If the file is not valid JSON, setup refuses to rewrite it and reports an + error instead. + +## Check + +1. `plugbrain setup cursor --dry-run` prints `unchanged` for Cursor when the + entry is already there — that is the cheap proof it was written as intended. +2. Start Cursor inside your project and ask the agent to call a PlugBrain tool, + for example `search` with a word from your code, or `swarm_board` for the + fleet view. The workspace comes from the folder Cursor starts PlugBrain in, so + it must be a registered and indexed project (`plugbrain init` does both). +3. If no tool answers, run `plugbrain mcp` by hand in that folder: it speaks + stdio, and a folder with no workspace gets a clear refusal rather than a + crash. + +## Undo + +```bash +plugbrain setup cursor --undo +``` + +Restores the newest `<file>.plugbrain-backup-<stamp>` next to the file. Your +current state is kept as `<file>.plugbrain-undobackup-<stamp>` first. If setup +created the file, undo removes only the `plugbrain` entry and deletes the file +when nothing of yours is left in it. + +## Agent protocol + +Cursor reads a project's `AGENTS.md`, which is also where the PlugBrain turn +rules belong: [agent-protocol.md](../agent-protocol.md). diff --git a/docs/agents/hermes.md b/docs/agents/hermes.md new file mode 100644 index 0000000..b356373 --- /dev/null +++ b/docs/agents/hermes.md @@ -0,0 +1,67 @@ +# Hermes + +Hermes keeps its MCP servers in an `mcp_servers` mapping in +`~/.hermes/config.yaml`. PlugBrain is added there as one more server. Hermes is +the only client of the seven whose config is YAML. + +## Prerequisite + +- PlugBrain, either the installed product or a checkout. A checkout entry runs + the TypeScript CLI directly and therefore needs Node.js 24 or newer + (the `engines` field in `package.json`). +- The `~/.hermes` folder must exist; `config.yaml` inside it may be missing. + Detection counts the folder as installed (`resolveClientTargets` in + `src/setup/clients.ts`). + +## Setup + +```bash +plugbrain setup hermes +``` + +## What is written + +In `~/.hermes/config.yaml`: + +```yaml +mcp_servers: + plugbrain: + command: '<node>' + args: + - '<path to the PlugBrain CLI>' + - 'mcp' +``` + +- Scalars are written in single quotes with literal backslashes, so a Windows + path needs no escaping (`yamlScalar` in `src/setup/clients.ts`). +- The block is inserted into the existing `mcp_servers` mapping, or the mapping + is created at the end of the file. Comments and the layout of your other + keys survive (`renderYaml`). + +## Check + +1. `plugbrain setup hermes --dry-run` prints `unchanged` for Hermes when the + entry is already there — that is the cheap proof it was written as intended. +2. Start Hermes inside your project and ask the agent to call a PlugBrain tool, + for example `search` with a word from your code, or `swarm_board` for the + fleet view. The workspace comes from the folder Hermes starts PlugBrain in, + so it must be a registered and indexed project (`plugbrain init` does both). +3. If no tool answers, run `plugbrain mcp` by hand in that folder: it speaks + stdio, and a folder with no workspace gets a clear refusal rather than a + crash. + +## Undo + +```bash +plugbrain setup hermes --undo +``` + +Restores the newest `<file>.plugbrain-backup-<stamp>` next to the file. Your +current state is kept as `<file>.plugbrain-undobackup-<stamp>` first. If setup +created the file, undo removes only the `plugbrain` entry and deletes the file +when nothing of yours is left in it. + +## Agent protocol + +The per-turn rules belong in the project's `AGENTS.md`: +[agent-protocol.md](../agent-protocol.md). diff --git a/docs/agents/opencode.md b/docs/agents/opencode.md new file mode 100644 index 0000000..d300800 --- /dev/null +++ b/docs/agents/opencode.md @@ -0,0 +1,74 @@ +# OpenCode + +OpenCode is the one client whose server map is not called `mcpServers`. It keeps +its MCP servers in a `mcp` map in `~/.config/opencode/opencode.json`, and +PlugBrain is added there as one more server. + +## Prerequisite + +- PlugBrain, either the installed product or a checkout. A checkout entry runs + the TypeScript CLI directly and therefore needs Node.js 24 or newer + (the `engines` field in `package.json`). +- The `~/.config/opencode` folder must exist; `opencode.json` inside it may be + missing. Detection counts the folder as installed (`resolveClientTargets` in + `src/setup/clients.ts`). + +## Setup + +```bash +plugbrain setup opencode +``` + +## What is written + +In `~/.config/opencode/opencode.json`: + +```json +{ + "mcp": { + "plugbrain": { + "command": "<node>", + "args": ["<path to the PlugBrain CLI>", "mcp"] + } + } +} +``` + +- The map key is `mcp`, not `mcpServers`; that is the one difference from the + other JSON clients (`CLIENTS` in `src/setup/clients.ts`). +- `command` is the node binary that ran the setup; `args` ends in `mcp`. + From a checkout the CLI is still a `.ts` file, so `args` starts with + `--experimental-strip-types` (`buildEntry`). +- Every other entry in the file is kept. The file is re-serialised with + two-space indentation, so its layout may change even where the content does + not. +- If the file is not valid JSON, setup refuses to rewrite it and reports an + error instead. + +## Check + +1. `plugbrain setup opencode --dry-run` prints `unchanged` for OpenCode when the + entry is already there — that is the cheap proof it was written as intended. +2. Start OpenCode inside your project and ask the agent to call a PlugBrain tool, + for example `search` with a word from your code, or `swarm_board` for the + fleet view. The workspace comes from the folder OpenCode starts PlugBrain in, + so it must be a registered and indexed project (`plugbrain init` does both). +3. If no tool answers, run `plugbrain mcp` by hand in that folder: it speaks + stdio, and a folder with no workspace gets a clear refusal rather than a + crash. + +## Undo + +```bash +plugbrain setup opencode --undo +``` + +Restores the newest `<file>.plugbrain-backup-<stamp>` next to the file. Your +current state is kept as `<file>.plugbrain-undobackup-<stamp>` first. If setup +created the file, undo removes only the `plugbrain` entry and deletes the file +when nothing of yours is left in it. + +## Agent protocol + +OpenCode reads a project's `AGENTS.md`, which is also where the PlugBrain turn +rules belong: [agent-protocol.md](../agent-protocol.md). diff --git a/docs/agents/windsurf.md b/docs/agents/windsurf.md new file mode 100644 index 0000000..23676d7 --- /dev/null +++ b/docs/agents/windsurf.md @@ -0,0 +1,72 @@ +# Windsurf + +Windsurf keeps its MCP servers in a `mcpServers` map in +`~/.codeium/windsurf/mcp_config.json`. PlugBrain is added there as one more +server. + +## Prerequisite + +- PlugBrain, either the installed product or a checkout. A checkout entry runs + the TypeScript CLI directly and therefore needs Node.js 24 or newer + (the `engines` field in `package.json`). +- The `~/.codeium/windsurf` folder must exist; `mcp_config.json` inside it may be + missing. Detection counts the folder as installed (`resolveClientTargets` in + `src/setup/clients.ts`). + +## Setup + +```bash +plugbrain setup windsurf +``` + +## What is written + +In `~/.codeium/windsurf/mcp_config.json`: + +```json +{ + "mcpServers": { + "plugbrain": { + "command": "<node>", + "args": ["<path to the PlugBrain CLI>", "mcp"] + } + } +} +``` + +- `command` is the node binary that ran the setup; `args` ends in `mcp`. + From a checkout the CLI is still a `.ts` file, so `args` starts with + `--experimental-strip-types` (`buildEntry` in `src/setup/clients.ts`). +- Every other server in the map is kept. The file is re-serialised with + two-space indentation, so its layout may change even where the content does + not. +- If the file is not valid JSON, setup refuses to rewrite it and reports an + error instead. + +## Check + +1. `plugbrain setup windsurf --dry-run` prints `unchanged` for Windsurf when the + entry is already there — that is the cheap proof it was written as intended. +2. Start Windsurf inside your project and ask the agent to call a PlugBrain tool, + for example `search` with a word from your code, or `swarm_board` for the + fleet view. The workspace comes from the folder Windsurf starts PlugBrain in, + so it must be a registered and indexed project (`plugbrain init` does both). +3. If no tool answers, run `plugbrain mcp` by hand in that folder: it speaks + stdio, and a folder with no workspace gets a clear refusal rather than a + crash. + +## Undo + +```bash +plugbrain setup windsurf --undo +``` + +Restores the newest `<file>.plugbrain-backup-<stamp>` next to the file. Your +current state is kept as `<file>.plugbrain-undobackup-<stamp>` first. If setup +created the file, undo removes only the `plugbrain` entry and deletes the file +when nothing of yours is left in it. + +## Agent protocol + +The per-turn rules belong in the project's `AGENTS.md`: +[agent-protocol.md](../agent-protocol.md). diff --git a/docs/cli.md b/docs/cli.md index 78f129e..4c465a5 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -13,16 +13,17 @@ usage: plugbrain <command> [options] ### `init` Initialize PlugBrain in the current directory or specified path with a single command. -Discovers the git repository root (or uses the directory path), registers the workspace, executes initial code indexing with a live progress report, prints the local web UI URL, and detects installed AI coding assistants (Claude Code, Codex, Cursor, Windsurf, Hermes, AGY, OpenCode) to enroll PlugBrain into their MCP client configurations. Running `init` multiple times is idempotent. +Discovers the git repository root (or uses the directory path), registers the workspace, executes initial code indexing with a live progress report, prints the local web UI URL, detects installed AI coding assistants (Claude Code, Codex, Cursor, Windsurf, Hermes, AGY, OpenCode) to enroll PlugBrain into their MCP client configurations, and adds the PlugBrain agent-protocol block to `AGENTS.md` (plus `CLAUDE.md` when present and not already referencing `AGENTS.md`). Running `init` multiple times is idempotent. ```bash -plugbrain init [path] [--no-clients] [--dry-run] +plugbrain init [path] [--no-clients] [--no-agents-file] [--dry-run] ``` **Flags:** - `[path]`: Directory path to initialize (defaults to the current working directory). - `--dry-run`: Preview planning actions without making any changes to disk. - `--no-clients`: Skip detecting and updating AI coding assistant client configurations. +- `--no-agents-file`: Skip adding or updating the agent-protocol block. **Behavior of `--dry-run`:** - Does not create or modify `plugbrain.db` on disk. If a database file already exists, it opens it strictly read-only; if no database exists, it uses an in-memory database (`:memory:`). @@ -67,6 +68,24 @@ plugbrain setup --all plugbrain setup --undo ``` +### `agents-file` + +Add or refresh the instructions from `docs/agent-protocol.md` between managed markers. Text outside those markers is preserved. The default writes `AGENTS.md` and also `CLAUDE.md` when that file exists without an `AGENTS.md` reference. + +```bash +plugbrain agents-file [--target AGENTS.md|CLAUDE.md|both] [--dry-run] [--undo] +``` + +`--dry-run` prints the proposed content without writing. `--undo` restores the pre-change backup, or removes the managed block when no backup exists. Duplicate or incomplete markers are refused with a repair hint. + +### `doctor` + +`doctor --agents` performs read-only checks for each supported client: installation, PlugBrain MCP config, the local daemon health endpoint, current-directory workspace registration, and the managed agent block. Each row includes a repair command. Use `--json` for machine-readable rows. Doctor never starts the daemon or edits configuration. + +```bash +plugbrain doctor --agents [--json] +``` + --- ## MCP Server and Daemon @@ -292,6 +311,34 @@ plugbrain repos --dirty --- +### `disk` +Scan directory metadata, inspect the saved directory tree, rank safe-to-review storage recommendations, or create a reinstallation checklist. Scanning never reads file contents except package lockfiles, which are opened only to compute duplicate fingerprints. Links and other reparse points are reported and skipped. + +```bash +plugbrain disk scan [<path>...] +plugbrain disk tree [<path>] +plugbrain disk recommend [--json] +plugbrain disk wipe-check [--json] +plugbrain disk largest [n] +``` + +- `scan` defaults to all fixed drives and replaces the previous scan. Explicit paths are useful for a bounded, read-only inventory. +- `tree` returns immediate children from the last completed scan. +- `recommend` groups recoverable dependency trees, caches and build output; review candidates and work that should be kept are marked separately. +- `wipe-check` lists uncommitted or unpushed Git work, recent user-profile folders and configuration or vault paths to back up before reinstalling. +- `largest` reports the largest files retained in the last scan (maximum 1,000). +- `--json` prints the structured report. + +```bash +plugbrain disk scan C:\\Users\\me\\Projects +plugbrain disk recommend --json +plugbrain disk wipe-check +``` + +The scan and recommendations only report. They do not delete or move files. + +--- + ## Workspace and Index Lifecycle ### `register` @@ -525,47 +572,197 @@ plugbrain swarm <subcommand> [args] [--workspace <id>] Without `--workspace` the single registered workspace is used. +**Subcommands:** +- `swarm watch [--for <agent>] [--agents <id,…>] [--dirs <path,…>] [--timeout 20m] [--json]`: Wait for relevant Brain events and sparse filesystem/resource changes. +- `swarm register <agentId> [name]`: Register a new agent worker. +- `swarm turn <agentId> <start|end>`: Check in at a turn boundary. +- `swarm claim <agentId> <path> --task <taskId>`: Acquire mutual exclusion lease on a path. +- `swarm release <agentId> <leaseId>`: Release held lease. +- `swarm board`: View active fleet status, unread messages, and worktrees. +- `swarm supersede <task> [--by <task>] --note <reason>`: Permanently prevent a pending task from being offered again, optionally linking its replacement. +- `swarm reassign <task> --to <agent>`: Restrict a pending task to a registered worker. +- `swarm priority <task> <n>`: Set an integer priority; larger values are offered first. +- `swarm reap [--repo <path>] [--target <branch>] [--apply]`: Preview or safely remove merged, clean linked worktrees; defaults to a read-only dry run. +- `swarm reap --auto on|off`: Enable or disable automatic cleanup for this workspace. +- `swarm send <fromAgent> <toAgent> --body <text>`: Send peer message. +- `swarm resources`: Display host CPU, RAM, disk quotas, and admission decisions. +- `swarm admit <test|build|install>`: Check admission gate for hardware-intensive actions. **Agents** -- `swarm register <agent> --surface <s> --account <label> [--key <resource>] [--model <m>] [--name <n>] [--worktree <path>]... [--takeover]`: +- `swarm register <agent> --surface <s> --account <label> [--quota-pool <pool>] [--key <resource>] [--model <m>] [--name <n>] [--worktree <path>]... [--takeover]`: register an agent. Surfaces: `claude-code`, `codex-app`, `freebuff`, `agy`, `native`, `other`. - `--worktree` binds the agent to a checkout so the board can show its branch. + `--worktree` binds the agent to a checkout so the board can show its branch. Workers + that draw from one provider project must share the same `--quota-pool`, even when they + carry different resource keys; it defaults to the account label. - `swarm turn <agent> start [--claim]`: check in at the start of a turn. Prints unread messages and the task offered to this agent; `--claim` takes it. -- `swarm turn <agent> end --state needs-task|awaiting-commit|blocked|paused [--summary <s>]`: - check out with exactly one state. +- `swarm turn <agent> end --state needs-task|awaiting-commit|blocked|paused [--summary <s>] [--deliver <evidence>] [--repo <worktree>] [--review]`: + check out with exactly one state. `--deliver` hands in the single held task; the + evidence must be a non-empty file inside the workspace. A delivery records the + agent, attempt, source revision when available, evidence path and SHA-256. Review + tasks titled `R-…` (or explicitly marked with `--review`) also need a judgment + (`PASS`, `PASS_MIT_AUFLAGEN` or `FAIL`) and a 40-character reviewed commit hash. + Without a delivery, the queue task stays `claimed` and appears under `swarm board --next`. - `swarm ack <agent> <messageId>`: mark a message as read. Unread messages are shown at every turn start until acknowledged. - `swarm retire <agent> --note <reason>`: take an agent off the board. **Work** -- `swarm enqueue <title> [--body <b>] [--to <agent>] [--plan <M00>] [--by <agent>]`: - queue a task, for one agent (`--to`) or for whoever takes it first. -- `swarm deliver <agent> <taskId> --path <evidence>`: hand in a claimed task with its evidence. +- `swarm enqueue <title> [--body <b>] [--to <agent>] [--plan <M00>] [--by <agent>] [--after <taskId>]`: + queue a task, for one agent (`--to`) or for whoever takes it first. `--after` + makes it wait: it is neither offered nor claimable until the predecessor is + delivered or its holder has ended a turn with `awaiting-commit` or + `needs-task`; the addressee then gets one message that the task is free. +- `swarm supersede <task> [--by <task>] --note <reason>`: + permanently remove a pending task from worker offers and record the actor, + optional replacement task, and reason. Superseded tasks and the latest queue + changes appear on `swarm board` and in its JSON projection. +- `swarm reassign <task> --to <agent>`: + limit a pending task to a registered worker. The change is audited and shown + on the board. +- `swarm priority <task> <n>`: + set a pending task's integer priority from -100000 to 100000. Higher values + are offered first; equal priorities remain first-in-first-out. +- `swarm deliver <agent> <taskId> --path <evidence> [--repo <worktree>] [--review]`: + hand in a claimed task. The named agent must hold it and the evidence file must + exist, be non-empty and resolve inside the workspace. The receipt includes the + task, agent, delivery attempt, source `HEAD` when `--repo` or the selected workspace root + identifies a Git checkout, normalized evidence path and SHA-256. Review tasks + titled `R-…` (or passed with `--review`) require a verdict and reviewed commit hash. +- `swarm wave-done <waveId> [--json]`: evaluate the authoritative + operator `wave.manifest` trace (`payload.waveId` and `payload.taskIds`) against + stored delivery receipts, an authoritative independent `review.completed` + trace (`payload.verdict` and `payload.commit`) for the delivered commit, and + either an `integration.accepted` trace or an explicit `wave.owner-decision` + trace per task. Missing evidence returns `BLOCKED` (exit 1) and emits no + event. A complete report is persisted and emits one `WAVE-DONE` event; + repeating the command returns the same report without emitting another event. - `swarm claim <agent> <path>... [--task <id>] [--ttl-min <n>]`: take a write lease on paths. A path another agent holds is refused. - `swarm release <agent> [<path>...] [--task <id>]`: release leases (all of the agent's, or the given ones). - `swarm send <agent> --subject <s> --body <b> [--from <agent>]`: message an - agent. The first argument is the recipient. + agent. The first argument is the recipient. The sender comes from `--from` + or from `PLUGBRAIN_AGENT`; with neither, the message is refused (exit 2) — + the Brain never books a message to the integrator that somebody else wrote. - `swarm approve <agent> [--note <n>] [--by <agent>]`: let an agent that ended with `awaiting-commit` commit. It arrives as a message. **Overview and machine** -- `swarm board [--git] [--json]`: every agent once with account, turn state, +- `swarm board [--git] [--json] [--next]`: every agent once with account, turn state, unread messages, task, leases and worktree; `--git` adds branch, head and - uncommitted files, plus overlaps where two agents write in one worktree. -- `swarm resources [--json]`: free disk and RAM, admission per kind of work, reported quotas. + uncommitted files, plus overlaps where two agents write in one worktree. A + `working` worker without Brain contact is marked `silent` and shown with its + minutes (`still? seit 47 min ohne Kontakt`). +- `swarm watchdog [--json] [--dry-run]`: one watchdog cycle. It reports every + silent worker, tells the integrator once per case (`--dry-run` only reads), + and releases waiting tasks whose predecessor has arrived. A worker is silent + when it has been `working` longer than the workspace threshold (default 45 + minutes) without any Brain contact — turn, claim, release, ack, message, + delivery or quota. Contact resets the reading; the Brain never calls a worker + dead. +- `swarm watchdog silent-after <minutes>`: set that threshold for the workspace. +- `swarm review-pool set <agent>...`: replace the workspace's ordered reviewer + list. +- `swarm review-pool auto on|off`: switch automatic review routing (default + off). When on, a turn that ends with `awaiting-commit` queues one review task + for the first reviewer whose account differs from the author's. +- `swarm review-pool show [--json]`: pool, routing switch and threshold. + uncommitted files, plus overlaps where two agents write in one worktree. The board + appends an `Als Nächstes` section with ranked suggestions, reasons and commands; + `--next` prints just those suggestions. +- `swarm chronik [--since <iso|2h>] [--json|--md]`: show stored queue, message, + lease and quota events in timestamp order. Markdown is the default and can be used + as a handoff note. Missing turn history is named explicitly until a version that + stores turn history is available; the chronicle does not invent events. Reads are + capped at 500 rows per source and output at 1,000 events; capped results say that + older events were omitted. +- `swarm resources [--json]`: free disk and RAM, admission per kind of work, reported quotas and configured pool limits. - `swarm quota <account> <remaining> <percent|credits|requests|rpm|tokens> [--resets <iso>] [--note <n>]`: report how much of an account's quota is left. Numbers only, never keys. +- `swarm quota-pool set <pool> --max-concurrent <n> [--rpm <n>]`: configure one shared + provider/project pool. Reservations for worker attempts count against the same concurrency + and optional rolling RPM limit. `swarm quota-pool show [--json]` reports active and settled + attempts; unreported usage remains unknown rather than being counted as zero. A provider + 429 applies a bounded, pool-wide cooldown using `Retry-After` when available. - `swarm admit <edit|test|index|build|install|worktree>`: exit 0 means there is room on this machine for that kind of work, exit 5 means there is not. +**Starting a worker: `swarm run`** + +A CLI worker (a Codex tab, any other command-line agent) can be started and +watched by the Brain itself, so a lane needs no handwork at its turn boundary. +- `swarm runner set <agent> --cmd <exe> [--model <m>] [--effort <e>] + [--sandbox bypass|workspace-write|read-only|danger-full-access] [--search] + [--args "<template>"] [--cwd <dir>]`: store how this worker is started. No key + is stored — Codex brings its own login. `--cmd codex` builds the + `codex exec` command line (`-m`, `-c model_reasoning_effort=`, `--sandbox`, + `-c tools.web_search=`, `--json`, `-o`); any other program is built from + `--args`, whose `{prompt}`, `{promptFile}`, `{log}`, `{last}`, `{cwd}`, + `{model}`, `{effort}` and `{sandbox}` are filled in per run. A token that + resolves to nothing is dropped. +- `swarm runner show <agent>`: the stored profile. +- `swarm run <agent>`: start a detached refill loop by default. It atomically + claims the next eligible queue task, starts the worker with that task in its + prompt, and waits for the run to end before starting another attempt. The + loop stops after 24 idle checks (five minutes each), on an authentication + failure, or when a task needs a lead decision. `--max-idle-checks`, + `--idle-ms`, `--max-attempts` (1–5) and `--poll-ms` are available for controlled + operation and tests. A failed attempt is retried at most twice by default; + quota errors use a bounded 30-minute cooldown, and auth errors stop immediately. + Each task attempt has a fencing token; stale attempts cannot issue further + `swarm` state changes. The token is generated locally and is not a credential. +- `swarm run <agent> --once`: start one process **detached** with an empty stdin and a + JSONL log at `<PLUGBRAIN_HOME>/runs/workers/<agent>-<stamp>.jsonl` (stderr + beside it, the last message at `…-last.md`, the rendered prompt at + `…-prompt.md`). The prompt comes from a template in the source and names the + protocol documents the registered workspace actually has — protocol, newest + lane rules and the Brain's own wrapper under `<workspace>/koordination` — so + nothing owner-specific is baked in. The run row is written before the process + starts, and the working directory defaults to the workspace root. +- `swarm run <agent> --stop-supervisor`: ask the refill loop to stop after its + current worker process settles. +- `swarm run <agent> --status`: pid, start time, log path, last log event and + the worker's turn state, plus the tail of the log. +- `swarm run <agent> --stop`: stop the process (on Windows the whole process + tree of a `.cmd` shim) and set the worker's turn to `paused` — unless it had + already ended its turn, which is kept. +- `swarm board` shows `runner: pid … running since …` and the last log event per + worker. Looking at the board is also what settles a run whose process is gone: + a worker that died **without ending its turn** is booked `blocked` with the + last log lines as its summary, and the integrator gets a message. A worker + that ended its turn first is left alone. Liveness is only ever + `process.kill(pid, 0)`; a quiet log never means dead. +- Nothing is committed and no approval is bypassed: a started worker still claims + its paths, admits its tests and ends its turn with a state like any other. + **Example: one task through one agent** ```bash plugbrain swarm enqueue "Fix the flaky login test" --body "Repro in issue #12" --to codex-1 plugbrain swarm turn codex-1 start --claim plugbrain swarm claim codex-1 src/auth/login.ts --task <task-id> plugbrain swarm admit test && npm test -plugbrain swarm turn codex-1 end --state awaiting-commit --summary "Fixed the race, login tests pass" +plugbrain swarm turn codex-1 end --state awaiting-commit --summary "Fixed the race, login tests pass" --deliver closeout/login.md plugbrain swarm release codex-1 --task <task-id> plugbrain swarm approve codex-1 --note "Reviewed. Commit it." ``` + +**Watching coordination** +```bash +plugbrain swarm turn agent-1 start +plugbrain swarm board +plugbrain swarm watch --for agent-1 --dirs ./review --timeout 20m --json +``` + +`swarm watch` streams coordination events from the local Brain live-event endpoint, batches events for five seconds, and checks watched Markdown directories and resource admission every 30 seconds. It reports turn changes, new messages and queued tasks for the watched agent, task deliveries, the `still?` watchdog, and the file/admission changes it polls itself. It exits with `0` on an event, `3` on timeout, and `1` after the live-event stream keeps failing. Set `PLUGBRAIN_URL` when the local Brain is served on a non-default URL. + +The stream carries a monotonic event id and reconnects with bounded backoff on a dropped connection. `swarm watch` remembers the last id it saw in `<store home>/swarm-watch-cursor` and sends it as `Last-Event-ID` on the next (re)connection; the server replays the events still in its bounded buffer after that id. A restart therefore resumes where the previous run stopped — events older than the replay buffer are the only gap. + +**Example: let the Brain start the agent** +```bash +plugbrain swarm runner set cx01 --cmd codex --model gpt-6-luna --effort high --sandbox bypass +plugbrain swarm run cx01 +plugbrain swarm run cx01 --once # one detached invocation, without queue refills +plugbrain swarm run cx01 --status # pid, last log event, turn state +plugbrain swarm board # runner column, and settles dead runs +plugbrain swarm run cx01 --stop # stops it, turn becomes paused +plugbrain swarm run cx01 --stop-supervisor +``` diff --git a/docs/http-api.md b/docs/http-api.md index dbb1b1e..aa1f9c5 100644 --- a/docs/http-api.md +++ b/docs/http-api.md @@ -243,6 +243,30 @@ Inspect hardware metrics across all local drives, pagefile, RAM, and CPU, includ --- +### 5a. Disk Inventory and Reinstallation Safety + +Every disk route requires the usual bearer token or `x-plug-auth-token` header. The inventory stores directory summaries and the largest 1,000 files. It never reads file contents except `package-lock.json`, `pnpm-lock.yaml`, and `yarn.lock`, which are read only for a SHA-256 duplicate fingerprint. Reparse points are counted and skipped. + +#### `POST /api/disk/scan` +Start a metadata-only scan. Omit `roots` to scan all fixed drives, or provide an array of paths for a bounded scan. + +```json +{ "roots": ["C:/Users/me/Projects"] } +``` + +Returns `202` with the scan state. `GET /api/disk/scan` reports progress and final totals (`scanId`, roots, directories, files, bytes, inaccessible entries, errors and timestamps). + +#### `GET /api/disk/tree?path=<directory>` +Return the immediate children of a directory from the latest scan. Omit `path` to return the scanned roots. Each entry includes bytes, file count, timestamps, category and reparse-point status. + +#### `GET /api/disk/recommendations` +Return size-ranked recommendations grouped as recoverable, review, or keep, with evidence, paths and a recovery route. Includes sums by risk class. + +#### `GET /api/disk/wipe-check` +Return paths and sizes for Git work that is not safely backed up, recently changed non-repository user folders, configuration and vault locations. No contents are returned. + +--- + ### 6. Machine-Wide Git Census #### `GET /api/repos` diff --git a/docs/mcp-tools.md b/docs/mcp-tools.md index 1669136..196ee28 100644 --- a/docs/mcp-tools.md +++ b/docs/mcp-tools.md @@ -1,6 +1,6 @@ # PlugBrain MCP Tools Reference -PlugBrain exposes 27 tools over the Model Context Protocol (MCP) JSON-RPC 2.0 stdio transport (`plugbrain mcp`). +PlugBrain exposes 31 tools over the Model Context Protocol (MCP) JSON-RPC 2.0 stdio transport (`plugbrain mcp`). When invoked via `plugbrain mcp` without `--workspace`, the server automatically resolves the workspace from the current working directory. Tool parameters accept an explicit `workspaceId` when multiple workspaces exist or to override the binding. @@ -8,6 +8,13 @@ All tool executions return JSON objects conforming to `{ ok: boolean, ... }`. On --- +## Worktree maintenance + +### `reap` +Preview safe cleanup of merged, clean Git worktrees. Set `apply` to `true` to remove eligible linked worktrees without force; branches are kept and recovery commands are recorded. `auto` enables or disables the workspace setting. + +**Parameters:** `workspaceId` (optional), `repo` (optional), `target` (optional), `apply` (optional, defaults to false), `auto` (optional boolean). + ## Code Intelligence and Search ### `ask` @@ -985,8 +992,17 @@ Turn boundary checkpoint: check in at turn start or submit a turn summary at tur --- +### `swarm_supersede` +Requires `taskId`, `note`, and `byAgent`; accepts optional `byTaskId` to link a replacement. A superseded pending task will never be offered again. The operation requires normal MCP authentication and is recorded in the board's audit history. + +### `swarm_reassign` +Requires `taskId`, `toAgent`, and `byAgent`. Restricts a pending task to a registered worker, requires normal MCP authentication, and records the change in the board's audit history. + +### `swarm_priority` +Requires `taskId`, integer `priority` (-100000 through 100000), and `byAgent`. Larger values are offered first. The operation requires normal MCP authentication and is recorded in the board's audit history. + ### `swarm_board` -Fleet overview showing every worker's status, unread messages, active leases, worktrees, and attention flags. +Fleet overview showing every worker's status, unread messages, active leases, worktrees, and attention flags. It also includes queue tasks and the 50 latest queue changes, with each actor, operation, old/new value, and supersede reason. **Parameters:** - `workspaceId` (`string`, optional): Target workspace ID. diff --git a/docs/quickstart.md b/docs/quickstart.md index 2734428..15c687d 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -22,6 +22,7 @@ That is the whole setup. It 2. indexes it, printing progress as it goes, 3. prints the URL of the local dashboard, 4. adds a `plugbrain` MCP entry to every client it finds installed. +5. adds the PlugBrain agent instructions to `AGENTS.md` (and to an unlinked `CLAUDE.md`, when present). Run it twice and nothing changes the second time — `init` is idempotent. @@ -32,6 +33,20 @@ plugbrain init ./my-notes --no-clients ``` `--no-clients` registers and indexes without touching any client config. +Use `--no-agents-file` to skip the managed agent-instructions block. + +The managed block sits between `<!-- plugbrain:agent-protocol:start -->` and +`<!-- plugbrain:agent-protocol:end -->`; text outside it stays intact. Preview, +target, or undo it with: + +```sh +plugbrain agents-file --dry-run +plugbrain agents-file --target both +plugbrain agents-file --undo +``` + +To check client setup without changing files or starting services, run +`plugbrain doctor --agents` (or add `--json` for machine-readable output). ## 2. See what would happen first diff --git a/package-lock.json b/package-lock.json index 3ca974b..733cd22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "plugbrain", - "version": "0.3.1", + "version": "0.5.0-dev.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "plugbrain", - "version": "0.3.1", + "version": "0.5.0-dev.1", "license": "MIT", "dependencies": { "typescript": "^5.9.3" diff --git a/package.json b/package.json index 9d7b2d2..f1db685 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "plugbrain", - "version": "0.3.1", + "version": "0.5.0-dev.1", "private": true, "type": "module", "description": "Local project and code memory for AI agents: real AST code intelligence plus Obsidian-style notes, exposed to Claude Code through MCP.", diff --git a/release/RELEASE-NOTES-v0.5.0-dev.1.md b/release/RELEASE-NOTES-v0.5.0-dev.1.md new file mode 100644 index 0000000..58515bf --- /dev/null +++ b/release/RELEASE-NOTES-v0.5.0-dev.1.md @@ -0,0 +1,31 @@ +# PlugBrain 0.5.0-dev.1 — development candidate + +**Status:** source candidate for review; not installed or published. + +This branch starts at public `litbitrim/plugbrain` main `0ab6f50` and applies +the current local integration tree `c8f9861` as a reviewable source delta. +The public release workflow's Authenticode verification and the existing +0.3.1 release notes are retained. + +## Included source work + +- Planet and checkout selection now scope Code Intelligence requests in the + HTTP and MCP surfaces. Ambiguous selections return an explicit result. +- Import impact resolves names through their target modules, follows + unshadowed star re-exports, and deduplicates file results. +- The UI provides responsive navigation, clearer search failures, and a + timeline of durable agent turns. +- The source tree contains coordination, fleet automation, model-family and + quota-pool work for further qualification. + +## Verification still required + +- The 30-question M18 comparison with GitNexus over PlugHarness, PlugBoard, + and PlugPT-Shell has a draft corpus only. Accuracy and p95 have not been + measured on the same frozen revision. +- Cross-platform CI, independent review, installer signing, upgrade backup, + live smoke test, and rollback proof are required before a local upgrade. +- Provider and model cards remain paused until the separate Gateway governor + and Paperclip paths have passed integrated tests. + +Do not tag or publish this development candidate as a stable 0.5 release. diff --git a/scripts/package-nsis.mjs b/scripts/package-nsis.mjs index 84fde64..d09cd0e 100644 --- a/scripts/package-nsis.mjs +++ b/scripts/package-nsis.mjs @@ -98,7 +98,7 @@ const deleteLines = [ .map(directory => `RMDir "$INSTDIR\\${nsiPath(directory)}"`), ] writeFileSync(ownedFiles, `${deleteLines.join('\n')}\n`, 'utf8') -execFileSync(makensis, [installer], { cwd: root, stdio: 'inherit' }) +execFileSync(makensis, [`/DPRODUCT_VERSION=${version}`, installer], { cwd: root, stdio: 'inherit' }) if (!existsSync(output)) throw new Error(`NSIS did not produce ${output}`) const sha256 = createHash('sha256').update(readFileSync(output)).digest('hex') diff --git a/scripts/verify-standalone.mjs b/scripts/verify-standalone.mjs index 00e0557..701b858 100644 --- a/scripts/verify-standalone.mjs +++ b/scripts/verify-standalone.mjs @@ -162,9 +162,18 @@ try { const uiAttached = await post('/api/agent/attach', { workspace: workspace.id, agentId: 'portable-ui', name: 'Portable UI verifier' }) if (uiAttached.status !== 200) throw new Error(`portable UI actor attach answered ${uiAttached.status}`) + // All views remain reachable through the current activity rail's view menu. + const chooseView = async name => { + const views = page.locator('.pb-activity__views') + if (!(await views.evaluate(element => element.open))) await views.locator('summary').click() + const item = views.getByRole('button', { name }).first() + if (await item.count() === 0) throw new Error(`View ${name} is missing from the activity menu`) + await item.click() + } + // ── Tier 1: Obsidian-Class Notes, Autosave, Backlinks & Wiki-Links ──── console.log('[Tier 1] Verifying note editing, optimistic autosave, backlinks, and wiki-links...') - await page.locator('.pb-tabs').getByRole('button', { name: 'Wissen' }).click() + await chooseView('Wissen') await page.locator('.notes-workbench').waitFor() await page.locator('.notes-search input').fill('Standalone Wissenssuche') await page.getByRole('button', { name: 'Suchen' }).click() @@ -216,8 +225,8 @@ try { await page.locator('.source-header__path').filter({ hasText: 'src/mod0.ts' }).waitFor() // Knowledge Graph navigation: Resilient locator scoped to sidebar head - await page.locator('.pb-tabs').getByRole('button', { name: 'Wissen' }).click() - await page.locator('.notes-sidebar__head').getByRole('button', { name: 'Graph' }).click() + await chooseView('Wissen') + await page.locator('.notes-sidebar__head').getByRole('button', { name: 'Graph-Ansicht' }).click() await page.locator('.knowledge-graph__inspector').waitFor() await page.locator('.knowledge-graph').press('ArrowDown') await page.keyboard.press('?') @@ -228,7 +237,7 @@ try { // ── Tier 2: Instant Dual Search (Notes & AST Code Symbols) ─────────── console.log('[Tier 2] Verifying instant search across notes and code symbols...') - await page.locator('.pb-tabs').getByRole('button', { name: 'Suche' }).click() + await chooseView('Suche') await page.locator('.search-view').waitFor() // 2.1 Code & AST Symbol search (/api/agent/search) @@ -249,7 +258,7 @@ try { // ── Tier 3: Explorer File Tree Opening into SourceView with Centered Line ─ console.log('[Tier 3] Verifying explorer file tree opening into SourceView...') - await page.locator('.pb-tabs').getByRole('button', { name: 'Explorer' }).click() + await chooseView('Explorer') await page.locator('.explorer-view').waitFor() // 3.1 Explorer file filter @@ -381,32 +390,14 @@ try { { name: /Suche/, label: 'Suche', selector: '.search-view' }, { name: /Explorer|Dateien/, label: 'Dateien', selector: '.explorer-view' }, { name: /Hygiene|Aufräumen/, label: 'Aufräumen', selector: '.hygiene-view' }, - { name: /Kontext-Pakete/, label: 'Kontext-Pakete', selector: '.workbench-split, .context-packs', agent: true }, - { name: /Aufgaben/, label: 'Aufgaben', selector: '.queue-view, .brain-view-queue, .queue, .brain-empty', agent: true }, - { name: /Agenten-Netz/, label: 'Agenten-Netz', selector: '.pb-mesh, .mesh-view, .brain-view-mesh', agent: true }, - { name: /Code-Stadt/, label: 'Code-Stadt', selector: '.pb-city, #city, canvas', agent: true }, + { name: /Kontext-Pakete/, label: 'Kontext-Pakete', selector: '.workbench-split, .context-packs' }, + { name: /Aufgaben/, label: 'Aufgaben', selector: '.queue-view, .brain-view-queue, .queue, .brain-empty' }, + { name: /Agenten-Netz/, label: 'Agenten-Netz', selector: '.pb-mesh, .mesh-view, .brain-view-mesh' }, + { name: /Code-Stadt/, label: 'Code-Stadt', selector: '.pb-city, #city, canvas' }, ] for (const v of viewsToTest) { - if (v.agent) { - const agentGroup = page.locator('.pb-tab-group') - if (await agentGroup.count() > 0) { - await agentGroup.hover() - } - const item = page.locator('.pb-tab-group-menu').getByRole('button', { name: v.name }) - if (await item.count() > 0) { - await item.first().click({ force: true }) - } else { - throw new Error(`Tab for agent view ${v.label} not found in agent menu`) - } - } else { - const tab = page.locator('.pb-tabs').getByRole('button', { name: v.name }) - if (await tab.count() > 0) { - await tab.first().click() - } else { - throw new Error(`Tab for main view ${v.label} not found`) - } - } + await chooseView(v.name) await page.locator(v.selector).first().waitFor({ timeout: 5000 }) console.log(`[Tier 4 View Transition] View '${v.label}' rendered cleanly.`) } diff --git a/src/cli.ts b/src/cli.ts index 2b8d837..227d6a2 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -18,7 +18,7 @@ * remove the rows of checkouts the selection does not keep * plugbrain compact give freed pages back to the disk (VACUUM) * - * plugbrain plan [status|next|task <M00>|gates] the master ledger joined with the brain's queue + * plugbrain plan [status|next|task <M00>|gates|decompose <gate-or-requirement>] inspect or decompose the ledger * * plugbrain notes query <filter> property query, e.g. typ=gate UND stand=offen * plugbrain notes search <text> [--lines] prose search across the vault, with snippets @@ -27,7 +27,7 @@ * plugbrain notes graph [--focus <path>] the note graph with type colour groups * plugbrain notes backlinks <path> who points at this note * - * plugbrain swarm <register|turn|ack|board|send|enqueue|approve|resources|quota|admit> … + * plugbrain swarm <register|turn|ack|board|chronik|send|enqueue|deliver|approve|resources|quota|admit|watchdog|review-pool|reap|runner|run> … * the fleet's check-in desk (see src/swarm-cli.ts) * * plugbrain hygiene [--workspace <id>] [--json] what git work sits on exactly one disk @@ -68,9 +68,13 @@ import { } from './hygiene/index.ts' import { machineReport, type MachineReport } from './machine/index.ts' import { gitCensus, type CensusReport } from './machine/git-census.ts' -import { runSwarmCli } from './swarm-cli.ts' +import { diskRecommendations, diskWipeCheck } from './disk/analysis.ts' +import { currentDiskScan, diskTree, largestDiskFiles, scanDisk } from './disk/scan.ts' +import { runSwarmCli, runSwarmSupervisorChild } from './swarm-cli.ts' +import { runSwarmWatchCli } from './swarm-watch.ts' import { compactStore, planPrune, prunePlanet } from './index/prune.ts' -import { planTask, planView, type PlanTask } from './plan.ts' +import { ledgerPath, planTask, planView, type PlanTask } from './plan.ts' +import { applyDecomposedTask, decomposeLedgerTarget, type DecompositionLedger } from './plan-decompose.ts' import { resolveBrainHome } from './home.ts' import { findGitRoot, planWorkspaceRoot, registerWorkspaceRoot, resolveMcpWorkspace, @@ -78,6 +82,10 @@ import { import { buildEntry, CLIENTS, parseClientSelection, setupClients, type ClientSetupResult, } from './setup/clients.ts' +import { + doctorAgents, InvalidAgentMarkersError, manageAgentFile, selectAgentFiles, + validateAgentFiles, type AgentFileTarget, +} from './setup/agent-protocol.ts' const HOME = resolveBrainHome() @@ -103,26 +111,25 @@ const DB_FILE = join(HOME, 'plugbrain.db') * does, so a dry run creates no database file at all. */ const DRY_RUN_INIT = process.argv[2] === 'init' && process.argv.includes('--dry-run') +const READ_ONLY_DOCTOR = process.argv[2] === 'doctor' && process.argv.includes('--agents') -const db = DRY_RUN_INIT +const db = DRY_RUN_INIT || READ_ONLY_DOCTOR ? (existsSync(DB_FILE) ? openStore(DB_FILE, { readOnly: true }) : openStore(':memory:')) : openStore(DB_FILE) function register(path: string, name?: string): void { - const root = resolve(path) - if (!existsSync(root) || !statSync(root).isDirectory()) { - console.error(`not a directory: ${root}`) + const given = resolve(path) + if (!existsSync(given) || !statSync(given).isDirectory()) { + console.error(`not a directory: ${given}`) process.exit(2) } - const id = workspaceIdFor(root) - const label = name ?? root.split(/[\\/]/).filter(Boolean).pop() ?? id - db.prepare( - `INSERT INTO workspaces (id, name, root, created_at) VALUES (?, ?, ?, ?) - ON CONFLICT(root) DO UPDATE SET name = excluded.name` - ).run(id, label, root, new Date().toISOString()) + // Store the canonical root (and update a legacy row in place) so a junction + // or 8.3 spelling cannot become a second workspace; the confirmation keeps + // the spelling the caller used. + const record = registerWorkspaceRoot(db, given, name) // Registration mints identity: pin it so a later move cannot take it away. - pinWorkspaceId(root, id) - console.log(`registered ${label} ${id}\n ${root}`) + pinWorkspaceId(given, record.id) + console.log(`registered ${record.name} ${record.id}\n ${given}`) } function reportIndex(name: string, r: IndexResult): void { @@ -311,6 +318,31 @@ function planCommand(args: string[]): void { const [step = 'status', ...rest] = args const workspaceId = flagValue(rest, '--workspace') ?? singlePlanetId() const json = args.includes('--json') + if (step === 'decompose') { + const target = rest.find(arg => !arg.startsWith('--')) + if (target === undefined) { + console.error('usage: plugbrain plan decompose <gate-or-requirement> [--apply] [--json]') + process.exit(1) + } + const path = ledgerPath(db, workspaceId) + const ledger = JSON.parse(readFileSync(path, 'utf8')) as DecompositionLedger + const result = decomposeLedgerTarget(ledger, target) + const task = args.includes('--apply') && result.state === 'executable' + ? applyDecomposedTask(db, workspaceId, result) + : null + if (json) return jsonOut({ ...result, task }) + console.log(`${result.state}: ${result.spec.id} ${result.spec.title}`) + if (result.reason) console.log(` lead: ${result.reason}`) + console.log(` category: ${result.spec.category ?? '(missing)'}`) + console.log(` brief: ${result.spec.brief || '(missing)'}`) + console.log(` acceptance: ${result.spec.acceptance.join('; ') || '(missing)'}`) + console.log(` reviewer: ${result.spec.reviewerRole ?? '(missing)'}`) + console.log(` dependencies: ${result.spec.dependsOn.join(', ') || '(none)'}`) + console.log(` evidence: ${result.spec.evidence.join(', ') || '(missing)'}`) + if (task) console.log(` queue task: ${task.id}`) + else if (args.includes('--apply')) console.log(' no queue mutation: lead review is required') + return + } if (step === 'task') { const id = rest.find(arg => !arg.startsWith('--')) if (id === undefined) { console.error('usage: plugbrain plan task <M00>'); process.exit(1) } @@ -667,6 +699,48 @@ function reposCommand(args: string[]): void { renderRepos(view, dirtyOnly) } +async function diskCommand(args: string[]): Promise<void> { + const [step, ...rest] = args + if (step === 'scan') { + const paths = rest.filter(value => !value.startsWith('--')) + const controller = new AbortController() + const interrupt = (): void => { controller.abort(); process.exitCode = 130 } + process.once('SIGINT', interrupt) + let state: Awaited<ReturnType<typeof scanDisk>> + try { + state = await scanDisk(db, { roots: paths.length ? paths : undefined, signal: controller.signal, onProgress: progress => { + if (progress.directories > 0 && progress.directories % 1024 === 0) { + console.error(`disk scan: ${progress.directories} directories, ${progress.files} files`) + } + } }) + } finally { process.off('SIGINT', interrupt) } + return jsonOut(state) + } + if (step === 'tree') { + const path = rest.find(value => !value.startsWith('--')) ?? null + return jsonOut({ scan: currentDiskScan(db), directories: diskTree(db, path) }) + } + if (step === 'recommend') { + const report = diskRecommendations(db, gitCensus(db)) + if (rest.includes('--json')) return jsonOut(report) + console.log(`Disk recommendations for scan ${report.scanId ?? 'none'} (GB by risk): recoverable ${report.totalsGb.recoverable}, review ${report.totalsGb.review}, keep ${report.totalsGb.keep}`) + for (const item of report.recommendations) console.log(` ${item.risk.padEnd(11)} ${String(item.gb).padStart(7)} GB ${item.paths.join(', ')}\n ${item.reason} Recovery: ${item.recovery}`) + return + } + if (step === 'wipe-check') { + const report = diskWipeCheck(db, gitCensus(db)) + if (rest.includes('--json')) return jsonOut(report) + console.log(`Reinstallation checklist: ${report.items.length} item(s), ${report.totalGb} GB`) + for (const item of report.items) console.log(` ${gbText(item.bytes)} ${item.path}\n ${item.reason}`) + return + } + if (step === 'largest') return jsonOut({ scan: currentDiskScan(db), files: largestDiskFiles(db, Number(rest[0]) || 20) }) + console.error('usage: plugbrain disk <scan [path...]|tree [path]|recommend [--json]|wipe-check [--json]|largest [n]>') + process.exit(1) +} + +function gbText(bytes: number): string { return `${Math.round(bytes / 1024 ** 3 * 10) / 10} GB` } + function notesSearch(args: string[]): void { const asJson = args.includes('--json') const text = args.filter(arg => !arg.startsWith('--') && !/^\d+$/.test(arg)).join(' ') @@ -1164,6 +1238,14 @@ function initCommand(args: string[]): void { console.log(`\nUI: http://127.0.0.1:${UI_PORT_DEFAULT}/ (start it with: plugbrain serve)`) + if (!args.includes('--no-agents-file')) { + const files = selectAgentFiles(root) + validateAgentFiles(files) + const results = files.map(file => manageAgentFile(file, { dryRun })) + console.log('\nagent instructions:') + for (const result of results) console.log(` ${result.action.padEnd(10)} ${result.path}`) + } + if (args.includes('--no-clients')) return const home = clientHome() const entry = buildEntry(selfCliPath()) @@ -1195,6 +1277,48 @@ function setupCommand(args: string[]): void { printClientResults(results, home) } +function agentsFileCommand(args: string[]): void { + const rawTarget = flagValue(args, '--target') + if (rawTarget !== null && !['AGENTS.md', 'CLAUDE.md', 'both'].includes(rawTarget)) { + console.error('usage: plugbrain agents-file [--target AGENTS.md|CLAUDE.md|both] [--dry-run] [--undo]') + process.exit(1) + } + const target = rawTarget as AgentFileTarget | undefined + const root = findGitRoot(process.cwd()) ?? process.cwd() + const files = selectAgentFiles(root, target) + const undo = args.includes('--undo') + validateAgentFiles(files, undo) + const results = files.map(file => manageAgentFile(file, { + dryRun: args.includes('--dry-run'), undo, + })) + for (const result of results) { + console.log(`${result.action.padEnd(10)} ${result.path}`) + if (args.includes('--dry-run') && result.before !== result.after) { + console.log(' --- before') + if (result.before !== null) console.log(result.before) + console.log(' +++ after') + if (result.after !== null) console.log(result.after) + } + if (result.backup) console.log(` backup: ${result.backup}`) + } +} + +async function doctorCommand(args: string[]): Promise<void> { + if (!args.includes('--agents')) { + console.error('usage: plugbrain doctor --agents [--json]') + process.exit(1) + } + const rows = await doctorAgents({ db, port: Number(process.env.PLUGBRAIN_DOCTOR_PORT ?? UI_PORT_DEFAULT) }) + if (args.includes('--json')) { + console.log(JSON.stringify(rows, null, 2)) + return + } + console.log('Client Installed MCP Daemon Workspace Agent block Fix') + for (const row of rows) { + console.log(`${row.client.padEnd(14)} ${row.installed.padEnd(10)} ${row.mcp.padEnd(5)} ${row.daemon.padEnd(7)} ${row.workspace.padEnd(10)} ${row.agentBlock.padEnd(12)} ${row.fix}`) + } +} + const [command, ...args] = process.argv.slice(2) try { switch (command) { @@ -1207,7 +1331,19 @@ switch (command) { case 'write': agentWrite(args[0], args[1], args[2], args.slice(3).join(' ')); break case 'who': who(args[0], args[1]); break case 'agents': agents(); break - case 'swarm': process.exitCode = runSwarmCli(db, args, singlePlanetId); break + case 'swarm': + if (args[0] === 'watch') { + void runSwarmWatchCli(args.slice(1)).then(code => { process.exitCode = code }).catch(error => { + console.error(error instanceof Error ? error.message : String(error)) + process.exitCode = 1 + }) + } else if (args[0] === 'run' && args.includes('--supervisor-child')) { + void runSwarmSupervisorChild(db, args, singlePlanetId).then(code => { process.exitCode = code }).catch(error => { + console.error(error instanceof Error ? error.message : String(error)) + process.exitCode = 1 + }) + } else process.exitCode = runSwarmCli(db, args, singlePlanetId) + break case 'progress': { progressReport(args[0]) break @@ -1231,6 +1367,7 @@ switch (command) { case 'hygiene': hygieneCommand(args); break case 'machine': machineCommand(args); break case 'repos': reposCommand(args); break + case 'disk': await diskCommand(args); break case 'notes': { const [step, ...rest] = args if (step === 'query') notesQuery(rest) @@ -1315,6 +1452,8 @@ switch (command) { } case 'init': initCommand(args); break case 'setup': setupCommand(args); break + case 'agents-file': agentsFileCommand(args); break + case 'doctor': await doctorCommand(args); break case 'mcp': { // No `--workspace` is the normal case now: the client starts the server in // the project folder, so the folder states the workspace. `--workspace` @@ -1368,9 +1507,11 @@ switch (command) { } default: console.log( - 'usage: plugbrain <init|setup|register|index|progress|status|search|attach|read|write|who|agents|swarm|serve|planet|notes|query|context|impact|detect-changes|cypher|intel-status|mcp|backup|restore> …\n' + - ' plugbrain init [path] [--no-clients] [--dry-run] register + index + enroll clients\n' + + 'usage: plugbrain <init|setup|agents-file|doctor|register|index|progress|status|search|attach|read|write|who|agents|swarm|serve|planet|notes|disk|query|context|impact|detect-changes|cypher|intel-status|mcp|backup|restore> …\n' + + ' plugbrain init [path] [--no-clients] [--no-agents-file] [--dry-run] register + index + enroll clients\n' + ' plugbrain setup [--all|claude|codex|cursor|windsurf|hermes|agy|opencode] [--dry-run] [--undo]\n' + + ' plugbrain agents-file [--target AGENTS.md|CLAUDE.md|both] [--dry-run] [--undo]\n' + + ' plugbrain doctor --agents [--json]\n' + ' plugbrain progress [workspaceId]\n' + ' plugbrain planet <register|select|scan|status|history> [path|workspaceId]\n' + ' plugbrain planet select [workspaceId] --checkout <checkoutId> [--checkout <checkoutId>]\n' + @@ -1379,7 +1520,9 @@ switch (command) { ' plugbrain hygiene [--workspace <ws>] [--json] [--wip-snapshot] [--repo <path>]\n' + ' plugbrain machine [--json]\n' + ' plugbrain repos [--dirty] [--refresh] [--json]\n' + - ' plugbrain swarm <register|turn|ack|board|send|enqueue|approve|resources|quota|admit> …\n' + + ' plugbrain disk <scan [path...]|tree [path]|recommend [--json]|wipe-check [--json]|largest [n]>\n' + + ' plugbrain swarm <register|turn|ack|board|chronik|send|enqueue|supersede|reassign|priority|deliver|approve|resources|quota|admit|watchdog|review-pool|reap|runner|run> …\n' + + ' plugbrain swarm runner set|show <agent> … · plugbrain swarm run <agent> [--status|--stop] start and watch a CLI worker\n' + ' plugbrain mcp [--workspace <ws>] [--auth-key <key>] (workspace from cwd when omitted)\n' + ' plugbrain backup [target_path]\n' + ' plugbrain restore <backup_path>') @@ -1398,6 +1541,10 @@ switch (command) { console.error(`refused: ${error.message}`) process.exit(3) } + if (error instanceof InvalidAgentMarkersError) { + console.error(`refused: ${error.message}`) + process.exit(2) + } // Not a failure of the command: another process is already doing this work. // A stack trace would suggest a bug where there is only a busy brain. if (error instanceof IndexRunBusy) { diff --git a/src/coord/chronicle.ts b/src/coord/chronicle.ts new file mode 100644 index 0000000..a6cd10a --- /dev/null +++ b/src/coord/chronicle.ts @@ -0,0 +1,238 @@ +import type { DatabaseSync, SQLInputValue } from 'node:sqlite' +import { requireWorkspace } from '../access.ts' +import { redactCredentialText } from './resources.ts' + +export interface SwarmChronicleEvent { + at: string + type: string + actor?: string | null + taskId?: string + title?: string + subject?: string + fromAgent?: string + toAgent?: string | null + state?: string + summary?: string | null + waitMs?: number + durationMs?: number + evidence?: string | null + messageId?: string + deliveredAt?: string | null + readAt?: string | null + leaseId?: string + paths?: string[] + account?: string + remaining?: number + unit?: string +} + +export interface SwarmChronicle { + workspaceId: string + since: string + generatedAt: string + events: SwarmChronicleEvent[] + sources: Array<{ name: string; available: boolean }> + missingSources: string[] + truncated: boolean + omittedEvents: number + sourceRowCapReached: boolean +} + +type SqlRow = Record<string, unknown> +const MAX_SOURCE_ROWS = 500 +const MAX_EVENTS = 1000 + +function hasTable(db: DatabaseSync, name: string): boolean { + return db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?").get(name) !== undefined +} + +function sinceDate(value: string | undefined, now: number): number { + if (value === undefined || value.trim() === '') return now - 2 * 60 * 60_000 + const relative = /^(\d+(?:\.\d+)?)(m|h|d)$/.exec(value.trim()) + if (relative) { + const amount = Number(relative[1]) + const multiplier = relative[2] === 'm' ? 60_000 : relative[2] === 'h' ? 60 * 60_000 : 24 * 60 * 60_000 + return now - amount * multiplier + } + const parsed = Date.parse(value) + if (!Number.isFinite(parsed)) throw new Error(`invalid --since value: ${value} (use an ISO timestamp or 2h)`) + return parsed +} + +const text = (row: SqlRow, key: string): string | null => { + const value = row[key] + return typeof value === 'string' ? value : null +} + +function readTurnEvents(db: DatabaseSync, workspaceId: string, cutoff: number): { events: SwarmChronicleEvent[]; source: string | null; truncated: boolean } { + const table = ['swarm_turn_history', 'swarm_turns', 'turn_history'].find(name => hasTable(db, name)) + if (!table) return { events: [], source: null, truncated: false } + const columns = new Set((db.prepare(`PRAGMA table_info(${table})`).all() as Array<{ name: string }>).map(column => column.name)) + if (!columns.has('workspace_id')) return { events: [], source: null, truncated: false } + const rows = db.prepare(`SELECT * FROM ${table} WHERE workspace_id = ? ORDER BY rowid DESC LIMIT ${MAX_SOURCE_ROWS + 1}`).all(workspaceId) as unknown as SqlRow[] + const truncated = rows.length > MAX_SOURCE_ROWS + const events: SwarmChronicleEvent[] = [] + for (const row of rows.slice(0, MAX_SOURCE_ROWS)) { + const at = text(row, 'ended_at') ?? text(row, 'created_at') ?? text(row, 'at') ?? text(row, 'turn_state_at') + const agent = text(row, 'agent_id') ?? text(row, 'agent') + if (!at || !agent || Date.parse(at) < cutoff) continue + events.push({ + at, type: 'turn.ended', actor: agent, + state: text(row, 'state') ?? text(row, 'turn_state') ?? undefined, + summary: text(row, 'summary') ?? text(row, 'turn_summary'), + }) + } + return { events, source: table, truncated } +} + +/** Read only recorded swarm state and present it as a time-ordered handoff. */ +export function buildSwarmChronicle( + db: DatabaseSync, + workspaceId: string, + options: { since?: string; now?: number } = {}, +): SwarmChronicle { + requireWorkspace(db, workspaceId) + const now = options.now ?? Date.now() + const cutoff = sinceDate(options.since, now) + const events: SwarmChronicleEvent[] = [] + let sourceTruncated = false + const bounded = (sql: string, ...params: SQLInputValue[]): SqlRow[] => { + const rows = db.prepare(sql).all(...params) as unknown as SqlRow[] + if (rows.length > MAX_SOURCE_ROWS) sourceTruncated = true + return rows.slice(0, MAX_SOURCE_ROWS) + } + const within = (at: string | null): at is string => at !== null && Number.isFinite(Date.parse(at)) && Date.parse(at) >= cutoff + + if (hasTable(db, 'queue_tasks')) { + const columns = new Set((db.prepare('PRAGMA table_info(queue_tasks)').all() as Array<{ name: string }>).map(column => column.name)) + const summaryColumn = columns.has('delivered_summary') ? 'delivered_summary' : 'NULL AS delivered_summary' + const tasks = bounded(`SELECT id, title, state, claimed_by, claimed_at, delivered_path, ${summaryColumn}, created_at, updated_at FROM queue_tasks WHERE workspace_id = ? ORDER BY updated_at DESC LIMIT ${MAX_SOURCE_ROWS + 1}`, workspaceId) + for (const row of tasks) { + const id = text(row, 'id')! + const title = text(row, 'title')! + const createdAt = text(row, 'created_at') + const claimedAt = text(row, 'claimed_at') + const updatedAt = text(row, 'updated_at') + const state = text(row, 'state') + const claimedBy = text(row, 'claimed_by') + if (within(createdAt)) events.push({ at: createdAt, type: 'task.enqueued', taskId: id, title }) + if (within(claimedAt)) events.push({ + at: claimedAt, type: 'task.claimed', actor: claimedBy, taskId: id, title, + waitMs: Math.max(0, Date.parse(claimedAt) - Date.parse(createdAt ?? claimedAt)), + }) + if (state === 'delivered' && within(updatedAt)) events.push({ + at: updatedAt, type: 'task.delivered', actor: claimedBy, taskId: id, title, + evidence: text(row, 'delivered_path'), + summary: text(row, 'delivered_summary'), + durationMs: claimedAt === null ? undefined : Math.max(0, Date.parse(updatedAt) - Date.parse(claimedAt)), + }) + if (state === 'cancelled' && within(updatedAt)) events.push({ at: updatedAt, type: 'task.cancelled', taskId: id, title }) + } + } + + if (hasTable(db, 'inbox_messages')) { + const messages = bounded(`SELECT id, from_agent, to_agent, subject, created_at, delivered_at, read_at + FROM inbox_messages WHERE workspace_id = ? ORDER BY COALESCE(read_at, delivered_at, created_at) DESC LIMIT ${MAX_SOURCE_ROWS + 1}`, workspaceId) + for (const row of messages) { + const common = { + messageId: text(row, 'id')!, fromAgent: text(row, 'from_agent')!, toAgent: text(row, 'to_agent'), + subject: text(row, 'subject')!, + } + const createdAt = text(row, 'created_at') + const deliveredAt = text(row, 'delivered_at') + const readAt = text(row, 'read_at') + if (within(createdAt)) events.push({ at: createdAt, type: 'message.sent', actor: common.fromAgent, ...common }) + if (within(deliveredAt)) events.push({ at: deliveredAt, type: 'message.delivered', actor: common.toAgent, ...common }) + if (within(readAt)) events.push({ at: readAt, type: 'message.read', actor: common.toAgent, ...common }) + } + } + + if (hasTable(db, 'leases')) { + const leases = bounded(`SELECT id, agent_id, paths_json, created_at, released_at FROM leases WHERE workspace_id = ? + ORDER BY COALESCE(released_at, created_at) DESC LIMIT ${MAX_SOURCE_ROWS + 1}`, workspaceId) + for (const row of leases) { + let paths: string[] = [] + try { paths = JSON.parse(text(row, 'paths_json') ?? '[]') as string[] } catch { /* malformed historical payload is omitted */ } + const leaseId = text(row, 'id')! + const actor = text(row, 'agent_id') + const createdAt = text(row, 'created_at') + const releasedAt = text(row, 'released_at') + if (within(createdAt)) events.push({ at: createdAt, type: 'lease.acquired', actor, leaseId, paths }) + if (within(releasedAt)) events.push({ at: releasedAt, type: 'lease.released', actor, leaseId, paths }) + } + } + + if (hasTable(db, 'resource_quotas')) { + const quotas = bounded(`SELECT account, remaining, unit, reported_by, updated_at FROM resource_quotas ORDER BY updated_at DESC LIMIT ${MAX_SOURCE_ROWS + 1}`) + for (const row of quotas) { + const at = text(row, 'updated_at') + if (within(at)) events.push({ + at, type: 'quota.reported', actor: text(row, 'reported_by'), account: text(row, 'account')!, + remaining: Number(row.remaining), unit: text(row, 'unit')!, + }) + } + } + + const turnHistory = readTurnEvents(db, workspaceId, cutoff) + sourceTruncated ||= turnHistory.truncated + events.push(...turnHistory.events) + for (const event of events) { + for (const [key, value] of Object.entries(event)) { + if (typeof value === 'string') event[key as keyof SwarmChronicleEvent] = redactCredentialText(value) as never + else if (Array.isArray(value)) event[key as keyof SwarmChronicleEvent] = value.map(item => + typeof item === 'string' ? redactCredentialText(item) : item) as never + } + } + events.sort((left, right) => Date.parse(left.at) - Date.parse(right.at) || left.type.localeCompare(right.type)) + const omittedEvents = Math.max(0, events.length - MAX_EVENTS) + if (omittedEvents > 0) events.splice(0, omittedEvents) + + const historyAvailable = turnHistory.source !== null + const sources = [ + { name: 'queue_tasks', available: hasTable(db, 'queue_tasks') }, + { name: 'inbox_messages', available: hasTable(db, 'inbox_messages') }, + { name: 'leases', available: hasTable(db, 'leases') }, + { name: 'resource_quotas', available: hasTable(db, 'resource_quotas') }, + { name: 'turn history', available: historyAvailable }, + ] + const missingSources = sources.filter(source => !source.available).map(source => source.name === 'turn history' + ? 'Turn-Verlauf erst ab Version mit Turn-Historie gespeichert.' + : `Quelle fehlt: ${source.name}.`) + if (historyAvailable) { + missingSources.push('Turn-Verlauf erst ab Version mit Turn-Historie gespeichert; älterer Verlauf fehlt.') + } + return { + workspaceId, + since: new Date(cutoff).toISOString(), + generatedAt: new Date(now).toISOString(), + events, + sources, + missingSources, + truncated: sourceTruncated || omittedEvents > 0, + omittedEvents, + sourceRowCapReached: sourceTruncated, + } +} + +export function formatSwarmChronicleMarkdown(chronicle: SwarmChronicle): string { + const lines = [`# Swarm-Chronik`, '', `Zeitraum ab ${chronicle.since} · erzeugt ${chronicle.generatedAt}`, '', '## Ereignisse', ''] + if (chronicle.events.length === 0) lines.push('Keine gespeicherten Ereignisse in diesem Zeitraum.', '') + for (const event of chronicle.events) { + const detail = [event.title, event.subject ? `„${event.subject}“` : null, event.actor ? `von ${event.actor}` : null, + event.state, event.evidence ? `Beleg: ${event.evidence}` : null, + event.waitMs === undefined ? null : `Wartezeit ${Math.round(event.waitMs / 1000)} s`, + event.durationMs === undefined ? null : `Dauer ${Math.round(event.durationMs / 1000)} s`, + event.account ? `${event.account}: ${event.remaining} ${event.unit}` : null, + event.paths?.length ? event.paths.join(', ') : null, + event.summary, + ].filter(Boolean).join(' · ') + lines.push(`- ${event.at} **${event.type}**${detail ? ` — ${detail}` : ''}`) + } + lines.push('', '## Quellen', '', ...chronicle.sources.map(source => `- ${source.name}: ${source.available ? 'verfügbar' : 'fehlt'}`)) + if (chronicle.missingSources.length > 0) lines.push('', '## Nicht gespeichert', '', ...chronicle.missingSources.map(source => `- ${source}`)) + if (chronicle.truncated) lines.push('', 'Weitere Ereignisse ausgelassen.', `- Höchstens ${MAX_EVENTS} Ereignisse und ${MAX_SOURCE_ROWS} Datensätze je Quelle werden eingelesen.`) + if (chronicle.omittedEvents > 0) lines.push(`- Durch das Ereignislimit entfernt: ${chronicle.omittedEvents}.`) + if (chronicle.sourceRowCapReached) lines.push('- Mindestens eine Quelle hat das Datensatzlimit erreicht; weitere Quellzeilen wurden abgeschnitten.') + lines.push('') + return lines.join('\n') +} diff --git a/src/coord/delivery-evidence.ts b/src/coord/delivery-evidence.ts new file mode 100644 index 0000000..96e46c0 --- /dev/null +++ b/src/coord/delivery-evidence.ts @@ -0,0 +1,57 @@ +import { createHash } from 'node:crypto' +import { execFileSync } from 'node:child_process' +import { realpathSync, readFileSync, statSync } from 'node:fs' +import { isAbsolute, relative, resolve, sep } from 'node:path' +import { AccessDenied } from '../access.ts' + +const MAX_EVIDENCE_BYTES = 10 * 1024 * 1024 +const JUDGMENT = /^\s*(?:#{1,6}\s*)?(?:[-*]\s*)?(?:urteil|verdict)\s*:\s*(PASS_MIT_AUFLAGEN|PASS|FAIL)\s*$/im +const COMMIT = /^\s*(?:[-*]\s*)?(?:geprüfte revision|reviewed commit)\s*:\s*(?:commit\s+)?`?([0-9a-f]{40})`?\s*$/im + +export interface DeliveryEvidence { + path: string + sha256: string + reviewJudgment: string | null + reviewedCommit: string | null +} + +export function inspectDeliveryEvidence( + workspaceRoot: string, + evidencePath: string, + options: { reviewRequired?: boolean } = {}, +): DeliveryEvidence { + const root = realpathSync.native(resolve(workspaceRoot)) + const candidate = isAbsolute(evidencePath) ? resolve(evidencePath) : resolve(root, evidencePath) + let actual: string + try { actual = realpathSync.native(candidate) } catch { throw new AccessDenied(`delivery evidence does not exist: ${evidencePath}`) } + const rel = relative(root, actual) + if (rel === '..' || rel.startsWith(`..${sep}`) || isAbsolute(rel)) { + throw new AccessDenied('delivery evidence must be inside the workspace') + } + const info = statSync(actual) + if (!info.isFile()) throw new AccessDenied('delivery evidence must be a file') + if (info.size === 0) throw new AccessDenied('delivery evidence file is empty') + if (info.size > MAX_EVIDENCE_BYTES) throw new AccessDenied('delivery evidence file exceeds 10 MiB') + const bytes = readFileSync(actual) + const text = bytes.toString('utf8') + const reviewText = text.replace(/\*\*/g, '') + const judgment = options.reviewRequired ? JUDGMENT.exec(reviewText)?.[1] ?? null : null + const commit = options.reviewRequired ? COMMIT.exec(reviewText)?.[1] ?? null : null + if (options.reviewRequired && (!judgment || !commit)) { + throw new AccessDenied('review evidence must include a PASS/PASS_MIT_AUFLAGEN/FAIL judgment and a 40-character commit hash') + } + return { + path: rel.split(sep).join('/'), + sha256: createHash('sha256').update(bytes).digest('hex'), + reviewJudgment: judgment, + reviewedCommit: commit, + } +} + +export function sourceRevision(repoPath: string): string | null { + try { + return execFileSync('git', ['-C', repoPath, 'rev-parse', 'HEAD'], { + encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 3000, + }).trim() || null + } catch { return null } +} diff --git a/src/coord/dependencies.ts b/src/coord/dependencies.ts new file mode 100644 index 0000000..fd54c78 --- /dev/null +++ b/src/coord/dependencies.ts @@ -0,0 +1,263 @@ +/** + * Task dependencies: work that waits for work. + * + * A queue that only knows "the next free agent takes the next task" cannot + * express "after this lands". The fleet needed exactly that on 26.09.2026: + * a review (R-BR3) had to follow cx01's delivery, INT-R6 had to follow another + * hand-in, and both orderings lived only in the integrator's head — so the + * integrator had to re-queue and nudge by hand every time. + * + * A task may therefore name the task it waits for (`swarm enqueue --after`). + * It stays out of every offer — the turn ping and the atomic claim — until the + * predecessor is handed in (`delivered`) or its holder ended a turn with + * `awaiting-commit` or `needs-task`, the two states in which a worker has done + * all it can do alone. The release is persisted the first time it is observed, + * so a later turn start by the holder cannot take it back, and the addressee + * is told once ("Deine Aufgabe … ist jetzt frei"), so a task waiting behind + * someone else becomes visible at the next turn boundary even when nobody + * looked at the queue in between. + * + * Reconciliation is both event-driven (`deliver`, turn end) and state-driven + * (every turn start, and the watchdog scan): a missed event must never strand + * a task behind a predecessor that is already gone. + */ +import type { DatabaseSync } from 'node:sqlite' +import { AccessDenied } from '../access.ts' +import { coordEvents } from './events.ts' +import { ensureSystemAgent, sendMessage } from './inbox.ts' + +const SCHEMA = ` +CREATE TABLE IF NOT EXISTS task_dependencies ( + task_id TEXT PRIMARY KEY, + after_task_id TEXT NOT NULL, + released_at TEXT, + announced_at TEXT, + created_at TEXT NOT NULL +); +` + +export function ensureDependencySchema(db: DatabaseSync): void { db.exec(SCHEMA) } + +/** + * The predicate every offer of pending work must carry. + * + * It is written against the bare table name `queue_tasks` on purpose: both the + * candidate query in `queue.ts` and the turn ping in `swarm-ops.ts` select from + * the table unaliased, so one fragment keeps the two paths from drifting apart + * — a task blocked in the ping but claimable, or the reverse, is exactly the + * kind of split the queue exists to prevent. + */ +export const UNBLOCKED_TASK_SQL = + `NOT EXISTS (SELECT 1 FROM task_dependencies d WHERE d.task_id = queue_tasks.id AND d.released_at IS NULL)` + +interface DependencyRow { + task_id: string + after_task_id: string + task_title: string + task_state: string + addressed_to: string | null + after_workspace_id: string | null + after_title: string | null + after_state: string | null + after_attempt: number | null + holder_state: string | null +} + +/** + * Record that `taskId` must wait for `afterTaskId`. + * + * The predecessor is validated now: a typo must fail where a human can see it, + * not silently block the task forever. The immediate reconciliation below also + * covers enqueueing behind a predecessor that is already released — there is + * nothing to wait for, so the task is free at once. + */ +export function addTaskDependency(db: DatabaseSync, taskId: string, afterTaskId: string): void { + ensureDependencySchema(db) + if (taskId === afterTaskId) throw new AccessDenied('a task cannot wait for itself') + const after = db.prepare('SELECT id, workspace_id FROM queue_tasks WHERE id = ?').get(afterTaskId) as + { id: string; workspace_id: string } | undefined + if (after === undefined) throw new AccessDenied(`unknown task to wait for: ${afterTaskId}`) + const task = db.prepare('SELECT id, workspace_id FROM queue_tasks WHERE id = ?').get(taskId) as + { id: string; workspace_id: string } | undefined + if (task === undefined) throw new AccessDenied(`unknown task: ${taskId}`) + if (after.workspace_id !== task.workspace_id) throw new AccessDenied('task dependencies must stay within the same workspace') + + const closesCycle = db.prepare(` + WITH RECURSIVE ancestors(id) AS ( + SELECT after_task_id FROM task_dependencies WHERE task_id = ? AND released_at IS NULL + UNION + SELECT d.after_task_id FROM task_dependencies d JOIN ancestors a ON d.task_id = a.id + WHERE d.released_at IS NULL + ) SELECT 1 AS found FROM ancestors WHERE id = ? LIMIT 1 + `).get(afterTaskId, taskId) + if (closesCycle !== undefined) throw new AccessDenied('task dependency would create a cycle') + + db.prepare(`INSERT INTO task_dependencies (task_id, after_task_id, released_at, announced_at, created_at) + VALUES (?, ?, NULL, NULL, ?)`) + .run(taskId, afterTaskId, new Date().toISOString()) + syncDependencies(db, task.workspace_id) +} + +interface DependencyEdge { task_id: string; after_task_id: string } + +function dependencyCycles(db: DatabaseSync, workspaceId: string): string[][] { + const edges = db.prepare(` + SELECT d.task_id, d.after_task_id FROM task_dependencies d + JOIN queue_tasks q ON q.id = d.task_id + WHERE q.workspace_id = ? AND d.released_at IS NULL + `).all(workspaceId) as unknown as DependencyEdge[] + const next = new Map(edges.map(edge => [edge.task_id, edge.after_task_id])) + const finished = new Set<string>() + const cycles: string[][] = [] + for (const start of next.keys()) { + if (finished.has(start)) continue + const path: string[] = [] + const pathIndex = new Map<string, number>() + let current: string | undefined = start + while (current !== undefined && next.has(current) && !finished.has(current)) { + const seenAt = pathIndex.get(current) + if (seenAt !== undefined) { cycles.push(path.slice(seenAt)); break } + pathIndex.set(current, path.length) + path.push(current) + current = next.get(current) + } + for (const taskId of path) finished.add(taskId) + } + return cycles +} + +function cancelBrokenDependency( + db: DatabaseSync, + workspaceId: string, + taskId: string, + afterTaskId: string, + title: string, + addressedTo: string | null, + reason: string, + nowIso: string, +): void { + db.exec('BEGIN IMMEDIATE') + try { + const updated = db.prepare(`UPDATE queue_tasks SET state = 'cancelled', body = + CASE WHEN body = '' THEN ? ELSE body || char(10) || char(10) || ? END, updated_at = ? + WHERE id = ? AND state IN ('pending', 'claimed')`) + .run(reason, reason, nowIso, taskId) + db.prepare('UPDATE task_dependencies SET released_at = ? WHERE task_id = ? AND released_at IS NULL') + .run(nowIso, taskId) + let messageId: string | null = null + if (Number(updated.changes) > 0 && addressedTo !== null) { + ensureSystemAgent(db, workspaceId) + messageId = sendMessage(db, { + workspaceId, fromAgent: 'integrator', toAgent: addressedTo, + subject: `Aufgabe ${title} wurde blockiert`, + body: `${reason} Die Aufgabe wurde sichtbar abgebrochen; bitte Integrator informieren.`, + }).id + } + db.exec('COMMIT') + if (Number(updated.changes) > 0) { + coordEvents.emitLive('task.dependency.failed', { taskId, afterTaskId, messageId, at: nowIso }) + } + } catch (error: unknown) { + db.exec('ROLLBACK') + throw error + } +} + +/** + * Release every waiting task in the workspace whose predecessor has arrived, + * and tell each addressee once. Returns the ids of tasks released by this pass. + */ +export function syncDependencies( + db: DatabaseSync, + workspaceId: string, + options: { now?: Date } = {}, +): string[] { + ensureDependencySchema(db) + const now = options.now ?? new Date() + const nowIso = now.toISOString() + const cycleTaskIds = dependencyCycles(db, workspaceId).flat() + for (const taskId of cycleTaskIds) { + const row = db.prepare(`SELECT d.after_task_id, q.title, q.addressed_to FROM task_dependencies d + JOIN queue_tasks q ON q.id = d.task_id WHERE d.task_id = ?`).get(taskId) as + { after_task_id: string; title: string; addressed_to: string | null } | undefined + if (row !== undefined) { + cancelBrokenDependency(db, workspaceId, taskId, row.after_task_id, row.title, row.addressed_to, + `Blocked: dependency cycle includes task ${taskId}.`, nowIso) + } + } + const state = holderStateSql(db) + const ready = db.prepare(` + SELECT d.task_id, d.after_task_id, q.title AS task_title, q.state AS task_state, q.addressed_to, + p.workspace_id AS after_workspace_id, p.title AS after_title, p.state AS after_state, + COALESCE(receipt.delivery_attempt, 1) AS after_attempt, ${state.select} + FROM task_dependencies d + JOIN queue_tasks q ON q.id = d.task_id + LEFT JOIN queue_tasks p ON p.id = d.after_task_id + LEFT JOIN queue_deliveries receipt ON receipt.task_id = p.id + LEFT JOIN agents holder ON holder.id = p.claimed_by + WHERE d.released_at IS NULL AND q.workspace_id = ? + AND (p.id IS NULL OR p.workspace_id <> q.workspace_id OR ${state.arrived}) + ORDER BY d.created_at ASC, d.rowid ASC + `).all(workspaceId) as unknown as DependencyRow[] + + const released: string[] = [] + for (const row of ready) { + if (row.after_title === null || row.after_workspace_id !== workspaceId) { + const reason = row.after_title === null + ? `Blocked: predecessor ${row.after_task_id} no longer exists.` + : `Blocked: predecessor ${row.after_task_id} belongs to another workspace.` + cancelBrokenDependency(db, workspaceId, row.task_id, row.after_task_id, row.task_title, row.addressed_to, reason, nowIso) + continue + } + const marked = db.prepare( + 'UPDATE task_dependencies SET released_at = ? WHERE task_id = ? AND released_at IS NULL', + ).run(nowIso, row.task_id) + if (Number(marked.changes) === 0) continue + released.push(row.task_id) + coordEvents.emitLive('task.released', { + taskId: row.task_id, afterTaskId: row.after_task_id, afterState: row.after_state, at: nowIso, + }) + + // Only a pending, addressed task needs the message: an unaddressed task is + // visible to whoever checks in next, and a task already claimed has an owner. + if (row.task_state !== 'pending' || row.addressed_to === null) continue + const announced = db.prepare( + 'UPDATE task_dependencies SET announced_at = ? WHERE task_id = ? AND announced_at IS NULL', + ).run(nowIso, row.task_id) + if (Number(announced.changes) === 0) continue + ensureSystemAgent(db, workspaceId) + sendMessage(db, { + workspaceId, + fromAgent: 'integrator', + toAgent: row.addressed_to, + subject: 'Deine Aufgabe ist jetzt frei', + body: `Deine Aufgabe "${row.task_title}" (${row.task_id}) ist jetzt frei: ` + + `"${row.after_title}" (${row.after_task_id}) ist ${describePredecessor(row)}.\n` + + `Nimm sie beim nächsten Turn-Start mit: plugbrain swarm turn <agent> start --claim`, + deliveryKey: `task.delivered:${row.after_task_id}:${row.after_attempt}`, + }) + } + return released +} + +/** + * The holder's turn state decides whether a predecessor has arrived. The turn + * columns belong to `ensureSwarmOpsSchema`; a store that only ever ran the + * queue has none, and nothing in such a store ever ended a turn — so the + * reading is honestly "no holder state" instead of a missing-column error. + */ +function holderStateSql(db: DatabaseSync): { select: string; arrived: string } { + const columns = db.prepare('PRAGMA table_info(agents)').all() as unknown as Array<{ name: string }> + return columns.some(column => column.name === 'turn_state') + ? { + select: 'holder.turn_state AS holder_state', + arrived: `(p.state = 'delivered' OR holder.turn_state IN ('awaiting-commit', 'needs-task'))`, + } + : { select: 'NULL AS holder_state', arrived: `p.state = 'delivered'` } +} + +/** One plain-language reason for the release, for the message body. */ +function describePredecessor(row: DependencyRow): string { + if (row.after_state === 'delivered') return 'geliefert' + return `beim Halter ${row.holder_state ?? 'unbekannt'} (Turn beendet)` +} diff --git a/src/coord/events.ts b/src/coord/events.ts index 529ba9b..5ebf63c 100644 --- a/src/coord/events.ts +++ b/src/coord/events.ts @@ -1,30 +1,63 @@ /** * Real-time event bus for PlugBrain coordination (M4). * Powers SSE live stream (/api/live/events) and inbox long-polling. + * + * Every event carries a monotonic `id` and the last few hundred stay in a + * bounded replay buffer. A consumer that reconnects with `Last-Event-ID` (or a + * fresh process that starts with a persisted cursor) replays what it missed + * instead of silently sleeping through it. */ import { EventEmitter } from 'node:events' import type { InboxMessage } from './types.ts' export interface LiveEventPayload { + id: number type: string data: unknown timestamp: string } -class CoordEventBus extends EventEmitter { +/** How many recent events are kept for resume. Bounded so a busy brain cannot grow without limit. */ +const REPLAY_LIMIT = 500 + +export class CoordEventBus extends EventEmitter { + private sequence = 0 + private readonly replayBuffer: LiveEventPayload[] = [] + constructor() { super() this.setMaxListeners(500) } - emitLive(type: string, data: unknown): void { + emitLive(type: string, data: unknown): LiveEventPayload { const payload: LiveEventPayload = { + id: ++this.sequence, type, data, timestamp: new Date().toISOString(), } + this.replayBuffer.push(payload) + if (this.replayBuffer.length > REPLAY_LIMIT) { + this.replayBuffer.splice(0, this.replayBuffer.length - REPLAY_LIMIT) + } this.emit('live', payload) this.emit(`event:${type}`, payload) + return payload + } + + /** The most recent id handed out; a cursor can start here to watch only new events. */ + latestEventId(): number { + return this.sequence + } + + /** + * Events still in the buffer after `lastEventId`. A missing or non-numeric + * cursor replays the whole buffer; a stale cursor simply gets what is kept. + */ + replaySince(lastEventId: string | number | null | undefined): LiveEventPayload[] { + const since = typeof lastEventId === 'number' ? lastEventId : Number(lastEventId) + if (!Number.isFinite(since)) return [...this.replayBuffer] + return this.replayBuffer.filter(event => event.id > since) } onLive(listener: (event: LiveEventPayload) => void): () => void { diff --git a/src/coord/fleet-automation.ts b/src/coord/fleet-automation.ts new file mode 100644 index 0000000..c670d7d --- /dev/null +++ b/src/coord/fleet-automation.ts @@ -0,0 +1,249 @@ +import type { DatabaseSync } from 'node:sqlite' +import { enqueueTask } from '../queue.ts' +import { ensureSystemAgent, sendMessage } from './inbox.ts' +import { routeReviewForAuthor } from './watchdog.ts' +import { ensureCoordSchema } from './registry.ts' +import { resolveReviewIndependence } from './model-family.ts' + +export interface FleetAutomationEvent { + id?: string | number + type: string + data: unknown +} + +export interface FleetAutomationResult { + action: 'review-queued' | 'fix-queued' | 'integration-queued' | 'lead-decision-required' | 'ignored' | 'duplicate' + taskId?: string +} + +const SCHEMA = ` +CREATE TABLE IF NOT EXISTS fleet_automation_events ( + workspace_id TEXT NOT NULL, + event_key TEXT NOT NULL, + action TEXT NOT NULL, + created_at TEXT NOT NULL, + PRIMARY KEY (workspace_id, event_key) +); +CREATE TABLE IF NOT EXISTS fleet_automation_cases ( + workspace_id TEXT NOT NULL, + source_task_id TEXT NOT NULL, + author_id TEXT NOT NULL, + fix_round INTEGER NOT NULL DEFAULT 0, + state TEXT NOT NULL, + created_at TEXT NOT NULL, + updated_at TEXT NOT NULL, + PRIMARY KEY (workspace_id, source_task_id) +); +CREATE TABLE IF NOT EXISTS fleet_automation_tasks ( + workspace_id TEXT NOT NULL, + task_id TEXT NOT NULL, + source_task_id TEXT NOT NULL, + role TEXT NOT NULL, + PRIMARY KEY (workspace_id, task_id) +); +` + +function object(value: unknown): Record<string, unknown> { + return typeof value === 'object' && value !== null ? value as Record<string, unknown> : {} +} + +function string(value: unknown): string | null { + return typeof value === 'string' && value.trim() !== '' ? value.trim() : null +} + +function eventKey(event: FleetAutomationEvent): string | null { + const data = object(event.data) + if (event.type === 'task.delivered') { + const taskId = string(data.taskId) + return taskId === null ? null : `task.delivered:${taskId}` + } + if (event.type === 'agent.turn' && data.state === 'blocked') { + const agentId = string(data.agentId) + return agentId === null ? null : `agent.blocked:${agentId}:${string(data.at) ?? event.id ?? 'unknown'}` + } + if (event.type === 'watchdog.silent') { + const agentId = string(data.agentId) + const workingSince = string(data.workingSince) ?? string(data.at) ?? event.id ?? 'unknown' + const lastContact = string(data.lastContactAt) ?? 'no-contact' + const heldTask = string(data.heldTaskId) ?? 'no-held-task' + return agentId === null ? null : `watchdog.silent:${agentId}:${workingSince}:${lastContact}:${heldTask}` + } + return null +} + +function linkTask(db: DatabaseSync, workspaceId: string, taskId: string, sourceTaskId: string, role: string): void { + db.prepare(`INSERT OR IGNORE INTO fleet_automation_tasks (workspace_id, task_id, source_task_id, role) + VALUES (?, ?, ?, ?)`) + .run(workspaceId, taskId, sourceTaskId, role) +} + +function markEvent(db: DatabaseSync, workspaceId: string, key: string, action: string): void { + db.prepare(`INSERT OR IGNORE INTO fleet_automation_events (workspace_id, event_key, action, created_at) + VALUES (?, ?, ?, ?)`) + .run(workspaceId, key, action, new Date().toISOString()) +} + +function queueReview(db: DatabaseSync, workspaceId: string, authorId: string, taskId: string, caseSourceTaskId = taskId): FleetAutomationResult { + const route = routeReviewForAuthor(db, workspaceId, authorId) + const reviewTaskId = route.taskId + if (reviewTaskId !== undefined) { + linkTask(db, workspaceId, taskId, caseSourceTaskId, caseSourceTaskId === taskId ? 'source' : 'fix') + linkTask(db, workspaceId, reviewTaskId, caseSourceTaskId, 'review') + if (caseSourceTaskId === taskId) { + db.prepare(`INSERT OR IGNORE INTO fleet_automation_cases + (workspace_id, source_task_id, author_id, fix_round, state, created_at, updated_at) + VALUES (?, ?, ?, 0, 'review-pending', ?, ?)`) + .run(workspaceId, taskId, authorId, new Date().toISOString(), new Date().toISOString()) + } + db.prepare(`UPDATE fleet_automation_cases SET state = 'review-pending', updated_at = ? + WHERE workspace_id = ? AND source_task_id = ? AND state NOT IN ('integration-ready', 'awaiting-lead')`) + .run(new Date().toISOString(), workspaceId, caseSourceTaskId) + return { action: route.routed ? 'review-queued' : 'duplicate', taskId: reviewTaskId } + } + return { action: 'lead-decision-required' } +} + +function leadDecision(db: DatabaseSync, workspaceId: string, subject: string, body: string): void { + ensureCoordSchema(db) + ensureSystemAgent(db, workspaceId) + sendMessage(db, { workspaceId, fromAgent: 'integrator', toAgent: 'integrator', subject, body }) +} + +function processDelivered(db: DatabaseSync, workspaceId: string, taskId: string): FleetAutomationResult { + const task = db.prepare(`SELECT id, title, claimed_by, state FROM queue_tasks WHERE workspace_id = ? AND id = ?`) + .get(workspaceId, taskId) as { id: string; title: string; claimed_by: string | null; state: string } | undefined + if (task === undefined || task.state !== 'delivered') return { action: 'ignored' } + + const mapped = db.prepare(`SELECT source_task_id, role FROM fleet_automation_tasks + WHERE workspace_id = ? AND task_id = ?`).get(workspaceId, taskId) as { source_task_id: string; role: string } | undefined + if (mapped?.role === 'review') { + const review = db.prepare(`SELECT review_judgment FROM queue_deliveries WHERE task_id = ?`).get(taskId) as { review_judgment: string | null } | undefined + const judgment = review?.review_judgment?.toUpperCase() + const sourceTaskId = mapped.source_task_id + const current = db.prepare(`SELECT author_id, fix_round, state FROM fleet_automation_cases + WHERE workspace_id = ? AND source_task_id = ?`).get(workspaceId, sourceTaskId) as { author_id: string; fix_round: number; state: string } | undefined + if (current === undefined) return { action: 'ignored' } + const identity = db.prepare(` + SELECT r.author_id, r.reviewer_id, author.account AS author_account, reviewer.account AS reviewer_account, + reviewer.retired_at + FROM review_routes r + JOIN agents author ON author.id = r.author_id + JOIN agents reviewer ON reviewer.id = r.reviewer_id + WHERE r.workspace_id = ? AND r.task_id = ? + `).get(workspaceId, taskId) as { + author_id: string; reviewer_id: string; author_account: string | null; reviewer_account: string | null; retired_at: string | null + } | undefined + const independence = identity === undefined ? null + : resolveReviewIndependence(db, identity.author_id, identity.reviewer_id) + if (identity === undefined || identity.author_id !== current.author_id || identity.retired_at !== null + || !identity.author_account || !identity.reviewer_account || identity.author_account === identity.reviewer_account + || independence === null || 'reason' in independence) { + db.prepare(`UPDATE fleet_automation_cases SET state = 'awaiting-lead', updated_at = ? + WHERE workspace_id = ? AND source_task_id = ?`).run(new Date().toISOString(), workspaceId, sourceTaskId) + leadDecision(db, workspaceId, `Review independence needs decision: ${sourceTaskId}`, + `Review task ${taskId} no longer has verifiable independent reviewer account and model-family metadata. No automatic integration or fix routing was created.`) + return { action: 'lead-decision-required' } + } + if (judgment === 'PASS' || judgment === 'PASS_MIT_AUFLAGEN') { + if (current.state === 'integration-ready') return { action: 'duplicate' } + const source = db.prepare('SELECT title FROM queue_tasks WHERE id = ?').get(sourceTaskId) as { title: string } + const integrationTask = enqueueTask(db, workspaceId, { + title: `Integrate: ${source.title}`, + body: `Candidate task: ${sourceTaskId}\nReview task: ${taskId}\nReviewer: ${task.claimed_by ?? 'unknown'}\nReview judgment: ${judgment}\nCreate or advance the integration candidate after checking the delivered evidence.`, + addressedTo: 'integrator', requestedBy: 'integrator', + }) + linkTask(db, workspaceId, integrationTask.id, sourceTaskId, 'integration') + db.prepare(`UPDATE fleet_automation_cases SET state = 'integration-ready', updated_at = ? + WHERE workspace_id = ? AND source_task_id = ?`).run(new Date().toISOString(), workspaceId, sourceTaskId) + return { action: 'integration-queued', taskId: integrationTask.id } + } + if (judgment === 'FAIL' && current.fix_round < 2) { + const nextRound = current.fix_round + 1 + const source = db.prepare('SELECT title FROM queue_tasks WHERE id = ?').get(sourceTaskId) as { title: string } + const fixTask = enqueueTask(db, workspaceId, { + title: `Fix: ${source.title} (round ${nextRound} of 2)`, + body: `Author: ${current.author_id}\nSource task: ${sourceTaskId}\nReview task: ${taskId}\n` + + `The review verdict is blocking FAIL. Address the findings in the existing candidate, deliver again, and preserve review evidence. This is fix round ${nextRound} of at most 2.`, + addressedTo: current.author_id, requestedBy: 'integrator', + }) + linkTask(db, workspaceId, fixTask.id, sourceTaskId, 'fix') + db.prepare(`UPDATE fleet_automation_cases SET fix_round = ?, state = 'fix-pending', updated_at = ? + WHERE workspace_id = ? AND source_task_id = ?`).run(nextRound, new Date().toISOString(), workspaceId, sourceTaskId) + return { action: 'fix-queued', taskId: fixTask.id } + } + db.prepare(`UPDATE fleet_automation_cases SET state = ?, updated_at = ? + WHERE workspace_id = ? AND source_task_id = ?`).run('awaiting-lead', new Date().toISOString(), workspaceId, sourceTaskId) + leadDecision(db, workspaceId, `Review needs decision: ${sourceTaskId}`, + `The review for source task ${sourceTaskId} needs a lead decision. Verdict: ${judgment ?? 'missing or invalid'}. ` + + `Author: ${current.author_id}. Fix rounds used: ${current.fix_round} of 2. Review task: ${taskId}.`) + return { action: 'lead-decision-required' } + } + + if (mapped?.role === 'fix') { + const authorId = task.claimed_by + if (authorId === null) return { action: 'ignored' } + const result = queueReview(db, workspaceId, authorId, taskId, mapped.source_task_id) + if (result.action === 'lead-decision-required') { + leadDecision(db, workspaceId, `Fix review needs decision: ${task.title}`, + `Delivered fix task ${taskId} by ${authorId} has no independent reviewer available in the configured review pool.`) + } + return result + } + + if (task.claimed_by === null || /^review:/i.test(task.title)) return { action: 'ignored' } + const result = queueReview(db, workspaceId, task.claimed_by, taskId) + if (result.action === 'lead-decision-required') { + leadDecision(db, workspaceId, `Review routing needs decision: ${task.title}`, + `Delivered task ${taskId} by ${task.claimed_by} has no independent reviewer available in the configured review pool.`) + } + return result +} + +/** Apply one event from the existing Brain event bus to the existing queue. */ +export function handleFleetAutomationEvent( + db: DatabaseSync, + workspaceId: string, + event: FleetAutomationEvent, +): FleetAutomationResult { + db.exec(SCHEMA) + const key = eventKey(event) + if (key === null) return { action: 'ignored' } + + db.exec('BEGIN IMMEDIATE') + try { + const duplicate = db.prepare(`SELECT 1 AS yes FROM fleet_automation_events WHERE workspace_id = ? AND event_key = ?`) + .get(workspaceId, key) + if (duplicate !== undefined) { db.exec('COMMIT'); return { action: 'duplicate' } } + + let result: FleetAutomationResult + if (event.type === 'task.delivered') { + const taskId = string(object(event.data).taskId) + result = taskId === null ? { action: 'ignored' } : processDelivered(db, workspaceId, taskId) + } else { + const data = object(event.data) + const agentId = string(data.agentId) + const task = agentId === null ? undefined : db.prepare(`SELECT id, title FROM queue_tasks + WHERE workspace_id = ? AND claimed_by = ? AND state = 'claimed' ORDER BY claimed_at DESC LIMIT 1`) + .get(workspaceId, agentId) as { id: string; title: string } | undefined + if (agentId === null || task === undefined) result = { action: 'ignored' } + else { + if (event.type === 'watchdog.silent') { + leadDecision(db, workspaceId, `Silent work needs decision: ${task.title}`, + `${agentId} has been silent while holding task ${task.id}. Decide whether to continue, reassign, or retire the worker.`) + db.prepare('UPDATE agents SET silence_alerted_at = ? WHERE workspace_id = ? AND id = ?') + .run(string(data.at) ?? new Date().toISOString(), workspaceId, agentId) + } else { + leadDecision(db, workspaceId, `Blocked work needs decision: ${task.title}`, + `${agentId} ended its turn blocked while holding task ${task.id}. Summary: ${string(data.summary) ?? 'none'}. Decide how this work should proceed.`) + } + result = { action: 'lead-decision-required' } + } + } + markEvent(db, workspaceId, key, result.action) + db.exec('COMMIT') + return result + } catch (error) { + db.exec('ROLLBACK') + throw error + } +} diff --git a/src/coord/inbox.ts b/src/coord/inbox.ts index 1fb4502..a5cdce3 100644 --- a/src/coord/inbox.ts +++ b/src/coord/inbox.ts @@ -4,7 +4,7 @@ */ import { randomUUID } from 'node:crypto' import type { DatabaseSync, SQLInputValue } from 'node:sqlite' -import { AccessDenied, requireAgent, requireWorkspace } from '../access.ts' +import { AccessDenied, registerAgent, requireAgent, requireWorkspace } from '../access.ts' import { rowsAs } from '../store/rows.ts' import { coordEvents } from './events.ts' import type { InboxMessage } from './types.ts' @@ -21,6 +21,8 @@ interface InboxDbRow { created_at: string delivered_at: string | null read_at: string | null + delivery_key?: string | null + processed_at?: string | null } const INBOX_SCHEMA = ` @@ -35,14 +37,44 @@ CREATE TABLE IF NOT EXISTS inbox_messages ( body TEXT NOT NULL, created_at TEXT NOT NULL, delivered_at TEXT, - read_at TEXT + read_at TEXT, + delivery_key TEXT, + processed_at TEXT ); CREATE INDEX IF NOT EXISTS idx_inbox_to ON inbox_messages(workspace_id, to_agent, delivered_at); CREATE INDEX IF NOT EXISTS idx_inbox_channel ON inbox_messages(workspace_id, channel, delivered_at); +CREATE TABLE IF NOT EXISTS inbox_delivery_receipts ( + message_id TEXT NOT NULL REFERENCES inbox_messages(id) ON DELETE CASCADE, + agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE, + delivered_at TEXT, + acknowledged_at TEXT, + processed_at TEXT, + PRIMARY KEY (message_id, agent_id) +); +CREATE INDEX IF NOT EXISTS idx_inbox_receipt_agent ON inbox_delivery_receipts(agent_id, acknowledged_at); +CREATE TABLE IF NOT EXISTS inbox_consumer_cursors ( + workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE, + agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE, + last_message_id TEXT, + last_rowid INTEGER NOT NULL DEFAULT 0, + updated_at TEXT NOT NULL, + PRIMARY KEY (workspace_id, agent_id) +); ` export function ensureInboxSchema(db: DatabaseSync): void { db.exec(INBOX_SCHEMA) + const columns = db.prepare('PRAGMA table_info(inbox_messages)').all() as Array<{ name: string }> + if (!columns.some(column => column.name === 'delivery_key')) db.exec('ALTER TABLE inbox_messages ADD COLUMN delivery_key TEXT') + if (!columns.some(column => column.name === 'processed_at')) db.exec('ALTER TABLE inbox_messages ADD COLUMN processed_at TEXT') + db.exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_inbox_delivery_key + ON inbox_messages(workspace_id, delivery_key, ifnull(to_agent, '*')) WHERE delivery_key IS NOT NULL`) + // Upgrade existing direct messages into durable per-consumer intents. + db.exec(`INSERT OR IGNORE INTO inbox_delivery_receipts(message_id, agent_id, delivered_at, acknowledged_at) + SELECT id, to_agent, delivered_at, read_at FROM inbox_messages WHERE to_agent IS NOT NULL`) + db.exec(`INSERT OR IGNORE INTO inbox_delivery_receipts(message_id, agent_id, delivered_at, acknowledged_at) + SELECT m.id, a.id, m.delivered_at, m.read_at FROM inbox_messages m CROSS JOIN agents a + WHERE m.to_agent IS NULL`) } function parseMessage(row: InboxDbRow): InboxMessage { @@ -57,9 +89,23 @@ function parseMessage(row: InboxDbRow): InboxMessage { createdAt: row.created_at, deliveredAt: row.delivered_at, readAt: row.read_at, + acknowledgedAt: row.read_at, + processedAt: row.processed_at ?? null, } } +/** + * Make sure a coordinating identity exists, so a message the Brain sends on + * its own (a release notice, a silence alert) has a real sender. `sendMessage` + * refuses an unknown `fromAgent`, and minting one silently at send time would + * hide a typo in the caller's own `--from`. + */ +export function ensureSystemAgent(db: DatabaseSync, workspaceId: string, agentId = 'integrator'): void { + ensureInboxSchema(db) + registerAgent(db, agentId, agentId === 'integrator' ? 'Integrator' : agentId) + db.prepare('UPDATE agents SET workspace_id = COALESCE(workspace_id, ?) WHERE id = ?').run(workspaceId, agentId) +} + export interface SendMessageInput { workspaceId: string fromAgent: string @@ -68,6 +114,19 @@ export interface SendMessageInput { missionId?: string | null subject?: string body: string + /** Stable event/task/attempt identity; repeats return the original message. */ + deliveryKey?: string +} + +/** + * A message needs both ends to exist, and the integrator is the recipient of + * every report a running fleet sends — including the first one. An unregistered + * integrator would turn a delivered report into "unknown agent" at the worst + * possible moment, so the row is created on demand. + */ +export function ensureIntegrator(db: DatabaseSync, workspaceId: string, id = 'integrator'): void { + registerAgent(db, id, id === 'integrator' ? 'Integrator' : id) + db.prepare('UPDATE agents SET workspace_id = COALESCE(workspace_id, ?) WHERE id = ?').run(workspaceId, id) } export function sendMessage(db: DatabaseSync, input: SendMessageInput): InboxMessage { @@ -89,34 +148,30 @@ export function sendMessage(db: DatabaseSync, input: SendMessageInput): InboxMes const body = input.body db.prepare(` - INSERT INTO inbox_messages (id, workspace_id, from_agent, to_agent, channel, mission_id, subject, body, created_at, delivered_at, read_at) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, NULL) - `).run( - id, - input.workspaceId, - input.fromAgent, - input.toAgent ?? null, - input.channel ?? null, - input.missionId ?? null, - subject, - body, - nowIso, - ) - - const msg: InboxMessage = { - id, - workspaceId: input.workspaceId, - fromAgent: input.fromAgent, - toAgent: input.toAgent ?? null, - channel: input.channel ?? null, - subject, - body, - createdAt: nowIso, - deliveredAt: null, - readAt: null, + INSERT OR IGNORE INTO inbox_messages + (id, workspace_id, from_agent, to_agent, channel, mission_id, subject, body, created_at, delivered_at, read_at, delivery_key) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, NULL, ?) + `).run(id, input.workspaceId, input.fromAgent, input.toAgent ?? null, input.channel ?? null, + input.missionId ?? null, subject, body, nowIso, input.deliveryKey ?? null) + + const row = (input.deliveryKey === undefined + ? db.prepare('SELECT * FROM inbox_messages WHERE id = ?').get(id) + : db.prepare(`SELECT * FROM inbox_messages + WHERE workspace_id = ? AND delivery_key = ? AND to_agent IS ? ORDER BY rowid LIMIT 1`) + .get(input.workspaceId, input.deliveryKey, input.toAgent ?? null)) as unknown as InboxDbRow | undefined + if (!row) throw new Error('message insert did not produce a durable row') + const inserted = row.id === id + if (row.to_agent !== null) { + db.prepare(`INSERT OR IGNORE INTO inbox_delivery_receipts(message_id, agent_id, delivered_at, acknowledged_at) + VALUES (?, ?, ?, ?)`) + .run(row.id, row.to_agent, row.delivered_at, row.read_at) + } else { + db.prepare(`INSERT OR IGNORE INTO inbox_delivery_receipts(message_id, agent_id) + SELECT ?, id FROM agents`).run(row.id) } - coordEvents.notifyInbox(msg) + const msg = parseMessage(row) + if (inserted) coordEvents.notifyInbox(msg) return msg } @@ -126,6 +181,8 @@ export interface ReadInboxInput { channel?: string | null missionId?: string | null unreadOnly?: boolean + /** Resume after the latest contiguous receipt ACK recorded for this consumer. */ + afterCursor?: boolean waitMs?: number } @@ -142,6 +199,13 @@ export async function readInbox(db: DatabaseSync, input: ReadInboxInput): Promis ` const params: SQLInputValue[] = [input.workspaceId, input.agentId] + if (input.afterCursor) { + const cursor = db.prepare(`SELECT last_rowid FROM inbox_consumer_cursors + WHERE workspace_id = ? AND agent_id = ?`).get(input.workspaceId, input.agentId) as { last_rowid: number } | undefined + sql += ' AND inbox_messages.rowid > ?' + params.push(cursor?.last_rowid ?? 0) + } + if (input.channel) { sql += ' AND channel = ?' params.push(input.channel) @@ -151,7 +215,9 @@ export async function readInbox(db: DatabaseSync, input: ReadInboxInput): Promis params.push(input.missionId) } if (input.unreadOnly) { - sql += ' AND read_at IS NULL' + sql += ` AND NOT EXISTS (SELECT 1 FROM inbox_delivery_receipts receipt + WHERE receipt.message_id = inbox_messages.id AND receipt.agent_id = ? AND receipt.acknowledged_at IS NOT NULL)` + params.push(input.agentId) } sql += ' ORDER BY created_at ASC' @@ -169,12 +235,21 @@ export async function readInbox(db: DatabaseSync, input: ReadInboxInput): Promis const messages: InboxMessage[] = [] for (const row of rows) { + db.prepare(`INSERT OR IGNORE INTO inbox_delivery_receipts(message_id, agent_id) + VALUES (?, ?)`).run(row.id, input.agentId) + const receipt = db.prepare(`SELECT delivered_at, acknowledged_at, processed_at + FROM inbox_delivery_receipts WHERE message_id = ? AND agent_id = ?`) + .get(row.id, input.agentId) as { delivered_at: string | null; acknowledged_at: string | null; processed_at: string | null } if (row.delivered_at === null) { db.prepare('UPDATE inbox_messages SET delivered_at = ? WHERE id = ?').run(nowIso, row.id) row.delivered_at = nowIso + } + if (receipt.delivered_at === null) { + db.prepare('UPDATE inbox_delivery_receipts SET delivered_at = ? WHERE message_id = ? AND agent_id = ?') + .run(nowIso, row.id, input.agentId) coordEvents.emitLive('message.delivered', { messageId: row.id, agentId: input.agentId, deliveredAt: nowIso }) } - messages.push(parseMessage(row)) + messages.push({ ...parseMessage(row), acknowledgedAt: receipt.acknowledged_at, processedAt: receipt.processed_at }) } return messages @@ -182,11 +257,75 @@ export async function readInbox(db: DatabaseSync, input: ReadInboxInput): Promis export function confirmDelivery(db: DatabaseSync, messageId: string, agentId: string): boolean { ensureInboxSchema(db) + const msg = db.prepare('SELECT workspace_id, to_agent FROM inbox_messages WHERE id = ?').get(messageId) as + { workspace_id: string; to_agent: string | null } | undefined + if (!msg || (msg.to_agent !== null && msg.to_agent !== agentId)) return false const nowIso = new Date().toISOString() - const res = db.prepare(` - UPDATE inbox_messages - SET read_at = ? - WHERE id = ? AND (to_agent = ? OR to_agent IS NULL) AND read_at IS NULL - `).run(nowIso, messageId, agentId) - return Number(res.changes) > 0 + db.prepare('INSERT OR IGNORE INTO inbox_delivery_receipts(message_id, agent_id) VALUES (?, ?)').run(messageId, agentId) + const res = db.prepare(`UPDATE inbox_delivery_receipts SET acknowledged_at = COALESCE(acknowledged_at, ?) + WHERE message_id = ? AND agent_id = ? AND acknowledged_at IS NULL`).run(nowIso, messageId, agentId) + if (Number(res.changes) === 0) return false + if (msg.to_agent === agentId) { + db.prepare('UPDATE inbox_messages SET read_at = COALESCE(read_at, ?) WHERE id = ?').run(nowIso, messageId) + } + advanceConsumerCursor(db, msg.workspace_id, agentId) + return true +} + +export interface ConsumerCursor { messageId: string | null; rowid: number; updatedAt: string } + +/** The contiguous ACK cursor for one consumer; processing never advances it. */ +export function getConsumerCursor(db: DatabaseSync, workspaceId: string, agentId: string): ConsumerCursor | null { + ensureInboxSchema(db) + const row = db.prepare(`SELECT last_message_id, last_rowid, updated_at FROM inbox_consumer_cursors + WHERE workspace_id = ? AND agent_id = ?`).get(workspaceId, agentId) as + { last_message_id: string | null; last_rowid: number; updated_at: string } | undefined + return row ? { messageId: row.last_message_id, rowid: Number(row.last_rowid), updatedAt: row.updated_at } : null +} + +function advanceConsumerCursor(db: DatabaseSync, workspaceId: string, agentId: string): void { + const messages = db.prepare(`SELECT m.rowid, m.id, r.acknowledged_at + FROM inbox_messages m JOIN inbox_delivery_receipts r ON r.message_id = m.id AND r.agent_id = ? + WHERE m.workspace_id = ? AND (m.to_agent = ? OR m.to_agent IS NULL) + ORDER BY m.rowid ASC`).all(agentId, workspaceId, agentId) as + Array<{ rowid: number; id: string; acknowledged_at: string | null }> + let last: { rowid: number; id: string } | null = null + for (const message of messages) { + if (message.acknowledged_at === null) break + last = { rowid: Number(message.rowid), id: message.id } + } + if (last === null) return + const now = new Date().toISOString() + db.prepare(`INSERT INTO inbox_consumer_cursors(workspace_id, agent_id, last_message_id, last_rowid, updated_at) + VALUES (?, ?, ?, ?, ?) + ON CONFLICT(workspace_id, agent_id) DO UPDATE SET + last_message_id = CASE WHEN excluded.last_rowid > inbox_consumer_cursors.last_rowid THEN excluded.last_message_id ELSE inbox_consumer_cursors.last_message_id END, + last_rowid = MAX(inbox_consumer_cursors.last_rowid, excluded.last_rowid), updated_at = excluded.updated_at`) + .run(workspaceId, agentId, last.id, last.rowid, now) +} + +export function getMessageDeliveryState( + db: DatabaseSync, messageId: string, agentId: string, +): { acknowledgedAt: string | null; processedAt: string | null } | null { + ensureInboxSchema(db) + const row = db.prepare(`SELECT acknowledged_at, processed_at FROM inbox_delivery_receipts + WHERE message_id = ? AND agent_id = ?`).get(messageId, agentId) as + { acknowledged_at: string | null; processed_at: string | null } | undefined + return row ? { acknowledgedAt: row.acknowledged_at, processedAt: row.processed_at } : null +} + +/** Record completed handling independently of the receipt ACK. */ +export function markMessageProcessed(db: DatabaseSync, messageId: string, agentId: string): boolean { + ensureInboxSchema(db) + const msg = db.prepare('SELECT to_agent FROM inbox_messages WHERE id = ?').get(messageId) as + { to_agent: string | null } | undefined + if (!msg || (msg.to_agent !== null && msg.to_agent !== agentId)) return false + db.prepare('INSERT OR IGNORE INTO inbox_delivery_receipts(message_id, agent_id) VALUES (?, ?)').run(messageId, agentId) + const nowIso = new Date().toISOString() + const result = db.prepare(`UPDATE inbox_delivery_receipts SET processed_at = COALESCE(processed_at, ?) + WHERE message_id = ? AND agent_id = ? AND processed_at IS NULL`).run(nowIso, messageId, agentId) + if (Number(result.changes) > 0 && msg.to_agent === agentId) { + db.prepare('UPDATE inbox_messages SET processed_at = COALESCE(processed_at, ?) WHERE id = ?').run(nowIso, messageId) + } + return Number(result.changes) > 0 } diff --git a/src/coord/index.ts b/src/coord/index.ts index be6ee06..419ab80 100644 --- a/src/coord/index.ts +++ b/src/coord/index.ts @@ -9,4 +9,13 @@ export * from './leases.ts' export * from './inbox.ts' export * from './task-context.ts' export * from './resources.ts' +export * from './quota-pools.ts' +export * from './dependencies.ts' +export * from './watchdog.ts' export * from './swarm-ops.ts' +export * from './reaper.ts' +export * from './swarm-read.ts' +export * from './runner.ts' +export * from './model-family.ts' +export * from './supervisor.ts' +export { listQueue, listQueueEvents, prioritizeTask, reassignTask, supersedeTask } from '../queue.ts' diff --git a/src/coord/model-family.ts b/src/coord/model-family.ts new file mode 100644 index 0000000..00e00d9 --- /dev/null +++ b/src/coord/model-family.ts @@ -0,0 +1,102 @@ +import type { DatabaseSync } from 'node:sqlite' + +export type ModelFamily = + | 'openai' + | 'anthropic' + | 'google' + | 'zhipu' + | 'deepseek' + | 'moonshot' + | 'alibaba' + | 'meta' + | 'xai' + | 'mistral' + +export type ModelFamilyResolution = + | { family: ModelFamily; modelId: string } + | { family: null; modelId: string | null; reason: 'missing-model' | 'unknown-model' } + +export type ReviewIndependence = + | { independent: true; authorFamily: ModelFamily; reviewerFamily: ModelFamily; authorModel: string; reviewerModel: string } + | { independent: false; reason: 'missing-model' | 'unknown-model' | 'stale-model-metadata' | 'same-model-family' } + +type RegisteredModelResolution = ModelFamilyResolution + | { family: null; modelId: null; reason: 'stale-model-metadata' } + +const FAMILY_PREFIXES: ReadonlyArray<readonly [ModelFamily, readonly string[]]> = [ + ['openai', ['gpt-', 'o1-', 'o3-', 'o4-', 'codex-', 'chatgpt-']], + ['anthropic', ['claude-']], + ['google', ['gemini-']], + ['zhipu', ['glm-']], + ['deepseek', ['deepseek-']], + ['moonshot', ['kimi-', 'moonshot-']], + ['alibaba', ['qwen-']], + ['meta', ['llama-']], + ['xai', ['grok-']], + ['mistral', ['mistral-']], +] + +/** Resolve only recognizable model IDs; account, surface, and provider labels are not inputs. */ +export function resolveModelFamily(modelId: string | null | undefined): ModelFamilyResolution { + const normalized = typeof modelId === 'string' ? modelId.trim().toLowerCase() : '' + if (normalized === '') return { family: null, modelId: null, reason: 'missing-model' } + + for (const [family, prefixes] of FAMILY_PREFIXES) { + if (prefixes.some(prefix => normalized.startsWith(prefix))) { + return { family, modelId: normalized } + } + } + return { family: null, modelId: normalized, reason: 'unknown-model' } +} + +interface ModelMetadataRow { model: string | null } + +function readRegisteredModels(db: DatabaseSync, agentId: string): Array<string | null> { + const profile = db.prepare('SELECT model FROM agents WHERE id = ?').get(agentId) as unknown as ModelMetadataRow | undefined + if (profile === undefined) return [] + + const models: Array<string | null> = [profile.model] + const hasRunnerTable = db.prepare(`SELECT 1 AS yes FROM sqlite_master + WHERE type = 'table' AND name = 'worker_runners'`).get() !== undefined + if (hasRunnerTable) { + const runner = db.prepare('SELECT model FROM worker_runners WHERE agent_id = ?').get(agentId) as unknown as ModelMetadataRow | undefined + if (runner !== undefined && runner.model !== null && runner.model.trim() !== '') models.push(runner.model) + } + return models +} + +/** + * Resolve the current registered model metadata for both workers on every call. + * If the agent registration and an installed runner profile disagree, callers + * must fail closed until their model metadata is reconciled. + */ +export function resolveReviewIndependence( + db: DatabaseSync, + authorId: string, + reviewerId: string, +): ReviewIndependence { + const resolveAgent = (agentId: string): RegisteredModelResolution => { + const raw = readRegisteredModels(db, agentId) + if (raw.length === 0) return { family: null, modelId: null, reason: 'missing-model' } + + const nonEmpty = raw.filter((model): model is string => typeof model === 'string' && model.trim() !== '') + if (nonEmpty.length === 0) return { family: null, modelId: null, reason: 'missing-model' } + + const ids = new Set(nonEmpty.map(model => model.trim().toLowerCase())) + if (ids.size > 1) return { family: null, modelId: null, reason: 'stale-model-metadata' } + return resolveModelFamily(nonEmpty[0]) + } + + const author = resolveAgent(authorId) + const reviewer = resolveAgent(reviewerId) + if ('reason' in author) return { independent: false, reason: author.reason } + if ('reason' in reviewer) return { independent: false, reason: reviewer.reason } + if (author.family === reviewer.family) return { independent: false, reason: 'same-model-family' } + return { + independent: true, + authorFamily: author.family, + reviewerFamily: reviewer.family, + authorModel: author.modelId, + reviewerModel: reviewer.modelId, + } +} diff --git a/src/coord/next-actions.ts b/src/coord/next-actions.ts new file mode 100644 index 0000000..c2e1f68 --- /dev/null +++ b/src/coord/next-actions.ts @@ -0,0 +1,97 @@ +import type { DatabaseSync } from 'node:sqlite' +import { requireWorkspace } from '../access.ts' +import { ensureQueueSchema } from '../queue.ts' +import { ensureLeaseSchema } from './leases.ts' +import { admitWork, ensureQuotaSchema, hostSnapshot, listQuotas, type HostSnapshot } from './resources.ts' +import { ensureSwarmOpsSchema } from './swarm-ops.ts' + +export interface SwarmNextAction { + id: string + priority: number + title: string + reason: string + command: string +} + +interface WorkerRow { id: string; turn_state: string | null; account: string | null; retired_at: string | null } +interface TaskRow { id: string; title: string; body: string; state: string; claimed_by: string | null; addressed_to: string | null } + +/** Calculate suggested operator actions. This function never executes them. */ +export function getSwarmNextActions( + db: DatabaseSync, + workspaceId: string, + options: { host?: HostSnapshot } = {}, +): SwarmNextAction[] { + ensureSwarmOpsSchema(db) + ensureQueueSchema(db) + ensureLeaseSchema(db) + ensureQuotaSchema(db) + requireWorkspace(db, workspaceId) + const workers = db.prepare('SELECT id, turn_state, account, retired_at FROM agents WHERE workspace_id = ? ORDER BY id') + .all(workspaceId) as unknown as WorkerRow[] + const active = workers.filter(worker => worker.retired_at === null) + const tasks = db.prepare('SELECT id, title, body, state, claimed_by, addressed_to FROM queue_tasks WHERE workspace_id = ? ORDER BY created_at, rowid') + .all(workspaceId) as unknown as TaskRow[] + const actions: SwarmNextAction[] = [] + + for (const worker of active) { + if (worker.turn_state === 'awaiting-commit') { + const hasReview = tasks.some(task => task.state === 'pending' || task.state === 'claimed' + ? /review|prüf/i.test(`${task.title} ${task.body}`) + && (task.addressed_to === worker.id || `${task.title} ${task.body}`.toLowerCase().includes(worker.id.toLowerCase())) + : false) + if (!hasReview) actions.push({ + id: `review:${worker.id}`, priority: 1, + title: `Review für ${worker.id} einreihen`, + reason: `${worker.id} wartet auf Commit-Freigabe und hat keine offene Review-Aufgabe.`, + command: `plugbrain swarm enqueue "Review ${worker.id}" --to <reviewer>`, + }) + } + if (worker.turn_state === 'needs-task') { + const held = tasks.find(task => task.state === 'claimed' && task.claimed_by === worker.id) + if (held) actions.push({ + id: `deliver:${held.id}`, priority: 2, + title: `Lieferung von ${worker.id} prüfen`, + reason: `Die Aufgabe ${held.id} ist noch claimed, obwohl ${worker.id} needs-task meldet; möglicherweise fehlt swarm deliver.`, + command: `plugbrain swarm deliver ${worker.id} ${held.id} --path <beleg>`, + }) + else if (tasks.some(task => task.state === 'pending' && (task.addressed_to === null || task.addressed_to === worker.id))) actions.push({ + id: `offer:${worker.id}`, priority: 5, + title: `Offene Aufgabe ${worker.id} anbieten`, + reason: `${worker.id} braucht eine Aufgabe und mindestens eine passende Queue-Aufgabe wartet.`, + command: `plugbrain swarm turn ${worker.id} start`, + }) + } + } + + const unread = (db.prepare(`SELECT COUNT(*) AS count FROM inbox_messages + WHERE workspace_id = ? AND (to_agent = 'integrator' OR to_agent IS NULL) AND read_at IS NULL`).get(workspaceId) as { count: number }).count + if (Number(unread) > 0) actions.push({ + id: 'inbox:integrator', priority: 1, + title: 'Nachrichten an den Integrator lesen', + reason: `${Number(unread)} ungelesene Nachricht(en) warten im Integrator-Postfach.`, + command: 'plugbrain swarm turn integrator start', + }) + + for (const quota of listQuotas(db).filter(item => item.exhausted)) actions.push({ + id: `quota:${quota.account}`, priority: 2, + title: `Kontingent ${quota.account} erschöpft`, + reason: `Gemeldet sind ${quota.remaining} ${quota.unit}${quota.resetsAt ? `; Reset ${quota.resetsAt}` : ''}.`, + command: 'plugbrain swarm resources', + }) + + const host = options.host ?? hostSnapshot() + for (const kind of ['test', 'build', 'install', 'worktree'] as const) { + const admission = admitWork(kind, host) + if (!admission.allowed) { + actions.push({ + id: `admission:${kind}`, priority: 3, + title: `${kind} wegen knapper Ressourcen zurückstellen`, + reason: `${kind} ist aktuell nicht zugelassen: ${admission.reasons.join('; ')}.`, + command: `plugbrain swarm admit ${kind}`, + }) + } + } + + return actions.sort((left, right) => left.priority - right.priority || left.id.localeCompare(right.id)) +} diff --git a/src/coord/quota-pools.ts b/src/coord/quota-pools.ts new file mode 100644 index 0000000..4c574a5 --- /dev/null +++ b/src/coord/quota-pools.ts @@ -0,0 +1,165 @@ +/** Shared, transactional quota pools for supervised fleet runs. */ +import { randomUUID } from 'node:crypto' +import type { DatabaseSync } from 'node:sqlite' +import { AccessDenied } from '../access.ts' + +export interface QuotaPool { + id: string + maxConcurrent: number + maxRequestsPerMinute: number | null + blockedUntil: string | null + updatedAt: string +} + +export interface QuotaReservation { + id: string + poolId: string + attemptId: string + reservedAt: string + settledAt: string | null + usage: number | null + outcome: string | null +} + +export interface QuotaAdmission { allowed: boolean; reason?: 'concurrency' | 'rpm' | 'cooldown'; retryAt?: string } +export interface QuotaPoolSummary extends QuotaPool { + activeReservations: number + settledAttempts: number + attemptsWithUnknownUsage: number + reportedUsage: number +} + +export function ensureQuotaPoolSchema(db: DatabaseSync): void { + db.exec(` +CREATE TABLE IF NOT EXISTS quota_pools ( + id TEXT PRIMARY KEY, max_concurrent INTEGER NOT NULL CHECK(max_concurrent > 0), + max_requests_per_minute INTEGER CHECK(max_requests_per_minute IS NULL OR max_requests_per_minute > 0), + blocked_until TEXT, updated_at TEXT NOT NULL +); +CREATE TABLE IF NOT EXISTS quota_reservations ( + id TEXT PRIMARY KEY, pool_id TEXT NOT NULL REFERENCES quota_pools(id), attempt_id TEXT NOT NULL UNIQUE, + reserved_at TEXT NOT NULL, settled_at TEXT, usage REAL, outcome TEXT +); +CREATE INDEX IF NOT EXISTS idx_quota_reservations_pool_active ON quota_reservations(pool_id, settled_at); +CREATE INDEX IF NOT EXISTS idx_quota_reservations_pool_time ON quota_reservations(pool_id, reserved_at); +`) +} + +export function configureQuotaPool(db: DatabaseSync, input: { + id: string; maxConcurrent: number; maxRequestsPerMinute?: number | null; now?: Date +}): QuotaPool { + ensureQuotaPoolSchema(db) + const id = input.id.trim() + if (id === '' || id.length > 80) throw new AccessDenied('quota pool id must contain 1 to 80 characters') + if (!Number.isInteger(input.maxConcurrent) || input.maxConcurrent < 1 || input.maxConcurrent > 1000) { + throw new AccessDenied('maxConcurrent must be an integer from 1 to 1000') + } + const rpm = input.maxRequestsPerMinute ?? null + if (rpm !== null && (!Number.isInteger(rpm) || rpm < 1 || rpm > 1_000_000)) { + throw new AccessDenied('maxRequestsPerMinute must be null or an integer from 1 to 1000000') + } + const updatedAt = (input.now ?? new Date()).toISOString() + db.prepare(`INSERT INTO quota_pools (id, max_concurrent, max_requests_per_minute, blocked_until, updated_at) + VALUES (?, ?, ?, NULL, ?) ON CONFLICT(id) DO UPDATE SET + max_concurrent=excluded.max_concurrent, max_requests_per_minute=excluded.max_requests_per_minute, + updated_at=excluded.updated_at`).run(id, input.maxConcurrent, rpm, updatedAt) + return getQuotaPool(db, id)! +} + +export function getQuotaPool(db: DatabaseSync, id: string): QuotaPool | null { + ensureQuotaPoolSchema(db) + const row = db.prepare('SELECT * FROM quota_pools WHERE id = ?').get(id) as + { id: string; max_concurrent: number; max_requests_per_minute: number | null; blocked_until: string | null; updated_at: string } | undefined + return row ? { id: row.id, maxConcurrent: row.max_concurrent, maxRequestsPerMinute: row.max_requests_per_minute, + blockedUntil: row.blocked_until, updatedAt: row.updated_at } : null +} + +export function listQuotaPools(db: DatabaseSync): QuotaPool[] { + ensureQuotaPoolSchema(db) + return (db.prepare('SELECT * FROM quota_pools ORDER BY id').all() as Array<{ + id: string; max_concurrent: number; max_requests_per_minute: number | null; blocked_until: string | null; updated_at: string + }>).map(row => ({ id: row.id, maxConcurrent: row.max_concurrent, maxRequestsPerMinute: row.max_requests_per_minute, + blockedUntil: row.blocked_until, updatedAt: row.updated_at })) +} + +export function quotaPoolSummary(db: DatabaseSync, id: string): QuotaPoolSummary | null { + const pool = getQuotaPool(db, id) + if (!pool) return null + const counts = db.prepare(`SELECT + SUM(CASE WHEN settled_at IS NULL THEN 1 ELSE 0 END) AS active, + SUM(CASE WHEN settled_at IS NOT NULL THEN 1 ELSE 0 END) AS settled, + SUM(CASE WHEN settled_at IS NOT NULL AND usage IS NULL THEN 1 ELSE 0 END) AS unknown_usage, + SUM(COALESCE(usage, 0)) AS reported_usage + FROM quota_reservations WHERE pool_id = ?`).get(id) as + { active: number | null; settled: number | null; unknown_usage: number | null; reported_usage: number | null } + return { ...pool, activeReservations: counts.active ?? 0, settledAttempts: counts.settled ?? 0, + attemptsWithUnknownUsage: counts.unknown_usage ?? 0, reportedUsage: counts.reported_usage ?? 0 } +} + +/** Reserve under BEGIN IMMEDIATE so simultaneous workers cannot pass the same slot. */ +export function reserveQuota(db: DatabaseSync, input: { poolId: string; attemptId: string; now?: Date }): QuotaAdmission & { reservation?: QuotaReservation } { + ensureQuotaPoolSchema(db) + const now = input.now ?? new Date() + const nowIso = now.toISOString() + db.exec('BEGIN IMMEDIATE') + try { + const pool = getQuotaPool(db, input.poolId) + if (!pool) throw new AccessDenied(`quota pool is not configured: ${input.poolId}`) + const duplicate = db.prepare('SELECT * FROM quota_reservations WHERE attempt_id = ?').get(input.attemptId) as unknown as ReservationRow | undefined + if (duplicate) { + db.exec('COMMIT') + return { allowed: duplicate.settled_at === null, ...(duplicate.settled_at === null ? { reservation: reservationOf(duplicate) } : { reason: 'concurrency' as const }) } + } + if (pool.blockedUntil !== null && Date.parse(pool.blockedUntil) > now.getTime()) { + db.exec('COMMIT'); return { allowed: false, reason: 'cooldown', retryAt: pool.blockedUntil } + } + const active = Number((db.prepare('SELECT COUNT(*) AS n FROM quota_reservations WHERE pool_id = ? AND settled_at IS NULL') + .get(input.poolId) as { n: number }).n) + if (active >= pool.maxConcurrent) { db.exec('COMMIT'); return { allowed: false, reason: 'concurrency' } } + if (pool.maxRequestsPerMinute !== null) { + const recent = Number((db.prepare('SELECT COUNT(*) AS n FROM quota_reservations WHERE pool_id = ? AND reserved_at >= ?') + .get(input.poolId, new Date(now.getTime() - 60_000).toISOString()) as { n: number }).n) + if (recent >= pool.maxRequestsPerMinute) { db.exec('COMMIT'); return { allowed: false, reason: 'rpm' } } + } + const row: ReservationRow = { id: `qres-${randomUUID()}`, pool_id: input.poolId, attempt_id: input.attemptId, + reserved_at: nowIso, settled_at: null, usage: null, outcome: null } + db.prepare('INSERT INTO quota_reservations (id,pool_id,attempt_id,reserved_at) VALUES (?,?,?,?)') + .run(row.id, row.pool_id, row.attempt_id, row.reserved_at) + db.exec('COMMIT') + return { allowed: true, reservation: reservationOf(row) } + } catch (error) { db.exec('ROLLBACK'); throw error } +} + +interface ReservationRow { id: string; pool_id: string; attempt_id: string; reserved_at: string; settled_at: string | null; usage: number | null; outcome: string | null } +const reservationOf = (row: ReservationRow): QuotaReservation => ({ id: row.id, poolId: row.pool_id, + attemptId: row.attempt_id, reservedAt: row.reserved_at, settledAt: row.settled_at, usage: row.usage, outcome: row.outcome }) + +/** Usage is nullable: missing provider usage is unknown and is never recorded as zero. */ +export function settleQuota(db: DatabaseSync, input: { reservationId: string; outcome: string; usage?: number | null; now?: Date }): QuotaReservation { + ensureQuotaPoolSchema(db) + const usage = input.usage ?? null + if (usage !== null && (!Number.isFinite(usage) || usage < 0)) throw new AccessDenied('quota usage must be null or a non-negative number') + const row = db.prepare('SELECT * FROM quota_reservations WHERE id = ?').get(input.reservationId) as unknown as ReservationRow | undefined + if (!row) throw new AccessDenied('quota reservation not found') + if (row.settled_at === null) db.prepare('UPDATE quota_reservations SET settled_at = ?, usage = ?, outcome = ? WHERE id = ? AND settled_at IS NULL') + .run((input.now ?? new Date()).toISOString(), usage, input.outcome, row.id) + return reservationOf(db.prepare('SELECT * FROM quota_reservations WHERE id = ?').get(row.id) as unknown as ReservationRow) +} + +/** Provider 429 state is shared by every worker using this pool and always bounded. */ +export function noteQuotaRateLimit(db: DatabaseSync, poolId: string, retryAfterMs: number, options: { now?: Date; maxBackoffMs?: number } = {}): string { + ensureQuotaPoolSchema(db) + if (!Number.isFinite(retryAfterMs) || retryAfterMs < 0) throw new AccessDenied('retry delay must be a non-negative number') + const delay = Math.min(options.maxBackoffMs ?? 30 * 60_000, Math.max(1000, retryAfterMs)) + const until = new Date((options.now ?? new Date()).getTime() + delay).toISOString() + const result = db.prepare('UPDATE quota_pools SET blocked_until = ?, updated_at = ? WHERE id = ?').run(until, (options.now ?? new Date()).toISOString(), poolId) + if (Number(result.changes) !== 1) throw new AccessDenied(`quota pool is not configured: ${poolId}`) + return until +} + +export function noteQuotaAuthFailure(db: DatabaseSync, poolId: string, now = new Date()): void { + ensureQuotaPoolSchema(db) + const result = db.prepare('UPDATE quota_pools SET blocked_until = ?, updated_at = ? WHERE id = ?') + .run('9999-12-31T23:59:59.999Z', now.toISOString(), poolId) + if (Number(result.changes) !== 1) throw new AccessDenied(`quota pool is not configured: ${poolId}`) +} diff --git a/src/coord/reaper.ts b/src/coord/reaper.ts new file mode 100644 index 0000000..256b05a --- /dev/null +++ b/src/coord/reaper.ts @@ -0,0 +1,279 @@ +import { appendFileSync, existsSync, mkdirSync, readdirSync, realpathSync, type Dirent } from 'node:fs' +import { execFileSync } from 'node:child_process' +import { basename, dirname, join, relative, resolve } from 'node:path' +import type { DatabaseSync } from 'node:sqlite' +import { storeHome } from '../index/runs.ts' +import { ensureLeaseSchema } from './leases.ts' + +export interface ReapOptions { repo?: string; target?: string; apply?: boolean; now?: Date; gracePeriodMs?: number } +export interface ReapCandidate { + path: string; branch: string | null; commit: string | null; target: string | null + eligible: boolean; reason: string; restoreCommand?: string + error?: string +} +export interface ReapResult { + dryRun: boolean; candidates: ReapCandidate[]; removed: ReapCandidate[] + missing: Array<{ path: string; quarantinedAt: string | null }>; pruneCommand: string | null; pruneReport: string[] +} + +interface Worktree { path: string; head: string | null; branch: string | null; locked: boolean } + +/** Canonicalize a path using fs.realpathSync.native (resolves symlinks, junctions, 8.3 names). + * Non-existent paths fall back to resolve()+lowercase without throwing. */ +const normalize = (path: string): string => { + try { return realpathSync.native(path).replace(/[\\/]+$/, '').toLowerCase() } + catch { return resolve(path).replace(/[\\/]+$/, '').toLowerCase() } +} +const git = (cwd: string, ...args: string[]): string => execFileSync('git', ['-C', cwd, ...args], { + encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 15000, windowsHide: true, +}).trim() + +function listWorktrees(repo: string): Worktree[] { + const raw = git(repo, 'worktree', 'list', '--porcelain') + return raw.split(/\r?\n\r?\n/).filter(Boolean).map(block => { + const lines = block.split(/\r?\n/) + const field = (prefix: string): string | null => lines.find(line => line.startsWith(prefix))?.slice(prefix.length) ?? null + return { path: field('worktree ' ) ?? '', head: field('HEAD '), branch: field('branch ')?.replace(/^refs\/heads\//, '') ?? null, + locked: lines.some(line => line === 'locked' || line.startsWith('locked ')) } + }) +} + +function ensureReaperSchema(db: DatabaseSync): void { + const agentColumns = db.prepare('PRAGMA table_info(agents)').all() as unknown as Array<{ name: string }> + const hasAgent = (name: string) => agentColumns.some(column => column.name === name) + if (!hasAgent('workspace_id')) db.exec('ALTER TABLE agents ADD COLUMN workspace_id TEXT') + if (!hasAgent('turn_state')) db.exec('ALTER TABLE agents ADD COLUMN turn_state TEXT') + if (!hasAgent('turn_state_at')) db.exec('ALTER TABLE agents ADD COLUMN turn_state_at TEXT') + if (!hasAgent('worktrees')) db.exec('ALTER TABLE agents ADD COLUMN worktrees TEXT') + if (!hasAgent('retired_at')) db.exec('ALTER TABLE agents ADD COLUMN retired_at TEXT') + const columns = db.prepare('PRAGMA table_info(checkouts)').all() as unknown as Array<{ name: string }> + if (columns.length > 0 && !columns.some(column => column.name === 'disk_state')) { + db.exec("ALTER TABLE checkouts ADD COLUMN disk_state TEXT NOT NULL DEFAULT 'present'") + } + db.exec(`CREATE TABLE IF NOT EXISTS worktree_reap_receipts ( + id INTEGER PRIMARY KEY, workspace_id TEXT NOT NULL, path TEXT NOT NULL, branch TEXT NOT NULL, + commit_hash TEXT NOT NULL, target TEXT NOT NULL, removed_at TEXT NOT NULL, restore_command TEXT NOT NULL + )`) + db.exec(`CREATE TABLE IF NOT EXISTS reap_settings ( + workspace_id TEXT PRIMARY KEY, auto_enabled INTEGER NOT NULL DEFAULT 0, updated_at TEXT NOT NULL + )`) + ensureLeaseSchema(db) +} + +export function setReapAuto(db: DatabaseSync, workspaceId: string, enabled: boolean): void { + ensureReaperSchema(db) + db.prepare(`INSERT INTO reap_settings (workspace_id, auto_enabled, updated_at) VALUES (?, ?, ?) + ON CONFLICT(workspace_id) DO UPDATE SET auto_enabled = excluded.auto_enabled, updated_at = excluded.updated_at`) + .run(workspaceId, enabled ? 1 : 0, new Date().toISOString()) +} + +export function isReapAutoEnabled(db: DatabaseSync, workspaceId: string): boolean { + ensureReaperSchema(db) + return (db.prepare('SELECT auto_enabled FROM reap_settings WHERE workspace_id = ?').get(workspaceId) as { auto_enabled?: number } | undefined) + ?.auto_enabled === 1 +} + +export function runAutoReap(db: DatabaseSync, workspaceId: string): ReapResult | null { + if (!isReapAutoEnabled(db, workspaceId)) return null + // Grace period: protect worktrees of workers who ended their turn recently + return reapWorktrees(db, workspaceId, { apply: true, gracePeriodMs: 5 * 60_000 }) +} + +/** Report missing paths; an explicit apply updates disk state without retiring recoverable bindings. */ +export function synchronizeMissingWorktrees(db: DatabaseSync, workspaceId: string, apply = false): ReapResult['missing'] { + ensureReaperSchema(db) + const rows = db.prepare(`SELECT c.id, c.path FROM checkouts c JOIN planets p ON p.id = c.planet_id + WHERE p.workspace_id = ? AND c.retired_at IS NULL`).all(workspaceId) as unknown as Array<{ id: string; path: string }> + const missing: ReapResult['missing'] = [] + const quarantineMatches = new Map<string, string>() + const quarantineRoot = resolve(process.env.PLUGBRAIN_QUARANTINE_ROOT ?? 'C:/PLUG/_quarantaene') + if (process.platform === 'win32' && rows.some(row => !existsSync(row.path)) && existsSync(quarantineRoot)) { + try { + const roots = execFileSync('powershell', ['-NoProfile', '-Command', + `Get-ChildItem -LiteralPath '${quarantineRoot.replace(/'/g, "''")}' -Directory -Recurse -ErrorAction SilentlyContinue | ForEach-Object { $_.FullName }`], + { encoding: 'utf8', timeout: 5000, windowsHide: true }).split(/\r?\n/) + for (const path of roots) { + const leaf = basename(path).toLowerCase() + if (path && !quarantineMatches.has(leaf)) quarantineMatches.set(leaf, path) + } + } catch { /* quarantine lookup is informational only */ } + } + for (const row of rows) { + if (existsSync(row.path)) { + if (apply) db.prepare("UPDATE checkouts SET disk_state = 'present' WHERE id = ?").run(row.id) + continue + } + const quarantinedAt = quarantineMatches.get(basename(row.path).toLowerCase()) ?? null + if (apply) db.prepare("UPDATE checkouts SET disk_state = 'fehlt' WHERE id = ?").run(row.id) + missing.push({ path: row.path, quarantinedAt }) + } + const bindings = db.prepare('SELECT id, worktrees FROM agents WHERE workspace_id = ? AND retired_at IS NULL') + .all(workspaceId) as unknown as Array<{ id: string; worktrees: string | null }> + for (const agent of bindings) { + let paths: string[] = [] + try { paths = JSON.parse(agent.worktrees ?? '[]') as string[] } catch { continue } + for (const path of paths) { + if (existsSync(path)) continue + if (!missing.some(row => normalize(row.path) === normalize(path))) missing.push({ path, quarantinedAt: null }) + } + } + return missing +} + +function workspaceRoot(db: DatabaseSync, workspaceId: string): string { + const row = db.prepare('SELECT root FROM workspaces WHERE id = ?').get(workspaceId) as { root: string } | undefined + if (!row) throw new Error(`unknown workspace: ${workspaceId}`) + return row.root +} + +function repositories(root: string, requested?: string): string[] { + if (requested) return [resolve(requested)] + const found: string[] = [] + const stack = [resolve(root)] + while (stack.length > 0) { + const dir = stack.pop()! + let entries: Dirent[] + try { entries = readdirSync(dir, { withFileTypes: true }) } catch { continue } + if (entries.some(entry => entry.name === '.git')) { found.push(dir); continue } + for (const entry of entries) { + if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name.toLowerCase() !== 'node_modules') stack.push(join(dir, entry.name)) + } + } + return found +} + +function safetyReason(db: DatabaseSync, workspaceId: string, root: string, candidate: Worktree, primary: Worktree, target: string, gracePeriodMs?: number): string | null { + if (normalize(candidate.path) === normalize(primary.path)) return 'primary checkout' + if (candidate.locked) return 'worktree locked' + if (!candidate.branch || !candidate.head) return 'detached branch' + try { git(candidate.path, 'merge-base', '--is-ancestor', candidate.head, target) } + catch { return `not merged into ${target}` } + const status = git(candidate.path, 'status', '--porcelain') + if (status !== '') { + const lines = status.split(/\r?\n/).filter(Boolean) + const hasUntracked = lines.some(line => line.startsWith('??')) + return hasUntracked ? `untracked: ${lines.filter(line => line.startsWith('??')).length} file(s)` + : `uncommitted: ${lines.length} file(s)` + } + const profiles = db.prepare(`SELECT id, turn_state, turn_state_at, worktrees FROM agents + WHERE workspace_id = ? AND retired_at IS NULL`).all(workspaceId) as unknown as Array<{ id: string; turn_state: string | null; turn_state_at: string | null; worktrees: string | null }> + const bound = profiles.filter(profile => { + try { return (JSON.parse(profile.worktrees ?? '[]') as string[]).some(path => normalize(path) === normalize(candidate.path)) } + catch { return false } + }) + const activeLeases = db.prepare(`SELECT agent_id FROM leases WHERE workspace_id = ? AND released_at IS NULL AND expires_at > ?`) + .all(workspaceId, new Date().toISOString()) as unknown as Array<{ agent_id: string }> + if (bound.some(profile => profile.turn_state === 'working')) return 'running turn bound to worktree' + if (bound.some(profile => activeLeases.some(lease => lease.agent_id === profile.id))) return 'active lease bound to worktree' + // Grace period: protect worktrees of workers who recently ended their turn + if (gracePeriodMs !== undefined && gracePeriodMs > 0) { + const graceCutoff = Date.now() - gracePeriodMs + for (const profile of bound) { + if (profile.turn_state_at) { + const turnEndTime = Date.parse(profile.turn_state_at) + if (!Number.isNaN(turnEndTime) && turnEndTime >= graceCutoff) { + return 'worker turn ended recently (grace period)' + } + } + } + } + const leasePaths = db.prepare(`SELECT paths_json FROM leases WHERE workspace_id = ? AND released_at IS NULL AND expires_at > ?`) + .all(workspaceId, new Date().toISOString()) as unknown as Array<{ paths_json: string }> + const prefix = relative(root, candidate.path).replace(/\\/g, '/').replace(/\/$/, '').toLowerCase() + for (const row of leasePaths) { + try { + const paths = JSON.parse(row.paths_json) as string[] + if (paths.some(path => { + const value = path.replace(/\\/g, '/').replace(/^\.\//, '').toLowerCase() + return value === prefix || value.startsWith(prefix + '/') + })) return 'active lease on worktree paths' + } catch { /* malformed lease data cannot be mapped to this worktree */ } + } + return null +} + +function detachReapedPath(db: DatabaseSync, workspaceId: string, path: string): void { + db.prepare("UPDATE checkouts SET disk_state = 'reaped', retired_at = ? WHERE path = ?") + .run(new Date().toISOString(), path) + const agents = db.prepare('SELECT id, worktrees FROM agents WHERE workspace_id = ? AND retired_at IS NULL') + .all(workspaceId) as unknown as Array<{ id: string; worktrees: string | null }> + for (const agent of agents) { + try { + const paths = JSON.parse(agent.worktrees ?? '[]') as string[] + const kept = paths.filter(item => normalize(item) !== normalize(path)) + if (kept.length !== paths.length) db.prepare('UPDATE agents SET worktrees = ? WHERE id = ?').run(JSON.stringify(kept), agent.id) + } catch { /* malformed bindings are retained for manual repair */ } + } +} + +/** Shared plan/apply core. Destructive git removal is always without --force. */ +export function reapWorktrees(db: DatabaseSync, workspaceId: string, options: ReapOptions = {}): ReapResult { + ensureReaperSchema(db) + const missing = synchronizeMissingWorktrees(db, workspaceId, options.apply === true) + const wsRoot = workspaceRoot(db, workspaceId) + const codeRoot = join(wsRoot, 'Code') + const roots = repositories(existsSync(codeRoot) ? codeRoot : wsRoot, options.repo) + const candidates: ReapCandidate[] = [] + const removed: ReapCandidate[] = [] + const at = (options.now ?? new Date()).toISOString() + for (const repo of roots) { + const trees = listWorktrees(repo) + const primary = trees[0] + if (!primary) continue + const target = options.target ?? primary.branch + if (!target) { + // No valid target (primary is detached and no --target given): mark all as ineligible + for (const tree of trees) { + candidates.push({ + path: tree.path, + branch: tree.branch, + commit: tree.head, + target: null, + eligible: false, + reason: 'no merge target (primary checkout detached; use --target)', + }) + } + continue + } + for (const tree of trees) { + const reason = safetyReason(db, workspaceId, wsRoot, tree, primary, target, options.gracePeriodMs) + const item: ReapCandidate = { + path: tree.path, branch: tree.branch, commit: tree.head, target, + eligible: reason === null, reason: reason ?? 'reapable', + } + if (reason === null) item.restoreCommand = `git worktree add "${tree.path}" "${tree.branch}"` + candidates.push(item) + if (reason !== null || options.apply !== true) continue + try { + git(repo, 'worktree', 'remove', tree.path) + } catch (error: unknown) { + const detail = error instanceof Error ? error.message : String(error) + item.eligible = false + item.reason = `git worktree remove failed: ${detail}` + item.error = detail + continue + } + detachReapedPath(db, workspaceId, tree.path) + const receipt = { path: tree.path, branch: tree.branch!, commit: tree.head!, target, removedAt: at, + restoreCommand: item.restoreCommand! } + try { + db.prepare(`INSERT INTO worktree_reap_receipts (workspace_id, path, branch, commit_hash, target, removed_at, restore_command) + VALUES (?, ?, ?, ?, ?, ?, ?)`).run(workspaceId, receipt.path, receipt.branch, receipt.commit, receipt.target, at, receipt.restoreCommand) + const file = join(storeHome(), 'worktree-reap-receipts.jsonl') + mkdirSync(dirname(file), { recursive: true }) + appendFileSync(file, `${JSON.stringify(receipt)}\n`, 'utf8') + } catch (error: unknown) { + const detail = error instanceof Error ? error.message : String(error) + item.error = (item.error ? item.error + '; ' : '') + `receipt write failed: ${detail}` + } + removed.push(item) + } + } + const pruneCommand = roots.length === 0 ? null : `git -C "${roots[0]}" worktree prune --dry-run` + let pruneReport: string[] = [] + if (roots.length > 0) { + try { pruneReport = git(roots[0]!, 'worktree', 'prune', '--dry-run', '--verbose').split(/\r?\n/).filter(Boolean) } + catch { pruneReport = ['prune dry-run unavailable'] } + } + return { dryRun: options.apply !== true, candidates, removed, missing, pruneCommand, pruneReport } +} diff --git a/src/coord/resources.ts b/src/coord/resources.ts index 40da0bd..72a8d60 100644 --- a/src/coord/resources.ts +++ b/src/coord/resources.ts @@ -144,6 +144,14 @@ export function assertNotCredential(field: string, value: string): void { } } +/** Keep historic or imported values safe when they are shown back to a worker. */ +export function redactCredentialText(value: string): string { + return CREDENTIAL_PATTERNS.reduce((safe, pattern) => { + const flags = pattern.flags.includes('g') ? pattern.flags : `${pattern.flags}g` + return safe.replace(new RegExp(pattern.source, flags), '[REDACTED]') + }, value) +} + export type QuotaUnit = 'percent' | 'credits' | 'requests' | 'rpm' | 'tokens' const QUOTA_UNITS = new Set<QuotaUnit>(['percent', 'credits', 'requests', 'rpm', 'tokens']) diff --git a/src/coord/runner-prompt.ts b/src/coord/runner-prompt.ts new file mode 100644 index 0000000..8327b43 --- /dev/null +++ b/src/coord/runner-prompt.ts @@ -0,0 +1,34 @@ +/** + * The prompt a CLI worker receives when the Brain starts it itself. + * + * It lives in TypeScript rather than in a `runner-prompt.md` beside this file + * on purpose. The standalone build bundles the TypeScript sources and copies + * only `ui-dist`, so a markdown asset would resolve in a checkout and vanish in + * the installed product — the one place `swarm run` has to work. As a string it + * travels with the bundle for free. + * + * It says the same as the hand-written `codex-prompt.tpl` the integrator drove + * three Codex tabs with on 26.09., with every machine-specific path replaced by + * a placeholder. `runner.ts` fills them from the workspace configuration — the + * workspace root and the coordination folder under it — so no owner path is + * baked into the public repository. + * + * Placeholders: `{{agentId}}`, `{{workspaceRoot}}`, `{{plugbrain}}`, + * `{{protocolDocs}}`, `{{claimFlag}}`, `{{taskContext}}`. + */ +export const RUNNER_PROMPT_TEMPLATE = `You are worker {{agentId}} in the PlugBrain fleet. Working directory: {{workspaceRoot}}. + +1. Check in: {{plugbrain}} swarm turn {{agentId}} start {{claimFlag}} + The output names your task and the path to its brief. Read every NACHRICHT printed with it and + acknowledge it: {{plugbrain}} swarm ack {{agentId}} <messageId>. +2. Read in full: {{protocolDocs}}. Then work through your brief completely, milestone by milestone. +3. Keep to the brain protocol: claim every path before you write it, admit test|build|install|worktree + before you run it, end your turn exactly once with a state and a summary, release your claims, and + send the integrator a message ({{plugbrain}} swarm send integrator --from {{agentId}} --subject ... + --body ...). +4. Hard limits: no keys or tokens in commands, files or text. Do not delete or move anything unless + your brief explicitly allows it. No push. Never write outside your own worktree. Do not commit + without the message "Commit freigegeben". +5. Your last answer: the verdict in one sentence, then the result files, test counts and exit codes. +{{taskContext}} +` diff --git a/src/coord/runner.ts b/src/coord/runner.ts new file mode 100644 index 0000000..a73b251 --- /dev/null +++ b/src/coord/runner.ts @@ -0,0 +1,1064 @@ +/** + * CLI worker runners — the Brain starts and watches a worker process itself. + * + * Until now the integrator started the three Codex tabs by hand: one prompt + * template, `gpt-6-luna`, a JSONL log each, and `codexstatus.sh` to read the + * log back. A lane hosted that way costs the integrator handwork at every turn + * boundary, and the fleet has no independent way to say whether the process is + * still alive. This module closes that loop: a runner profile per worker in the + * store, `swarm run <agent>` to start it detached, and the board showing pid, + * start time and the last log event. + * + * Three rules shape the code: + * + * 1. THE BRAIN NEVER GUESSES LIVENESS. `process.kill(pid, 0)` is the only + * answer to "is it still there". Nothing is inferred from a quiet log: a + * worker reasoning for ten minutes writes nothing and is not dead. + * 2. THE PROCESS, NOT THE PROMPT, DECIDES THE TURN. A run that disappears + * while the worker has not ended its turn is that worker's problem, and + * the Brain books it as `blocked` with the log tail — it does not invent + * a `needs-task` for a worker that never came back. + * 3. NO SECRETS IN THE STORE. A profile holds a program, a model and flags. + * Codex brings its own login, and BYOK keys stay out of the database. + */ +import { execFileSync, spawn } from 'node:child_process' +import { closeSync, existsSync, mkdirSync, openSync, readdirSync, readSync, realpathSync, statSync, writeFileSync } from 'node:fs' +import { basename, delimiter, isAbsolute, join, relative, resolve } from 'node:path' +import type { DatabaseSync } from 'node:sqlite' +import { AccessDenied, requireAgent, requireWorkspace } from '../access.ts' +import { resolveBrainHome } from '../home.ts' +import { assertNotCredential } from './resources.ts' +import { ensureIntegrator, sendMessage } from './inbox.ts' +import { RUNNER_PROMPT_TEMPLATE } from './runner-prompt.ts' + +/** The one worker the Brain reports a broken run to. */ +const INTEGRATOR = 'integrator' +/** A turn that ended within this window of the run start still counts as this run's. */ +const TURN_END_SLACK_MS = 1000 +/** An argv that cannot fit comfortably in a Windows command line is a mistaken prompt. */ +const MAX_PROMPT_CHARS = 24_000 +/** How much of a log is read to find its last event, and how much is quoted into a summary. */ +const LOG_TAIL_BYTES = 64 * 1024 +const SUMMARY_LINES = 8 +const EVENT_TEXT_CHARS = 200 + +// --------------------------------------------------------------------------- +// Schema +// --------------------------------------------------------------------------- + +/** + * One profile per worker and at most one run per worker. History lives in the + * log files: the row is the live handle the board reads, not an archive. + */ +export function ensureRunnerSchema(db: DatabaseSync): void { + db.exec(` +CREATE TABLE IF NOT EXISTS worker_runners ( + agent_id TEXT PRIMARY KEY, + cmd TEXT NOT NULL, + args TEXT NOT NULL DEFAULT '[]', + model TEXT, + effort TEXT, + sandbox TEXT, + search INTEGER NOT NULL DEFAULT 0, + cwd TEXT, + updated_at TEXT NOT NULL +); + +CREATE TABLE IF NOT EXISTS worker_runs ( + agent_id TEXT PRIMARY KEY, + run_id TEXT NOT NULL, + pid INTEGER, + started_at TEXT NOT NULL, + cwd TEXT NOT NULL, + log_path TEXT NOT NULL, + last_doc_path TEXT, + prompt_path TEXT, + command_text TEXT NOT NULL, + via_comspec INTEGER NOT NULL DEFAULT 0, + log_mtime_ms INTEGER, + log_size INTEGER, + last_event_at TEXT, + last_event_kind TEXT, + last_event_text TEXT, + ended_at TEXT, + ended_reason TEXT, + stopped_at TEXT, + blocked_at TEXT +); +`) + const columns = db.prepare('PRAGMA table_info(worker_runs)').all() as unknown as Array<{ name: string }> + if (!columns.some(column => column.name === 'process_started_at')) { + db.exec('ALTER TABLE worker_runs ADD COLUMN process_started_at TEXT') + } + if (!columns.some(column => column.name === 'task_id')) db.exec('ALTER TABLE worker_runs ADD COLUMN task_id TEXT') + if (!columns.some(column => column.name === 'attempt_token')) db.exec('ALTER TABLE worker_runs ADD COLUMN attempt_token TEXT') +} + +// --------------------------------------------------------------------------- +// Runner profile +// --------------------------------------------------------------------------- + +export type RunnerSandbox = 'bypass' | 'workspace-write' | 'read-only' | 'danger-full-access' +export const RUNNER_SANDBOXES: readonly RunnerSandbox[] = ['bypass', 'workspace-write', 'read-only', 'danger-full-access'] +/** Programs the fleet may launch. `node` is retained for the documented fake-worker harness. */ +export const RUNNER_COMMANDS = ['codex', 'claude', 'gemini', 'node'] as const + +export interface RunnerProfile { + agentId: string + /** The program to start, e.g. `codex` or `node`. */ + cmd: string + /** Argument template for a non-Codex command; `{prompt}` and `{log}` are filled in. */ + args: string[] + model: string | null + effort: string | null + sandbox: RunnerSandbox | null + search: boolean + cwd: string | null + updatedAt: string +} + +interface ProfileDbRow { + agent_id: string + cmd: string + args: string + model: string | null + effort: string | null + sandbox: string | null + search: number + cwd: string | null + updated_at: string +} + +const parseJsonArray = (raw: string): string[] => { + try { + const value = JSON.parse(raw) as unknown + return Array.isArray(value) ? value.filter((item): item is string => typeof item === 'string') : [] + } catch { return [] } +} + +const profileOf = (row: ProfileDbRow): RunnerProfile => ({ + agentId: row.agent_id, + cmd: row.cmd, + args: parseJsonArray(row.args), + model: row.model, + effort: row.effort, + sandbox: row.sandbox as RunnerSandbox | null, + search: row.search !== 0, + cwd: row.cwd, + updatedAt: row.updated_at, +}) + +export interface SetRunnerInput { + agentId: string + cmd: string + args?: string[] + model?: string + effort?: string + sandbox?: string + search?: boolean + /** Working directory for the process; defaults to the workspace root. */ + cwd?: string +} + +const shortField = (name: string, value: string | undefined, max: number): string | null => { + if (value === undefined) return null + const trimmed = value.trim() + if (trimmed === '') return null + if (trimmed.length > max) throw new AccessDenied(`${name} needs at most ${max} characters`) + assertNotCredential(name, trimmed) + return trimmed +} + +/** Store the runner profile for a worker. Replaces the previous one. */ +export function setRunnerProfile(db: DatabaseSync, input: SetRunnerInput): RunnerProfile { + ensureRunnerSchema(db) + requireAgent(db, input.agentId) + const cmd = shortField('cmd', input.cmd, 200) + if (cmd === null) throw new AccessDenied('a runner profile needs a program: --cmd <exe>') + assertAllowedCommand(cmd) + const sandbox = shortField('sandbox', input.sandbox, 40) + if (sandbox !== null && !RUNNER_SANDBOXES.includes(sandbox as RunnerSandbox)) { + throw new AccessDenied(`unknown sandbox: ${sandbox} (${RUNNER_SANDBOXES.join(', ')})`) + } + const args = (input.args ?? []).map(token => { + const trimmed = token.trim() + if (trimmed.length > 2000) throw new AccessDenied('runner args need at most 2000 characters per token') + assertNotCredential('args', trimmed) + return trimmed + }).filter(token => token !== '') + const cwd = input.cwd === undefined ? null : resolve(input.cwd) + if (cwd !== null) { + const agent = db.prepare('SELECT workspace_id, worktrees FROM agents WHERE id = ?').get(input.agentId) as + { workspace_id: string; worktrees: string | null } | undefined + if (agent === undefined) throw new AccessDenied(`unknown agent: ${input.agentId}`) + validateRunnerCwd(db, agent.workspace_id, agent.worktrees, cwd) + } + const now = new Date().toISOString() + + db.prepare(` + INSERT INTO worker_runners (agent_id, cmd, args, model, effort, sandbox, search, cwd, updated_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) + ON CONFLICT(agent_id) DO UPDATE SET + cmd = excluded.cmd, args = excluded.args, model = excluded.model, effort = excluded.effort, + sandbox = excluded.sandbox, search = excluded.search, cwd = excluded.cwd, + updated_at = excluded.updated_at + `).run( + input.agentId, cmd, JSON.stringify(args), + shortField('model', input.model, 120), shortField('effort', input.effort, 40), sandbox, + input.search === true ? 1 : 0, + cwd, + now, + ) + return getRunnerProfile(db, input.agentId)! +} + +function assertAllowedCommand(cmd: string): void { + if (/\.(?:cmd|bat)$/i.test(cmd)) throw new AccessDenied(`batch shims are not supported by swarm run: ${cmd}`) + if (cmd.includes('/') || cmd.includes('\\') || cmd.includes(':')) { + throw new AccessDenied('runner command must be a known executable name from PATH, not a file path') + } + const name = basename(cmd).replace(/\.(?:exe|com|cmd|bat)$/i, '').toLowerCase() + if (!RUNNER_COMMANDS.includes(name as typeof RUNNER_COMMANDS[number])) { + throw new AccessDenied(`not an allowed worker command: ${cmd} (allowed: ${RUNNER_COMMANDS.join(', ')})`) + } +} + +function within(root: string, path: string): boolean { + const rel = relative(root, path) + return rel === '' || (!rel.startsWith(`..${process.platform === 'win32' ? '\\' : '/'}`) && rel !== '..' && !isAbsolute(rel)) +} + +function validateRunnerCwd(db: DatabaseSync, workspaceId: string, worktreesJson: string | null, candidate: string): string { + const workspace = requireWorkspace(db, workspaceId) + let actual: string + try { actual = realpathSync(candidate) } catch { throw new AccessDenied(`runner cwd does not exist: ${candidate}`) } + let registered: string[] = [workspace.root] + try { + const values = worktreesJson === null ? [] : JSON.parse(worktreesJson) as unknown + if (Array.isArray(values)) registered.push(...values.filter((item): item is string => typeof item === 'string')) + } catch { /* malformed legacy metadata grants no extra roots */ } + const accepted = registered.some(root => { + try { return within(realpathSync(root), actual) } catch { return false } + }) + if (!accepted) throw new AccessDenied(`runner cwd is outside registered workspace/worktrees: ${candidate}`) + return actual +} + +export function getRunnerProfile(db: DatabaseSync, agentId: string): RunnerProfile | null { + ensureRunnerSchema(db) + const row = db.prepare('SELECT * FROM worker_runners WHERE agent_id = ?').get(agentId) as unknown as ProfileDbRow | undefined + return row === undefined ? null : profileOf(row) +} + +export function listRunnerProfiles(db: DatabaseSync): RunnerProfile[] { + ensureRunnerSchema(db) + const rows = db.prepare('SELECT * FROM worker_runners ORDER BY agent_id').all() as unknown as ProfileDbRow[] + return rows.map(profileOf) +} + +// --------------------------------------------------------------------------- +// Prompt and command line +// --------------------------------------------------------------------------- + +export interface ProtocolDocs { + /** `<workspaceRoot>/koordination`, when it exists. */ + coordinationDir: string | null + protocolPath: string | null + /** The newest `closeout/<lane>/briefs/_REGELN.md`, when there is one. */ + rulesPath: string | null +} + +/** + * Rules that live beside the brief, newest lane first. + * + * Lane folders carry the date of their closeout somewhere in the name — at the + * end (`brain-standalone-20260926`) as often as at the start (`r3-finish-20260922`) + * — so the date is compared, not the path. Sorting the whole paths picked the + * older lane whenever its name happened to sort later, which is how the owner's + * fleet got `r3-finish-20260922` rules into a 26.09. prompt. Guessing wrong here + * is visible rather than harmful: the prompt names the file it read. + */ +function newestRulesFile(closeoutDir: string): string | null { + if (!existsSync(closeoutDir)) return null + const found: Array<{ lane: string; path: string; date: string }> = [] + for (const lane of readdirSync(closeoutDir, { withFileTypes: true })) { + if (!lane.isDirectory()) continue + const candidate = join(closeoutDir, lane.name, 'briefs', '_REGELN.md') + if (existsSync(candidate)) found.push({ lane: lane.name, path: candidate, date: /\d{8}/.exec(lane.name)?.[0] ?? '' }) + } + found.sort((left, right) => left.date === right.date + ? left.lane.localeCompare(right.lane) + : left.date.localeCompare(right.date)) + return found.at(-1)?.path ?? null +} + +export function discoverProtocolDocs(workspaceRoot: string): ProtocolDocs { + const coordinationDir = join(workspaceRoot, 'koordination') + const protocolPath = join(coordinationDir, 'BRAIN-PROTOKOLL.md') + return { + coordinationDir: existsSync(coordinationDir) ? coordinationDir : null, + protocolPath: existsSync(protocolPath) ? protocolPath : null, + rulesPath: newestRulesFile(join(coordinationDir, 'closeout')), + } +} + +/** The Brain's own wrapper under the coordination folder, or the bare command. */ +function brainCommand(workspaceRoot: string): string { + const bin = join(workspaceRoot, 'koordination', 'bin') + for (const name of ['plugbrain', 'plugbrain.cmd']) { + const candidate = join(bin, name) + if (existsSync(candidate)) return candidate + } + return 'plugbrain' +} + +/** `a, b and c` — a prompt sentence must read as a sentence when a doc is missing. */ +function joinClause(parts: string[]): string { + if (parts.length === 1) return parts[0]! + return `${parts.slice(0, -1).join(', ')} and ${parts.at(-1)!}` +} + +export interface PromptInput { + agentId: string + workspaceRoot: string + docs: ProtocolDocs + task?: { id: string; title: string; body: string } +} + +/** Fill the template. The brief is named by the task, so the prompt cannot point at one. */ +export function renderRunnerPrompt(input: PromptInput): string { + const named = ['the brief named by your task'] + if (input.docs.protocolPath !== null) named.push(input.docs.protocolPath) + if (input.docs.rulesPath !== null) named.push(input.docs.rulesPath) + const values: Record<string, string> = { + agentId: input.agentId, + workspaceRoot: input.workspaceRoot, + plugbrain: brainCommand(input.workspaceRoot), + protocolDocs: joinClause(named), + claimFlag: input.task === undefined ? '--claim' : '', + taskContext: input.task === undefined ? '' : + `\nAssigned queue task (already atomically claimed): ${input.task.id}\nTitle: ${input.task.title}\nBrief/body:\n${input.task.body}\n` + + `Do not claim a second task. Finish this task, deliver it, and end the turn once.`, + } + return RUNNER_PROMPT_TEMPLATE.replace(/\{\{(\w+)\}\}/g, (whole, key: string) => values[key] ?? whole) +} + +export interface ArgvContext { + prompt: string + promptPath: string + logPath: string + lastDocPath: string + cwd: string +} + +export interface RunnerArgv { + file: string + args: string[] + /** True when the command is a Windows batch shim and has to go through `cmd.exe`. */ + viaComspec: boolean + /** What the board shows. The prompt is a file path here, never the prompt text. */ + commandText: string +} + +/** + * `codex exec` for a Codex tab. The flag set matches codex-cli 0.156.1. + * + * `--search` is a flag of the INTERACTIVE Codex CLI; `codex exec` does not + * accept it, so live web search goes in as the documented config override + * instead of a flag that would make the process die on startup. + */ +function codexArgs(profile: RunnerProfile, context: ArgvContext): string[] { + const args = ['exec'] + if (profile.model !== null) args.push('-m', profile.model) + if (profile.effort !== null) args.push('-c', `model_reasoning_effort=${profile.effort}`) + if (profile.sandbox === 'bypass') args.push('--dangerously-bypass-approvals-and-sandbox') + else if (profile.sandbox !== null) args.push('--sandbox', profile.sandbox) + if (profile.search) args.push('-c', 'tools.web_search=true') + args.push('--json', '-o', context.lastDocPath, context.prompt) + return args +} + +/** + * A `{placeholder}` template, for every CLI that is not Codex. A token that is + * empty after substitution is dropped, so an unset `{model}` does not leave a + * bare flag behind; write `--model={model}` to keep a flag and its value in one + * token. + */ +function templateArgs(profile: RunnerProfile, context: ArgvContext): string[] { + const values: Record<string, string> = { + prompt: context.prompt, + promptFile: context.promptPath, + log: context.logPath, + last: context.lastDocPath, + cwd: context.cwd, + model: profile.model ?? '', + effort: profile.effort ?? '', + sandbox: profile.sandbox ?? '', + } + return profile.args + .map(token => token.replace(/\{(\w+)\}/g, (whole, key: string) => values[key] ?? whole)) + .filter(token => token !== '') +} + +/** + * PATH + PATHEXT resolution, done here rather than by the shell. + * + * `spawn('codex')` cannot start the `codex.cmd` that an npm install leaves + * behind, and a shell would put a quoting layer between the prompt and the + * program. Resolving the executable up front also turns "no such program" into + * a refusal before a run row exists, instead of a process that dies silently. + */ +export function resolveExecutable(cmd: string): string { + if (cmd.includes('/') || cmd.includes('\\')) return cmd + const path = process.env.PATH ?? process.env.Path ?? '' + const exts = process.platform === 'win32' + ? ['', ...(process.env.PATHEXT ?? '.COM;.EXE;.BAT;.CMD').split(';').filter(ext => ext !== '')] + : [''] + for (const dir of path.split(delimiter)) { + if (dir === '') continue + for (const ext of exts) { + const candidate = join(dir, `${cmd}${ext}`) + if (existsSync(candidate)) return candidate + } + } + return cmd +} + +/** Every token quoted, `%` doubled: `cmd.exe` expands `%VAR%` even inside quotes. */ +export function buildRunnerArgv(profile: RunnerProfile, context: ArgvContext): RunnerArgv { + assertAllowedCommand(profile.cmd) + const args = basename(profile.cmd).replace(/\.exe$/i, '').toLowerCase() === 'codex' + ? codexArgs(profile, context) : templateArgs(profile, context) + const file = resolveExecutable(profile.cmd) + if (/\.(cmd|bat)$/i.test(file)) throw new AccessDenied(`batch shims are not supported by swarm run: ${file}`) + const plan = { file, args } + const shown = plan.args.map(arg => (arg === context.prompt ? context.promptPath : arg)) + return { ...plan, viaComspec: false, commandText: [plan.file, ...shown].join(' ') } +} + +// --------------------------------------------------------------------------- +// Log reading +// --------------------------------------------------------------------------- + +/** Read at most the last `maxBytes` of a file, dropping the partial first line. */ +function readTail(path: string, maxBytes: number): { text: string; mtimeMs: number; size: number } | null { + let size: number + let mtimeMs: number + try { + const stats = statSync(path) + size = stats.size + mtimeMs = stats.mtimeMs + } catch { return null } + if (size === 0) return { text: '', mtimeMs, size } + const start = Math.max(0, size - maxBytes) + const length = size - start + const buffer = Buffer.alloc(length) + let fd: number + try { fd = openSync(path, 'r') } catch { return null } + try { + readSync(fd, buffer, 0, length, start) + } finally { closeSync(fd) } + const raw = buffer.toString('utf8') + const text = start === 0 ? raw : raw.slice(Math.max(0, raw.indexOf('\n') + 1)) + return { text, mtimeMs, size } +} + +export interface LogEvent { + at: string | null + kind: string + text: string +} + +const text = (value: unknown): string | null => (typeof value === 'string' && value.trim() !== '' ? value : null) +const collapse = (value: string, max = EVENT_TEXT_CHARS): string => { + const one = value.replace(/\s+/g, ' ').trim() + return one.length > max ? `${one.slice(0, max - 1)}…` : one +} + +/** + * One JSONL line as the board should show it: what kind of event, and enough + * text to recognise it. Codex wraps most events in `item`, so both levels are + * read; an unparsable line is still shown rather than dropped. + */ +export function summarizeLogEvent(line: string): LogEvent | null { + const trimmed = line.trim() + if (trimmed === '') return null + let parsed: unknown + try { parsed = JSON.parse(trimmed) } catch { return { at: null, kind: 'raw', text: collapse(trimmed) } } + if (parsed === null || typeof parsed !== 'object') return { at: null, kind: 'raw', text: collapse(trimmed) } + const event = parsed as Record<string, unknown> + const item = (event.item !== null && typeof event.item === 'object' ? event.item : {}) as Record<string, unknown> + const kind = text(item.type) ?? text(event.type) ?? 'event' + const at = text(event.timestamp) ?? text(event.ts) ?? text(event.time) ?? text(item.timestamp) + + let body: string | null = null + if (kind === 'command_execution') { + const command = text(item.command) ?? '' + body = item.exit_code === undefined || item.exit_code === null + ? `CMD ${command}` + : `CMD ${command} -> exit ${String(item.exit_code)}` + } else if (kind === 'agent_message') { + body = text(item.text) ?? text(event.text) + } else if (kind === 'file_change') { + body = `FILE ${JSON.stringify(item.changes ?? item)}` + } else if (kind === 'error') { + body = text(item.message) ?? text(event.message) + } else if (kind === 'turn.completed' || kind === 'turn_completed') { + body = `DONE usage ${JSON.stringify(event.usage ?? {})}` + } else if (kind === 'plugbrain.run.spawn_error') { + body = text(event.message) + } + return { at, kind, text: collapse(body ?? text(event.message) ?? text(event.text) ?? trimmed) } +} + +/** The last events and a readable tail of the log, for `--status` and for a blocked summary. */ +export function tailOfLog(path: string, lines = SUMMARY_LINES): string[] { + const tail = readTail(path, LOG_TAIL_BYTES) + if (tail === null) return [] + return tail.text.split('\n').map(line => line.trim()).filter(line => line !== '').slice(-lines) + .map(line => collapse(line, 300)) +} + +// --------------------------------------------------------------------------- +// Starting a run +// --------------------------------------------------------------------------- + +export interface WorkerRun { + agentId: string + runId: string + pid: number | null + startedAt: string + cwd: string + logPath: string + lastDocPath: string | null + promptPath: string | null + commandText: string + lastEventAt: string | null + lastEventKind: string | null + lastEventText: string | null + endedAt: string | null + endedReason: string | null + stoppedAt: string | null + /** + * Observed from the process, never inferred from the log — and latched: once + * the run is settled this stays false, whatever a recycled pid claims. + */ + alive: boolean +} + +interface RunDbRow { + agent_id: string + run_id: string + pid: number | null + process_started_at: string | null + started_at: string + cwd: string + log_path: string + last_doc_path: string | null + prompt_path: string | null + command_text: string + via_comspec: number + log_mtime_ms: number | null + log_size: number | null + last_event_at: string | null + last_event_kind: string | null + last_event_text: string | null + ended_at: string | null + ended_reason: string | null + stopped_at: string | null + blocked_at: string | null +} + +const runOf = (row: RunDbRow): WorkerRun => ({ + agentId: row.agent_id, + runId: row.run_id, + pid: row.pid, + startedAt: row.started_at, + cwd: row.cwd, + logPath: row.log_path, + lastDocPath: row.last_doc_path, + promptPath: row.prompt_path, + commandText: row.command_text, + lastEventAt: row.last_event_at, + lastEventKind: row.last_event_kind, + lastEventText: row.last_event_text, + endedAt: row.ended_at, + endedReason: row.ended_reason, + stoppedAt: row.stopped_at, + alive: runAlive(row), +}) + +const loadRunRow = (db: DatabaseSync, agentId: string): RunDbRow | null => + (db.prepare('SELECT * FROM worker_runs WHERE agent_id = ?').get(agentId) as unknown as RunDbRow | undefined) ?? null + +/** + * Is the process there? Signal 0 asks the operating system and nothing else. + * A PID can be reused after a reboot; the start time in the run row is what + * keeps that from being mistaken for a live worker. + */ +function getProcessStartTime(pid: number): string | null { + if (!Number.isInteger(pid) || pid <= 0 || process.platform !== 'win32') return null + try { + const command = `$p = Get-Process -Id ${pid} -ErrorAction SilentlyContinue; if ($p) { $p.StartTime.ToUniversalTime().ToString('o') }` + const value = execFileSync('powershell.exe', ['-NoLogo', '-NoProfile', '-NonInteractive', '-Command', command], { + encoding: 'utf8', windowsHide: true, timeout: 3000, stdio: ['ignore', 'pipe', 'ignore'], + }).trim() + return Number.isFinite(Date.parse(value)) ? new Date(value).toISOString() : null + } catch { return null } +} + +function processExists(pid: number): boolean { + try { process.kill(pid, 0); return true } catch (error: unknown) { + return (error as NodeJS.ErrnoException).code === 'EPERM' + } +} + +/** True only when both the PID and its operating-system creation time match this run. */ +export function isOwnedProcess(pid: number | null, processStartedAt: string | null): boolean { + if (pid === null || !Number.isInteger(pid) || pid <= 0 || processStartedAt === null) return false + const actual = getProcessStartTime(pid) + if (actual === null || !Number.isFinite(Date.parse(processStartedAt))) return false + return Math.abs(Date.parse(actual) - Date.parse(processStartedAt)) <= 100 +} + +export function isAlive(pid: number | null, processStartedAt: string | null = null): boolean { + if (!isOwnedProcess(pid, processStartedAt)) return false + try { process.kill(pid!, 0); return true } catch { return false } +} + +/** + * A run is live while it is open AND its process answers. + * + * `ended_at` latches the answer, and that latch is not cosmetic: Windows hands + * a freed pid straight to the next process, so a pid re-checked after the run + * was settled can belong to a completely different program wearing the same + * number. A test caught exactly that — a settled run reported as running again + * a few hundred milliseconds later. Once the Brain has seen a run end, it does + * not un-see it; the remaining window (a process that exits and has its pid + * reused before the Brain ever looks) is small and cannot be closed from a pid + * alone. + */ +export function runAlive(row: { pid: number | null; process_started_at?: string | null; ended_at: string | null }): boolean { + return row.ended_at === null && isAlive(row.pid, row.process_started_at ?? null) +} + +export function getWorkerRun(db: DatabaseSync, agentId: string): WorkerRun | null { + ensureRunnerSchema(db) + const row = loadRunRow(db, agentId) + return row === null ? null : runOf(row) +} + +/** `<agent>-<stamp>`, sortable and collision-free enough for one worker. */ +function stampOf(now: Date): string { + const pad = (value: number, width = 2) => String(value).padStart(width, '0') + return `${now.getUTCFullYear()}${pad(now.getUTCMonth() + 1)}${pad(now.getUTCDate())}` + + `-${pad(now.getUTCHours())}${pad(now.getUTCMinutes())}${pad(now.getUTCSeconds())}${pad(now.getUTCMilliseconds(), 3)}` +} + +export interface StartRunInput { + workspaceId: string + agentId: string + /** Working directory; defaults to the profile's, then the workspace root. */ + cwd?: string + home?: string + now?: Date + /** A task preclaimed by the persistent supervisor. */ + task?: { id: string; title: string; body: string } + /** Fences Brain commands made by a stale retry attempt. */ + attemptToken?: string +} + +/** + * Start the worker's process detached and record the run. + * + * Detached is the point: the Brain is a CLI that answers and exits, so the + * worker cannot be its child in any meaningful sense. stdin is empty (a worker + * that waits for input would never finish), stdout and stderr go to the log, + * and the run row is written BEFORE the process starts — otherwise a process + * that dies instantly would leave nothing to reconcile. + */ +export function startWorkerRun(db: DatabaseSync, input: StartRunInput): WorkerRun { + ensureRunnerSchema(db) + if (process.platform !== 'win32') throw new AccessDenied('swarm run is not supported on this platform; Windows process-tree identity is required') + const workspace = requireWorkspace(db, input.workspaceId) + requireAgent(db, input.agentId) + const profile = getRunnerProfile(db, input.agentId) + if (profile === null) { + throw new AccessDenied( + `no runner profile for ${input.agentId}; set one first:\n` + + ` plugbrain swarm runner set ${input.agentId} --cmd codex --model <model>`, + ) + } + // A dead run has to be booked before it is replaced, or the worker that never + // came back keeps a clean-looking board row. + reconcileWorkerRuns(db, input.workspaceId, { now: input.now }) + const previous = getWorkerRun(db, input.agentId) + if (previous !== null && previous.alive) { + throw new AccessDenied( + `${input.agentId} already has a live run (pid ${previous.pid}, started ${previous.startedAt}); ` + + `stop it with: plugbrain swarm run ${input.agentId} --stop`, + ) + } + + const now = input.now ?? new Date() + const nowIso = now.toISOString() + const workspaceRoot = resolve(workspace.root) + const cwd = resolve(input.cwd ?? profile.cwd ?? workspaceRoot) + const agent = db.prepare('SELECT worktrees FROM agents WHERE id = ?').get(input.agentId) as { worktrees: string | null } | undefined + const verifiedCwd = validateRunnerCwd(db, workspace.id, agent?.worktrees ?? null, cwd) + const home = input.home ?? resolveBrainHome() + const stamp = stampOf(now) + const workersDir = join(home, 'runs', 'workers') + const logPath = join(workersDir, `${input.agentId}-${stamp}.jsonl`) + const lastDocPath = join(workersDir, `${input.agentId}-${stamp}-last.md`) + const promptPath = join(workersDir, `${input.agentId}-${stamp}-prompt.md`) + + if (input.task !== undefined) { + assertNotCredential('task title', input.task.title) + assertNotCredential('task body', input.task.body) + } + const prompt = renderRunnerPrompt({ agentId: input.agentId, workspaceRoot, docs: discoverProtocolDocs(workspaceRoot), task: input.task }) + if (prompt.length > MAX_PROMPT_CHARS) { + throw new AccessDenied(`the rendered prompt is ${prompt.length} characters; the limit is ${MAX_PROMPT_CHARS}`) + } + const plan = buildRunnerArgv(profile, { prompt, promptPath, logPath, lastDocPath, cwd }) + if (!existsSync(plan.file)) { + throw new AccessDenied(`command not found: ${profile.cmd} (resolved to ${plan.file}); check the --cmd on the runner profile`) + } + + mkdirSync(workersDir, { recursive: true }) + // The prompt is kept, not just passed: a worker whose log shows the wrong + // brief is otherwise impossible to explain after the fact. + writeFileSync(promptPath, prompt, 'utf8') + + const runId = `${input.agentId}-${stamp}` + db.prepare(` + INSERT INTO worker_runs (agent_id, run_id, pid, started_at, cwd, log_path, last_doc_path, prompt_path, + command_text, via_comspec, log_mtime_ms, log_size, ended_at, ended_reason, stopped_at, blocked_at, + task_id, attempt_token) + VALUES (?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, NULL, NULL, NULL, NULL, NULL, NULL, ?, ?) + ON CONFLICT(agent_id) DO UPDATE SET + run_id = excluded.run_id, pid = NULL, started_at = excluded.started_at, cwd = excluded.cwd, + log_path = excluded.log_path, last_doc_path = excluded.last_doc_path, prompt_path = excluded.prompt_path, + command_text = excluded.command_text, via_comspec = excluded.via_comspec, + log_mtime_ms = NULL, log_size = NULL, last_event_at = NULL, last_event_kind = NULL, last_event_text = NULL, + ended_at = NULL, ended_reason = NULL, stopped_at = NULL, blocked_at = NULL, + task_id = excluded.task_id, attempt_token = excluded.attempt_token + `).run( + input.agentId, runId, nowIso, verifiedCwd, logPath, lastDocPath, promptPath, plan.commandText, + plan.viaComspec ? 1 : 0, input.task?.id ?? null, input.attemptToken ?? null, + ) + + let pid: number | null = null + try { + const logFd = openSync(logPath, 'a') + const errPath = `${logPath.slice(0, -'.jsonl'.length)}.stderr.log` + const errFd = openSync(errPath, 'a') + try { + const child = spawn(plan.file, plan.args, { + cwd: verifiedCwd, + detached: true, + windowsHide: true, + stdio: ['ignore', logFd, errFd], + env: { ...process.env, PLUGBRAIN_HOME: home, + ...(input.attemptToken === undefined ? {} : { + PLUGBRAIN_ATTEMPT_TOKEN: input.attemptToken, PLUGBRAIN_SUPERVISED_AGENT: input.agentId, + }), + }, + ...(plan.viaComspec ? { windowsVerbatimArguments: true } : {}), + }) + pid = child.pid ?? null + if (pid !== null) { + const processStartedAt = getProcessStartTime(pid) + if (processStartedAt === null && child.exitCode === null && processExists(pid)) { + child.kill() + throw new AccessDenied(`could not verify process start time for worker ${input.agentId}; process stopped`) + } + if (processStartedAt !== null) { + db.prepare('UPDATE worker_runs SET process_started_at = ? WHERE agent_id = ?').run(processStartedAt, input.agentId) + } + } + // Best effort: a spawn failure that arrives after this process exits would + // otherwise leave an empty log and no explanation for it. + child.on('error', (error: Error) => { + try { writeFileSync(logPath, `${JSON.stringify({ type: 'plugbrain.run.spawn_error', message: error.message })}\n`, { flag: 'a' }) } catch { /* the log is a courtesy here */ } + }) + child.unref() + } finally { + closeSync(logFd) + closeSync(errFd) + } + } catch (error: unknown) { + db.prepare('DELETE FROM worker_runs WHERE agent_id = ?').run(input.agentId) + throw error + } + if (pid !== null) db.prepare('UPDATE worker_runs SET pid = ? WHERE agent_id = ?').run(pid, input.agentId) + return getWorkerRun(db, input.agentId)! +} + +// --------------------------------------------------------------------------- +// Watching a run +// --------------------------------------------------------------------------- + +function refreshRunLog(db: DatabaseSync, row: RunDbRow): void { + const tail = readTail(row.log_path, LOG_TAIL_BYTES) + if (tail === null) return + if (row.log_mtime_ms === tail.mtimeMs && row.log_size === tail.size) return + const lines = tail.text.split('\n').map(line => line.trim()).filter(line => line !== '') + const last = lines.length === 0 ? null : summarizeLogEvent(lines.at(-1)!) + db.prepare(` + UPDATE worker_runs + SET log_mtime_ms = ?, log_size = ?, last_event_at = COALESCE(?, last_event_at), last_event_kind = ?, last_event_text = ? + WHERE agent_id = ? + `).run(tail.mtimeMs, tail.size, last?.at ?? new Date(tail.mtimeMs).toISOString(), last?.kind ?? null, last?.text ?? null, row.agent_id) +} + +interface TurnRow { turn_state: string | null; turn_state_at: string | null } + +const TURN_ENDED = new Set(['needs-task', 'awaiting-commit', 'blocked', 'paused', 'commit-approved']) + +function turnOf(db: DatabaseSync, agentId: string): TurnRow { + const row = db.prepare('SELECT turn_state, turn_state_at FROM agents WHERE id = ?').get(agentId) as unknown as TurnRow | undefined + return row ?? { turn_state: null, turn_state_at: null } +} + +/** + * Did the worker end its turn DURING this run? A state left over from an + * earlier turn is not an answer: when it predates the start, the process that + * just died never got to speak. + */ +function endedTurnWithin(row: RunDbRow, turn: TurnRow): boolean { + if (turn.turn_state === null || !TURN_ENDED.has(turn.turn_state)) return false + const at = turn.turn_state_at === null ? Number.NaN : Date.parse(turn.turn_state_at) + const started = Date.parse(row.started_at) + return Number.isFinite(at) && at >= started - TURN_END_SLACK_MS +} + +export interface RunReconcileEvent { + agentId: string + runId: string + /** `ended-without-turn-end` is the one the Brain has to act on. */ + reason: 'ended-without-turn-end' | 'ended-after-turn-end' + summary: string + /** The message the integrator received, when one was sent. */ + messageId: string | null +} + +export interface ReconcileOptions { + now?: Date + /** Send the integrator a message when a run died mid-turn. Default true. */ + notify?: boolean +} + +/** + * Look at every run and settle the ones whose process is gone. + * + * Called before a run is replaced and from `swarm board`, so the fleet state + * heals whenever somebody looks at it. It is idempotent: a concluded run is + * left alone, and a live one only gets its log tail refreshed. + */ +export function reconcileWorkerRuns(db: DatabaseSync, workspaceId: string, options: ReconcileOptions = {}): RunReconcileEvent[] { + ensureRunnerSchema(db) + const rows = db.prepare(` + SELECT worker_runs.* FROM worker_runs + JOIN agents ON agents.id = worker_runs.agent_id + WHERE agents.workspace_id = ? AND agents.retired_at IS NULL + `).all(workspaceId) as unknown as RunDbRow[] + const now = options.now ?? new Date() + const nowIso = now.toISOString() + const events: RunReconcileEvent[] = [] + + for (const row of rows) { + if (runAlive(row)) { refreshRunLog(db, row); continue } + if (row.ended_at !== null) continue + refreshRunLog(db, row) + const refreshed = loadRunRow(db, row.agent_id) ?? row + const turn = turnOf(db, row.agent_id) + const endedCleanly = endedTurnWithin(row, turn) + const tail = tailOfLog(refreshed.log_path) + const summary = `${row.agent_id}: runner exited without a turn end (pid ${row.pid ?? '?'}, started ${row.started_at})` + + (tail.length === 0 ? '\nno log output' : `\n${tail.join('\n')}`) + db.exec('BEGIN IMMEDIATE') + try { + const current = loadRunRow(db, row.agent_id) + if (current === null || current.ended_at !== null) { db.exec('COMMIT'); continue } + if (runAlive(current)) { db.exec('COMMIT'); refreshRunLog(db, current); continue } + const currentTurn = turnOf(db, row.agent_id) + const clean = endedTurnWithin(current, currentTurn) + if (clean) { + db.prepare('UPDATE worker_runs SET ended_at = ?, ended_reason = ? WHERE agent_id = ? AND ended_at IS NULL') + .run(nowIso, 'turn-end', row.agent_id) + db.exec('COMMIT') + events.push({ + agentId: row.agent_id, runId: row.run_id, reason: 'ended-after-turn-end', + summary: `${row.agent_id}: runner exited after a turn end (${currentTurn.turn_state})`, messageId: null, + }) + continue + } + db.prepare(` + UPDATE agents SET turn_state = 'blocked', turn_state_at = ?, turn_summary = ?, + last_heartbeat = ?, last_seen = ? + WHERE id = ? + `).run(nowIso, summary.slice(0, 2000), nowIso, nowIso, row.agent_id) + db.prepare('UPDATE worker_runs SET blocked_at = ?, ended_at = ?, ended_reason = ? WHERE agent_id = ? AND ended_at IS NULL') + .run(nowIso, nowIso, 'process-gone', row.agent_id) + let messageId: string | null = null + if (options.notify !== false) { + ensureIntegrator(db, workspaceId) + messageId = sendMessage(db, { + workspaceId, + fromAgent: row.agent_id, + toAgent: INTEGRATOR, + subject: `swarm run: ${row.agent_id} exited without a turn end`, + body: `Worker ${row.agent_id} was started by the Brain and its process is gone before the turn ended.\n` + + `Command: ${row.command_text}\nLog: ${row.log_path}\n\nLast log lines:\n${tail.length === 0 ? '(empty)' : tail.join('\n')}`, + }).id + } + db.exec('COMMIT') + events.push({ agentId: row.agent_id, runId: row.run_id, reason: 'ended-without-turn-end', summary, messageId }) + } catch (error: unknown) { + db.exec('ROLLBACK') + throw error + } + } + return events +} + +/** + * Stop the worker's process and pause its turn. + * + * A worker that already ended its turn keeps that state: `awaiting-commit` must + * survive its process being stopped, or the integrator's approval would arrive + * at an agent that no longer looks like it is waiting for one. + */ +export function stopWorkerRun(db: DatabaseSync, input: { agentId: string; workspaceId: string; now?: Date }): WorkerRun { + ensureRunnerSchema(db) + requireWorkspace(db, input.workspaceId) + requireAgent(db, input.agentId) + const row = loadRunRow(db, input.agentId) + if (row === null) throw new AccessDenied(`${input.agentId} has no recorded run to stop`) + const now = input.now ?? new Date() + const nowIso = now.toISOString() + const turn = turnOf(db, input.agentId) + const hadEndedTurn = endedTurnWithin(row, turn) + + if (runAlive(row)) { + if (process.platform === 'win32') { + if (!isOwnedProcess(row.pid, row.process_started_at)) { + throw new AccessDenied(`${input.agentId} pid ${row.pid} no longer matches the recorded process start time`) + } + try { + execFileSync('taskkill.exe', ['/PID', String(row.pid), '/T', '/F'], { + windowsHide: true, timeout: 5000, stdio: 'ignore', + }) + } catch { /* a concurrent process exit is confirmed by the identity check below */ } + } else { + try { process.kill(-row.pid!, 'SIGTERM') } catch { /* already gone */ } + } + const deadline = Date.now() + 3000 + while (isAlive(row.pid, row.process_started_at) && Date.now() < deadline) { + // A short, bounded wait: `--stop` must not hang because a worker ignores + // the signal, and the state written below depends on the process being gone. + try { execSleep(50) } catch { break } + } + if (isAlive(row.pid, row.process_started_at)) { + throw new AccessDenied(`${input.agentId} (pid ${row.pid}) ignored the stop; its turn stays ${turn.turn_state ?? 'unset'}`) + } + } + db.prepare('UPDATE worker_runs SET stopped_at = ?, ended_at = COALESCE(ended_at, ?), ended_reason = COALESCE(ended_reason, ?) WHERE agent_id = ?') + .run(nowIso, nowIso, 'stopped', input.agentId) + if (!hadEndedTurn) { + db.prepare('UPDATE agents SET turn_state = ?, turn_state_at = ?, turn_summary = ? WHERE id = ?') + .run('paused', nowIso, `run stopped by request (${row.command_text})`.slice(0, 2000), input.agentId) + } + return getWorkerRun(db, input.agentId)! +} + +/** A blocking sleep without a dependency; `Atomics.wait` needs a typed array to wait on. */ +function execSleep(ms: number): void { + const shared = new Int32Array(new SharedArrayBuffer(4)) + Atomics.wait(shared, 0, 0, ms) +} + +// --------------------------------------------------------------------------- +// `swarm run <agent> --status` +// --------------------------------------------------------------------------- + +export interface WorkerRunStatus { + agentId: string + profile: RunnerProfile | null + run: WorkerRun | null + turnState: string | null + turnStateAt: string | null + /** The last log lines, so a status call needs no second command. */ + tail: string[] +} + +/** + * Everything known about a worker's run. Settles a dead run first, so asking + * for the status is also the moment the Brain books a process that never + * finished its turn — the one place a worker's absence would otherwise go + * unnoticed until somebody reads the log by hand. + */ +export function workerRunStatus(db: DatabaseSync, workspaceId: string, agentId: string): WorkerRunStatus { + ensureRunnerSchema(db) + reconcileWorkerRuns(db, workspaceId) + const run = getWorkerRun(db, agentId) + const turn = turnOf(db, agentId) + return { + agentId, + profile: getRunnerProfile(db, agentId), + run, + turnState: turn.turn_state, + turnStateAt: turn.turn_state_at, + tail: run === null ? [] : tailOfLog(run.logPath), + } +} + +// --------------------------------------------------------------------------- +// The board's view +// --------------------------------------------------------------------------- + +export interface RunnerBoardState { + runId: string + pid: number | null + alive: boolean + startedAt: string + command: string + logPath: string + lastDocPath: string | null + lastEventAt: string | null + lastEventKind: string | null + lastEventText: string | null + endedAt: string | null + endedReason: string | null + stoppedAt: string | null + /** Set when the run died mid-turn and the Brain booked the worker `blocked`. */ + blockedAt: string | null +} + +/** Read-only: the board shows the runner, it does not settle runs. */ +export function runnerBoardStates(db: DatabaseSync): Map<string, RunnerBoardState> { + ensureRunnerSchema(db) + const rows = db.prepare('SELECT * FROM worker_runs').all() as unknown as RunDbRow[] + const states = new Map<string, RunnerBoardState>() + for (const row of rows) { + states.set(row.agent_id, { + runId: row.run_id, + pid: row.pid, + alive: runAlive(row), + startedAt: row.started_at, + command: row.command_text, + logPath: row.log_path, + lastDocPath: row.last_doc_path, + lastEventAt: row.last_event_at, + lastEventKind: row.last_event_kind, + lastEventText: row.last_event_text, + endedAt: row.ended_at, + endedReason: row.ended_reason, + stoppedAt: row.stopped_at, + blockedAt: row.blocked_at, + }) + } + return states +} diff --git a/src/coord/supervisor.ts b/src/coord/supervisor.ts new file mode 100644 index 0000000..12f0ec5 --- /dev/null +++ b/src/coord/supervisor.ts @@ -0,0 +1,344 @@ +/** + * Persistent, headless refill loop for one registered worker. + * + * The supervisor owns one task attempt at a time. It claims through the same + * compare-and-set queue path as a human worker, starts the configured runner, + * waits for its process to settle, and only retries a task after its previous + * process has exited. A per-attempt token is passed to the worker and checked + * on every subsequent `swarm` command, fencing a stale process from Brain + * state changes after a newer attempt has started. + */ +import { randomUUID } from 'node:crypto' +import { spawn } from 'node:child_process' +import { readFileSync } from 'node:fs' +import type { DatabaseSync } from 'node:sqlite' +import { AccessDenied, requireAgent, requireWorkspace } from '../access.ts' +import { claimNextTask } from '../queue.ts' +import { ensureIntegrator, sendMessage } from './inbox.ts' +import { getRunnerProfile, getWorkerRun, reconcileWorkerRuns, startWorkerRun } from './runner.ts' +import { configureQuotaPool, ensureQuotaPoolSchema, getQuotaPool, noteQuotaRateLimit, reserveQuota, settleQuota } from './quota-pools.ts' + +const DEFAULT_IDLE_MS = 5 * 60_000 +const DEFAULT_MAX_IDLE_CHECKS = 24 +const DEFAULT_POLL_MS = 1000 +const DEFAULT_MAX_ATTEMPTS = 2 +const DEFAULT_QUOTA_BACKOFF_MS = 30 * 60_000 +const MAX_QUOTA_BACKOFF_MS = 30 * 60_000 + +export type SupervisorFailure = 'clean' | 'quota' | 'auth' | 'crash' + +/** Classify only explicit CLI errors. Quiet logs and arbitrary nonzero exits are crashes. */ +export function classifySupervisorFailure(input: { exitCode: number | null; output: string }): SupervisorFailure { + const text = input.output.toLowerCase() + if (/usage limit|hit your .*limit|insufficient_quota|rate.?limit|retry-after|too many requests|\b429\b/.test(text)) return 'quota' + if (/unauthorized|authentication failed|invalid api key|token expired|login required|http 401|http 403|\b401\b/.test(text)) return 'auth' + return input.exitCode === 0 ? 'clean' : 'crash' +} + +function quotaBackoffMs(output: string, now = Date.now()): number { + const value = /retry-after\s*:\s*([^\r\n]+)/i.exec(output)?.[1]?.trim() + if (value === undefined) return DEFAULT_QUOTA_BACKOFF_MS + const seconds = Number(value) + const delay = Number.isFinite(seconds) && seconds >= 0 ? seconds * 1000 : Date.parse(value) - now + if (!Number.isFinite(delay)) return DEFAULT_QUOTA_BACKOFF_MS + return Math.max(1000, Math.min(MAX_QUOTA_BACKOFF_MS, delay)) +} + +export function ensureSupervisorSchema(db: DatabaseSync): void { + ensureQuotaPoolSchema(db) + db.exec(` +CREATE TABLE IF NOT EXISTS worker_supervisors ( + agent_id TEXT PRIMARY KEY, + supervisor_id TEXT NOT NULL, + pid INTEGER, + started_at TEXT NOT NULL, + active INTEGER NOT NULL DEFAULT 1, + current_task_id TEXT, + attempt INTEGER NOT NULL DEFAULT 0, + last_failure TEXT, + updated_at TEXT NOT NULL +); +CREATE TABLE IF NOT EXISTS worker_task_attempts ( + task_id TEXT NOT NULL, + agent_id TEXT NOT NULL, + attempt INTEGER NOT NULL, + attempt_token TEXT NOT NULL, + run_id TEXT, + started_at TEXT NOT NULL, + ended_at TEXT, + outcome TEXT, + PRIMARY KEY(task_id, attempt) +); +`) +} + +export interface StartSupervisorInput { + workspaceId: string + agentId: string + /** Used by deterministic fake-worker tests; production values are clamped. */ + idleMs?: number + maxIdleChecks?: number + maxAttempts?: number + pollMs?: number +} + +function processAlive(pid: number | null): boolean { + if (pid === null || !Number.isInteger(pid) || pid <= 0) return false + try { process.kill(pid, 0); return true } catch { return false } +} + +/** Start one detached supervisor process; an SQLite transaction prevents two refill loops per worker. */ +export function startSupervisor(db: DatabaseSync, input: StartSupervisorInput): { supervisorId: string; pid: number } { + ensureSupervisorSchema(db) + const workspace = requireWorkspace(db, input.workspaceId) + requireAgent(db, input.agentId) + if (getRunnerProfile(db, input.agentId) === null) { + throw new AccessDenied(`no runner profile for ${input.agentId}; set one first with swarm runner set`) + } + const supervisorId = `sup-${randomUUID()}` + const now = new Date().toISOString() + db.exec('BEGIN IMMEDIATE') + try { + const current = db.prepare('SELECT supervisor_id, pid, active FROM worker_supervisors WHERE agent_id = ?') + .get(input.agentId) as { supervisor_id: string; pid: number | null; active: number } | undefined + if (current?.active === 1 && processAlive(current.pid)) { + throw new AccessDenied(`${input.agentId} already has a live supervisor (pid ${current.pid})`) + } + db.prepare(`INSERT INTO worker_supervisors + (agent_id, supervisor_id, pid, started_at, active, current_task_id, attempt, last_failure, updated_at) + VALUES (?, ?, NULL, ?, 1, NULL, 0, NULL, ?) + ON CONFLICT(agent_id) DO UPDATE SET supervisor_id = excluded.supervisor_id, pid = NULL, + started_at = excluded.started_at, active = 1, current_task_id = NULL, attempt = 0, + last_failure = NULL, updated_at = excluded.updated_at`) + .run(input.agentId, supervisorId, now, now) + + const entry = process.argv[1] + if (!entry) throw new AccessDenied('cannot locate the PlugBrain CLI entry point for detached supervisor') + const args = ['--experimental-strip-types', '--no-warnings', entry, 'swarm', 'run', input.agentId, '--supervisor-child', '--workspace', input.workspaceId, + '--idle-ms', String(input.idleMs ?? DEFAULT_IDLE_MS), '--max-idle-checks', String(input.maxIdleChecks ?? DEFAULT_MAX_IDLE_CHECKS), + '--max-attempts', String(input.maxAttempts ?? DEFAULT_MAX_ATTEMPTS), '--poll-ms', String(input.pollMs ?? DEFAULT_POLL_MS)] + const child = spawn(process.execPath, args, { + cwd: workspace.root, + detached: true, + windowsHide: true, + stdio: 'ignore', + env: { ...process.env, PLUGBRAIN_SUPERVISOR_ID: supervisorId }, + }) + const pid = child.pid + if (pid === undefined) throw new AccessDenied('could not start supervisor process') + db.prepare('UPDATE worker_supervisors SET pid = ?, updated_at = ? WHERE agent_id = ? AND supervisor_id = ?') + .run(pid, new Date().toISOString(), input.agentId, supervisorId) + child.unref() + db.exec('COMMIT') + return { supervisorId, pid } + } catch (error) { + db.exec('ROLLBACK') + throw error + } +} + +function supervisorOwns(db: DatabaseSync, agentId: string, supervisorId: string): boolean { + const row = db.prepare('SELECT active, supervisor_id FROM worker_supervisors WHERE agent_id = ?').get(agentId) as + { active: number; supervisor_id: string } | undefined + return row?.active === 1 && row.supervisor_id === supervisorId +} + +function setSupervisor(db: DatabaseSync, agentId: string, supervisorId: string, updates: { + taskId?: string | null; attempt?: number; failure?: string | null; active?: boolean +}): void { + const now = new Date().toISOString() + db.prepare(`UPDATE worker_supervisors SET + current_task_id = CASE WHEN ? = 1 THEN ? ELSE current_task_id END, + attempt = COALESCE(?, attempt), + last_failure = CASE WHEN ? = 1 THEN ? ELSE last_failure END, + active = COALESCE(?, active), updated_at = ? + WHERE agent_id = ? AND supervisor_id = ?`) + .run(updates.taskId === undefined ? 0 : 1, updates.taskId ?? null, updates.attempt ?? null, + updates.failure === undefined ? 0 : 1, updates.failure ?? null, + updates.active === undefined ? null : updates.active ? 1 : 0, + now, agentId, supervisorId) +} + +function readRunOutput(run: { logPath: string } | null): string { + if (!run) return '' + try { + const stderr = run.logPath.replace(/\.jsonl$/, '.stderr.log') + return `${readFileSync(run.logPath, 'utf8')}\n${readFileSync(stderr, 'utf8')}`.slice(-64 * 1024) + } catch { return '' } +} + +function stopSupervisorAfterAttempt( + db: DatabaseSync, taskId: string, agentId: string, supervisorId: string, + failure: string, releaseTask: boolean, + notice: { workspaceId: string; subject: string; body: string }, +): boolean { + db.exec('BEGIN IMMEDIATE') + try { + if (!supervisorOwns(db, agentId, supervisorId)) { + db.exec('ROLLBACK') + return false + } + if (releaseTask) { + db.prepare(`UPDATE queue_tasks SET state = 'pending', claimed_by = NULL, claimed_at = NULL, + updated_at = ? WHERE id = ? AND state = 'claimed' AND claimed_by = ?`) + .run(new Date().toISOString(), taskId, agentId) + } + setSupervisor(db, agentId, supervisorId, { failure, active: false }) + notifyLead(db, notice.workspaceId, agentId, notice.subject, notice.body) + db.exec('COMMIT') + return true + } catch (error) { db.exec('ROLLBACK'); throw error } +} + +function notifyLead(db: DatabaseSync, workspaceId: string, agentId: string, subject: string, body: string): void { + ensureIntegrator(db, workspaceId) + sendMessage(db, { workspaceId, fromAgent: agentId, toAgent: 'integrator', subject, body }) +} + +/** Fences every swarm command issued by a worker whose attempt has been superseded. */ +export function assertActiveSupervisorAttempt(db: DatabaseSync): void { + const attemptToken = process.env.PLUGBRAIN_ATTEMPT_TOKEN + const agentId = process.env.PLUGBRAIN_SUPERVISED_AGENT + if (!attemptToken || !agentId) return + ensureSupervisorSchema(db) + const active = db.prepare('SELECT run_id, ended_at, task_id, attempt_token FROM worker_runs WHERE agent_id = ?').get(agentId) as + { run_id: string; ended_at: string | null; task_id: string | null; attempt_token: string | null } | undefined + const attempt = db.prepare('SELECT run_id, ended_at FROM worker_task_attempts WHERE agent_id = ? AND attempt_token = ?') + .get(agentId, attemptToken) as { run_id: string | null; ended_at: string | null } | undefined + if (!attempt || attempt.ended_at !== null || active?.ended_at !== null || + (attempt.run_id === null ? active?.attempt_token !== attemptToken : attempt.run_id !== active?.run_id) || + active?.task_id === null) { + throw new AccessDenied('stale supervisor attempt is fenced and may not change swarm state') + } +} + +/** Run loop, called only in the detached child started by `startSupervisor`. */ +export async function runSupervisorLoop(db: DatabaseSync, input: StartSupervisorInput & { supervisorId: string }): Promise<void> { + ensureSupervisorSchema(db) + const idleMs = Math.max(50, Math.floor(input.idleMs ?? DEFAULT_IDLE_MS)) + const maxIdleChecks = Math.max(1, Math.floor(input.maxIdleChecks ?? DEFAULT_MAX_IDLE_CHECKS)) + const maxAttempts = Math.max(1, Math.min(5, Math.floor(input.maxAttempts ?? DEFAULT_MAX_ATTEMPTS))) + const pollMs = Math.max(50, Math.floor(input.pollMs ?? DEFAULT_POLL_MS)) + let idleChecks = 0 + while (supervisorOwns(db, input.agentId, input.supervisorId)) { + // A previous supervisor may have exited while its detached worker was still + // alive. Let that process settle before claiming or launching another run. + let running = getWorkerRun(db, input.agentId) + while (running?.alive && supervisorOwns(db, input.agentId, input.supervisorId)) { + await new Promise(resolve => setTimeout(resolve, pollMs)) + reconcileWorkerRuns(db, input.workspaceId, { notify: false }) + running = getWorkerRun(db, input.agentId) + } + if (!supervisorOwns(db, input.agentId, input.supervisorId)) break + let task: ReturnType<typeof claimNextTask> + try { + const claimed = db.prepare(`SELECT id, workspace_id, title, body, addressed_to, requested_by, state, claimed_by, + claimed_at, delivered_path, delivered_summary, created_at, updated_at FROM queue_tasks + WHERE workspace_id = ? AND state = 'claimed' AND claimed_by = ? ORDER BY claimed_at ASC LIMIT 1`) + .get(input.workspaceId, input.agentId) + task = claimed === undefined + ? claimNextTask(db, input.workspaceId, input.agentId) + : claimed as unknown as NonNullable<ReturnType<typeof claimNextTask>> + } + catch (error) { + setSupervisor(db, input.agentId, input.supervisorId, { failure: 'queue-error' }) + notifyLead(db, input.workspaceId, input.agentId, 'swarm run: queue unavailable', String(error)) + break + } + if (task === null) { + idleChecks += 1 + if (idleChecks >= maxIdleChecks) break + await new Promise(resolve => setTimeout(resolve, idleMs)) + continue + } + idleChecks = 0 + let attemptNumber = Number((db.prepare('SELECT COALESCE(MAX(attempt), 0) AS n FROM worker_task_attempts WHERE task_id = ?') + .get(task.id) as { n: number }).n) + let finished = false + while (!finished && attemptNumber < maxAttempts && supervisorOwns(db, input.agentId, input.supervisorId)) { + const profile = db.prepare('SELECT account, quota_pool FROM agents WHERE id = ?').get(input.agentId) as + { account: string | null; quota_pool: string | null } | undefined + const poolId = profile?.quota_pool?.trim() || profile?.account?.trim() || '' + if (!poolId) throw new AccessDenied(`no quota pool or account is configured for ${input.agentId}`) + if (!getQuotaPool(db, poolId)) configureQuotaPool(db, { id: poolId, maxConcurrent: 1 }) + const nextAttempt = attemptNumber + 1 + const reservation = reserveQuota(db, { poolId, attemptId: `${task.id}:${nextAttempt}` }) + if (!reservation.allowed) { + const delay = reservation.reason === 'cooldown' && reservation.retryAt + ? Math.max(pollMs, Math.min(MAX_QUOTA_BACKOFF_MS, Date.parse(reservation.retryAt) - Date.now())) + : pollMs + await new Promise(resolve => setTimeout(resolve, delay)) + continue + } + attemptNumber = nextAttempt + const attemptToken = `attempt-${randomUUID()}` + const startedAt = new Date().toISOString() + setSupervisor(db, input.agentId, input.supervisorId, { taskId: task.id, attempt: attemptNumber, failure: null }) + db.prepare(`INSERT INTO worker_task_attempts (task_id, agent_id, attempt, attempt_token, run_id, started_at, ended_at, outcome) + VALUES (?, ?, ?, ?, NULL, ?, NULL, NULL)`) + .run(task.id, input.agentId, attemptNumber, attemptToken, startedAt) + let finalizingDecision = false + try { + const run = startWorkerRun(db, { workspaceId: input.workspaceId, agentId: input.agentId, + task: { id: task.id, title: task.title, body: task.body }, attemptToken }) + db.prepare('UPDATE worker_task_attempts SET run_id = ? WHERE task_id = ? AND attempt = ?') + .run(run.runId, task.id, attemptNumber) + let current = run + while (current.endedAt === null) { + await new Promise(resolve => setTimeout(resolve, pollMs)) + reconcileWorkerRuns(db, input.workspaceId, { notify: false }) + current = getWorkerRun(db, input.agentId)! + } + const output = readRunOutput(current) + const failure = classifySupervisorFailure({ exitCode: current.endedReason === 'turn-end' ? 0 : 1, output }) + if (failure === 'quota') { + noteQuotaRateLimit(db, poolId, quotaBackoffMs(output)) + } + settleQuota(db, { reservationId: reservation.reservation!.id, outcome: failure }) + const queue = db.prepare('SELECT state, claimed_by FROM queue_tasks WHERE id = ?').get(task.id) as + { state: string; claimed_by: string | null } | undefined + const endedTurn = db.prepare('SELECT turn_state FROM agents WHERE id = ?').get(input.agentId) as { turn_state: string | null } + const delivered = queue?.state === 'delivered' + const decision = !delivered && (failure === 'auth' || endedTurn.turn_state === 'awaiting-commit' || attemptNumber >= maxAttempts) + db.prepare('UPDATE worker_task_attempts SET ended_at = ?, outcome = ? WHERE task_id = ? AND attempt = ?') + .run(new Date().toISOString(), delivered ? 'delivered' : failure, task.id, attemptNumber) + if (delivered) { + finished = true + setSupervisor(db, input.agentId, input.supervisorId, { taskId: null, failure: null }) + continue + } + if (decision) { + const detail = `task ${task.id} (${task.title}) stopped after attempt ${attemptNumber}; outcome=${failure}; turn=${endedTurn.turn_state ?? 'unset'}.\n` + + `Run log: ${current.logPath}. Inspect the local log before deciding whether to retry.` + finalizingDecision = true + stopSupervisorAfterAttempt(db, task.id, input.agentId, input.supervisorId, failure, + endedTurn.turn_state !== 'awaiting-commit' && queue?.state === 'claimed' && queue.claimed_by === input.agentId, + { workspaceId: input.workspaceId, subject: `swarm run: decision required for ${task.id}`, body: detail }) + return + } + if (failure === 'quota') await new Promise(resolve => setTimeout(resolve, quotaBackoffMs(output))) + } catch (error) { + if (finalizingDecision) throw error + const message = String(error) + settleQuota(db, { reservationId: reservation.reservation!.id, outcome: 'launch-error' }) + db.prepare('UPDATE worker_task_attempts SET ended_at = ?, outcome = ? WHERE task_id = ? AND attempt = ?') + .run(new Date().toISOString(), 'launch-error', task.id, attemptNumber) + if (attemptNumber >= maxAttempts) { + stopSupervisorAfterAttempt(db, task.id, input.agentId, input.supervisorId, 'launch-error', true, + { workspaceId: input.workspaceId, subject: `swarm run: could not start ${task.id}`, body: message }) + return + } + await new Promise(resolve => setTimeout(resolve, pollMs)) + } + } + } + setSupervisor(db, input.agentId, input.supervisorId, { active: false, taskId: null }) +} + +/** Stop is a DB request; the supervisor observes it at its next poll boundary. */ +export function stopSupervisor(db: DatabaseSync, agentId: string): void { + ensureSupervisorSchema(db) + const result = db.prepare('UPDATE worker_supervisors SET active = 0, updated_at = ? WHERE agent_id = ? AND active = 1') + .run(new Date().toISOString(), agentId) + if (Number(result.changes) !== 1) throw new AccessDenied(`${agentId} has no active supervisor`) +} diff --git a/src/coord/swarm-ops.ts b/src/coord/swarm-ops.ts index 5eb2038..e77e54b 100644 --- a/src/coord/swarm-ops.ts +++ b/src/coord/swarm-ops.ts @@ -17,14 +17,20 @@ * worktree branch and uncommitted files) and never infers liveness. */ import { execFileSync } from 'node:child_process' +import { randomUUID } from 'node:crypto' import { resolve } from 'node:path' import type { DatabaseSync } from 'node:sqlite' import { AccessDenied, requireAgent, requireWorkspace } from '../access.ts' -import { claimNextTask, ensureQueueSchema } from '../queue.ts' +import { claimNextTask, ensureQueueSchema, listQueue, listQueueEvents } from '../queue.ts' +import { handleFleetAutomationEvent } from './fleet-automation.ts' +import { syncDependencies, UNBLOCKED_TASK_SQL } from './dependencies.ts' import { coordEvents } from './events.ts' +import { ensureWatchdogSchema, silenceMinutesFor, watchdogSettings, watchTurnEnd } from './watchdog.ts' import { ensureInboxSchema, sendMessage } from './inbox.ts' import { listActiveLeases } from './leases.ts' +import { runAutoReap } from './reaper.ts' import { ensureCoordSchema, getAgentPresence } from './registry.ts' +import { runnerBoardStates, type RunnerBoardState } from './runner.ts' import type { InboxMessage, PresenceState } from './types.ts' import { admitWork, assertNotCredential, hostSnapshot, listQuotas, @@ -39,17 +45,19 @@ export type TurnState = export type TurnEndState = 'needs-task' | 'awaiting-commit' | 'blocked' | 'paused' export const TURN_END_STATES: readonly TurnEndState[] = ['needs-task', 'awaiting-commit', 'blocked', 'paused'] -export type AttentionFlag = 'awaiting-commit' | 'needs-task' | 'blocked' | 'quota-exhausted' | 'stale-turn' +export type AttentionFlag = 'awaiting-commit' | 'needs-task' | 'blocked' | 'quota-exhausted' | 'stale-turn' | 'silent' const DEFAULT_STALE_TURN_MS = 30 * 60_000 const MAX_SUMMARY = 2000 export function ensureSwarmOpsSchema(db: DatabaseSync): void { ensureCoordSchema(db) + ensureWatchdogSchema(db) const columns = db.prepare('PRAGMA table_info(agents)').all() as unknown as Array<{ name: string }> const has = (name: string) => columns.some(column => column.name === name) if (!has('surface')) db.exec('ALTER TABLE agents ADD COLUMN surface TEXT') if (!has('account')) db.exec('ALTER TABLE agents ADD COLUMN account TEXT') + if (!has('quota_pool')) db.exec('ALTER TABLE agents ADD COLUMN quota_pool TEXT') if (!has('resource_key')) db.exec('ALTER TABLE agents ADD COLUMN resource_key TEXT') if (!has('turn_state')) db.exec('ALTER TABLE agents ADD COLUMN turn_state TEXT') if (!has('turn_state_at')) db.exec('ALTER TABLE agents ADD COLUMN turn_state_at TEXT') @@ -60,23 +68,40 @@ export function ensureSwarmOpsSchema(db: DatabaseSync): void { ensureQueueSchema(db) } +function ensureTurnHistorySchema(db: DatabaseSync): void { + db.exec(`CREATE TABLE IF NOT EXISTS swarm_turn_history ( + id TEXT PRIMARY KEY, + workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE, + agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE, + task_id TEXT, + started_at TEXT NOT NULL, + ended_at TEXT, + end_state TEXT, + summary TEXT + ); + CREATE INDEX IF NOT EXISTS idx_swarm_turn_history_workspace ON swarm_turn_history(workspace_id, started_at);`) +} + interface ProfileRow { id: string model: string | null surface: string | null account: string | null + quota_pool: string | null resource_key: string | null turn_state: string | null turn_state_at: string | null turn_summary: string | null worktrees: string | null retired_at: string | null + last_contact_at: string | null } export interface WorkerProfile { agentId: string surface: WorkerSurface | null account: string | null + quotaPool: string | null resourceKey: string | null model: string | null worktrees: string[] @@ -96,6 +121,7 @@ const profileOf = (row: ProfileRow): WorkerProfile => ({ agentId: row.id, surface: row.surface as WorkerSurface | null, account: row.account, + quotaPool: row.quota_pool, resourceKey: row.resource_key, model: row.model, worktrees: parseWorktrees(row.worktrees), @@ -104,7 +130,7 @@ const profileOf = (row: ProfileRow): WorkerProfile => ({ }) const loadProfile = (db: DatabaseSync, agentId: string): ProfileRow => - db.prepare(`SELECT id, model, surface, account, resource_key, turn_state, turn_state_at, turn_summary, worktrees, retired_at + db.prepare(`SELECT id, model, surface, account, quota_pool, resource_key, turn_state, turn_state_at, turn_summary, worktrees, retired_at, last_contact_at FROM agents WHERE id = ?`).get(agentId) as unknown as ProfileRow export interface RegisterWorkerInput { @@ -112,6 +138,8 @@ export interface RegisterWorkerInput { surface: WorkerSurface /** A name for the account the worker spends, e.g. `owner:chatgpt` or `nvidia:key-01`. */ account: string + /** Shared provider/project quota identity; defaults to account. */ + quotaPool?: string /** An exclusive resource such as one BYOK key. At most one active worker carries it. */ resourceKey?: string model?: string @@ -129,6 +157,9 @@ export function registerWorkerProfile(db: DatabaseSync, input: RegisterWorkerInp const account = input.account.trim() if (account === '' || account.length > 80) throw new AccessDenied('a worker needs an account label of at most 80 characters') assertNotCredential('account', account) + const quotaPool = (input.quotaPool ?? account).trim() + if (quotaPool === '' || quotaPool.length > 80) throw new AccessDenied('a quota pool needs a label of at most 80 characters') + assertNotCredential('quota pool', quotaPool) const resourceKey = input.resourceKey === undefined ? null : input.resourceKey.trim().toLowerCase() if (resourceKey !== null) { @@ -137,7 +168,6 @@ export function registerWorkerProfile(db: DatabaseSync, input: RegisterWorkerInp } const worktrees = (input.worktrees ?? []).map(path => resolve(path)) const now = new Date().toISOString() - db.exec('BEGIN IMMEDIATE') try { if (resourceKey !== null) { @@ -155,10 +185,10 @@ export function registerWorkerProfile(db: DatabaseSync, input: RegisterWorkerInp } db.prepare(` UPDATE agents - SET surface = ?, account = ?, resource_key = ?, model = COALESCE(?, model), + SET surface = ?, account = ?, quota_pool = ?, resource_key = ?, model = COALESCE(?, model), worktrees = ?, retired_at = NULL, last_seen = ? WHERE id = ? - `).run(input.surface, account, resourceKey, input.model ?? null, JSON.stringify(worktrees), now, input.agentId) + `).run(input.surface, account, quotaPool, resourceKey, input.model ?? null, JSON.stringify(worktrees), now, input.agentId) db.exec('COMMIT') } catch (error: unknown) { db.exec('ROLLBACK') @@ -224,25 +254,35 @@ export interface TurnPing { interface InboxRow { id: string; workspace_id: string; from_agent: string; to_agent: string | null; channel: string | null subject: string; body: string; created_at: string; delivered_at: string | null; read_at: string | null + receipt_delivered_at: string | null; acknowledged_at: string | null; processed_at: string | null } function unreadInbox(db: DatabaseSync, workspaceId: string, agentId: string): InboxMessage[] { const rows = db.prepare(` - SELECT * FROM inbox_messages - WHERE workspace_id = ? AND (to_agent = ? OR to_agent IS NULL) AND read_at IS NULL - ORDER BY created_at ASC - `).all(workspaceId, agentId) as unknown as InboxRow[] + SELECT m.*, r.delivered_at AS receipt_delivered_at, r.acknowledged_at, r.processed_at + FROM inbox_messages m LEFT JOIN inbox_delivery_receipts r ON r.message_id = m.id AND r.agent_id = ? + WHERE m.workspace_id = ? AND (m.to_agent = ? OR m.to_agent IS NULL) + AND COALESCE(r.acknowledged_at, CASE WHEN m.to_agent = ? THEN m.read_at END) IS NULL + ORDER BY m.created_at ASC, m.rowid ASC + `).all(agentId, workspaceId, agentId, agentId) as unknown as InboxRow[] const now = new Date().toISOString() return rows.map(row => { + db.prepare('INSERT OR IGNORE INTO inbox_delivery_receipts(message_id, agent_id) VALUES (?, ?)').run(row.id, agentId) if (row.delivered_at === null) { db.prepare('UPDATE inbox_messages SET delivered_at = ? WHERE id = ?').run(now, row.id) row.delivered_at = now + } + if (row.receipt_delivered_at === null) { + db.prepare('UPDATE inbox_delivery_receipts SET delivered_at = ? WHERE message_id = ? AND agent_id = ?').run(now, row.id, agentId) coordEvents.emitLive('message.delivered', { messageId: row.id, agentId, deliveredAt: now }) } return { id: row.id, workspaceId: row.workspace_id, fromAgent: row.from_agent, toAgent: row.to_agent, channel: row.channel, subject: row.subject, body: row.body, - createdAt: row.created_at, deliveredAt: row.delivered_at, readAt: row.read_at, + createdAt: row.created_at, deliveredAt: row.delivered_at, + readAt: row.acknowledged_at ?? row.read_at, + acknowledgedAt: row.acknowledged_at ?? row.read_at, + processedAt: row.processed_at, } }) } @@ -254,7 +294,8 @@ function nextTaskFor(db: DatabaseSync, workspaceId: string, agentId: string): Qu return taskRef(db.prepare(` SELECT id, title, body FROM queue_tasks WHERE workspace_id = ? AND state = 'pending' AND (addressed_to IS NULL OR addressed_to = ?) - ORDER BY created_at ASC, rowid ASC LIMIT 1 + AND ${UNBLOCKED_TASK_SQL} + ORDER BY priority DESC, created_at ASC, rowid ASC LIMIT 1 `).get(workspaceId, agentId) as { id: string; title: string; body: string } | undefined) } @@ -282,23 +323,34 @@ export function recordTurn(db: DatabaseSync, input: TurnInput, options: { host?: if (profile.retired_at !== null) throw new AccessDenied(`${input.agentId} is retired; register it again to resume`) let state: TurnState + let endState: TurnEndState | null = null if (input.phase === 'start') { state = 'working' } else { const wanted = input.state ?? 'needs-task' if (!TURN_END_STATES.includes(wanted)) throw new AccessDenied(`unknown turn end state: ${String(wanted)}`) state = wanted + endState = wanted } const summary = input.summary === undefined ? null : input.summary.slice(0, MAX_SUMMARY) if (summary !== null) assertNotCredential('summary', summary) const now = new Date().toISOString() + const previous = db.prepare('SELECT turn_state, turn_summary FROM agents WHERE id = ?').get(input.agentId) as + { turn_state: string | null; turn_summary: string | null } | undefined db.prepare(` UPDATE agents SET turn_state = ?, turn_state_at = ?, turn_summary = COALESCE(?, turn_summary), - last_heartbeat = ?, last_seen = ? + last_heartbeat = ?, last_seen = ?, last_contact_at = ? WHERE id = ? - `).run(state, now, summary, now, now, input.agentId) + `).run(state, now, summary, now, now, now, input.agentId) + + // A turn boundary is Brain contact by definition. An ending turn can be the + // event another task was waiting for and can owe the fleet a review; a + // starting turn reconciles dependencies that a missed event left behind, so + // a release notice lands in this very ping. + if (endState !== null) watchTurnEnd(db, input.workspaceId, input.agentId, endState) + else syncDependencies(db, input.workspaceId) let claimedTask: QueueTaskRef | null = null if (input.claimNext === true) { @@ -309,6 +361,21 @@ export function recordTurn(db: DatabaseSync, input: TurnInput, options: { host?: } } + if (input.phase === 'start') { + ensureTurnHistorySchema(db) + const task = currentTaskOf(db, input.workspaceId, input.agentId) + db.prepare(`INSERT INTO swarm_turn_history (id, workspace_id, agent_id, task_id, started_at) + VALUES (?, ?, ?, ?, ?)`) + .run(`turn-${randomUUID().slice(0, 12)}`, input.workspaceId, input.agentId, task?.id ?? null, now) + } else { + ensureTurnHistorySchema(db) + db.prepare(`UPDATE swarm_turn_history SET ended_at = ?, end_state = ?, summary = ? + WHERE id = (SELECT id FROM swarm_turn_history + WHERE workspace_id = ? AND agent_id = ? AND ended_at IS NULL + ORDER BY started_at DESC LIMIT 1)`) + .run(now, state, summary, input.workspaceId, input.agentId) + } + const host = options.host ?? hostSnapshot() const ping: TurnPing = { agentId: input.agentId, @@ -319,7 +386,24 @@ export function recordTurn(db: DatabaseSync, input: TurnInput, options: { host?: currentTask: currentTaskOf(db, input.workspaceId, input.agentId), admission: (['test', 'build', 'worktree'] as const).map(kind => admitWork(kind, host)), } - coordEvents.emitLive('agent.turn', { agentId: input.agentId, phase: input.phase, state, at: now }) + coordEvents.emitLive('agent.turn', { + agentId: input.agentId, + phase: input.phase, + previousState: previous?.turn_state ?? null, + state, + summary: summary ?? (input.phase === 'end' ? previous?.turn_summary : null) ?? null, + at: now, + }) + if (state === 'blocked') { + handleFleetAutomationEvent(db, input.workspaceId, { + type: 'agent.turn', + data: { agentId: input.agentId, phase: input.phase, state, summary, at: now }, + }) + } + if (input.phase === 'end') { + try { runAutoReap(db, input.workspaceId) } + catch (error) { console.error(`[swarm-ops] auto-reap failed at turn end: ${error instanceof Error ? error.message : String(error)}`) } + } return ping } @@ -346,6 +430,8 @@ export function approveCommit( const now = new Date().toISOString() db.prepare(`UPDATE agents SET turn_state = 'commit-approved', turn_state_at = ? WHERE id = ?`).run(now, input.agentId) coordEvents.emitLive('agent.commit.approved', { agentId: input.agentId, by: input.by, at: now }) + try { runAutoReap(db, input.workspaceId) } + catch (error) { console.error(`[swarm-ops] auto-reap failed at commit approval: ${error instanceof Error ? error.message : String(error)}`) } return message } @@ -375,11 +461,15 @@ export interface BoardRow { turnState: TurnState | null turnStateAt: string | null turnSummary: string | null + /** Whole minutes a `working` worker has been without Brain contact, or null. */ + silentMinutes: number | null retired: boolean unread: number task: QueueTaskRef | null leases: Array<{ id: string; paths: string[]; symbols: string[]; mode: 'write' | 'read'; expiresAt: string }> worktrees: WorktreeState[] + /** The CLI process the Brain started for this worker, when it started one. */ + runner: RunnerBoardState | null attention: AttentionFlag[] } @@ -398,6 +488,16 @@ export interface SwarmBoard { overlaps: Array<{ worktree: string; agents: string[] }> host: HostSnapshot admission: Admission[] + queue: { + tasks: Array<{ + id: string; title: string; state: string; addressedTo: string | null; priority: number; supersededBy: string | null + claimedBy: string | null; updatedAt: string + }> + recentChanges: Array<{ + id: string; taskId: string; operation: string; byAgent: string; oldValue: string | null + newValue: string | null; note: string | null; occurredAt: string + }> + } } const normalizePath = (path: string): string => resolve(path).replace(/[\\/]+$/, '').toLowerCase() @@ -430,21 +530,33 @@ export function agentsBoard(db: DatabaseSync, workspaceId: string, options: Boar requireWorkspace(db, workspaceId) const host = options.host ?? hostSnapshot() const staleTurnMs = options.staleTurnMs ?? DEFAULT_STALE_TURN_MS + const silentAfterMinutes = watchdogSettings(db, workspaceId).silentAfterMinutes const now = Date.now() + const nowDate = new Date(now) const exhausted = new Set(listQuotas(db).filter(quota => quota.exhausted).map(quota => quota.account)) const leases = listActiveLeases(db, workspaceId) + const runners = runnerBoardStates(db) const agents: BoardRow[] = getAgentPresence(db, { workspaceId }).map(presence => { const profile = loadProfile(db, presence.id) const retired = profile.retired_at !== null const turnState = profile.turn_state as TurnState | null const unread = (db.prepare(` - SELECT COUNT(*) AS n FROM inbox_messages - WHERE workspace_id = ? AND (to_agent = ? OR to_agent IS NULL) AND read_at IS NULL - `).get(workspaceId, presence.id) as { n: number }).n + SELECT COUNT(*) AS n FROM inbox_messages m + WHERE m.workspace_id = ? AND (m.to_agent = ? OR m.to_agent IS NULL) + AND COALESCE((SELECT r.acknowledged_at FROM inbox_delivery_receipts r + WHERE r.message_id = m.id AND r.agent_id = ?), CASE WHEN m.to_agent = ? THEN m.read_at END) IS NULL + `).get(workspaceId, presence.id, presence.id, presence.id) as { n: number }).n + + // The still-reading answers "has this working worker been in touch?" — it is + // a reading with a minute count, never a verdict about the process. + const silentMinutes = turnState === 'working' && !retired + ? silenceMinutesFor(nowDate, profile.turn_state_at, profile.last_contact_at, silentAfterMinutes) + : null const attention: AttentionFlag[] = [] if (!retired) { + if (silentMinutes !== null) attention.push('silent') if (turnState === 'awaiting-commit') attention.push('awaiting-commit') if (turnState === 'needs-task') attention.push('needs-task') if (turnState === 'blocked') attention.push('blocked') @@ -466,13 +578,16 @@ export function agentsBoard(db: DatabaseSync, workspaceId: string, options: Boar turnState, turnStateAt: profile.turn_state_at, turnSummary: profile.turn_summary, + silentMinutes, retired, unread: Number(unread), task: currentTaskOf(db, workspaceId, presence.id), leases: leases.filter(lease => lease.agentId === presence.id).map(lease => ({ - id: lease.id, paths: lease.paths, symbols: lease.symbols, mode: lease.mode, expiresAt: lease.expiresAt, + id: lease.id, taskId: lease.taskId, paths: lease.paths, symbols: lease.symbols, + mode: lease.mode, createdAt: lease.createdAt, expiresAt: lease.expiresAt, })), worktrees: parseWorktrees(profile.worktrees).map(path => worktreeState(path, options.gitStatus === true)), + runner: runners.get(presence.id) ?? null, attention, } }) @@ -496,5 +611,15 @@ export function agentsBoard(db: DatabaseSync, workspaceId: string, options: Boar overlaps, host, admission: (['test', 'build', 'worktree'] as const).map(kind => admitWork(kind, host)), + queue: { + tasks: listQueue(db, workspaceId).map(task => ({ + id: task.id, title: task.title, state: task.state, addressedTo: task.addressed_to, + priority: task.priority, supersededBy: task.superseded_by, claimedBy: task.claimed_by, updatedAt: task.updated_at, + })), + recentChanges: listQueueEvents(db, workspaceId, { limit: 50 }).map(event => ({ + id: event.id, taskId: event.task_id, operation: event.operation, byAgent: event.by_agent, + oldValue: event.old_value, newValue: event.new_value, note: event.note, occurredAt: event.occurred_at, + })), + }, } } diff --git a/src/coord/swarm-read.ts b/src/coord/swarm-read.ts new file mode 100644 index 0000000..a9a16b9 --- /dev/null +++ b/src/coord/swarm-read.ts @@ -0,0 +1,58 @@ +import type { DatabaseSync } from 'node:sqlite' +import { agentsBoard } from './swarm-ops.ts' + +export type SwarmTurnRow = { + id: string + agentId: string + taskId: string | null + startedAt: string + endedAt: string | null + state: string | null + summary: string | null +} + +export function swarmBoard(db: DatabaseSync, workspaceId: string) { + const board = agentsBoard(db, workspaceId, { gitStatus: true }) + return { + measuredAt: board.measuredAt, + overlaps: board.overlaps, + agents: board.agents.map(({ resourceKey: _resourceKey, ...worker }) => worker), + } +} + +export function swarmTurns(db: DatabaseSync, workspaceId: string, limit = 1000): { + historyAvailable: boolean + turns: SwarmTurnRow[] +} { + const exists = db.prepare("SELECT 1 AS found FROM sqlite_master WHERE type = 'table' AND name = 'swarm_turn_history'").get() + if (!exists) return { historyAvailable: false, turns: [] } + const bounded = Math.min(5000, Math.max(1, Math.floor(Number.isFinite(limit) ? limit : 1000))) + const turns = db.prepare(` + SELECT id, agent_id AS agentId, task_id AS taskId, started_at AS startedAt, + ended_at AS endedAt, end_state AS state, summary + FROM swarm_turn_history WHERE workspace_id = ? + ORDER BY started_at DESC LIMIT ? + `).all(workspaceId, bounded) as unknown as SwarmTurnRow[] + return { historyAvailable: true, turns: turns.reverse() } +} + +export function swarmMessages(db: DatabaseSync, workspaceId: string, limit = 1000) { + const bounded = Math.min(5000, Math.max(1, Math.floor(Number.isFinite(limit) ? limit : 1000))) + return db.prepare(` + SELECT id, from_agent AS fromAgent, to_agent AS toAgent, channel, subject, body, + created_at AS createdAt, delivered_at AS deliveredAt, read_at AS readAt + FROM inbox_messages WHERE workspace_id = ? + ORDER BY created_at DESC LIMIT ? + `).all(workspaceId, bounded).reverse() +} + +export function swarmApprovals(db: DatabaseSync, workspaceId: string, limit = 500) { + const bounded = Math.min(2000, Math.max(1, Math.floor(Number.isFinite(limit) ? limit : 500))) + return db.prepare(` + SELECT id, from_agent AS byAgent, to_agent AS agentId, subject, body, + created_at AS approvedAt, read_at AS acknowledgedAt + FROM inbox_messages WHERE workspace_id = ? + AND lower(subject) = 'commit freigegeben' + ORDER BY created_at DESC LIMIT ? + `).all(workspaceId, bounded).reverse() +} diff --git a/src/coord/turn-delivery.ts b/src/coord/turn-delivery.ts new file mode 100644 index 0000000..006d334 --- /dev/null +++ b/src/coord/turn-delivery.ts @@ -0,0 +1,66 @@ +import type { DatabaseSync } from 'node:sqlite' +import { AccessDenied, requireWorkspace } from '../access.ts' +import { deliverTask, ensureQueueSchema, type QueueTask } from '../queue.ts' +import { handleFleetAutomationEvent } from './fleet-automation.ts' +import { inspectDeliveryEvidence, sourceRevision } from './delivery-evidence.ts' + +/** Mark the worker's sole held task delivered at the turn boundary. */ +export function currentTaskForTurnDelivery( + db: DatabaseSync, + workspaceId: string, + agentId: string, +): string { + ensureQueueSchema(db) + requireWorkspace(db, workspaceId) + const held = db.prepare(`SELECT id FROM queue_tasks + WHERE workspace_id = ? AND claimed_by = ? AND state = 'claimed' + ORDER BY claimed_at DESC, created_at DESC`).all(workspaceId, agentId) as Array<{ id: string }> + if (held.length === 0) throw new AccessDenied(`${agentId} has no claimed task to deliver`) + if (held.length > 1) throw new AccessDenied(`${agentId} has ${held.length} claimed tasks; deliver one with swarm deliver <agent> <taskId> --path <evidence>`) + return held[0]!.id +} + +export function deliverTaskAtTurnEnd( + db: DatabaseSync, + taskId: string, + agentId: string, + evidencePath: string, + summary: string | undefined, + options: { workspaceId: string; workspaceRoot: string; repoPath?: string; reviewRequired?: boolean }, +): QueueTask { + return deliverTaskWithEvidence(db, taskId, agentId, evidencePath, summary, options) +} + +export function deliverTaskWithEvidence( + db: DatabaseSync, + taskId: string, + agentId: string, + evidencePath: string, + summary: string | undefined, + options: { workspaceId: string; workspaceRoot: string; repoPath?: string; reviewRequired?: boolean }, +): QueueTask { + const task = db.prepare('SELECT title, state, claimed_by FROM queue_tasks WHERE id = ? AND workspace_id = ?') + .get(taskId, options.workspaceId) as { title: string; state: string; claimed_by: string | null } | undefined + if (!task) throw new AccessDenied(`unknown task: ${taskId}`) + // Check ownership before opening the evidence path supplied by the caller. + // deliverTask repeats this check in its transaction to catch a concurrent change. + if (task.state !== 'claimed') throw new AccessDenied(`task ${taskId} is ${task.state}, not claimed`) + if (task.claimed_by !== agentId) { + throw new AccessDenied(`task ${taskId} is held by ${task.claimed_by ?? 'nobody'}, not ${agentId}`) + } + const evidence = inspectDeliveryEvidence(options.workspaceRoot, evidencePath, { + reviewRequired: options.reviewRequired || /^R-/i.test(task.title), + }) + const delivered = deliverTask(db, taskId, agentId, evidence.path, summary, { + workspaceId: options.workspaceId, + deliveredBy: agentId, + sourceRevision: sourceRevision(options.repoPath ?? options.workspaceRoot), + sha256: evidence.sha256, + reviewJudgment: evidence.reviewJudgment, + reviewedCommit: evidence.reviewedCommit, + }) + handleFleetAutomationEvent(db, options.workspaceId, { + type: 'task.delivered', data: { taskId: delivered.id }, + }) + return delivered +} diff --git a/src/coord/types.ts b/src/coord/types.ts index a73ef14..0352791 100644 --- a/src/coord/types.ts +++ b/src/coord/types.ts @@ -69,4 +69,8 @@ export interface InboxMessage { createdAt: string deliveredAt: string | null readAt: string | null + /** Receipt ACK, stored per recipient and independent of processing. */ + acknowledgedAt?: string | null + /** When the consumer completed handling this message. */ + processedAt?: string | null } diff --git a/src/coord/watchdog.ts b/src/coord/watchdog.ts new file mode 100644 index 0000000..8d059f5 --- /dev/null +++ b/src/coord/watchdog.ts @@ -0,0 +1,467 @@ +/** + * The watchdog: who went quiet, and where a finished turn goes next. + * + * Two failures of 26.09.2026 are the reason this module exists. First, nv03 + * hung in an output loop and nv04 never got an answer again; both sat on the + * board as `working` for hours, because a single worker heartbeat is valid for + * six hours and nothing else asked whether a `working` worker had been in + * touch since. Second, when a worker ended with `awaiting-commit`, the + * integrator had to hand-place the review (R-BR3, INT-R6) and nudge the + * reviewer — the Brain could queue work but not route it. + * + * So the reading here is deliberately about *observed contact*, not about + * liveness: a worker is `still` when it has been `working` for longer than a + * per-workspace threshold (default 45 minutes) and has not touched the Brain + * since — claim, release, ack, message, quota, delivery, or the turn itself. + * The Brain never declares a worker dead; it says how long the room has been + * silent and tells the integrator once per case. + * + * Alongside that sits the reviewer pool: a per-workspace, ordered list of + * reviewers, and a switch (off by default) that turns a finished turn into a + * review task only when registered account and model family both differ. + * Neither a distinct account nor a distinct model label alone proves an + * independent review. + */ +import type { DatabaseSync } from 'node:sqlite' +import { AccessDenied, requireAgent, requireWorkspace } from '../access.ts' +import { enqueueTask } from '../queue.ts' +import { syncDependencies } from './dependencies.ts' +import { coordEvents } from './events.ts' +import { ensureSystemAgent } from './inbox.ts' +import { handleFleetAutomationEvent } from './fleet-automation.ts' +import { resolveReviewIndependence } from './model-family.ts' + +export const DEFAULT_SILENT_AFTER_MINUTES = 45 + +const SCHEMA = ` +CREATE TABLE IF NOT EXISTS watchdog_settings ( + workspace_id TEXT PRIMARY KEY, + silent_after_minutes INTEGER NOT NULL DEFAULT ${DEFAULT_SILENT_AFTER_MINUTES}, + review_auto INTEGER NOT NULL DEFAULT 0, + updated_at TEXT NOT NULL +); +CREATE TABLE IF NOT EXISTS review_pool ( + workspace_id TEXT NOT NULL, + agent_id TEXT NOT NULL, + position INTEGER NOT NULL, + PRIMARY KEY (workspace_id, agent_id) +); +CREATE TABLE IF NOT EXISTS review_routes ( + workspace_id TEXT NOT NULL, + author_id TEXT NOT NULL, + source_task_id TEXT NOT NULL DEFAULT '', + reviewer_id TEXT NOT NULL, + task_id TEXT NOT NULL, + created_at TEXT NOT NULL, + PRIMARY KEY (workspace_id, author_id, source_task_id) +); +` + +/** Add the observations the watchdog needs to the agents table. */ +export function ensureWatchdogSchema(db: DatabaseSync): void { + db.exec(SCHEMA) + const columns = db.prepare('PRAGMA table_info(agents)').all() as unknown as Array<{ name: string }> + const has = (name: string) => columns.some(column => column.name === name) + if (!has('last_contact_at')) db.exec('ALTER TABLE agents ADD COLUMN last_contact_at TEXT') + if (!has('silence_alerted_at')) db.exec('ALTER TABLE agents ADD COLUMN silence_alerted_at TEXT') +} + +/** + * Record that an agent touched the Brain just now. + * + * This is what makes the still-reading meaningful: `turn_state_at` alone says + * when a turn *started*, so a worker that claimed a path or acknowledged a + * message in between was still reported as quiet. Every CLI operation that + * stands for "the process is alive" calls this. It is metadata, never a + * liveness proof — nothing here reaps a worker. + */ +export function touchAgentContact(db: DatabaseSync, agentId: string, at: Date = new Date()): void { + ensureWatchdogSchema(db) + const row = db.prepare('SELECT id FROM agents WHERE id = ?').get(agentId) as { id: string } | undefined + if (row === undefined) return + const nowIso = at.toISOString() + db.prepare(`UPDATE agents SET last_contact_at = ? + WHERE id = ? AND (last_contact_at IS NULL OR last_contact_at < ?)`) + .run(nowIso, agentId, nowIso) +} + +// --------------------------------------------------------------------------- +// Per-workspace settings and the reviewer pool +// --------------------------------------------------------------------------- + +export interface WatchdogSettings { + workspaceId: string + silentAfterMinutes: number + reviewAuto: boolean +} + +interface SettingsRow { + workspace_id: string + silent_after_minutes: number + review_auto: number + updated_at: string +} + +export function watchdogSettings(db: DatabaseSync, workspaceId: string): WatchdogSettings { + ensureWatchdogSchema(db) + requireWorkspace(db, workspaceId) + const row = db.prepare('SELECT * FROM watchdog_settings WHERE workspace_id = ?').get(workspaceId) as unknown as SettingsRow | undefined + return { + workspaceId, + silentAfterMinutes: row?.silent_after_minutes ?? DEFAULT_SILENT_AFTER_MINUTES, + reviewAuto: (row?.review_auto ?? 0) !== 0, + } +} + +function writeSettings(db: DatabaseSync, workspaceId: string, patch: { silentAfterMinutes?: number; reviewAuto?: boolean }): WatchdogSettings { + const current = watchdogSettings(db, workspaceId) + const silentAfterMinutes = patch.silentAfterMinutes ?? current.silentAfterMinutes + const reviewAuto = patch.reviewAuto ?? current.reviewAuto + db.prepare(`INSERT INTO watchdog_settings (workspace_id, silent_after_minutes, review_auto, updated_at) + VALUES (?, ?, ?, ?) + ON CONFLICT(workspace_id) DO UPDATE SET + silent_after_minutes = excluded.silent_after_minutes, + review_auto = excluded.review_auto, + updated_at = excluded.updated_at`) + .run(workspaceId, silentAfterMinutes, reviewAuto ? 1 : 0, new Date().toISOString()) + return { workspaceId, silentAfterMinutes, reviewAuto } +} + +/** How long a `working` worker may go without Brain contact before it is called still. */ +export function setSilentAfterMinutes(db: DatabaseSync, workspaceId: string, minutes: number): WatchdogSettings { + if (!Number.isInteger(minutes) || minutes < 1 || minutes > 24 * 60) { + throw new AccessDenied('silent-after must be a whole number of minutes between 1 and 1440') + } + return writeSettings(db, workspaceId, { silentAfterMinutes: minutes }) +} + +export interface ReviewPoolEntry { agentId: string; account: string | null; retired: boolean } + +interface PoolRow { agent_id: string; position: number; account: string | null; retired_at: string | null } + +export function readReviewPool(db: DatabaseSync, workspaceId: string): ReviewPoolEntry[] { + ensureWatchdogSchema(db) + requireWorkspace(db, workspaceId) + const rows = db.prepare(` + SELECT p.agent_id, p.position, a.account, a.retired_at + FROM review_pool p LEFT JOIN agents a ON a.id = p.agent_id + WHERE p.workspace_id = ? ORDER BY p.position ASC + `).all(workspaceId) as unknown as PoolRow[] + return rows.map(row => ({ agentId: row.agent_id, account: row.account, retired: row.retired_at !== null })) +} + +/** Replace the workspace's reviewer list, in the order given. */ +export function setReviewPool(db: DatabaseSync, workspaceId: string, agentIds: string[]): ReviewPoolEntry[] { + ensureWatchdogSchema(db) + requireWorkspace(db, workspaceId) + const unique = [...new Set(agentIds.map(id => id.trim()).filter(id => id !== ''))] + for (const agentId of unique) requireAgent(db, agentId) + db.exec('BEGIN IMMEDIATE') + try { + db.prepare('DELETE FROM review_pool WHERE workspace_id = ?').run(workspaceId) + unique.forEach((agentId, position) => { + db.prepare('INSERT INTO review_pool (workspace_id, agent_id, position) VALUES (?, ?, ?)') + .run(workspaceId, agentId, position) + }) + db.exec('COMMIT') + } catch (error: unknown) { + db.exec('ROLLBACK') + throw error + } + coordEvents.emitLive('review.pool.set', { workspaceId, reviewers: unique }) + return readReviewPool(db, workspaceId) +} + +/** Turn the automatic review routing on or off. Off is the default. */ +export function setReviewAuto(db: DatabaseSync, workspaceId: string, on: boolean): WatchdogSettings { + const settings = writeSettings(db, workspaceId, { reviewAuto: on }) + coordEvents.emitLive('review.auto', { workspaceId, on }) + return settings +} + +// --------------------------------------------------------------------------- +// Still-Erkennung (the silence reading) +// --------------------------------------------------------------------------- + +export interface SilenceReading { + agentId: string + account: string | null + /** Whole minutes since the last observed Brain contact in this working turn. */ + minutes: number + workingSince: string + lastContactAt: string | null + /** When the integrator was last told about this case, if ever. */ + alertedAt: string | null +} + +interface SilenceRow { + id: string + account: string | null + turn_state_at: string | null + last_contact_at: string | null + silence_alerted_at: string | null +} + +/** + * How many silent minutes a `working` agent has, or null while it is not still. + * + * Pure on purpose: the board and the scan answer the same question with the + * same arithmetic, and a test can move the clock by passing `now` instead of + * waiting 45 real minutes. + */ +export function silenceMinutesFor( + now: Date, + turnStateAt: string | null, + lastContactAt: string | null, + silentAfterMinutes: number, +): number | null { + if (turnStateAt === null) return null + const startedAt = Date.parse(turnStateAt) + if (!Number.isFinite(startedAt)) return null + const contactAt = lastContactAt === null ? Number.NEGATIVE_INFINITY : Date.parse(lastContactAt) + const lastActivity = Number.isFinite(contactAt) ? Math.max(startedAt, contactAt) : startedAt + const elapsedMs = now.getTime() - lastActivity + if (elapsedMs <= silentAfterMinutes * 60_000) return null + return Math.floor(elapsedMs / 60_000) +} + +/** Every agent of the workspace that is `working` and quiet past the threshold. */ +export function silentWorkers( + db: DatabaseSync, + workspaceId: string, + options: { now?: Date; silentAfterMinutes?: number } = {}, +): SilenceReading[] { + ensureWatchdogSchema(db) + requireWorkspace(db, workspaceId) + const now = options.now ?? new Date() + const threshold = options.silentAfterMinutes ?? watchdogSettings(db, workspaceId).silentAfterMinutes + const rows = db.prepare(` + SELECT id, account, turn_state_at, last_contact_at, silence_alerted_at + FROM agents + WHERE workspace_id = ? AND retired_at IS NULL AND turn_state = 'working' AND turn_state_at IS NOT NULL + ORDER BY turn_state_at ASC + `).all(workspaceId) as unknown as SilenceRow[] + + const readings: SilenceReading[] = [] + for (const row of rows) { + const minutes = silenceMinutesFor(now, row.turn_state_at, row.last_contact_at, threshold) + if (minutes === null) continue + readings.push({ + agentId: row.id, + account: row.account, + minutes, + workingSince: row.turn_state_at as string, + lastContactAt: row.last_contact_at, + alertedAt: row.silence_alerted_at, + }) + } + return readings +} + +/** + * Is this case still unannounced? + * + * A case is one working turn with no contact. Any contact after the previous + * alert, or a new turn start, opens a new case — so a worker that came back and + * went quiet again is reported again, while a worker that stays quiet is + * reported exactly once. + */ +function caseIsUnannounced(reading: SilenceReading): boolean { + if (reading.alertedAt === null) return true + const alerted = Date.parse(reading.alertedAt) + if (!Number.isFinite(alerted)) return true + const started = Date.parse(reading.workingSince) + const contact = reading.lastContactAt === null ? Number.NEGATIVE_INFINITY : Date.parse(reading.lastContactAt) + const lastActivity = Number.isFinite(contact) ? Math.max(started, contact) : started + return lastActivity > alerted +} + +export interface WatchdogReport { + workspaceId: string + measuredAt: string + silentAfterMinutes: number + silent: Array<SilenceReading & { alerted: boolean }> + /** Agents the integrator was told about in this scan. */ + alerted: string[] + /** Waiting tasks this scan released because their predecessor had arrived. */ + released: string[] +} + +/** + * One watchdog cycle: read who is still, tell the integrator once per case, + * and reconcile dependencies that a missed event left waiting. + * + * The alert is a message, not a state change: the Brain still does not touch + * another agent's turn. The integrator decides what to do with a quiet tab. + */ +export function scanWatchdog( + db: DatabaseSync, + workspaceId: string, + options: { now?: Date; alert?: boolean } = {}, +): WatchdogReport { + const now = options.now ?? new Date() + const settings = watchdogSettings(db, workspaceId) + const silent = silentWorkers(db, workspaceId, { now, silentAfterMinutes: settings.silentAfterMinutes }) + const alerted: string[] = [] + + if (options.alert !== false) { + for (const reading of silent) { + if (!caseIsUnannounced(reading)) continue + db.exec('BEGIN IMMEDIATE') + try { + const current = silentWorkers(db, workspaceId, { now, silentAfterMinutes: settings.silentAfterMinutes }) + .find(item => item.agentId === reading.agentId) + if (current === undefined || !caseIsUnannounced(current)) { db.exec('COMMIT'); continue } + db.exec('COMMIT') + } catch (error: unknown) { + db.exec('ROLLBACK') + throw error + } + const heldTask = db.prepare(`SELECT id FROM queue_tasks + WHERE workspace_id = ? AND claimed_by = ? AND state = 'claimed' + ORDER BY claimed_at DESC LIMIT 1`).get(workspaceId, reading.agentId) as { id: string } | undefined + const event = coordEvents.emitLive('watchdog.silent', { + workspaceId, agentId: reading.agentId, minutes: reading.minutes, + workingSince: reading.workingSince, lastContactAt: reading.lastContactAt, + heldTaskId: heldTask?.id ?? null, at: now.toISOString(), + }) + const automation = handleFleetAutomationEvent(db, workspaceId, event) + if (automation.action === 'lead-decision-required') { + alerted.push(reading.agentId) + } + } + } + + const released = syncDependencies(db, workspaceId, { now }) + return { + workspaceId, + measuredAt: now.toISOString(), + silentAfterMinutes: settings.silentAfterMinutes, + silent: silent.map(reading => ({ ...reading, alerted: alerted.includes(reading.agentId) })), + alerted, + released, + } +} + +// --------------------------------------------------------------------------- +// Review routing +// --------------------------------------------------------------------------- + +export interface ReviewRoute { + routed: boolean + reason: string + reviewer?: string + taskId?: string +} + +interface AuthorTaskRow { + id: string + title: string + state: string + delivered_path: string | null +} + +/** + * Queue a review for a turn that ended with `awaiting-commit`. + * + * Only when the workspace has switched this on. The reviewer is the first + * pool entry that is not the author, not retired, has a different known + * account, and has current registered model metadata resolving to a different + * family. Unknown, stale, or same-family metadata is skipped. + * + * One review per author and source task: ending the same turn again (or the + * integrator reopening it) must not pile up duplicate review tasks. + */ +export function routeReviewForAuthor( + db: DatabaseSync, + workspaceId: string, + authorId: string, + options: { now?: Date } = {}, +): ReviewRoute { + const now = options.now ?? new Date() + const settings = watchdogSettings(db, workspaceId) + if (!settings.reviewAuto) return { routed: false, reason: 'review routing is off' } + requireAgent(db, authorId) + + const author = db.prepare('SELECT account, worktrees FROM agents WHERE id = ?').get(authorId) as + { account: string | null; worktrees: string | null } | undefined + const sourceTask = db.prepare(` + SELECT id, title, state, delivered_path FROM queue_tasks + WHERE workspace_id = ? AND claimed_by = ? ORDER BY claimed_at DESC LIMIT 1 + `).get(workspaceId, authorId) as unknown as AuthorTaskRow | undefined + const sourceTaskId = sourceTask?.id ?? '' + + const already = db.prepare(` + SELECT r.task_id, r.reviewer_id, reviewer.account AS reviewer_account, reviewer.retired_at + FROM review_routes r JOIN agents reviewer ON reviewer.id = r.reviewer_id + WHERE r.workspace_id = ? AND r.author_id = ? AND r.source_task_id = ? + `).get(workspaceId, authorId, sourceTaskId) as { + task_id: string; reviewer_id: string; reviewer_account: string | null; retired_at: string | null + } | undefined + if (already !== undefined) { + const current = resolveReviewIndependence(db, authorId, already.reviewer_id) + if (already.retired_at !== null || !already.reviewer_account || !author?.account + || already.reviewer_account === author.account || 'reason' in current) { + return { routed: false, reason: 'existing review route independence cannot be proven' } + } + return { routed: false, reason: `already routed (${already.task_id})`, reviewer: already.reviewer_id, taskId: already.task_id } + } + + const reviewer = readReviewPool(db, workspaceId).find(entry => { + if (entry.agentId === authorId || entry.retired) return false + if (entry.account === null || entry.account.trim() === '' || author?.account == null || author.account.trim() === '') return false + if (entry.account === author.account) return false + return resolveReviewIndependence(db, authorId, entry.agentId).independent + }) + if (reviewer === undefined) return { routed: false, reason: 'no reviewer with a different account and known different model family in the pool' } + + const independence = resolveReviewIndependence(db, authorId, reviewer.agentId) + if ('reason' in independence) return { routed: false, reason: `reviewer independence cannot be proven: ${independence.reason}` } + + ensureSystemAgent(db, workspaceId) + const task = enqueueTask(db, workspaceId, { + title: sourceTask === undefined ? `Review: Turn of ${authorId}` : `Review: ${sourceTask.title}`, + body: [ + `Author: ${authorId}${author?.account == null ? '' : ` (${author.account})`}`, + `Model families: ${independence.authorFamily} (${independence.authorModel}) -> ${independence.reviewerFamily} (${independence.reviewerModel})`, + sourceTask === undefined ? 'Task: none claimed' : `Task: ${sourceTask.id}: ${sourceTask.title} (${sourceTask.state})`, + `Evidence: ${sourceTask?.delivered_path ?? '—'}`, + `Worktree: ${parseWorktrees(author?.worktrees).join(', ') || '—'}`, + '', + 'Review this independently, then report the result to integrator.', + ].join('\n'), + addressedTo: reviewer.agentId, + requestedBy: 'integrator', + }) + db.prepare(`INSERT INTO review_routes (workspace_id, author_id, source_task_id, reviewer_id, task_id, created_at) + VALUES (?, ?, ?, ?, ?, ?)`) + .run(workspaceId, authorId, sourceTaskId, reviewer.agentId, task.id, now.toISOString()) + coordEvents.emitLive('review.routed', { + workspaceId, authorId, reviewer: reviewer.agentId, taskId: task.id, at: now.toISOString(), + }) + return { routed: true, reason: 'queued', reviewer: reviewer.agentId, taskId: task.id } +} + +const parseWorktrees = (raw: string | null | undefined): string[] => { + if (raw === null || raw === undefined || raw === '') return [] + try { + const value = JSON.parse(raw) as unknown + return Array.isArray(value) ? value.filter((item): item is string => typeof item === 'string') : [] + } catch { return [] } +} + +/** + * The turn-end half of the watchdog: a worker stopped because its candidate is + * ready (`awaiting-commit`) or because it needs work. Either way a task waiting + * on it may now proceed, and a finished turn may need a reviewer. + */ +export function watchTurnEnd( + db: DatabaseSync, + workspaceId: string, + agentId: string, + state: 'needs-task' | 'awaiting-commit' | 'blocked' | 'paused', +): { released: string[]; review: ReviewRoute | null } { + const released = syncDependencies(db, workspaceId) + const review = state === 'awaiting-commit' ? routeReviewForAuthor(db, workspaceId, agentId) : null + return { released, review } +} diff --git a/src/coord/wave-done.ts b/src/coord/wave-done.ts new file mode 100644 index 0000000..e8a5ee1 --- /dev/null +++ b/src/coord/wave-done.ts @@ -0,0 +1,170 @@ +import { randomUUID } from 'node:crypto' +import type { DatabaseSync } from 'node:sqlite' +import { coordEvents } from './events.ts' + +export interface WaveEvidenceRef { + source: string + authorityRef: string + confidence: string +} + +export interface WaveTaskEvidence { + taskId: string + title: string + state: string + claimedBy: string | null + reviewJudgment: string | null + sourceRevision: string | null + reviewedCommit: string | null + independentReview: (WaveEvidenceRef & { reviewerId: string; judgment: string; commit: string }) | null + integration: (WaveEvidenceRef & { commit: string | null }) | null + ownerDecision: (WaveEvidenceRef & { decision: string }) | null +} + +export interface WaveManifestEvidence extends WaveEvidenceRef { + waveId: string + taskIds: string[] +} + +export interface WaveDoneTask { + taskId: string + title: string + resolution: 'integrated' | 'owner-decision' | 'incomplete' + evidence: string[] +} + +export interface WaveDoneReport { + status: 'DONE' | 'BLOCKED' + waveId: string + eventId: string | null + manifestRef: string | null + generatedAt: string + tasks: WaveDoneTask[] + blockers: string[] + alreadyRecorded?: boolean +} + +export function ensureWaveDoneSchema(db: DatabaseSync): void { + db.exec(`CREATE TABLE IF NOT EXISTS wave_done_reports ( + workspace_id TEXT NOT NULL, + wave_id TEXT NOT NULL, + event_id TEXT NOT NULL, + report_json TEXT NOT NULL, + created_at TEXT NOT NULL, + PRIMARY KEY (workspace_id, wave_id) + )`) +} + +function evaluateWave( + waveId: string, + manifest: WaveManifestEvidence | null, + input: WaveTaskEvidence[], +): Omit<WaveDoneReport, 'eventId' | 'generatedAt'> { + const blockers: string[] = [] + if (waveId.trim() === '') blockers.push('wave id is missing') + const validManifest = manifest !== null && manifest.waveId === waveId + && manifest.source === 'operator' && manifest.confidence === 'authoritative' + && manifest.authorityRef.trim() !== '' && manifest.taskIds.length > 0 + && manifest.taskIds.every(id => id.trim() !== '') + && new Set(manifest.taskIds).size === manifest.taskIds.length + if (!validManifest) blockers.push('authoritative wave manifest is missing or invalid') + const manifestIds = new Set(validManifest ? manifest!.taskIds : []) + const inputIds = new Set(input.map(task => task.taskId)) + for (const taskId of manifestIds) { + if (!inputIds.has(taskId)) blockers.push(`${taskId}: task from the manifest has no task record`) + } + for (const taskId of inputIds) { + if (!manifestIds.has(taskId)) blockers.push(`${taskId}: task is not in the authoritative wave manifest`) + } + if (input.length === 0 && !validManifest) blockers.push('wave has no task evidence') + const seen = new Set<string>() + const tasks = input.map(task => { + const taskBlockers: string[] = [] + if (task.taskId.trim() === '' || seen.has(task.taskId)) taskBlockers.push('task id is missing or duplicated') + seen.add(task.taskId) + if (task.state !== 'delivered') taskBlockers.push('task is not delivered') + const validReview = task.reviewJudgment === 'PASS' + && Boolean(task.reviewedCommit && task.sourceRevision && task.reviewedCommit === task.sourceRevision) + && task.independentReview !== null + && task.independentReview.reviewerId !== '' + && task.independentReview.reviewerId !== task.claimedBy + && task.independentReview.judgment === 'PASS' + && task.independentReview.commit === task.sourceRevision + && task.independentReview.confidence === 'authoritative' + && task.independentReview.authorityRef.trim() !== '' + if (!validReview) { + taskBlockers.push('independent review is missing or does not match the delivered revision') + } + + const validIntegration = task.integration !== null + && task.integration.confidence === 'authoritative' + && task.integration.authorityRef.trim() !== '' + && task.integration.commit !== null + && task.integration.commit.trim() !== '' + const validDecision = task.ownerDecision !== null + && task.ownerDecision.source === 'operator' + && task.ownerDecision.confidence === 'authoritative' + && task.ownerDecision.authorityRef.trim() !== '' + && task.ownerDecision.decision.trim() !== '' + if (!validIntegration && !validDecision) taskBlockers.push('integration proof or explicit owner decision is missing') + blockers.push(...taskBlockers.map(reason => `${task.taskId || '(unknown task)'}: ${reason}`)) + + const refs = [ + validReview ? `review:${task.independentReview!.authorityRef}@${task.independentReview!.commit}` : '', + validIntegration ? task.integration!.authorityRef : '', + validDecision ? task.ownerDecision!.authorityRef : '', + ].filter(Boolean) + return { + taskId: task.taskId, + title: task.title, + resolution: taskBlockers.length > 0 ? 'incomplete' : validIntegration ? 'integrated' : 'owner-decision', + evidence: refs, + } satisfies WaveDoneTask + }) + return { + status: blockers.length === 0 ? 'DONE' : 'BLOCKED', + waveId, + manifestRef: validManifest ? manifest!.authorityRef : null, + tasks, + blockers, + } +} + +/** Persist the first complete report and emit exactly one live WAVE-DONE event. */ +export function recordWaveDone( + db: DatabaseSync, + workspaceId: string, + waveId: string, + manifest: WaveManifestEvidence | null, + evidence: WaveTaskEvidence[], +): WaveDoneReport { + ensureWaveDoneSchema(db) + const existing = db.prepare('SELECT report_json FROM wave_done_reports WHERE workspace_id = ? AND wave_id = ?') + .get(workspaceId, waveId) as { report_json: string } | undefined + if (existing) return { ...(JSON.parse(existing.report_json) as WaveDoneReport), alreadyRecorded: true } + + const evaluated = evaluateWave(waveId, manifest, evidence) + const generatedAt = new Date().toISOString() + if (evaluated.status === 'BLOCKED') return { ...evaluated, eventId: null, generatedAt } + + const report: WaveDoneReport = { ...evaluated, eventId: randomUUID(), generatedAt } + db.exec('BEGIN IMMEDIATE') + try { + const inserted = db.prepare(`INSERT OR IGNORE INTO wave_done_reports + (workspace_id, wave_id, event_id, report_json, created_at) VALUES (?, ?, ?, ?, ?)`) + .run(workspaceId, waveId, report.eventId, JSON.stringify(report), generatedAt) + if (Number(inserted.changes) !== 1) { + const raced = db.prepare('SELECT report_json FROM wave_done_reports WHERE workspace_id = ? AND wave_id = ?') + .get(workspaceId, waveId) as { report_json: string } | undefined + if (!raced) throw new Error('wave completion report was not persisted') + db.exec('COMMIT') + return { ...(JSON.parse(raced.report_json) as WaveDoneReport), alreadyRecorded: true } + } + db.exec('COMMIT') + } catch (error) { + db.exec('ROLLBACK') + throw error + } + coordEvents.emitLive('WAVE-DONE', report) + return report +} diff --git a/src/daemon.ts b/src/daemon.ts index 40dff97..d70c69c 100644 --- a/src/daemon.ts +++ b/src/daemon.ts @@ -24,8 +24,10 @@ import { watch, type FSWatcher } from 'node:fs' import { resolve } from 'node:path' import type { DatabaseSync } from 'node:sqlite' import { canonicalPath, indexPlanetWorkspace } from './planet.ts' +import { cachedOnce, generationCache } from './store/count-cache.ts' import { IndexRunBusy, IndexRunStartFailed, startIndexRun } from './index/runner.ts' import { isNeverIndexedDir } from './indexer/scan.ts' +import { runAutoReap } from './coord/reaper.ts' import { appraiseRun, readPendingReindex, removePendingReindex, storeHome, writePendingReindex, } from './index/runs.ts' @@ -38,12 +40,15 @@ const MIN_INTERVAL_MS = 20_000 const SWEEP_MS = 5 * 60_000 /** How often to look for workspaces registered while this daemon was running. */ const DISCOVERY_MS = 15_000 +const REAP_INTERVAL_MS = 10 * 60_000 /** When another run holds the lock, perform one deferred recheck. */ const BUSY_RETRY_MS = 10_000 /** Poll a retained dirty request without repeatedly probing SQLite's writer lock. */ const BUSY_MONITOR_MS = 30_000 /** Do not turn a persistent worker failure into an autonomous start loop. */ const MAX_PENDING_FAILURES = 2 +/** Bounded memo for the store-containment verdict; event bursts repeat paths. */ +const noiseCache = generationCache('daemon-noise') /** * Is this watcher event about something the indexer would never read? @@ -65,9 +70,15 @@ export function isNoisePath(filename: string | null, home = storeHome()): boolea if (filename === null) return false const segments = filename.split(/[\\/]+/).filter(segment => segment !== '') if (segments.some(segment => isNeverIndexedDir(segment))) return true - const abs = canonicalPath(filename) - const store = canonicalPath(home) - return abs === store || abs.startsWith(store + '\\') || abs.startsWith(store + '/') + // The store test is a pure function of (filename, home) but costs a realpath + // per call, and an indexing burst re-reports the same paths again and again. + // Memoize the verdict, keyed by both so another store home cannot read this + // one's answer; `cachedOnce` keeps the map bounded. + return cachedOnce(noiseCache, `${home}\u0000${filename}`, () => { + const abs = canonicalPath(filename) + const store = canonicalPath(home) + return abs === store || abs.startsWith(store + '\\') || abs.startsWith(store + '/') + }) } export interface DaemonHandle { @@ -423,6 +434,13 @@ export function startDaemon(db: DatabaseSync, options: DaemonOptions = {}): Daem refresh() const sweepTimer = options.timers === false ? null : setInterval(sweep, SWEEP_MS) const discoverTimer = options.timers === false ? null : setInterval(refresh, DISCOVERY_MS) + const reapTimer = options.timers === false ? null : setInterval(() => { + for (const ws of workspaces()) { + try { runAutoReap(db, ws.id) } + catch (error) { log(`[daemon] ${ws.name}: automatic worktree reap failed — ${error instanceof Error ? error.message : String(error)}`) } + } + }, REAP_INTERVAL_MS) + reapTimer?.unref?.() const handle: DaemonHandle = { refresh, watching: () => list() @@ -432,6 +450,7 @@ export function startDaemon(db: DatabaseSync, options: DaemonOptions = {}): Daem stopped = true if (sweepTimer !== null) clearInterval(sweepTimer) if (discoverTimer !== null) clearInterval(discoverTimer) + if (reapTimer !== null) clearInterval(reapTimer) for (const timer of timers.values()) clearTimeout(timer) timers.clear() for (const timer of busyMonitors.values()) clearTimeout(timer) diff --git a/src/disk/analysis.ts b/src/disk/analysis.ts new file mode 100644 index 0000000..00a4fa3 --- /dev/null +++ b/src/disk/analysis.ts @@ -0,0 +1,114 @@ +/** Recommendations and reinstallation checklist built only from inventory metadata. */ +import { existsSync, statSync } from 'node:fs' +import { homedir } from 'node:os' +import { join, resolve } from 'node:path' +import type { DatabaseSync } from 'node:sqlite' +import type { CensusReport } from '../machine/git-census.ts' +import { currentDiskScan, type DiskCategory, type DiskDirectory } from './scan.ts' + +export type DiskRisk = 'recoverable' | 'review' | 'keep' +export interface DiskRecommendation { category: DiskCategory | 'worktree'; paths: string[]; bytes: number; gb: number; risk: DiskRisk; reason: string; recovery: string } +export interface DiskRecommendations { scanId: string | null; totalsGb: Record<DiskRisk, number>; recommendations: DiskRecommendation[] } +export interface WipeCheckItem { path: string; bytes: number; reason: string } +export interface WipeCheck { checkedAt: string; items: WipeCheckItem[]; totalGb: number; unavailable: string[] } + +const GB = 1024 ** 3 +function daysOld(iso: string | null, now: number): number | null { return iso ? Math.floor((now - Date.parse(iso)) / 86_400_000) : null } +function gb(bytes: number): number { return Math.round(bytes / GB * 10) / 10 } + +function rowsFor(db: DatabaseSync): DiskDirectory[] { + const state = currentDiskScan(db) + if (!state) return [] + return db.prepare(`SELECT path,parent,name,category,bytes,files,modified_newest AS modifiedNewest, + modified_oldest AS modifiedOldest,reparse_point AS reparsePoint,error,lock_hash AS lockHash,duplicate_group AS duplicateGroup + FROM disk_directories WHERE scan_id=?`).all(state.scanId) as unknown as DiskDirectory[] +} + +export function diskRecommendations(db: DatabaseSync, census?: CensusReport, now = new Date()): DiskRecommendations { + const state = currentDiskScan(db) + const rows = rowsFor(db) + const repoMap = new Map((census?.repos ?? []).map(repo => [resolve(repo.path).toLowerCase(), repo])) + const items: DiskRecommendation[] = [] + for (const row of rows) { + if (row.reparsePoint || row.error) continue + const age = daysOld(row.modifiedNewest, now.getTime()) + if (row.category === 'dependencies' && row.lockHash) { + items.push({ category: row.category, paths: [row.path], bytes: row.bytes, gb: gb(row.bytes), risk: 'recoverable', + reason: `Lockfile fingerprint ${row.lockHash.slice(0, 12)} is available${age === null ? '' : `; last changed ${age} days ago`}.`, recovery: 'npm ci, pnpm install, or yarn install' }) + } else if (row.category === 'cache' || row.category === 'build') { + items.push({ category: row.category, paths: [row.path], bytes: row.bytes, gb: gb(row.bytes), risk: 'recoverable', + reason: `${row.category === 'cache' ? 'Package cache' : 'Generated build output'}; source files are retained.`, recovery: row.category === 'cache' ? 'download again from the package registry' : 'rebuild from source' }) + } else if (row.category === 'downloads' && age !== null && age >= 180) { + items.push({ category: row.category, paths: [row.path], bytes: row.bytes, gb: gb(row.bytes), risk: 'review', + reason: `Downloads directory last changed ${age} days ago; contents were not read.`, recovery: 'verify each file can be downloaded or recreated' }) + } else if (row.name.toLowerCase() === '.git') { + const repo = repoMap.get(resolve(row.parent ?? row.path).toLowerCase()) + if (repo && (repo.dirtyFiles ?? 0) + (repo.untrackedFiles ?? 0) + repo.unpushed.length + repo.stashes > 0) { + items.push({ category: 'source', paths: [row.parent ?? row.path], bytes: row.bytes, gb: gb(row.bytes), risk: 'keep', + reason: 'Git census reports uncommitted, unpushed or stashed work.', recovery: 'back up the repository, its refs and working files' }) + } else if (repo && repo.orphanWorktrees > 0) { + items.push({ category: 'worktree', paths: [row.parent ?? row.path], bytes: 0, gb: 0, risk: 'review', + reason: `Git census reports ${repo.orphanWorktrees} orphaned worktree registration(s).`, recovery: 'inspect the worktree list and confirm every worktree is backed up before pruning' }) + } else if (age !== null && age >= 365) { + items.push({ category: 'source', paths: [row.parent ?? row.path], bytes: row.bytes, gb: gb(row.bytes), risk: 'review', + reason: `Repository metadata last changed ${age} days ago; check owner and remotes.`, recovery: 'clone again from its remote after confirming all local refs are backed up' }) + } else if (repo) { + items.push({ category: 'source', paths: [row.parent ?? row.path], bytes: row.bytes, gb: gb(row.bytes), risk: 'keep', + reason: 'Git census found no unsaved work; repository ownership and purpose should remain explicit.', recovery: 'retain the repository and its remote reference' }) + } + } + } + const groups = new Map<string, DiskRecommendation[]>() + for (const item of items) if (item.category === 'dependencies') { + const row = rows.find(candidate => candidate.path === item.paths[0]) + if (row?.duplicateGroup) groups.set(row.duplicateGroup, [...(groups.get(row.duplicateGroup) ?? []), item]) + } + for (const group of groups.values()) if (group.length > 1) { + const sorted = [...group].sort((a, b) => b.bytes - a.bytes) + const duplicates = sorted.slice(1) + const paths = duplicates.flatMap(item => item.paths) + const bytes = duplicates.reduce((sum, item) => sum + item.bytes, 0) + items.push({ category: 'dependencies', paths, bytes, gb: gb(bytes), risk: 'recoverable', + reason: `${group.length} dependency trees have the same lockfile hash; one copy can be kept.`, recovery: 'recreate an individual tree with the matching package manager' }) + const duplicatePaths = new Set(paths) + for (let index = items.length - 2; index >= 0; index -= 1) { + const item = items[index]! + if (item.category === 'dependencies' && item.paths.some(path => duplicatePaths.has(path))) items.splice(index, 1) + } + } + items.sort((a, b) => b.bytes - a.bytes) + const totalsGb: Record<DiskRisk, number> = { recoverable: 0, review: 0, keep: 0 } + for (const item of items) totalsGb[item.risk] += item.gb + return { scanId: state?.scanId ?? null, totalsGb, recommendations: items } +} + +export function diskWipeCheck(db: DatabaseSync, census?: CensusReport, now = new Date(), home = homedir()): WipeCheck { + const rows = rowsFor(db) + const byPath = new Map(rows.map(row => [resolve(row.path).toLowerCase(), row])) + const items = new Map<string, WipeCheckItem>() + const repoPaths = (census?.repos ?? []).map(repo => resolve(repo.path).toLowerCase()) + const add = (path: string, reason: string): void => { + const normalized = resolve(path) + if (!existsSync(normalized)) return + const row = byPath.get(normalized.toLowerCase()) + let bytes = row?.bytes ?? 0 + if (!row) { try { bytes = statSync(normalized).size } catch { /* reported as zero when metadata cannot be read */ } } + items.set(normalized.toLowerCase(), { path: normalized, bytes, reason }) + } + for (const repo of census?.repos ?? []) { + if ((repo.dirtyFiles ?? 0) > 0 || (repo.untrackedFiles ?? 0) > 0 || repo.unpushed.length > 0 || (repo.stashes ?? 0) > 0) { + add(repo.path, 'Git census found work that is uncommitted, unpushed or stashed.') + } + } + for (const name of ['.ssh', '.gitconfig', '.claude', '.codex', '.config']) add(join(home, name), 'Personal configuration or credentials metadata; copy securely.') + for (const row of rows) if (row.name === '.obsidian') add(row.parent ?? row.path, 'Workspace contains an Obsidian vault configuration.') + for (const row of rows) { + const inHome = row.path.toLowerCase().startsWith(`${resolve(home).toLowerCase()}\\`) + const inRepo = repoPaths.some(repo => row.path.toLowerCase() === repo || row.path.toLowerCase().startsWith(`${repo}\\`)) + const age = daysOld(row.modifiedNewest, now.getTime()) + if (inHome && !inRepo && age !== null && age <= 90 && !row.reparsePoint) add(row.path, 'Non-repository user-profile folder changed in the last 90 days.') + } + const unavailable = currentDiskScan(db)?.errors ?? [] + const result = [...items.values()].sort((a, b) => b.bytes - a.bytes) + return { checkedAt: now.toISOString(), items: result, totalGb: gb(result.reduce((sum, item) => sum + item.bytes, 0)), unavailable } +} diff --git a/src/disk/scan.ts b/src/disk/scan.ts new file mode 100644 index 0000000..d681903 --- /dev/null +++ b/src/disk/scan.ts @@ -0,0 +1,295 @@ +/** Read-only directory inventory for the disk atlas. File bodies are never read. */ +import { createHash, randomUUID } from 'node:crypto' +import { lstat, readdir, readFile } from 'node:fs/promises' +import { basename, dirname, join, parse, resolve } from 'node:path' +import type { DatabaseSync } from 'node:sqlite' +import { driveRoots } from '../coord/resources.ts' + +export type DiskCategory = 'source' | 'dependencies' | 'cache' | 'build' | 'models' | 'games' | 'virtual-machines' | 'media' | 'downloads' | 'system' | 'other' +export interface DiskDirectory { + path: string; parent: string | null; name: string; category: DiskCategory + bytes: number; files: number; modifiedNewest: string | null; modifiedOldest: string | null + reparsePoint: boolean; error: string | null; lockHash: string | null; duplicateGroup: string | null +} +export interface DiskScanState { + scanId: string; startedAt: string; completedAt: string | null; running: boolean + complete: boolean; roots: string[]; directories: number; files: number; bytes: number; inaccessible: number + errors: string[] +} +export interface DiskScanOptions { roots?: string[]; concurrency?: number; onProgress?: (state: DiskScanState) => void; now?: () => Date; signal?: AbortSignal } + +const SCHEMA = ` +CREATE TABLE IF NOT EXISTS disk_scan_state ( + singleton INTEGER PRIMARY KEY CHECK(singleton = 1), scan_id TEXT NOT NULL, started_at TEXT NOT NULL, + completed_at TEXT, running INTEGER NOT NULL, complete INTEGER NOT NULL DEFAULT 0, roots_json TEXT NOT NULL, directories INTEGER NOT NULL, + files INTEGER NOT NULL, bytes INTEGER NOT NULL, inaccessible INTEGER NOT NULL, errors_json TEXT NOT NULL +); +CREATE TABLE IF NOT EXISTS disk_scan_history ( + scan_id TEXT PRIMARY KEY, started_at TEXT NOT NULL, completed_at TEXT, running INTEGER NOT NULL, + complete INTEGER NOT NULL, roots_json TEXT NOT NULL, directories INTEGER NOT NULL, files INTEGER NOT NULL, + bytes INTEGER NOT NULL, inaccessible INTEGER NOT NULL, errors_json TEXT NOT NULL +); +CREATE TABLE IF NOT EXISTS disk_directories ( + scan_id TEXT NOT NULL, path TEXT NOT NULL, parent TEXT, name TEXT NOT NULL, category TEXT NOT NULL, + bytes INTEGER NOT NULL, files INTEGER NOT NULL, modified_newest TEXT, modified_oldest TEXT, + reparse_point INTEGER NOT NULL, error TEXT, lock_hash TEXT, duplicate_group TEXT, + PRIMARY KEY(scan_id, path) +); +CREATE INDEX IF NOT EXISTS idx_disk_directories_parent ON disk_directories(scan_id, parent); +CREATE TABLE IF NOT EXISTS disk_largest_files ( + scan_id TEXT NOT NULL, path TEXT NOT NULL, bytes INTEGER NOT NULL, modified_at TEXT, + PRIMARY KEY(scan_id, path) +); +` + +export function ensureDiskSchema(db: DatabaseSync): void { + db.exec(SCHEMA) + const columns = db.prepare('PRAGMA table_info(disk_scan_state)').all() as { name: string }[] + if (!columns.some(column => column.name === 'complete')) db.exec('ALTER TABLE disk_scan_state ADD COLUMN complete INTEGER NOT NULL DEFAULT 0') +} + +function categoryFor(path: string): DiskCategory { + const parts = resolve(path).split(/[\\/]+/).filter(Boolean).map(p => p.toLowerCase()) + const name = parts.at(-1) ?? '' + if (name === 'node_modules' || name === '.venv' || name === 'venv' || name === 'vendor') return 'dependencies' + if (parts.some(p => p.includes('ollama') || p.includes('huggingface') || p === 'models') || /\.(gguf|safetensors)$/i.test(name)) return 'models' + if (['cache', 'temp', 'tmp', '.npm', '.npm-cache', '_cacache', 'npm-cache', '.pnpm-store', 'pnpm', '.yarn', 'yarn', '.cache', 'pip', 'cargo', '.gradle', 'nuget'].includes(name)) return 'cache' + if (['dist', 'build', 'out', 'target', '.next', 'coverage'].includes(name)) return 'build' + if (parts.some(p => ['steamapps', 'epic games', 'riot games', 'battle.net'].includes(p))) return 'games' + if (/\.(vhdx|vdi|vmdk|qcow2)$/i.test(name) || parts.some(p => ['docker', 'containers', 'wsl'].includes(p))) return 'virtual-machines' + if (parts.some(p => ['music', 'videos', 'pictures', 'camera roll'].includes(p)) || /\.(mp[34]|wav|flac|mkv|mp4|mov|jpg|jpeg|png|raw)$/i.test(name)) return 'media' + if (parts.some(p => p === 'downloads')) return 'downloads' + if (/^(windows|program files|program files \(x86\)|programdata|system volume information)$/i.test(name)) return 'system' + if (parts.some(p => p === '.git')) return 'source' + return 'other' +} + +function iso(value: Date | null): string | null { return value !== null && Number.isFinite(value.getTime()) ? value.toISOString() : null } +function minDate(a: string | null, b: string | null): string | null { return a === null ? b : b === null ? a : a < b ? a : b } +function maxDate(a: string | null, b: string | null): string | null { return a === null ? b : b === null ? a : a > b ? a : b } +function isReparse(stats: { isSymbolicLink(): boolean; attributes?: number }): boolean { + return stats.isSymbolicLink() || ((stats.attributes ?? 0) & 0x400) !== 0 +} + +/** Keep a bounded min-heap so millions of files do not become a memory leak. */ +function keepLargest(heap: { path: string; bytes: number; modifiedAt: string | null }[], item: typeof heap[number], limit = 1000): void { + if (heap.length < limit) { + heap.push(item) + let index = heap.length - 1 + while (index > 0) { + const parent = Math.floor((index - 1) / 2) + if (heap[parent]!.bytes <= heap[index]!.bytes) break + ;[heap[parent], heap[index]] = [heap[index]!, heap[parent]!] + index = parent + } + return + } + if (item.bytes <= heap[0]!.bytes) return + heap[0] = item + let index = 0 + while (true) { + const left = index * 2 + 1; const right = left + 1 + let smallest = index + if (left < heap.length && heap[left]!.bytes < heap[smallest]!.bytes) smallest = left + if (right < heap.length && heap[right]!.bytes < heap[smallest]!.bytes) smallest = right + if (smallest === index) break + ;[heap[index], heap[smallest]] = [heap[smallest]!, heap[index]!] + index = smallest + } +} + +export function currentDiskScan(db: DatabaseSync): DiskScanState | null { + ensureDiskSchema(db) + const row = db.prepare('SELECT * FROM disk_scan_state WHERE singleton=1').get() as Record<string, unknown> | undefined + if (!row) return null + return { + scanId: String(row.scan_id), startedAt: String(row.started_at), completedAt: row.completed_at === null ? null : String(row.completed_at), + running: Number(row.running) === 1, complete: Number(row.complete) === 1, + roots: JSON.parse(String(row.roots_json)) as string[], directories: Number(row.directories), + files: Number(row.files), bytes: Number(row.bytes), inaccessible: Number(row.inaccessible), errors: JSON.parse(String(row.errors_json)) as string[], + } +} + +export async function scanDisk(db: DatabaseSync, options: DiskScanOptions = {}): Promise<DiskScanState> { + ensureDiskSchema(db) + const now = options.now ?? (() => new Date()) + const roots = [...new Set((options.roots ?? driveRoots()).map(p => resolve(p)))] + const state: DiskScanState = { scanId: randomUUID(), startedAt: now().toISOString(), completedAt: null, running: true, complete: false, + roots, directories: 0, files: 0, bytes: 0, inaccessible: 0, errors: [] } + db.exec('BEGIN IMMEDIATE') + try { + db.exec('DELETE FROM disk_scan_state;') + db.prepare(`INSERT INTO disk_scan_state(singleton,scan_id,started_at,completed_at,running,complete,roots_json,directories,files,bytes,inaccessible,errors_json) + VALUES (1,?,?,NULL,1,0,?,0,0,0,0,'[]')`).run(state.scanId, state.startedAt, JSON.stringify(roots)) + db.prepare(`INSERT INTO disk_scan_history(scan_id,started_at,completed_at,running,complete,roots_json,directories,files,bytes,inaccessible,errors_json) + VALUES (?,?,NULL,1,0,?,0,0,0,0,'[]')`).run(state.scanId, state.startedAt, JSON.stringify(roots)) + db.exec('COMMIT') + } catch (error) { db.exec('ROLLBACK'); throw error } + + const rows = new Map<string, DiskDirectory>() + const largest: { path: string; bytes: number; modifiedAt: string | null }[] = [] + const errors = new Set<string>() + const queue = roots.map(path => ({ path, parent: null as string | null })) + const concurrency = Math.min(32, Math.max(1, options.concurrency ?? 8)) + let cursor = 0 + const worker = async (): Promise<void> => { + while (!options.signal?.aborted) { + const task = queue[cursor] + if (!task) return + cursor += 1 + let row: DiskDirectory = { path: task.path, parent: task.parent, name: basename(task.path) || task.path, + category: categoryFor(task.path), bytes: 0, files: 0, modifiedNewest: null, modifiedOldest: null, + reparsePoint: false, error: null, lockHash: null, duplicateGroup: null } + try { + const st = await lstat(task.path) + row.reparsePoint = isReparse(st) + row.modifiedNewest = row.modifiedOldest = iso(st.mtime) + if (!st.isDirectory() || row.reparsePoint) { rows.set(row.path, row); continue } + const children = await readdir(task.path, { withFileTypes: true }) + for (const child of children) { + const childPath = join(task.path, child.name) + try { + const stChild = await lstat(childPath) + const reparse = isReparse(stChild) + if (reparse) { + rows.set(childPath, { path: childPath, parent: task.path, name: child.name, category: categoryFor(childPath), + bytes: 0, files: 0, modifiedNewest: iso(stChild.mtime), modifiedOldest: iso(stChild.mtime), reparsePoint: true, error: null, + lockHash: null, duplicateGroup: null }) + } else if (stChild.isDirectory()) { + queue.push({ path: childPath, parent: task.path }) + } else if (stChild.isFile()) { + row.bytes += stChild.size; row.files += 1 + const fileCategory = categoryFor(childPath) + if (row.category === 'other' && ['models', 'media', 'virtual-machines'].includes(fileCategory)) row.category = fileCategory + const changed = iso(stChild.mtime) + row.modifiedNewest = maxDate(row.modifiedNewest, changed); row.modifiedOldest = minDate(row.modifiedOldest, changed) + if (['package-lock.json', 'pnpm-lock.yaml', 'yarn.lock'].includes(child.name.toLowerCase())) { + // Lockfiles are the only contents the disk contract permits hashing. + row.lockHash = createHash('sha256').update(await readFile(childPath)).digest('hex') + } + keepLargest(largest, { path: childPath, bytes: stChild.size, modifiedAt: changed }) + state.files += 1; state.bytes += stChild.size + } + } catch (error) { state.inaccessible += 1; errors.add(`${childPath}: ${error instanceof Error ? error.message : String(error)}`) } + } + } catch (error) { row.error = error instanceof Error ? error.message : String(error); state.inaccessible += 1; errors.add(`${task.path}: ${row.error}`) } + rows.set(row.path, row) + state.directories = rows.size + if (state.directories % 256 === 0) { + const snapshot = { ...state, errors: [...errors].slice(0, 100) } + db.prepare(`UPDATE disk_scan_state SET directories=?,files=?,bytes=?,inaccessible=?,errors_json=? WHERE singleton=1 AND scan_id=?`) + .run(snapshot.directories, snapshot.files, snapshot.bytes, snapshot.inaccessible, JSON.stringify(snapshot.errors), state.scanId) + db.prepare(`UPDATE disk_scan_history SET directories=?,files=?,bytes=?,inaccessible=?,errors_json=? WHERE scan_id=?`) + .run(snapshot.directories, snapshot.files, snapshot.bytes, snapshot.inaccessible, JSON.stringify(snapshot.errors), state.scanId) + options.onProgress?.(snapshot) + } + } + } + try { + await Promise.all(Array.from({ length: concurrency }, () => worker())) + } catch (error) { + state.running = false; state.complete = false; state.completedAt = now().toISOString(); state.errors = [...errors, String(error)].slice(0, 100) + db.prepare(`UPDATE disk_scan_state SET running=0,complete=0,completed_at=?,inaccessible=?,errors_json=? WHERE singleton=1 AND scan_id=?`) + .run(state.completedAt, state.inaccessible, JSON.stringify(state.errors), state.scanId) + db.prepare(`UPDATE disk_scan_history SET running=0,complete=0,completed_at=?,inaccessible=?,errors_json=? WHERE scan_id=?`) + .run(state.completedAt, state.inaccessible, JSON.stringify(state.errors), state.scanId) + throw error + } + + if (options.signal?.aborted) { + state.running = false; state.complete = false; state.completedAt = now().toISOString() + state.errors = [...errors, 'scan interrupted before completion'].slice(0, 100) + db.prepare(`UPDATE disk_scan_state SET running=0,complete=0,completed_at=?,directories=?,files=?,bytes=?,inaccessible=?,errors_json=? WHERE singleton=1 AND scan_id=?`) + .run(state.completedAt, state.directories, state.files, state.bytes, state.inaccessible, JSON.stringify(state.errors), state.scanId) + db.prepare(`UPDATE disk_scan_history SET running=0,complete=0,completed_at=?,directories=?,files=?,bytes=?,inaccessible=?,errors_json=? WHERE scan_id=?`) + .run(state.completedAt, state.directories, state.files, state.bytes, state.inaccessible, JSON.stringify(state.errors), state.scanId) + options.onProgress?.(state) + return state + } + + // Aggregate child totals into parents without counting reparse points twice. + for (const row of [...rows.values()].sort((a, b) => b.path.length - a.path.length)) { + if (!row.parent || row.reparsePoint) continue + const parent = rows.get(row.parent) + if (parent) { + parent.bytes += row.bytes; parent.files += row.files + parent.modifiedNewest = maxDate(parent.modifiedNewest, row.modifiedNewest) + parent.modifiedOldest = minDate(parent.modifiedOldest, row.modifiedOldest) + } + } + const projects = [...rows.values()].filter(row => row.category === 'dependencies') + for (const row of projects) { + const parent = rows.get(dirname(row.path)) + if (parent?.lockHash) row.lockHash = parent.lockHash + } + const hashes = new Map<string, string[]>() + for (const row of projects) if (row.lockHash) hashes.set(row.lockHash, [...(hashes.get(row.lockHash) ?? []), row.path]) + for (const row of projects) if (row.lockHash && (hashes.get(row.lockHash)?.length ?? 0) > 1) row.duplicateGroup = row.lockHash + + const insertDir = db.prepare(`INSERT INTO disk_directories VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)`) + const insertFile = db.prepare(`INSERT INTO disk_largest_files VALUES (?,?,?,?)`) + db.exec('BEGIN IMMEDIATE') + try { + for (const row of rows.values()) insertDir.run(state.scanId, row.path, row.parent, row.name, row.category, row.bytes, row.files, + row.modifiedNewest, row.modifiedOldest, row.reparsePoint ? 1 : 0, row.error, row.lockHash, row.duplicateGroup) + largest.sort((a, b) => b.bytes - a.bytes) + for (const file of largest.slice(0, 1000)) insertFile.run(state.scanId, file.path, file.bytes, file.modifiedAt) + state.directories = rows.size; state.inaccessible = Math.max(state.inaccessible, [...rows.values()].filter(row => row.error !== null).length) + state.errors = [...errors].slice(0, 100); state.running = false; state.complete = true; state.completedAt = now().toISOString() + db.prepare(`UPDATE disk_scan_state SET completed_at=?,running=0,complete=1,directories=?,files=?,bytes=?,inaccessible=?,errors_json=? WHERE singleton=1`) + .run(state.completedAt, state.directories, state.files, state.bytes, state.inaccessible, JSON.stringify(state.errors)) + db.prepare(`UPDATE disk_scan_history SET completed_at=?,running=0,complete=1,directories=?,files=?,bytes=?,inaccessible=?,errors_json=? WHERE scan_id=?`) + .run(state.completedAt, state.directories, state.files, state.bytes, state.inaccessible, JSON.stringify(state.errors), state.scanId) + db.exec('COMMIT') + } catch (error) { db.exec('ROLLBACK'); throw error } + options.onProgress?.(state) + return state +} + +export function diskTree(db: DatabaseSync, path: string | null = null): DiskDirectory[] { + ensureDiskSchema(db) + const state = currentDiskScan(db) + if (!state) return [] + const parent = path === null ? null : resolve(path) + const rows = db.prepare(`SELECT path,parent,name,category,bytes,files,modified_newest AS modifiedNewest, + modified_oldest AS modifiedOldest,reparse_point AS reparsePoint,error,lock_hash AS lockHash,duplicate_group AS duplicateGroup + FROM disk_directories WHERE scan_id=? AND parent IS ? ORDER BY bytes DESC,name`) + .all(state.scanId, parent) as unknown as DiskDirectory[] + return rows.map(row => ({ ...row, reparsePoint: Boolean(row.reparsePoint) })) +} + +export function diskScanHistory(db: DatabaseSync, limit = 10): { scans: DiskScanState[]; largestGrowth: Array<{ path: string; bytes: number; previousBytes: number; deltaBytes: number }> } { + ensureDiskSchema(db) + const rows = db.prepare(`SELECT scan_id,started_at,completed_at,running,complete,roots_json,directories,files,bytes,inaccessible,errors_json + FROM disk_scan_history ORDER BY started_at DESC LIMIT ?`).all(Math.max(2, Math.min(50, Math.trunc(limit)))) as Record<string, unknown>[] + const scans = rows.map(row => ({ + scanId: String(row.scan_id), startedAt: String(row.started_at), completedAt: row.completed_at === null ? null : String(row.completed_at), + running: Number(row.running) === 1, complete: Number(row.complete) === 1, roots: JSON.parse(String(row.roots_json)) as string[], + directories: Number(row.directories), files: Number(row.files), bytes: Number(row.bytes), inaccessible: Number(row.inaccessible), + errors: JSON.parse(String(row.errors_json)) as string[], + })) + const [latest, previous] = scans.filter(scan => scan.complete) + if (!latest || !previous) return { scans, largestGrowth: [] } + const latestRows = db.prepare('SELECT path,parent,bytes FROM disk_directories WHERE scan_id=? AND parent IS NOT NULL').all(latest.scanId) as Array<{ path: string; parent: string; bytes: number }> + const previousRows = db.prepare('SELECT path,bytes FROM disk_directories WHERE scan_id=? AND parent IS NOT NULL').all(previous.scanId) as Array<{ path: string; bytes: number }> + const before = new Map(previousRows.map(row => [row.path.toLowerCase(), Number(row.bytes)])) + const deltas = latestRows.map(row => ({ path: row.path, bytes: Number(row.bytes), previousBytes: before.get(row.path.toLowerCase()) ?? 0, + deltaBytes: Number(row.bytes) - (before.get(row.path.toLowerCase()) ?? 0) })).filter(row => row.deltaBytes > 0).sort((a, b) => b.deltaBytes - a.deltaBytes) + const largestGrowth: typeof deltas = [] + for (const row of deltas) { + if (largestGrowth.some(parent => row.path.toLowerCase().startsWith(`${parent.path.toLowerCase()}\\`))) continue + largestGrowth.push(row) + if (largestGrowth.length >= 12) break + } + return { scans, largestGrowth } +} + +export function largestDiskFiles(db: DatabaseSync, limit = 1000): { path: string; bytes: number; modifiedAt: string | null }[] { + ensureDiskSchema(db) + const state = currentDiskScan(db) + if (!state) return [] + return db.prepare(`SELECT path,bytes,modified_at AS modifiedAt FROM disk_largest_files WHERE scan_id=? ORDER BY bytes DESC LIMIT ?`) + .all(state.scanId, Math.max(1, Math.min(1000, Math.trunc(limit)))) as unknown as { path: string; bytes: number; modifiedAt: string | null }[] +} + +export function diskRoot(path: string): string { return parse(resolve(path)).root } diff --git a/src/intel/changes.ts b/src/intel/changes.ts index 656a0c3..26ef265 100644 --- a/src/intel/changes.ts +++ b/src/intel/changes.ts @@ -8,6 +8,7 @@ import type { DatabaseSync } from 'node:sqlite' import { resolve } from 'node:path' import type { DetectChangesResult, DiffSymbolChange } from './types.ts' +import { canonicalPath } from '../planet.ts' import { gitText } from '../indexer/git.ts' export interface DetectChangesOptions { @@ -143,17 +144,29 @@ export function detectChanges( // A planet without a persisted vector is deliberately not allowed to pick // an arbitrary on-disk checkout. An explicit path/id is not an escape // hatch: it must name one of the selected rows. - if (active.length === 0) return noChanges() + if (active.length === 0) { + if (checkoutId || checkoutPath || repoId || options.diffText) { + throw new Error('no selected code checkout is available for change detection') + } + return noChanges() + } const candidates = repoId === undefined ? active : active.filter(checkout => checkout.repoId === repoId) - if (repoId !== undefined && candidates.length === 0) return noChanges() + if (repoId !== undefined && candidates.length === 0) { + throw new Error(`repository is not in the active checkout selection: ${repoId}`) + } const selected = checkoutId ? candidates.find(checkout => checkout.id === checkoutId) : checkoutPath - ? candidates.find(checkout => resolve(checkout.path).toLowerCase() === resolve(checkoutPath!).toLowerCase()) - : undefined - if ((checkoutId || checkoutPath) && selected === undefined) return noChanges() + ? candidates.find(checkout => canonicalPath(checkout.path) === canonicalPath(checkoutPath!)) + : candidates.length === 1 ? candidates[0] : undefined + if ((checkoutId || checkoutPath) && selected === undefined) { + throw new Error('checkout is not in the active selection for this workspace') + } + if (selected === undefined && candidates.length > 1) { + throw new Error('checkout is ambiguous; specify checkoutId or checkoutPath') + } if (selected !== undefined) { checkoutId = selected.id repoId = selected.repoId @@ -162,9 +175,8 @@ export function detectChanges( } if (!checkoutPath && !options.diffText) { - // The no-argument route is allowed to pick only the first persisted - // selection. Plain workspaces retain their earlier fallback because they - // have no Planet checkout inventory at all. + // A selected Planet checkout was resolved above. Plain workspaces retain + // their earlier fallback because they have no Planet checkout inventory. const checkoutRow = active === null ? db.prepare( `SELECT c.id, c.repo_id AS repoId, c.path @@ -172,7 +184,7 @@ export function detectChanges( WHERE p.workspace_id = ? AND c.retired_at IS NULL ORDER BY c.rel_prefix LIMIT 1`) .get(workspaceId) as { id: string; repoId: string; path: string } | undefined - : (repoId === undefined ? active[0] : active.find(checkout => checkout.repoId === repoId)) + : active.find(checkout => checkout.id === checkoutId) if (checkoutRow !== undefined) { checkoutId = checkoutRow.id repoId = checkoutRow.repoId @@ -193,7 +205,9 @@ export function detectChanges( if (!diffText || !diffText.trim()) return noChanges(checkoutId ?? null, repoId ?? null) const parsedHunks = parseDiffHunks(diffText) - const selectedIds = active === null ? null : active.map(checkout => checkout.id) + // A diff belongs to the one checkout resolved above, even when the Planet + // intentionally indexes other checkouts with the same relative file names. + const selectedIds = active === null ? null : checkoutId ? [checkoutId] : [] const fileScope = activeFileScope(selectedIds) const callerScope = activeFileScope(selectedIds, 'f.checkout_id') const changedSymbols: DiffSymbolChange[] = [] diff --git a/src/intel/context.ts b/src/intel/context.ts index 313ffad..dd6100a 100644 --- a/src/intel/context.ts +++ b/src/intel/context.ts @@ -13,6 +13,7 @@ import type { SymbolContextResult, } from './types.ts' import { annotateSymbolVendor } from './vendor.ts' +import { activePlanetFileScope } from '../planet.ts' export interface SymbolContextOptions { workspaceId?: string @@ -40,6 +41,19 @@ export function getSymbolContext( name: string, options?: SymbolContextOptions ): SymbolContextResult { + const fileScope = options?.workspaceId + ? activePlanetFileScope(db, options.workspaceId, 'f.checkout_id', 'f.path') + : null + const relatedScopeSql = options?.workspaceId + ? ` AND e.workspace_id = ? AND f.workspace_id = ? AND ${fileScope!.sql}` + : '' + const relatedScopeParams: SQLInputValue[] = options?.workspaceId + ? [options.workspaceId, options.workspaceId, ...fileScope!.params] + : [] + const outgoingScopeSql = options?.workspaceId + ? ` AND e.workspace_id = ? AND ((e.dst_symbol IS NULL AND e.dst_file IS NULL) + OR (f.workspace_id = ? AND ${fileScope!.sql}))` + : '' let sql = ` SELECT s.id, s.name, s.kind, f.path as file, s.line, s.end_line as endLine, s.exported, s.container, f.repo_id as repoId, f.checkout_id as checkoutId @@ -52,6 +66,7 @@ export function getSymbolContext( sql += ' AND f.workspace_id = ?' params.push(options.workspaceId) } + if (fileScope !== null) { sql += ` AND ${fileScope.sql}`; params.push(...fileScope.params) } if (options?.repoId) { sql += ' AND f.repo_id = ?' @@ -106,8 +121,8 @@ export function getSymbolContext( }) ) - // If ambiguous across distinct files without file option - if (candidates.length > 1 && !options?.file) { + // A partial file hint may still match multiple files. + if (candidates.length > 1) { const distinctFiles = new Set(candidates.map(c => c.file)) if (distinctFiles.size > 1) { return { @@ -132,11 +147,11 @@ export function getSymbolContext( FROM edges e LEFT JOIN symbols s ON e.src_symbol = s.id LEFT JOIN files f ON (s.file_id = f.id OR e.src_file = f.id) - WHERE e.dst_symbol = ? AND e.kind = 'calls' + WHERE e.dst_symbol = ? AND e.kind = 'calls'${relatedScopeSql} ORDER BY e.id ASC ` ) - .all(target.id) as unknown as Array<{ + .all(target.id, ...relatedScopeParams) as unknown as Array<{ edge_id: number src_symbol: number | null src_file: number | null @@ -165,11 +180,11 @@ export function getSymbolContext( FROM edges e LEFT JOIN symbols s ON e.src_symbol = s.id LEFT JOIN files f ON (s.file_id = f.id OR e.src_file = f.id) - WHERE e.dst_symbol = ? AND e.kind IN ('references', 'imports') + WHERE e.dst_symbol = ? AND e.kind IN ('references', 'imports')${relatedScopeSql} ORDER BY e.id ASC ` ) - .all(target.id) as unknown as Array<{ + .all(target.id, ...relatedScopeParams) as unknown as Array<{ edge_id: number src_symbol: number | null src_file: number | null @@ -198,11 +213,11 @@ export function getSymbolContext( FROM edges e LEFT JOIN symbols s ON e.dst_symbol = s.id LEFT JOIN files f ON (s.file_id = f.id OR e.dst_file = f.id) - WHERE e.src_symbol = ? AND e.kind = 'calls' + WHERE e.src_symbol = ? AND e.kind = 'calls'${outgoingScopeSql} ORDER BY e.id ASC ` ) - .all(target.id) as unknown as Array<{ + .all(target.id, ...relatedScopeParams) as unknown as Array<{ edge_id: number dst_symbol: number | null dst_file: number | null @@ -231,11 +246,11 @@ export function getSymbolContext( FROM edges e LEFT JOIN symbols s ON e.dst_symbol = s.id LEFT JOIN files f ON (s.file_id = f.id OR e.dst_file = f.id) - WHERE e.src_symbol = ? AND e.kind IN ('references', 'imports') + WHERE e.src_symbol = ? AND e.kind IN ('references', 'imports')${outgoingScopeSql} ORDER BY e.id ASC ` ) - .all(target.id) as unknown as Array<{ + .all(target.id, ...relatedScopeParams) as unknown as Array<{ edge_id: number dst_symbol: number | null dst_file: number | null diff --git a/src/intel/cypher.ts b/src/intel/cypher.ts index 5c3f913..c864dd1 100644 --- a/src/intel/cypher.ts +++ b/src/intel/cypher.ts @@ -6,6 +6,7 @@ */ import type { DatabaseSync, SQLInputValue } from 'node:sqlite' import type { CypherQueryResult } from './types.ts' +import { activePlanetFileScope } from '../planet.ts' export interface JsonGraphQuery { match?: { @@ -38,6 +39,36 @@ function formatMarkdownTable(columns: string[], rows: Array<Record<string, unkno return [header, separator, ...dataRows].join('\n') } +function safeWhere(clause: string, columns: Record<string, string>): { sql: string; params: SQLInputValue[] } | null { + if (!clause) return { sql: '', params: [] } + const predicates: string[] = [] + const params: SQLInputValue[] = [] + let rest = clause.trim() + while (rest) { + const match = rest.match(/^([A-Za-z_]\w*)\.([A-Za-z_]\w*)\s*(=|!=|<=|>=|<|>|LIKE)\s*('(?:''|[^'])*'|"(?:""|[^"])*"|-?\d+(?:\.\d+)?)/i) + if (!match) return null + const column = columns[`${match[1]}.${match[2]}`] + if (!column) return null + const value = match[4] + const quoted = value.startsWith("'") || value.startsWith('"') + params.push(quoted ? value.slice(1, -1).replace(value[0] === "'" ? /''/g : /""/g, value[0]) : Number(value)) + predicates.push(`${column} ${match[3].toUpperCase()} ?`) + rest = rest.slice(match[0].length).trim() + if (!rest) break + const and = rest.match(/^AND\b\s*/i) + if (!and) return null + rest = rest.slice(and[0].length) + } + return { sql: predicates.join(' AND '), params } +} + +function queryError(start: number, error: string): CypherQueryResult { + return { + columns: ['error'], rows: [{ error }], markdown: formatMarkdownTable(['error'], [{ error }]), + rowCount: 0, timingMs: Math.round((performance.now() - start) * 10) / 10, + } +} + /** * Translates and executes a Cypher or JSON-DSL graph query against the SQLite database. */ @@ -47,6 +78,9 @@ export function executeCypherQuery( options?: { limit?: number; workspaceId?: string } ): CypherQueryResult { const start = performance.now() + if (options?.limit !== undefined && !validLimit(options.limit)) { + return queryError(start, 'limit must be an integer from 1 to 500') + } // Handle JSON-DSL if passed as object or JSON string if (typeof queryOrDsl === 'object' || (typeof queryOrDsl === 'string' && queryOrDsl.trim().startsWith('{'))) { @@ -61,6 +95,10 @@ export function executeCypherQuery( return executeCypherString(db, cypher, start, options?.limit, options?.workspaceId) } +function validLimit(value: unknown): value is number { + return typeof value === 'number' && Number.isSafeInteger(value) && value >= 1 && value <= 500 +} + function executeCypherString( db: DatabaseSync, cypher: string, @@ -71,6 +109,7 @@ function executeCypherString( const defaultLimit = overrideLimit ?? 50 let sql = '' let columns: string[] = [] + let whereColumns: Record<string, string> = {} // Extract LIMIT if present let limit = defaultLimit @@ -78,6 +117,7 @@ function executeCypherString( if (limitMatch) { limit = parseInt(limitMatch[1], 10) } + if (!validLimit(limit)) return queryError(start, 'limit must be an integer from 1 to 500') // Extract RETURN clause const returnMatch = cypher.match(/\bRETURN\s+(.+?)(?:\s+ORDER\s+BY|\s+LIMIT|$)/i) @@ -112,6 +152,7 @@ function executeCypherString( JOIN symbols s ON s.file_id = f.id ` columns = [`${srcAlias}.path`, `${dstAlias}.name`, `${dstAlias}.kind`] + whereColumns = { [`${srcAlias}.path`]: 'f.path', [`${dstAlias}.name`]: 's.name', [`${dstAlias}.kind`]: 's.kind' } } else if (relType === 'LINKS_TO') { sql = ` SELECT f1.path as "${srcAlias}.path", nl.target as "${dstAlias}.path" @@ -119,6 +160,7 @@ function executeCypherString( JOIN files f1 ON nl.file_id = f1.id ` columns = [`${srcAlias}.path`, `${dstAlias}.path`] + whereColumns = { [`${srcAlias}.path`]: 'f1.path' } } else { // Default: CALLS or REFERENCES const kindFilter = relType === 'REFERENCES' ? "('references', 'imports')" : "('calls')" @@ -132,6 +174,8 @@ function executeCypherString( WHERE e.kind IN ${kindFilter} ` columns = [`${srcAlias}.name`, `${dstAlias}.name`] + whereColumns = { [`${srcAlias}.name`]: 'src.name', [`${srcAlias}.kind`]: 'src.kind', + [`${dstAlias}.name`]: 'dst.name', [`${dstAlias}.kind`]: 'dst.kind' } } } else { // Pattern 2: Single node match (n:Label) or (n:Label {prop: 'val'}) @@ -143,12 +187,15 @@ function executeCypherString( if (label === 'file') { sql = `SELECT f.path as "${alias}.path", f.size as "${alias}.size", f.loc as "${alias}.loc" FROM files f WHERE 1=1` columns = [`${alias}.path`] + whereColumns = { [`${alias}.path`]: 'f.path', [`${alias}.size`]: 'f.size', [`${alias}.loc`]: 'f.loc' } } else if (label === 'note') { sql = `SELECT f.path as "${alias}.path", nb.text as "${alias}.text" FROM files f JOIN note_body nb ON f.id = nb.file_id WHERE 1=1` columns = [`${alias}.path`] + whereColumns = { [`${alias}.path`]: 'f.path' } } else if (label === 'repo') { sql = `SELECT r.name as "${alias}.name", r.remote_url as "${alias}.remote_url" FROM repos r WHERE 1=1` columns = [`${alias}.name`] + whereColumns = { [`${alias}.name`]: 'r.name' } } else { // Symbol or specific symbol kind (Function, Class, Interface, Method, etc.) const kindFilter = @@ -163,6 +210,9 @@ function executeCypherString( WHERE 1=1${kindFilter} ` columns = [`${alias}.name`, `${alias}.kind`, `${alias}.filePath`] + whereColumns = { [`${alias}.name`]: 's.name', [`${alias}.kind`]: 's.kind', + [`${alias}.filePath`]: 'f.path', [`${alias}.path`]: 'f.path', + [`${alias}.line`]: 's.line', [`${alias}.exported`]: 's.exported' } } if (props) { @@ -178,20 +228,12 @@ function executeCypherString( } } - // Apply WHERE conditions if present - if (whereClause) { - const translatedWhere = whereClause - .replace(/\b(\w+)\.filePath\b/g, 'f.path') - .replace(/\b(\w+)\.path\b/g, 'f.path') - .replace(/\b(\w+)\.name\b/g, 's.name') - .replace(/\b(\w+)\.kind\b/g, 's.kind') - .replace(/\b(\w+)\.line\b/g, 's.line') - .replace(/\b(\w+)\.exported\b/g, 's.exported') - sql += ` AND (${translatedWhere})` - } + const parsedWhere = safeWhere(whereClause, whereColumns) + if (parsedWhere === null) return queryError(start, 'unsupported WHERE predicate') + if (parsedWhere.sql) sql += ` AND (${parsedWhere.sql})` // Scope before wrapping a count query: the aliases live inside its subquery. - const scoped = scopeSql(sql, workspaceId) + const scoped = scopeSql(db, sql, workspaceId) sql = scoped.sql // Handle custom RETURN projection if simple @@ -206,7 +248,7 @@ function executeCypherString( let rows: Array<Record<string, unknown>> = [] try { - rows = db.prepare(sql).all(...scoped.params) as Array<Record<string, unknown>> + rows = db.prepare(sql).all(...parsedWhere.params, ...scoped.params) as Array<Record<string, unknown>> if (rows.length > 0 && columns.length === 0) { columns = Object.keys(rows[0]) } @@ -239,6 +281,7 @@ function executeJsonDsl( workspaceId?: string, ): CypherQueryResult { const limit = overrideLimit ?? dsl.limit ?? 50 + if (!validLimit(limit)) return queryError(start, 'limit must be an integer from 1 to 500') const match = dsl.match let sql = '' let columns: string[] = [] @@ -279,7 +322,7 @@ function executeJsonDsl( } } - const scoped = scopeSql(sql, workspaceId) + const scoped = scopeSql(db, sql, workspaceId) sql = scoped.sql + ` LIMIT ${limit}` let rows: Array<Record<string, unknown>> = [] @@ -309,16 +352,30 @@ function executeJsonDsl( } /** Every generated query anchors its rows in one of these workspace-aware aliases. */ -function scopeSql(sql: string, workspaceId?: string): { sql: string; params: SQLInputValue[] } { +function scopeSql(db: DatabaseSync, sql: string, workspaceId?: string): { sql: string; params: SQLInputValue[] } { if (!workspaceId) return { sql, params: [] } if (/\bFROM repos r\b/i.test(sql)) { - return { sql: `${sql} AND r.planet_id IN (SELECT id FROM planets WHERE workspace_id = ?)`, params: [workspaceId] } + const files = activePlanetFileScope(db, workspaceId, 'rf.checkout_id', 'rf.path') + return { + sql: `${sql} AND r.planet_id IN (SELECT id FROM planets WHERE workspace_id = ?) + AND EXISTS (SELECT 1 FROM files rf WHERE rf.repo_id = r.id AND rf.workspace_id = ? AND ${files.sql})`, + params: [workspaceId, workspaceId, ...files.params], + } } if (/\bFROM note_links nl\b/i.test(sql)) { - return { sql: `${sql} AND f1.workspace_id = ?`, params: [workspaceId] } + const files = activePlanetFileScope(db, workspaceId, 'f1.checkout_id', 'f1.path') + return { sql: `${sql} AND f1.workspace_id = ? AND ${files.sql}`, params: [workspaceId, ...files.params] } } if (/\bFROM edges e\b/i.test(sql)) { - return { sql: `${sql} AND e.workspace_id = ?`, params: [workspaceId] } + const src = activePlanetFileScope(db, workspaceId, 'sf.checkout_id', 'sf.path') + const dst = activePlanetFileScope(db, workspaceId, 'df.checkout_id', 'df.path') + return { + sql: `${sql} AND e.workspace_id = ? + AND EXISTS (SELECT 1 FROM files sf WHERE sf.id = src.file_id AND sf.workspace_id = ? AND ${src.sql}) + AND EXISTS (SELECT 1 FROM files df WHERE df.id = dst.file_id AND df.workspace_id = ? AND ${dst.sql})`, + params: [workspaceId, workspaceId, ...src.params, workspaceId, ...dst.params], + } } - return { sql: `${sql} AND f.workspace_id = ?`, params: [workspaceId] } + const files = activePlanetFileScope(db, workspaceId, 'f.checkout_id', 'f.path') + return { sql: `${sql} AND f.workspace_id = ? AND ${files.sql}`, params: [workspaceId, ...files.params] } } diff --git a/src/intel/impact.ts b/src/intel/impact.ts index 53555a3..bf03614 100644 --- a/src/intel/impact.ts +++ b/src/intel/impact.ts @@ -6,12 +6,14 @@ */ import type { DatabaseSync, SQLInputValue } from 'node:sqlite' import type { BlastRadiusResult, ImpactNode } from './types.ts' +import { activePlanetFileScope } from '../planet.ts' export interface BlastRadiusOptions { workspaceId?: string direction?: 'upstream' | 'downstream' | 'both' maxDepth?: number repoId?: string + checkoutId?: string file?: string } @@ -29,27 +31,38 @@ export function getBlastRadius( const direction = options?.direction ?? 'both' const maxDepth = Math.max(1, Math.min(5, options?.maxDepth ?? 3)) + const fileScope = options?.workspaceId + ? activePlanetFileScope(db, options.workspaceId, 'f.checkout_id', 'f.path') + : null + const scopeSql = fileScope === null ? '' : ` AND f.workspace_id = ? AND ${fileScope.sql}` + const scopeParams: SQLInputValue[] = options?.workspaceId + ? [options.workspaceId, ...fileScope!.params] : [] + const targetCheckoutSql = options?.checkoutId ? ' AND f.checkout_id = ?' : '' + const targetCheckoutParams: SQLInputValue[] = options?.checkoutId ? [options.checkoutId] : [] // Find initial symbol - let initialSymbol: { id: number; name: string; file: string } | null = null + let initialSymbol: { id: number; name: string; file: string; fileId: number } | null = null if (targetId !== undefined) { const row = db .prepare( - `SELECT s.id, s.name, f.path as file + `SELECT s.id, s.name, f.path as file, f.id as fileId FROM symbols s JOIN files f ON s.file_id = f.id - WHERE s.id = ?${options?.workspaceId ? ' AND f.workspace_id = ?' : ''}` + WHERE s.id = ?${scopeSql}${targetCheckoutSql}${options?.repoId ? ' AND f.repo_id = ?' : ''}` ) - .get(targetId, ...(options?.workspaceId ? [options.workspaceId] : [])) as { id: number; name: string; file: string } | undefined + .get(targetId, ...scopeParams, ...targetCheckoutParams, ...(options?.repoId ? [options.repoId] : [])) as { id: number; name: string; file: string; fileId: number } | undefined if (row) initialSymbol = row } - if (!initialSymbol) { - let sql = `SELECT s.id, s.name, f.path as file + if (!initialSymbol && targetId === undefined) { + let sql = `SELECT s.id, s.name, f.path as file, f.id as fileId FROM symbols s JOIN files f ON s.file_id = f.id WHERE s.name = ?` const params: SQLInputValue[] = [targetName] - if (options?.workspaceId) { sql += ' AND f.workspace_id = ?'; params.push(options.workspaceId) } + sql += scopeSql + params.push(...scopeParams) + sql += targetCheckoutSql + params.push(...targetCheckoutParams) if (targetFile) { sql += ' AND f.path LIKE ?' params.push(`%${targetFile.replace(/\\/g, '/')}%`) @@ -58,15 +71,22 @@ export function getBlastRadius( sql += ' AND f.repo_id = ?' params.push(options.repoId) } - sql += ' ORDER BY s.id ASC LIMIT 1' - const row = db.prepare(sql).get(...params) as - | { id: number; name: string; file: string } - | undefined - if (row) initialSymbol = row + sql += ' ORDER BY s.id ASC LIMIT 51' + const rows = db.prepare(sql).all(...params) as Array<{ id: number; name: string; file: string; fileId: number }> + if (rows.length > 1) { + return { + status: 'ambiguous', + candidates: rows.map(row => ({ id: row.id, file: row.file })), + target: { name: targetName }, direction, maxDepth, + totalImpacted: 0, nodes: {}, risk: 'low', + } + } + if (rows[0]) initialSymbol = rows[0] } if (!initialSymbol) { return { + status: 'not_found', target: { name: targetName }, direction, maxDepth, @@ -80,15 +100,28 @@ export function getBlastRadius( const nodesByDepth: Record<number, ImpactNode[]> = {} let currentLevelIds = [initialSymbol.id] const impactedRepos = new Set<string>() + const upstreamSymbolFileDepth = new Map<number, number>() + const shadowedExport = db.prepare( + 'SELECT 1 FROM symbols WHERE file_id = ? AND name = ? AND exported = 1 LIMIT 1' + ) + // An import binding proves symbol impact only for a unique exported symbol. + const exported = db.prepare( + 'SELECT id FROM symbols WHERE file_id = ? AND name = ? AND exported = 1 LIMIT 2' + ).all(initialSymbol.fileId, initialSymbol.name) as Array<{ id: number }> + type ImportTarget = { fileId: number; name: string } + let importFrontier: ImportTarget[] = exported.length === 1 && exported[0].id === initialSymbol.id + ? [{ fileId: initialSymbol.fileId, name: initialSymbol.name }] : [] + const seenImportTargets = new Set(importFrontier.map(target => `${target.fileId}:${target.name}`)) - for (let d = 1; d <= maxDepth && currentLevelIds.length > 0; d++) { + for (let d = 1; d <= maxDepth && (currentLevelIds.length > 0 || importFrontier.length > 0); d++) { const nextLevelNodes: ImpactNode[] = [] - const placeholders = currentLevelIds.map(() => '?').join(',') + const nextImportFrontier: ImportTarget[] = [] + const placeholders = currentLevelIds.length > 0 ? currentLevelIds.map(() => '?').join(',') : 'NULL' const confidence = Math.round(0.9 * Math.pow(0.85, d - 1) * 100) / 100 if (direction === 'upstream' || direction === 'both') { const upstreamSql = ` - SELECT DISTINCT s.id, s.name, s.kind, f.path as file, + SELECT DISTINCT s.id, s.name, s.kind, f.id as fileId, f.path as file, f.repo_id as repoId, f.checkout_id as checkoutId, e.kind as relationType FROM edges e @@ -97,11 +130,13 @@ export function getBlastRadius( WHERE e.dst_symbol IN (${placeholders}) AND e.src_symbol IS NOT NULL ${options?.workspaceId ? 'AND e.workspace_id = ?' : ''} + ${scopeSql} ` - const rows = db.prepare(upstreamSql).all(...currentLevelIds, ...(options?.workspaceId ? [options.workspaceId] : [])) as unknown as Array<{ + const rows = db.prepare(upstreamSql).all(...currentLevelIds, ...(options?.workspaceId ? [options.workspaceId] : []), ...scopeParams) as unknown as Array<{ id: number name: string kind: string + fileId: number file: string repoId: string | null checkoutId: string | null @@ -111,6 +146,7 @@ export function getBlastRadius( for (const r of rows) { if (!visited.has(r.id)) { visited.add(r.id) + if (!upstreamSymbolFileDepth.has(r.fileId)) upstreamSymbolFileDepth.set(r.fileId, d) if (r.repoId) impactedRepos.add(r.repoId) nextLevelNodes.push({ depth: d, @@ -126,11 +162,11 @@ export function getBlastRadius( } } - // Fallback on level 1: Check unresolved call edges (raw_target) and import graph + // Fallback on level 1: check unresolved call edges by their raw target. if (d === 1) { try { const rawSql = ` - SELECT DISTINCT s.id, s.name, s.kind, f.path as file, + SELECT DISTINCT s.id, s.name, s.kind, f.id as fileId, f.path as file, f.repo_id as repoId, f.checkout_id as checkoutId, e.kind as relationType FROM edges e @@ -139,12 +175,14 @@ export function getBlastRadius( WHERE e.raw_target = ? AND e.src_symbol IS NOT NULL ${options?.workspaceId ? 'AND e.workspace_id = ?' : ''} + ${scopeSql} LIMIT 50 ` - const rawRows = db.prepare(rawSql).all(initialSymbol.name, ...(options?.workspaceId ? [options.workspaceId] : [])) as typeof rows + const rawRows = db.prepare(rawSql).all(initialSymbol.name, ...(options?.workspaceId ? [options.workspaceId] : []), ...scopeParams) as typeof rows for (const r of rawRows) { if (!visited.has(r.id)) { visited.add(r.id) + if (!upstreamSymbolFileDepth.has(r.fileId)) upstreamSymbolFileDepth.set(r.fileId, d) if (r.repoId) impactedRepos.add(r.repoId) nextLevelNodes.push({ depth: d, @@ -160,56 +198,68 @@ export function getBlastRadius( } } - // Import graph fallback - const moduleBase = initialSymbol.file.split('/').pop()?.replace(/\.[^.]+$/, '') ?? '' - const importSql = ` - SELECT DISTINCT COALESCE(s.id, -f.id) as id, - COALESCE(s.name, f.path) as name, - COALESCE(s.kind, 'file') as kind, - f.path as file, - f.repo_id as repoId, - f.checkout_id as checkoutId, - 'imports' as relationType - FROM file_imports fi - JOIN files f ON fi.file_id = f.id - LEFT JOIN symbols s ON s.file_id = f.id AND s.exported = 1 - WHERE (fi.imported_name = ? OR fi.local_name = ? - ${moduleBase ? "OR fi.specifier LIKE '%' || ? OR fi.specifier = ?" : ''}) - AND f.path != ? - ${options?.workspaceId ? 'AND fi.workspace_id = ?' : ''} - LIMIT 50 - ` - const importParams = [ - initialSymbol.name, initialSymbol.name, - ...(moduleBase ? [moduleBase, moduleBase] : []), - initialSymbol.file, - ...(options?.workspaceId ? [options.workspaceId] : []), - ] - const importRows = db.prepare(importSql).all(...importParams) as typeof rows - for (const r of importRows) { - if (!visited.has(r.id)) { - visited.add(r.id) - if (r.repoId) impactedRepos.add(r.repoId) - nextLevelNodes.push({ - depth: d, - id: r.id, - name: r.name, - kind: r.kind, - file: r.file, - repoId: r.repoId, - checkoutId: r.checkoutId, - relationType: 'imports', - confidence: confidence * 0.8, - }) + } catch {} + } + + // Follow named bindings and explicit star re-exports through resolved + // file edges. Plain namespace/default imports cannot identify a symbol. + for (const target of importFrontier) { + const importSql = ` + SELECT DISTINCT -f.id as id, f.id as fileId, + f.path as name, 'file' as kind, f.path as file, + f.repo_id as repoId, f.checkout_id as checkoutId, + fi.local_name as localName, fi.imported_name as importedName + FROM file_imports fi + JOIN files f ON fi.file_id = f.id + JOIN edges e ON e.workspace_id = fi.workspace_id + AND e.kind = 'imports' AND e.resolved = 1 + AND e.src_file = fi.file_id AND e.raw_target = fi.specifier + AND e.dst_file = ? + WHERE (fi.imported_name = ? OR (fi.local_name = '*' AND fi.imported_name = '*')) + AND f.id != ? + ${options?.workspaceId ? 'AND fi.workspace_id = ?' : ''} + ${scopeSql} + LIMIT 50 + ` + const importRows = db.prepare(importSql).all( + target.fileId, target.name, target.fileId, + ...(options?.workspaceId ? [options.workspaceId] : []), ...scopeParams, + ) as Array<{ + id: number; fileId: number; name: string; kind: string; file: string + repoId: string | null; checkoutId: string | null + localName: string | null; importedName: string | null + }> + for (const r of importRows) { + const starReexport = r.localName === '*' && r.importedName === '*' + if (starReexport && shadowedExport.get(r.fileId, target.name)) continue + if (starReexport) { + const key = `${r.fileId}:${target.name}` + if (!seenImportTargets.has(key)) { + seenImportTargets.add(key) + nextImportFrontier.push({ fileId: r.fileId, name: target.name }) } } - } catch {} + if (visited.has(r.id)) continue + visited.add(r.id) + if (r.repoId) impactedRepos.add(r.repoId) + nextLevelNodes.push({ + depth: d, + id: r.id, + name: r.name, + kind: r.kind, + file: r.file, + repoId: r.repoId, + checkoutId: r.checkoutId, + relationType: 'imports', + confidence: confidence * 0.8, + }) + } } } if (direction === 'downstream' || direction === 'both') { const downstreamSql = ` - SELECT DISTINCT s.id, s.name, s.kind, f.path as file, + SELECT DISTINCT s.id, s.name, s.kind, f.id as fileId, f.path as file, f.repo_id as repoId, f.checkout_id as checkoutId, e.kind as relationType FROM edges e @@ -218,11 +268,13 @@ export function getBlastRadius( WHERE e.src_symbol IN (${placeholders}) AND e.dst_symbol IS NOT NULL ${options?.workspaceId ? 'AND e.workspace_id = ?' : ''} + ${scopeSql} ` - const rows = db.prepare(downstreamSql).all(...currentLevelIds, ...(options?.workspaceId ? [options.workspaceId] : [])) as unknown as Array<{ + const rows = db.prepare(downstreamSql).all(...currentLevelIds, ...(options?.workspaceId ? [options.workspaceId] : []), ...scopeParams) as unknown as Array<{ id: number name: string kind: string + fileId: number file: string repoId: string | null checkoutId: string | null @@ -249,7 +301,16 @@ export function getBlastRadius( } nodesByDepth[d] = nextLevelNodes - currentLevelIds = nextLevelNodes.map(n => n.id) + currentLevelIds = nextLevelNodes.filter(node => node.id > 0).map(node => node.id) + importFrontier = nextImportFrontier + } + + // Prefer a proven upstream caller at the same or earlier depth. Keep an + // earlier direct import, and never hide an upstream import for a downstream + // callee in the same file. + for (const [depth, list] of Object.entries(nodesByDepth)) { + nodesByDepth[Number(depth)] = list.filter(node => + node.kind !== 'file' || (upstreamSymbolFileDepth.get(-node.id) ?? Infinity) > node.depth) } let totalImpacted = 0 @@ -265,6 +326,7 @@ export function getBlastRadius( } return { + status: 'found', target: { name: initialSymbol.name, file: initialSymbol.file, diff --git a/src/intel/query.ts b/src/intel/query.ts index 443e0d2..5750ae6 100644 --- a/src/intel/query.ts +++ b/src/intel/query.ts @@ -9,6 +9,7 @@ import type { ConceptSearchResult, ExecutionFlow, IntelSymbol } from './types.ts import { annotateSymbolVendor } from './vendor.ts' import { getSymbolContext } from './context.ts' import { searchNotes } from '../notes/search.ts' +import { activePlanetFileScope } from '../planet.ts' export interface ConceptSearchOptions { limit?: number @@ -87,6 +88,9 @@ export function conceptSearch( const start = performance.now() const limit = Math.max(1, Math.min(100, options?.limit ?? 25)) const cleanQuery = queryText.trim() + const fileScope = options?.workspaceId + ? activePlanetFileScope(db, options.workspaceId, 'f.checkout_id', 'f.path') + : null if (!cleanQuery) { return { @@ -155,6 +159,7 @@ export function conceptSearch( symbolSql += ' AND f.workspace_id = ?' params.push(options.workspaceId) } + if (fileScope !== null) { symbolSql += ` AND ${fileScope.sql}`; params.push(...fileScope.params) } if (options?.checkoutId) { symbolSql += ' AND f.checkout_id = ?' params.push(options.checkoutId) @@ -197,6 +202,7 @@ export function conceptSearch( const exactParams: SQLInputValue[] = [cleanQuery, cleanQuery] if (options?.repoId) { exactSql += ' AND f.repo_id = ?'; exactParams.push(options.repoId) } if (options?.workspaceId) { exactSql += ' AND f.workspace_id = ?'; exactParams.push(options.workspaceId) } + if (fileScope !== null) { exactSql += ` AND ${fileScope.sql}`; exactParams.push(...fileScope.params) } if (options?.checkoutId) { exactSql += ' AND f.checkout_id = ?'; exactParams.push(options.checkoutId) } exactSql += ' LIMIT 100' const exactHits = db.prepare(exactSql).all(...exactParams) as typeof rawSymbols @@ -229,6 +235,7 @@ export function conceptSearch( const triParams: SQLInputValue[] = [safe] if (options?.repoId) { triSql += ' AND f.repo_id = ?'; triParams.push(options.repoId) } if (options?.workspaceId) { triSql += ' AND f.workspace_id = ?'; triParams.push(options.workspaceId) } + if (fileScope !== null) { triSql += ` AND ${fileScope.sql}`; triParams.push(...fileScope.params) } if (options?.checkoutId) { triSql += ' AND f.checkout_id = ?'; triParams.push(options.checkoutId) } triSql += ' LIMIT 100' const triHits = db.prepare(triSql).all(...triParams) as typeof rawSymbols @@ -252,6 +259,7 @@ export function conceptSearch( const fileParams: SQLInputValue[] = [cleanQuery, cleanQuery, cleanQuery] if (options?.repoId) { fileSql += ' AND f.repo_id = ?'; fileParams.push(options.repoId) } if (options?.workspaceId) { fileSql += ' AND f.workspace_id = ?'; fileParams.push(options.workspaceId) } + if (fileScope !== null) { fileSql += ` AND ${fileScope.sql}`; fileParams.push(...fileScope.params) } if (options?.checkoutId) { fileSql += ' AND f.checkout_id = ?'; fileParams.push(options.checkoutId) } fileSql += ' LIMIT 20' const fileHits = db.prepare(fileSql).all(...fileParams) as typeof rawSymbols @@ -282,10 +290,13 @@ export function conceptSearch( s.exported, s.container, f.repo_id as repoId, f.checkout_id as checkoutId FROM symbols s JOIN files f ON s.file_id = f.id - WHERE (s.name LIKE ? OR f.path LIKE ?)` + (options?.workspaceId ? ' AND f.workspace_id = ?' : '') + ` - LIMIT ? - ` - rawSymbols = db.prepare(fallbackSql).all(pattern, pattern, ...(options?.workspaceId ? [options.workspaceId] : []), limit) as typeof rawSymbols + WHERE (s.name LIKE ? OR f.path LIKE ?)` + (options?.workspaceId ? ' AND f.workspace_id = ?' : '') + + (fileScope === null ? '' : ` AND ${fileScope.sql}`) + ` + LIMIT ? + ` + rawSymbols = db.prepare(fallbackSql).all( + pattern, pattern, ...(options?.workspaceId ? [options.workspaceId] : []), + ...(fileScope?.params ?? []), limit) as typeof rawSymbols } } diff --git a/src/intel/types.ts b/src/intel/types.ts index 1e85994..63bbfc8 100644 --- a/src/intel/types.ts +++ b/src/intel/types.ts @@ -69,6 +69,9 @@ export interface ImpactNode { } export interface BlastRadiusResult { + /** A name alone is not authority to choose one of several matching symbols. */ + status?: 'found' | 'not_found' | 'ambiguous' + candidates?: Array<{ id: number; file: string }> target: { name: string file?: string diff --git a/src/llm/client.ts b/src/llm/client.ts new file mode 100644 index 0000000..97ec843 --- /dev/null +++ b/src/llm/client.ts @@ -0,0 +1,271 @@ +export type LlmProviderId = 'gemini' | 'nvidia' + +export interface LlmMessage { + role: 'system' | 'user' | 'assistant' + content: string +} + +export interface LlmProviderConfig { + id: LlmProviderId + baseUrl: string + keyEnvNames: readonly string[] + pricing?: { + inputUsdPerMillion: number + outputUsdPerMillion: number + } +} + +export interface LlmCredential { + keyName: string + value: string +} + +export type LlmCredentialResolver = ( + provider: LlmProviderId, + keyEnvNames: readonly string[], +) => LlmCredential | Promise<LlmCredential> + +export interface LlmUsage { + provider: LlmProviderId + model: string + inputTokens: number | null + outputTokens: number | null + costUsd: number | null +} + +export interface LlmCompletionRequest { + provider: LlmProviderId + model: string + messages: readonly LlmMessage[] + temperature?: number + maxTokens?: number +} + +export interface LlmCompletion { + content: string + usage: LlmUsage +} + +export interface LlmClientOptions { + providers: readonly LlmProviderConfig[] + resolveCredential?: LlmCredentialResolver + fetchImpl?: typeof fetch + maxRetries?: number + retryBaseMs?: number + maxRetryDelayMs?: number + timeoutMs?: number + sleep?: (milliseconds: number) => Promise<void> + onUsage?: (usage: LlmUsage) => void | Promise<void> +} + +const PROVIDER_DEFAULTS: Record<LlmProviderId, { baseUrl: string; keyPrefix: string; keyCount: number }> = { + gemini: { + baseUrl: 'https://generativelanguage.googleapis.com/v1beta/openai', + keyPrefix: 'GEMINI_API_KEY_', + keyCount: 5, + }, + nvidia: { + baseUrl: 'https://integrate.api.nvidia.com/v1', + keyPrefix: 'NVIDIA_API_KEY_', + keyCount: 4, + }, +} + +export class LlmAuthError extends Error { + constructor(provider: LlmProviderId) { + super(`LLM provider ${provider} rejected credentials`) + this.name = 'LlmAuthError' + } +} + +export class LlmQuotaError extends Error { + constructor(provider: LlmProviderId) { + super(`LLM provider ${provider} quota is unavailable after bounded retries`) + this.name = 'LlmQuotaError' + } +} + +export function loadLlmProviders(env: NodeJS.ProcessEnv = process.env): LlmProviderConfig[] { + return (Object.keys(PROVIDER_DEFAULTS) as LlmProviderId[]).flatMap(id => { + const defaults = PROVIDER_DEFAULTS[id] + const keyEnvNames = Array.from({ length: defaults.keyCount }, (_, index) => `${defaults.keyPrefix}${String(index + 1).padStart(2, '0')}`) + .filter(name => Boolean(env[name]?.trim())) + if (keyEnvNames.length === 0) return [] + return [{ + id, + baseUrl: env[`${id.toUpperCase()}_OPENAI_BASE_URL`]?.trim() || defaults.baseUrl, + keyEnvNames, + }] + }) +} + +export function environmentCredentialResolver(env: NodeJS.ProcessEnv = process.env): LlmCredentialResolver { + return (provider, keyEnvNames) => { + const validNames = keyEnvNames.filter(name => isProviderKeyName(provider, name)) + if (validNames.length !== keyEnvNames.length || validNames.length === 0) { + throw new Error(`LLM provider ${provider} has an invalid key name configuration`) + } + const configured = validNames.flatMap(keyName => { + const value = env[keyName]?.trim() + return value ? [{ keyName, value }] : [] + }) + if (configured.length === 0) throw new Error(`LLM provider ${provider} has no key configured in the environment`) + if (configured.length > 1) { + throw new Error(`LLM provider ${provider} has multiple keys; a QUOTA-01 credential pool is required`) + } + return configured[0]! + } +} + +export function createLlmClient(options: LlmClientOptions) { + const providers = new Map<LlmProviderId, LlmProviderConfig>() + for (const provider of options.providers) { + if (!PROVIDER_DEFAULTS[provider.id] || provider.keyEnvNames.length === 0 + || provider.keyEnvNames.some(name => !isProviderKeyName(provider.id, name))) { + throw new Error(`LLM provider ${String(provider.id)} has invalid key name configuration`) + } + if (!isHttpsUrl(provider.baseUrl)) throw new Error(`LLM provider ${provider.id} has an invalid base URL`) + if (provider.pricing && ![provider.pricing.inputUsdPerMillion, provider.pricing.outputUsdPerMillion] + .every(rate => Number.isFinite(rate) && rate >= 0)) { + throw new Error(`LLM provider ${provider.id} has invalid pricing configuration`) + } + if (providers.has(provider.id)) throw new Error(`LLM provider ${provider.id} is configured more than once`) + providers.set(provider.id, { ...provider, baseUrl: provider.baseUrl.replace(/\/+$/, '') }) + } + + const resolveCredential = options.resolveCredential ?? environmentCredentialResolver() + const fetchImpl = options.fetchImpl ?? fetch + const maxRetries = boundedInteger(options.maxRetries, 2, 0, 8) + const retryBaseMs = boundedNumber(options.retryBaseMs, 250, 0, 30_000) + const maxRetryDelayMs = boundedNumber(options.maxRetryDelayMs, 30_000, 0, 60_000) + const timeoutMs = boundedNumber(options.timeoutMs, 30_000, 1, 120_000) + const sleep = options.sleep ?? (milliseconds => new Promise(resolve => setTimeout(resolve, milliseconds))) + + async function complete(request: LlmCompletionRequest): Promise<LlmCompletion> { + const provider = providers.get(request.provider) + if (!provider) throw new Error(`LLM provider ${request.provider} is not configured`) + if (!request.model.trim()) throw new Error('LLM model must be specified') + let sawQuotaFailure = false + for (let attempt = 0; attempt <= maxRetries; attempt += 1) { + const credential = await resolveCredential(provider.id, provider.keyEnvNames) + if (!isProviderKeyName(provider.id, credential.keyName) || !credential.value.trim() + || !provider.keyEnvNames.includes(credential.keyName)) { + throw new Error(`LLM provider ${provider.id} returned an invalid credential`) + } + let response: Response + try { + response = await fetchImpl(`${provider.baseUrl}/chat/completions`, { + method: 'POST', + headers: { + authorization: `Bearer ${credential.value}`, + 'content-type': 'application/json', + }, + body: JSON.stringify({ + model: request.model, + messages: request.messages, + ...(request.temperature === undefined ? {} : { temperature: request.temperature }), + ...(request.maxTokens === undefined ? {} : { max_tokens: request.maxTokens }), + }), + signal: AbortSignal.timeout(timeoutMs), + }) + } catch { + if (attempt >= maxRetries) throw new Error('LLM request failed or timed out before receiving a response') + await sleep(Math.min(maxRetryDelayMs, retryBaseMs * (2 ** attempt))) + continue + } + + if (response.status === 401 || response.status === 403) throw new LlmAuthError(provider.id) + if (response.status === 429) { + sawQuotaFailure = true + if (attempt >= maxRetries) throw new LlmQuotaError(provider.id) + const retryAfterMs = parseRetryAfter(response.headers.get('retry-after')) + await sleep(Math.min(maxRetryDelayMs, retryAfterMs ?? retryBaseMs * (2 ** attempt))) + continue + } + if (!response.ok) { + if (response.status >= 500 && attempt < maxRetries) { + await sleep(Math.min(maxRetryDelayMs, retryBaseMs * (2 ** attempt))) + continue + } + throw new Error(`LLM provider request failed with HTTP ${response.status}`) + } + + let payload: unknown + try { + payload = await response.json() + } catch { + throw new Error('LLM provider returned invalid JSON') + } + const completion = parseCompletion(payload, provider, request.model) + await options.onUsage?.(completion.usage) + return completion + } + if (sawQuotaFailure) throw new LlmQuotaError(provider.id) + throw new Error(`LLM provider ${provider.id} request failed after bounded retries`) + } + + return { complete } +} + +function isProviderKeyName(provider: LlmProviderId, name: string): boolean { + const prefix = PROVIDER_DEFAULTS[provider]?.keyPrefix + return typeof name === 'string' && Boolean(prefix) && new RegExp(`^${prefix}\\d{2}$`).test(name) +} + +function isHttpsUrl(value: string): boolean { + try { + const url = new URL(value) + return url.protocol === 'https:' + } catch { + return false + } +} + +function parseRetryAfter(value: string | null, now = Date.now()): number | null { + if (!value) return null + const seconds = Number(value) + if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1000 + const date = Date.parse(value) + return Number.isNaN(date) ? null : Math.max(0, date - now) +} + +function parseCompletion(payload: unknown, provider: LlmProviderConfig, model: string): LlmCompletion { + if (typeof payload !== 'object' || payload === null) throw new Error('LLM provider returned an invalid response') + const record = payload as { + choices?: Array<{ message?: { content?: unknown } }> + usage?: { prompt_tokens?: unknown; completion_tokens?: unknown } + } + const content = normalizeContent(record.choices?.[0]?.message?.content) + if (content === null) throw new Error('LLM provider response did not include message content') + + const inputTokens = asNonNegativeInteger(record.usage?.prompt_tokens) + const outputTokens = asNonNegativeInteger(record.usage?.completion_tokens) + const pricing = provider.pricing + const costUsd = pricing && inputTokens !== null && outputTokens !== null + ? inputTokens * pricing.inputUsdPerMillion / 1_000_000 + outputTokens * pricing.outputUsdPerMillion / 1_000_000 + : null + + return { content, usage: { provider: provider.id, model, inputTokens, outputTokens, costUsd } } +} + +function normalizeContent(content: unknown): string | null { + if (typeof content === 'string') return content + if (!Array.isArray(content)) return null + const text = content.flatMap(part => { + if (typeof part !== 'object' || part === null || !('text' in part)) return [] + return typeof part.text === 'string' ? [part.text] : [] + }).join('') + return text || null +} + +function asNonNegativeInteger(value: unknown): number | null { + return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 ? value : null +} + +function boundedNumber(value: number | undefined, fallback: number, min: number, max: number): number { + return typeof value === 'number' && Number.isFinite(value) ? Math.min(max, Math.max(min, value)) : fallback +} + +function boundedInteger(value: number | undefined, fallback: number, min: number, max: number): number { + return Math.floor(boundedNumber(value, fallback, min, max)) +} diff --git a/src/mcp/server.ts b/src/mcp/server.ts index 21f8b54..4a95f95 100644 --- a/src/mcp/server.ts +++ b/src/mcp/server.ts @@ -129,6 +129,8 @@ export const MCP_TOOLS = [ direction: { type: 'string', enum: ['upstream', 'downstream', 'both'], description: 'Analysis direction' }, maxDepth: { type: 'number', description: 'Depth 1..5 (default 3)' }, repoId: { type: 'string', description: 'Optional repo ID' }, + file: { type: 'string', description: 'File hint to distinguish same-name symbols' }, + symbolId: { type: 'number', description: 'Exact symbol ID returned by an ambiguous result' }, checkoutId: { type: 'string', description: 'Optional checkout ID' }, workspaceId: { type: 'string', description: 'Optional workspace ID for an explicit revision vector' }, }, @@ -268,6 +270,20 @@ export const MCP_TOOLS = [ required: ['agentId'], }, }, + { + name: 'reap', + description: 'Preview or safely remove merged clean Git worktrees and report missing checkout registry entries.', + inputSchema: { + type: 'object', + properties: { + workspaceId: { type: 'string', description: 'Workspace ID' }, + repo: { type: 'string', description: 'Repository path (optional; default scans the workspace)' }, + target: { type: 'string', description: 'Target branch (optional; defaults to the primary checkout branch)' }, + apply: { type: 'boolean', description: 'Remove eligible worktrees (default false)' }, + auto: { type: 'boolean', description: 'Enable periodic automatic reaping for this workspace' }, + }, + }, + }, { name: 'swarm_turn', description: 'Check in at a turn boundary. Returns unread messages, the next or claimed task and host admission. ' @@ -285,6 +301,49 @@ export const MCP_TOOLS = [ required: ['agentId', 'phase'], }, }, + { + name: 'swarm_supersede', + description: 'Mark a pending queue task as superseded so it can never be offered again; record the reason.', + inputSchema: { + type: 'object', + properties: { + taskId: { type: 'string', description: 'Pending task to supersede' }, + byTaskId: { type: 'string', description: 'Optional replacement task ID' }, + note: { type: 'string', description: 'Reason for superseding the task' }, + byAgent: { type: 'string', description: 'Registered actor making the change' }, + workspaceId: { type: 'string', description: 'Workspace ID' }, + }, + required: ['taskId', 'note', 'byAgent'], + }, + }, + { + name: 'swarm_reassign', + description: 'Restrict a pending queue task to a registered worker and record the change.', + inputSchema: { + type: 'object', + properties: { + taskId: { type: 'string', description: 'Pending task to reassign' }, + toAgent: { type: 'string', description: 'Registered worker who may claim the task' }, + byAgent: { type: 'string', description: 'Registered actor making the change' }, + workspaceId: { type: 'string', description: 'Workspace ID' }, + }, + required: ['taskId', 'toAgent', 'byAgent'], + }, + }, + { + name: 'swarm_priority', + description: 'Set the priority of a pending queue task; larger values are offered first.', + inputSchema: { + type: 'object', + properties: { + taskId: { type: 'string', description: 'Pending task to prioritize' }, + priority: { type: 'number', description: 'Integer from -100000 to 100000' }, + byAgent: { type: 'string', description: 'Registered actor making the change' }, + workspaceId: { type: 'string', description: 'Workspace ID' }, + }, + required: ['taskId', 'priority', 'byAgent'], + }, + }, { name: 'swarm_board', description: 'The fleet board: every worker with surface, account, turn state, unread messages, task, leases, ' @@ -628,7 +687,16 @@ export class McpServer { const direction = (args.direction as 'upstream' | 'downstream' | 'both') ?? 'both' const maxDepth = Number(args.maxDepth ?? 3) const repoId = args.repoId ? String(args.repoId) : undefined - const result = intel.getBlastRadius(this.db, target, { workspaceId, direction, maxDepth, repoId }) + const file = args.file ? String(args.file) : undefined + const checkoutId = args.checkoutId ? String(args.checkoutId) : undefined + const symbolId = args.symbolId === undefined ? undefined : Number(args.symbolId) + if (symbolId !== undefined && (!Number.isSafeInteger(symbolId) || symbolId < 1)) { + return { ok: false, error: 'symbolId must be a positive integer' } + } + const result = intel.getBlastRadius(this.db, { name: target, file, id: symbolId }, { workspaceId, direction, maxDepth, repoId, checkoutId, file }) + if (result.status === 'ambiguous') { + return { ok: false, error: 'ambiguous impact target; provide a file or symbol id', candidates: result.candidates } + } return { ok: true, result, provenance: mcpProvenance(this.db, name, { ...args, workspaceId }) } } @@ -753,9 +821,58 @@ export class McpServer { return { ok: true, ping } } + case 'swarm_supersede': { + this.checkAuth(args) + const workspaceId = this.getWorkspaceId(args) + const byAgent = String(args.byAgent ?? '') + const task = coord.supersedeTask(this.db, workspaceId, String(args.taskId ?? ''), { + byAgent, + byTaskId: args.byTaskId ? String(args.byTaskId) : undefined, + note: String(args.note ?? ''), + }) + return { ok: true, task, provenance: mcpProvenance(this.db, name, { ...args, workspaceId }) } + } + + case 'swarm_reassign': { + this.checkAuth(args) + const workspaceId = this.getWorkspaceId(args) + const task = coord.reassignTask(this.db, workspaceId, String(args.taskId ?? ''), { + byAgent: String(args.byAgent ?? ''), addressedTo: String(args.toAgent ?? ''), + }) + return { ok: true, task, provenance: mcpProvenance(this.db, name, { ...args, workspaceId }) } + } + + case 'swarm_priority': { + this.checkAuth(args) + const priority = Number(args.priority) + const workspaceId = this.getWorkspaceId(args) + const task = coord.prioritizeTask(this.db, workspaceId, String(args.taskId ?? ''), { + byAgent: String(args.byAgent ?? ''), priority, + }) + return { ok: true, task, provenance: mcpProvenance(this.db, name, { ...args, workspaceId }) } + } + + case 'reap': { + this.checkAuth(args) + const ws = this.getWorkspaceId(args) + if (typeof args.auto === 'boolean') coord.setReapAuto(this.db, ws, args.auto) + const result = args.auto === true || args.auto === false + ? { autoEnabled: args.auto } + : coord.reapWorktrees(this.db, ws, { repo: args.repo ? String(args.repo) : undefined, + target: args.target ? String(args.target) : undefined, apply: args.apply === true }) + return { ok: true, result } + } + case 'swarm_board': { - const board = coord.agentsBoard(this.db, this.getWorkspaceId(args), { gitStatus: args.git === true }) - return { ok: true, board } + const workspaceId = this.getWorkspaceId(args) + const reaper = coord.reapWorktrees(this.db, workspaceId) + const board = coord.agentsBoard(this.db, workspaceId, { gitStatus: args.git === true }) + return { ok: true, board, reaper: { + eligible: reaper.candidates.filter(row => row.eligible).length, + retained: reaper.candidates.filter(row => !row.eligible).length, + reasons: reaper.candidates.filter(row => !row.eligible).map(row => ({ path: row.path, reason: row.reason })), + missing: reaper.missing, + } } } case 'swarm_resources': { diff --git a/src/notes/search.ts b/src/notes/search.ts index d3833ff..d48264f 100644 --- a/src/notes/search.ts +++ b/src/notes/search.ts @@ -23,6 +23,7 @@ */ import type { DatabaseSync } from 'node:sqlite' import { toMatchExpression } from '../store/search.ts' +import { activePlanetFileScope } from '../planet.ts' export interface NoteHit { path: string @@ -88,20 +89,24 @@ export function searchNotes( const match = toMatchExpression(query) if (match === null) return { query, total: 0, returned: 0, hits: [] } const limit = Math.min(Math.max(1, options.limit ?? 50), 500) + const fileScope = activePlanetFileScope(db, workspaceId, 'f.checkout_id', 'f.path') const rows = db.prepare( `SELECT b.path AS path, b.file_id AS fileId, snippet(note_search, 1, ?, ?, ' … ', 18) AS snippet, bm25(note_search) AS score FROM note_body b JOIN note_search ON note_search.rowid = b.id - WHERE note_search MATCH ? AND b.workspace_id = ? + JOIN files f ON f.id = b.file_id + WHERE note_search MATCH ? AND b.workspace_id = ? AND f.workspace_id = ? AND ${fileScope.sql} ORDER BY score, b.path - LIMIT ?`).all(SNIPPET_MARK[0], SNIPPET_MARK[1], match, workspaceId, limit) as + LIMIT ?`).all(SNIPPET_MARK[0], SNIPPET_MARK[1], match, workspaceId, workspaceId, ...fileScope.params, limit) as unknown as Array<{ path: string; fileId: number; snippet: string | null; score: number }> const total = Number((db.prepare( `SELECT COUNT(*) AS n FROM note_body b JOIN note_search ON note_search.rowid = b.id - WHERE note_search MATCH ? AND b.workspace_id = ?`).get(match, workspaceId) as { n: number }).n) + JOIN files f ON f.id = b.file_id + WHERE note_search MATCH ? AND b.workspace_id = ? AND f.workspace_id = ? AND ${fileScope.sql}`) + .get(match, workspaceId, workspaceId, ...fileScope.params) as { n: number }).n) return { query, diff --git a/src/plan-decompose.ts b/src/plan-decompose.ts new file mode 100644 index 0000000..c74b62d --- /dev/null +++ b/src/plan-decompose.ts @@ -0,0 +1,121 @@ +/** Turn explicit Master-ledger decomposition metadata into an auditable queue spec. */ +import { createHash } from 'node:crypto' +import type { DatabaseSync } from 'node:sqlite' +import { addTaskDependency } from './coord/dependencies.ts' +import { enqueueTask, ensureQueueSchema, type QueueTask } from './queue.ts' + +export const AUTO_EXECUTABLE_CATEGORIES = new Set(['bugfix', 'documentation', 'maintenance', 'tests']) + +interface LedgerEntry { + id: string + title: string + category?: string + brief?: string + acceptance?: string[] + reviewerRole?: string + dependsOn?: string[] + evidence?: string[] +} + +export interface DecompositionLedger { + gates?: LedgerEntry[] + requirements?: LedgerEntry[] +} + +export interface TaskSpec { + id: string + title: string + category: string | null + brief: string + acceptance: string[] + reviewerRole: string | null + dependsOn: string[] + evidence: string[] + sourceHash: string + idempotencyKey: string +} + +export interface DecompositionResult { + state: 'executable' | 'waiting-for-lead' + reason: string | null + spec: TaskSpec +} + +const text = (value: unknown): value is string => typeof value === 'string' && value.trim().length > 0 +const strings = (value: unknown): value is string[] => Array.isArray(value) && value.every(text) + +export function decomposeLedgerTarget(ledger: DecompositionLedger, target: string): DecompositionResult { + const entry = [...(ledger.gates ?? []), ...(ledger.requirements ?? [])] + .find(candidate => candidate.id.toLowerCase() === target.toLowerCase()) + if (!entry) throw new Error(`no gate or requirement ${target} in Master ledger`) + + const canonical = JSON.stringify({ + id: entry.id, title: entry.title, category: entry.category ?? null, brief: entry.brief ?? '', + acceptance: entry.acceptance ?? [], reviewerRole: entry.reviewerRole ?? null, + dependsOn: entry.dependsOn ?? [], evidence: entry.evidence ?? [], + }) + const sourceHash = createHash('sha256').update(canonical).digest('hex') + const idempotencyKey = `plan:${entry.id.toLowerCase()}:${sourceHash}` + const spec: TaskSpec = { + id: entry.id, title: entry.title, category: text(entry.category) ? entry.category.trim().toLowerCase() : null, + brief: text(entry.brief) ? entry.brief.trim() : '', + acceptance: strings(entry.acceptance) ? entry.acceptance.map(item => item.trim()) : [], + reviewerRole: text(entry.reviewerRole) ? entry.reviewerRole.trim() : null, + dependsOn: strings(entry.dependsOn) ? entry.dependsOn.map(item => item.trim()) : [], + evidence: strings(entry.evidence) ? entry.evidence.map(item => item.trim()) : [], + sourceHash, idempotencyKey, + } + + const missing: string[] = [] + if (!spec.category || !AUTO_EXECUTABLE_CATEGORIES.has(spec.category)) missing.push('category is not on the automatic-execution allowlist') + if (!spec.brief) missing.push('brief is missing') + if (spec.acceptance.length === 0) missing.push('acceptance criteria are missing') + if (!spec.reviewerRole) missing.push('reviewer role is missing') + if (spec.evidence.length === 0) missing.push('evidence is missing') + return { state: missing.length === 0 ? 'executable' : 'waiting-for-lead', reason: missing.join('; ') || null, spec } +} + +const sourceMarker = (id: string): string => `<!-- plugbrain-plan-source:${id.toLowerCase()} -->` + +/** Persist only approved-by-policy specs to the canonical queue; all other results are read-only. */ +export function applyDecomposedTask( + db: DatabaseSync, workspaceId: string, result: DecompositionResult, requestedBy?: string, +): QueueTask { + if (result.state !== 'executable') throw new Error('decomposition is waiting-for-lead and cannot be applied') + const { spec } = result + ensureQueueSchema(db) + + const existing = db.prepare('SELECT * FROM queue_tasks WHERE decomposition_key = ?').get(spec.idempotencyKey) as unknown as QueueTask | undefined + if (existing) return existing + + const dependencies = spec.dependsOn.map(id => { + const row = db.prepare('SELECT id FROM queue_tasks WHERE body LIKE ? ORDER BY created_at LIMIT 1') + .get(`%${sourceMarker(id)}%`) as { id: string } | undefined + if (!row) throw new Error(`dependency ${id} has no existing decomposed queue task`) + return row.id + }) + const body = [ + `Master reference: ${spec.id}`, + `Category: ${spec.category}`, + `Brief:\n${spec.brief}`, + `Acceptance:\n${spec.acceptance.map(item => `- ${item}`).join('\n')}`, + `Reviewer role: ${spec.reviewerRole}`, + `Dependencies: ${spec.dependsOn.join(', ') || '(none)'}`, + `Evidence:\n${spec.evidence.map(item => `- ${item}`).join('\n')}`, + `Source SHA-256: ${spec.sourceHash}`, + sourceMarker(spec.id), + ].join('\n\n') + + db.exec('BEGIN IMMEDIATE') + try { + const raced = db.prepare('SELECT * FROM queue_tasks WHERE decomposition_key = ?').get(spec.idempotencyKey) as unknown as QueueTask | undefined + if (raced) { db.exec('COMMIT'); return raced } + const task = enqueueTask(db, workspaceId, { title: spec.title, body, requestedBy, decompositionKey: spec.idempotencyKey }) + for (const dependencyId of dependencies) addTaskDependency(db, task.id, dependencyId) + db.exec('COMMIT') + return task + } catch (error) { + db.exec('ROLLBACK') + throw error + } +} diff --git a/src/queue.ts b/src/queue.ts index cb02694..c9c6497 100644 --- a/src/queue.ts +++ b/src/queue.ts @@ -12,6 +12,11 @@ * cases where capability genuinely differs (a job only Freebuff can do) — not * as a second mechanism with its own table and its own bugs. * + * A task may additionally wait for another task (`afterTaskId`). Ordering is + * the one thing pull alone cannot express, and on 26.09.2026 the integrator + * was the ordering mechanism by hand. The waiting itself lives in + * `coord/dependencies.ts`; this module only keeps its offers honest. + * * Leases: PlugBrain deliberately does not own them (see projections/conflicts.ts * — "PlugBrain does not own leases, the Operator and @plug/work do"). This * module therefore *reports* that a claim has gone quiet and never reaps one. @@ -21,9 +26,13 @@ import { randomUUID } from 'node:crypto' import type { DatabaseSync } from 'node:sqlite' import { AccessDenied, requireAgent, requireWorkspace } from './access.ts' +import { addTaskDependency, ensureDependencySchema, syncDependencies, UNBLOCKED_TASK_SQL } from './coord/dependencies.ts' +import { assertNotCredential } from './coord/resources.ts' import { rowAs, rowsAs } from './store/rows.ts' +import { coordEvents } from './coord/events.ts' -export type QueueState = 'pending' | 'claimed' | 'delivered' | 'cancelled' +export type QueueState = 'pending' | 'claimed' | 'delivered' | 'cancelled' | 'superseded' +export type QueueOperation = 'supersede' | 'reassign' | 'priority' export interface QueueTask { id: string @@ -37,6 +46,10 @@ export interface QueueTask { claimed_by: string | null claimed_at: string | null delivered_path: string | null + delivered_summary: string | null + decomposition_key?: string | null + priority: number + superseded_by: string | null created_at: string updated_at: string } @@ -58,14 +71,61 @@ CREATE TABLE IF NOT EXISTS queue_tasks ( claimed_by TEXT REFERENCES agents(id) ON DELETE SET NULL, claimed_at TEXT, delivered_path TEXT, + delivered_summary TEXT, + decomposition_key TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL + ,priority INTEGER NOT NULL DEFAULT 0 + ,superseded_by TEXT ); -- The claim query filters on exactly this, and it runs on every idle agent. CREATE INDEX IF NOT EXISTS idx_queue_ws_state ON queue_tasks(workspace_id, state, created_at); +CREATE TABLE IF NOT EXISTS queue_deliveries ( + task_id TEXT PRIMARY KEY REFERENCES queue_tasks(id) ON DELETE CASCADE, + delivered_by TEXT NOT NULL, + delivery_attempt INTEGER NOT NULL, + source_revision TEXT, + delivered_path TEXT NOT NULL, + delivered_sha256 TEXT NOT NULL, + review_judgment TEXT, + reviewed_commit TEXT, + delivered_at TEXT NOT NULL +); +CREATE TABLE IF NOT EXISTS queue_task_events ( + id TEXT PRIMARY KEY, + workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE, + task_id TEXT NOT NULL REFERENCES queue_tasks(id) ON DELETE CASCADE, + operation TEXT NOT NULL, + by_agent TEXT NOT NULL REFERENCES agents(id), + old_value TEXT, + new_value TEXT, + note TEXT, + occurred_at TEXT NOT NULL +); +CREATE INDEX IF NOT EXISTS idx_queue_task_events_workspace ON queue_task_events(workspace_id, occurred_at DESC); ` -export function ensureQueueSchema(db: DatabaseSync): void { db.exec(SCHEMA) } +// The candidates every offer reads are filtered by the dependency table, so it +// belongs to the queue schema: a store that only ever touched the queue must +// still be able to answer "who is next" without a missing-table error. +export function ensureQueueSchema(db: DatabaseSync): void { + db.exec(SCHEMA) + const columns = db.prepare('PRAGMA table_info(queue_tasks)').all() as unknown as Array<{ name: string }> + if (!columns.some(column => column.name === 'delivered_summary')) { + db.exec('ALTER TABLE queue_tasks ADD COLUMN delivered_summary TEXT') + } + if (!columns.some(column => column.name === 'decomposition_key')) { + db.exec('ALTER TABLE queue_tasks ADD COLUMN decomposition_key TEXT') + } + if (!columns.some(column => column.name === 'priority')) { + db.exec('ALTER TABLE queue_tasks ADD COLUMN priority INTEGER NOT NULL DEFAULT 0') + } + if (!columns.some(column => column.name === 'superseded_by')) { + db.exec('ALTER TABLE queue_tasks ADD COLUMN superseded_by TEXT') + } + db.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_queue_decomposition_key ON queue_tasks(decomposition_key) WHERE decomposition_key IS NOT NULL') + ensureDependencySchema(db) +} const load = (db: DatabaseSync, id: string): QueueTask => { const row = rowAs<QueueTask>(db.prepare('SELECT * FROM queue_tasks WHERE id = ?').get(id)) @@ -84,7 +144,7 @@ const load = (db: DatabaseSync, id: string): QueueTask => { export function enqueueTask( db: DatabaseSync, workspaceId: string, - input: { title: string; body?: string; addressedTo?: string; requestedBy?: string }, + input: { title: string; body?: string; addressedTo?: string; requestedBy?: string; afterTaskId?: string; decompositionKey?: string }, ): QueueTask { ensureQueueSchema(db) requireWorkspace(db, workspaceId) @@ -99,13 +159,22 @@ export function enqueueTask( db.prepare( `INSERT INTO queue_tasks (id, workspace_id, title, body, addressed_to, requested_by, state, - claimed_by, claimed_at, delivered_path, created_at, updated_at) - VALUES (?, ?, ?, ?, ?, ?, 'pending', NULL, NULL, NULL, ?, ?)`, + claimed_by, claimed_at, delivered_path, created_at, updated_at, decomposition_key) + VALUES (?, ?, ?, ?, ?, ?, 'pending', NULL, NULL, NULL, ?, ?, ?)`, ).run( id, workspaceId, title, input.body ?? '', - input.addressedTo ?? null, input.requestedBy ?? null, now, now, + input.addressedTo ?? null, input.requestedBy ?? null, now, now, input.decompositionKey ?? null, ) - return load(db, id) + if (input.afterTaskId !== undefined) addTaskDependency(db, id, input.afterTaskId) + const task = load(db, id) + coordEvents.emitLive('task.enqueued', { + taskId: task.id, + title: task.title, + addressedTo: task.addressed_to, + requestedBy: task.requested_by, + createdAt: task.created_at, + }) + return task } /** @@ -133,7 +202,8 @@ export function claimNextTask( `SELECT id FROM queue_tasks WHERE workspace_id = ? AND state = 'pending' AND (addressed_to IS NULL OR addressed_to = ?) - ORDER BY created_at ASC, rowid ASC + AND ${UNBLOCKED_TASK_SQL} + ORDER BY priority DESC, created_at ASC, rowid ASC LIMIT 1`, ).get(workspaceId, agentId) as { id: string } | undefined @@ -156,6 +226,199 @@ export function claimNextTask( } } +export interface QueueEvent { + id: string + workspace_id: string + task_id: string + operation: QueueOperation + by_agent: string + old_value: string | null + new_value: string | null + note: string | null + occurred_at: string +} + +function recordQueueOperation( + db: DatabaseSync, + input: { workspaceId: string; taskId: string; operation: QueueOperation; byAgent: string; oldValue: string | null; newValue: string | null; note?: string }, +): void { + db.prepare(`INSERT INTO queue_task_events + (id, workspace_id, task_id, operation, by_agent, old_value, new_value, note, occurred_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`) + .run(`qevent-${randomUUID().slice(0, 12)}`, input.workspaceId, input.taskId, input.operation, + input.byAgent, input.oldValue, input.newValue, input.note ?? null, new Date().toISOString()) +} + +function mutableTask(db: DatabaseSync, workspaceId: string, taskId: string): QueueTask { + const task = load(db, taskId) + if (task.workspace_id !== workspaceId) throw new AccessDenied(`task ${taskId} belongs to another workspace`) + if (task.state !== 'pending' && task.state !== 'claimed') { + throw new AccessDenied(`task ${taskId} is ${task.state}, not pending or safely claimed`) + } + if (task.state === 'claimed') { + const hasLeaseTable = db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'leases'").get() !== undefined + if (hasLeaseTable) { + const activeLease = db.prepare(`SELECT 1 FROM leases + WHERE workspace_id = ? AND task_id = ? AND released_at IS NULL AND expires_at > ? LIMIT 1`) + .get(workspaceId, taskId, new Date().toISOString()) + if (activeLease !== undefined) throw new AccessDenied(`task ${taskId} has an active lease and cannot be changed`) + } + const agentColumns = new Set((db.prepare('PRAGMA table_info(agents)').all() as Array<{ name: string }>).map(column => column.name)) + if (task.claimed_by && (agentColumns.has('turn_state') || agentColumns.has('worktrees'))) { + const columns = ['turn_state', 'worktrees'].filter(column => agentColumns.has(column)).join(', ') + const holder = db.prepare(`SELECT ${columns} FROM agents WHERE id = ?`).get(task.claimed_by) as + { turn_state?: string | null; worktrees?: string | null } | undefined + if (holder?.turn_state === 'working') throw new AccessDenied(`task ${taskId} is held by active worker ${task.claimed_by}`) + if (holder?.worktrees) { + let paths: unknown + try { paths = JSON.parse(holder.worktrees) } catch { + throw new AccessDenied(`cannot verify the worktrees for task ${taskId}; refusing to change its claim`) + } + if (!Array.isArray(paths) || paths.length > 0) { + throw new AccessDenied(`task ${taskId} is held by worker ${task.claimed_by} with a registered worktree`) + } + } + } + } + return task +} + +function pendingTask(db: DatabaseSync, workspaceId: string, taskId: string): QueueTask { + const task = load(db, taskId) + if (task.workspace_id !== workspaceId) throw new AccessDenied(`task ${taskId} belongs to another workspace`) + if (task.state !== 'pending') throw new AccessDenied(`task ${taskId} is ${task.state}, not pending`) + return task +} + +/** Permanently remove an obsolete unstarted task from every offer and record why. */ +export function supersedeTask( + db: DatabaseSync, + workspaceId: string, + taskId: string, + input: { byAgent: string; byTaskId?: string; note: string }, +): QueueTask { + ensureQueueSchema(db) + requireWorkspace(db, workspaceId) + requireAgent(db, input.byAgent) + const note = input.note.trim().slice(0, 2000) + if (note === '') throw new AccessDenied('superseding a task needs a note') + assertNotCredential('queue operation note', note) + if (input.byTaskId === taskId) throw new AccessDenied('a task cannot supersede itself') + if (input.byTaskId !== undefined) { + const replacement = load(db, input.byTaskId) + if (replacement.workspace_id !== workspaceId) throw new AccessDenied(`replacement task ${input.byTaskId} belongs to another workspace`) + } + const now = new Date().toISOString() + db.exec('BEGIN IMMEDIATE') + try { + const task = mutableTask(db, workspaceId, taskId) + const result = db.prepare(`UPDATE queue_tasks SET state = 'superseded', superseded_by = ?, claimed_by = NULL, + claimed_at = NULL, updated_at = ? WHERE workspace_id = ? AND id = ? AND state = ?`) + .run(input.byTaskId ?? null, now, workspaceId, taskId, task.state) + if (Number(result.changes) !== 1) throw new AccessDenied(`task ${taskId} changed before superseding`) + recordQueueOperation(db, { + workspaceId, taskId, operation: 'supersede', byAgent: input.byAgent, + oldValue: `${task.state}:${task.claimed_by ?? ''}`, newValue: input.byTaskId ?? 'superseded', note, + }) + db.exec('COMMIT') + } catch (error) { + db.exec('ROLLBACK') + throw error + } + const updated = load(db, taskId) + coordEvents.emitLive('task.updated', { taskId, operation: 'supersede', byAgent: input.byAgent, at: now }) + return updated +} + +/** Route an unstarted task to one registered worker and record the change. */ +export function reassignTask( + db: DatabaseSync, + workspaceId: string, + taskId: string, + input: { byAgent: string; addressedTo: string }, +): QueueTask { + ensureQueueSchema(db) + requireWorkspace(db, workspaceId) + requireAgent(db, input.byAgent) + requireAgent(db, input.addressedTo) + const now = new Date().toISOString() + db.exec('BEGIN IMMEDIATE') + try { + const task = mutableTask(db, workspaceId, taskId) + if (task.addressed_to === input.addressedTo && task.state === 'pending') { + throw new AccessDenied(`task ${taskId} is already addressed to ${input.addressedTo}`) + } + const result = db.prepare(`UPDATE queue_tasks SET addressed_to = ?, state = 'pending', claimed_by = NULL, + claimed_at = NULL, updated_at = ? WHERE workspace_id = ? AND id = ? AND state = ?`) + .run(input.addressedTo, now, workspaceId, taskId, task.state) + if (Number(result.changes) !== 1) throw new AccessDenied(`task ${taskId} changed before reassignment`) + recordQueueOperation(db, { + workspaceId, taskId, operation: 'reassign', byAgent: input.byAgent, + oldValue: `${task.state}:${task.claimed_by ?? task.addressed_to ?? ''}`, newValue: `pending:${input.addressedTo}`, + }) + db.exec('COMMIT') + } catch (error) { + db.exec('ROLLBACK') + throw error + } + const updated = load(db, taskId) + coordEvents.emitLive('task.updated', { taskId, operation: 'reassign', byAgent: input.byAgent, at: now }) + return updated +} + +/** Set the pending-task claim order. Larger values are offered first. */ +export function prioritizeTask( + db: DatabaseSync, + workspaceId: string, + taskId: string, + input: { byAgent: string; priority: number }, +): QueueTask { + ensureQueueSchema(db) + requireWorkspace(db, workspaceId) + requireAgent(db, input.byAgent) + if (!Number.isSafeInteger(input.priority)) throw new AccessDenied('priority must be an integer') + if (input.priority < -100_000 || input.priority > 100_000) throw new AccessDenied('priority must be between -100000 and 100000') + const now = new Date().toISOString() + db.exec('BEGIN IMMEDIATE') + try { + const task = pendingTask(db, workspaceId, taskId) + if (task.priority === input.priority) throw new AccessDenied(`task ${taskId} already has priority ${input.priority}`) + const result = db.prepare(`UPDATE queue_tasks SET priority = ?, updated_at = ? + WHERE workspace_id = ? AND id = ? AND state = ?`) + .run(input.priority, now, workspaceId, taskId, task.state) + if (Number(result.changes) !== 1) throw new AccessDenied(`task ${taskId} changed before reprioritizing`) + recordQueueOperation(db, { + workspaceId, taskId, operation: 'priority', byAgent: input.byAgent, + oldValue: String(task.priority), newValue: String(input.priority), + }) + db.exec('COMMIT') + } catch (error) { + db.exec('ROLLBACK') + throw error + } + const updated = load(db, taskId) + coordEvents.emitLive('task.updated', { taskId, operation: 'priority', byAgent: input.byAgent, at: now }) + return updated +} + +/** Recent auditable queue changes, newest first. */ +export function listQueueEvents( + db: DatabaseSync, + workspaceId: string, + options: { taskId?: string; limit?: number } = {}, +): QueueEvent[] { + ensureQueueSchema(db) + requireWorkspace(db, workspaceId) + const limit = Math.min(500, Math.max(1, Math.floor(options.limit ?? 100))) + if (options.taskId !== undefined) { + return rowsAs<QueueEvent>(db.prepare(`SELECT * FROM queue_task_events + WHERE workspace_id = ? AND task_id = ? ORDER BY occurred_at DESC LIMIT ?`) + .all(workspaceId, options.taskId, limit)) + } + return rowsAs<QueueEvent>(db.prepare(`SELECT * FROM queue_task_events + WHERE workspace_id = ? ORDER BY occurred_at DESC LIMIT ?`).all(workspaceId, limit)) +} + /** * Record that the holder finished and where the output went. * @@ -168,18 +431,53 @@ export function deliverTask( taskId: string, agentId: string, deliveredPath: string, + deliveredSummary?: string, + receipt?: { workspaceId: string; deliveredBy: string; sourceRevision: string | null; sha256: string; reviewJudgment: string | null; reviewedCommit: string | null }, ): QueueTask { ensureQueueSchema(db) - const task = load(db, taskId) - if (task.state !== 'claimed') throw new AccessDenied(`task ${taskId} is ${task.state}, not claimed`) - if (task.claimed_by !== agentId) { - throw new AccessDenied(`task ${taskId} is held by ${task.claimed_by ?? 'nobody'}, not ${agentId}`) - } + const summary = deliveredSummary === undefined ? null : deliveredSummary.slice(0, 2000) + if (summary !== null) assertNotCredential('delivery summary', summary) const now = new Date().toISOString() - db.prepare( - `UPDATE queue_tasks SET state = 'delivered', delivered_path = ?, updated_at = ? WHERE id = ?`, - ).run(deliveredPath, now, taskId) - return load(db, taskId) + db.exec('BEGIN IMMEDIATE') + try { + const task = load(db, taskId) + if (receipt && task.workspace_id !== receipt.workspaceId) { + throw new AccessDenied(`task ${taskId} belongs to another workspace`) + } + if (task.state !== 'claimed') throw new AccessDenied(`task ${taskId} is ${task.state}, not claimed`) + if (task.claimed_by !== agentId) { + throw new AccessDenied(`task ${taskId} is held by ${task.claimed_by ?? 'nobody'}, not ${agentId}`) + } + const changed = db.prepare( + `UPDATE queue_tasks SET state = 'delivered', delivered_path = ?, delivered_summary = ?, updated_at = ? + WHERE id = ? AND state = 'claimed' AND claimed_by = ? AND (? IS NULL OR workspace_id = ?)`, + ).run(deliveredPath, summary, now, taskId, agentId, receipt?.workspaceId ?? null, receipt?.workspaceId ?? null) + if (Number(changed.changes) !== 1) throw new AccessDenied(`task ${taskId} changed before delivery`) + if (receipt) { + db.prepare(`INSERT INTO queue_deliveries + (task_id, delivered_by, delivery_attempt, source_revision, delivered_path, delivered_sha256, + review_judgment, reviewed_commit, delivered_at) + VALUES (?, ?, 1, ?, ?, ?, ?, ?, ?)`) + .run(taskId, receipt.deliveredBy, receipt.sourceRevision, deliveredPath, receipt.sha256, + receipt.reviewJudgment, receipt.reviewedCommit, now) + } + // The delivery may be exactly the event another task was waiting for. + syncDependencies(db, task.workspace_id) + const delivered = load(db, taskId) + db.exec('COMMIT') + coordEvents.emitLive('task.delivered', { + taskId: delivered.id, + title: delivered.title, + agentId: delivered.claimed_by, + addressedTo: delivered.addressed_to, + evidence: delivered.delivered_path, + deliveredAt: delivered.updated_at, + }) + return delivered + } catch (error) { + db.exec('ROLLBACK') + throw error + } } /** How many tasks are waiting. The one number that predicts trouble. */ diff --git a/src/server/api.ts b/src/server/api.ts index d4331db..2294745 100644 --- a/src/server/api.ts +++ b/src/server/api.ts @@ -20,10 +20,13 @@ import { workspaceIdFor, } from '../planet.ts' import * as notes from '../notes/vault.ts' +import { deliverTaskWithEvidence } from '../coord/turn-delivery.ts' import * as attachments from '../notes/attachments.ts' import { hygieneReport } from '../hygiene/index.ts' import { machineReport } from '../machine/index.ts' import { gitCensusCached } from '../machine/git-census.ts' +import { diskRecommendations, diskWipeCheck } from '../disk/analysis.ts' +import { currentDiskScan, diskScanHistory, diskTree, scanDisk } from '../disk/scan.ts' import { exportNote, exportVault } from '../notes/export.ts' import { cachedOnce, generationCache, publishedGeneration } from '../store/count-cache.ts' import { resolveBrainHome } from '../home.ts' @@ -75,6 +78,7 @@ function withLocalSession(html: string, ctx: Ctx): string { } import * as missions from '../missions.ts' import * as queue from '../queue.ts' +import { planView } from '../plan.ts' import { createAwarenessPort, type TaskAwarenessPack } from '../projections/awareness.ts' import { cityDelta, citySnapshot } from '../projections/city.ts' import { evaluateClaim } from '../projections/conflicts.ts' @@ -85,6 +89,7 @@ import { buildContextPack, packStaleness } from '../chronicle.ts' import * as intel from '../intel/index.ts' import { askQuestion, buildProjectBriefing } from '../ask/index.ts' import * as coord from '../coord/index.ts' +import { swarmApprovals, swarmBoard, swarmMessages, swarmTurns } from '../coord/swarm-read.ts' import { homedir } from 'node:os' import { backupStore, verifyBackupFile } from '../store/backup.ts' import { IndexRunBusy, IndexWorkerUnavailable, startIndexRun } from '../index/runner.ts' @@ -1062,10 +1067,20 @@ export function serve(ctx: Ctx, port = 0): Promise<ServerHandle> { 'Cache-Control': 'no-cache, no-transform', 'Connection': 'keep-alive', }) + // A reconnect may carry `Last-Event-ID`; replay what the buffer still has + // and never send an id twice (the live listener and the replay can both + // carry the same event when it fires while we are catching up). + const requested = req.headers['last-event-id'] + const lastEventId = Array.isArray(requested) ? requested[0] : requested + let highestSent = Number.isFinite(Number(lastEventId)) ? Number(lastEventId) : 0 + const send = (evt: { id: number; type: string; data: unknown }) => { + if (evt.id <= highestSent) return + highestSent = evt.id + res.write(`id: ${evt.id}\nevent: ${evt.type}\ndata: ${JSON.stringify(evt.data)}\n\n`) + } + const unsubscribe = coord.coordEvents.onLive(send) + for (const evt of coord.coordEvents.replaySince(lastEventId)) send(evt) res.write(': connected\n\n') - const unsubscribe = coord.coordEvents.onLive((evt) => { - res.write(`event: ${evt.type}\ndata: ${JSON.stringify(evt.data)}\n\n`) - }) req.on('close', () => { unsubscribe() }) @@ -1158,11 +1173,61 @@ export function serve(ctx: Ctx, port = 0): Promise<ServerHandle> { }) } + // ── Disk atlas: metadata-only inventory; no file bodies are read ────── + if (p === '/api/disk/scan' && req.method === 'POST') { + checkAuth(req, ctx) + const previous = currentDiskScan(db) + if (previous?.running) return json(res, { ok: false, error: 'disk scan already running', scan: previous }, 409) + const body = await readBody(req) + const roots = Array.isArray(body.roots) && body.roots.every(root => typeof root === 'string') + ? body.roots as string[] : undefined + // scanDisk allocates its own id. Starting it in the background keeps the + // request responsive; callers poll the authenticated status route. + void scanDisk(db, { roots }).catch(error => { + console.error(`disk scan failed: ${error instanceof Error ? error.message : String(error)}`) + }) + return json(res, { ok: true, started: true, scan: currentDiskScan(db) }, 202) + } + if (p === '/api/disk/scan' && req.method === 'GET') { + checkAuth(req, ctx) + return json(res, { ok: true, scan: currentDiskScan(db) }) + } + if (p === '/api/disk/tree' && req.method === 'GET') { + checkAuth(req, ctx) + return json(res, { ok: true, directories: diskTree(db, q.get('path')) }) + } + if (p === '/api/disk/recommendations' && req.method === 'GET') { + checkAuth(req, ctx) + return json(res, { ok: true, ...diskRecommendations(db, gitCensusCached(db)) }) + } + if (p === '/api/disk/wipe-check' && req.method === 'GET') { + checkAuth(req, ctx) + return json(res, { ok: true, ...diskWipeCheck(db, gitCensusCached(db)) }) + } + if (p === '/api/agents') { access.requireWorkspace(db, ws) return json(res, { ok: true, agents: agentsOf(db, ws) }) } + // The fleet coordination ledger is the authoritative source for turns, + // messages, claims, worktrees and queue state. These endpoints are read-only + // and require the same bearer session used by authenticated UI operations. + if (p.startsWith('/api/swarm/')) { + if (req.method !== 'GET') return json(res, { ok: false, error: 'method not allowed' }, 405) + checkAuth(req, ctx) + access.requireWorkspace(db, ws) + const limit = clampLimit(q.get('limit'), 1000, 5000) + if (p === '/api/swarm/board') return json(res, { ok: true, board: swarmBoard(db, ws) }) + if (p === '/api/swarm/turns') return json(res, { ok: true, ...swarmTurns(db, ws, limit) }) + if (p === '/api/swarm/messages') return json(res, { ok: true, messages: swarmMessages(db, ws, limit) }) + if (p === '/api/swarm/approvals') return json(res, { ok: true, approvals: swarmApprovals(db, ws, limit) }) + if (p === '/api/swarm/queue') return json(res, { + ok: true, depth: queue.queueDepth(db, ws), tasks: queue.listQueue(db, ws), + }) + return json(res, { ok: false, error: 'not found' }, 404) + } + // ── Agent Mesh: one trace-backed projection, never a registry fallback ── // // The registry can describe a configured identity, but it cannot prove a @@ -2072,6 +2137,7 @@ export function serve(ctx: Ctx, port = 0): Promise<ServerHandle> { } if (p === '/api/resources' && req.method === 'GET') { + checkAuth(req, ctx) const host = coord.hostSnapshot() return json(res, { ok: true, @@ -2080,6 +2146,21 @@ export function serve(ctx: Ctx, port = 0): Promise<ServerHandle> { admission: coord.WORK_KINDS.map(kind => coord.admitWork(kind, host)), }) } + if (p === '/api/disk/history' && req.method === 'GET') { + checkAuth(req, ctx) + return json(res, { ok: true, ...diskScanHistory(db, clampLimit(q.get('limit'), 10, 50)) }) + } + + if (p === '/api/plan' && req.method === 'GET') { + checkAuth(req, ctx) + access.requireWorkspace(db, ws) + try { + return json(res, { ok: true, plan: planView(db, ws) }) + } catch (cause) { + const message = cause instanceof Error ? cause.message : String(cause) + return json(res, { ok: false, error: message }, 503) + } + } if (p === '/api/resources/quota' && req.method === 'POST') { checkAuth(req, ctx) @@ -2190,6 +2271,7 @@ export function serve(ctx: Ctx, port = 0): Promise<ServerHandle> { const channel = (body.channel as string | undefined) ?? q.get('channel') ?? undefined const missionId = (body.missionId as string | undefined) ?? q.get('missionId') ?? undefined const unreadOnly = body.unreadOnly === true || q.get('unreadOnly') === 'true' + const afterCursor = body.afterCursor === true || q.get('afterCursor') === 'true' const waitMs = typeof body.waitMs === 'number' ? body.waitMs : Number(q.get('waitMs') ?? 0) const messages = await coord.readInbox(db, { @@ -2198,6 +2280,7 @@ export function serve(ctx: Ctx, port = 0): Promise<ServerHandle> { channel, missionId, unreadOnly, + afterCursor, waitMs: Number.isFinite(waitMs) ? waitMs : 0, }) return json(res, { ok: true, messages }) @@ -2213,6 +2296,16 @@ export function serve(ctx: Ctx, port = 0): Promise<ServerHandle> { return json(res, { ok: true, acked }) } + if (p === '/api/agent/inbox/processed' && req.method === 'POST') { + checkAuth(req, ctx) + const body = await readBody(req) + const messageId = String(body.messageId ?? '').trim() + const agentId = String(body.agentId ?? '').trim() + if (!messageId || !agentId) return json(res, { ok: false, error: 'messageId and agentId required' }, 400) + const processed = coord.markMessageProcessed(db, messageId, agentId) + return json(res, { ok: true, processed }) + } + // ── Swarm Agent Inspect (M5: Mesh Inspection) ─────────────────────── if (p === '/api/agent/inspect' && (req.method === 'GET' || req.method === 'POST')) { const body = req.method === 'POST' ? await readBody(req) : {} @@ -2375,13 +2468,19 @@ export function serve(ctx: Ctx, port = 0): Promise<ServerHandle> { if (p === '/api/queue/deliver' && req.method === 'POST') { checkAuth(req, ctx) const body = await readBody(req) + const workspaceId = String(body.workspace ?? ws).trim() const taskId = String(body.taskId ?? '').trim() const agentId = String(body.agentId ?? '').trim() const path = String(body.deliveredPath ?? '').trim() if (!taskId || !agentId || !path) { return json(res, { ok: false, error: 'taskId, agentId and deliveredPath required' }, 400) } - return json(res, { ok: true, task: queue.deliverTask(db, taskId, agentId, path) }) + return json(res, { ok: true, task: deliverTaskWithEvidence(db, taskId, agentId, path, undefined, { + workspaceId, + workspaceRoot: access.requireWorkspace(db, workspaceId).root, + repoPath: typeof body.repoPath === 'string' ? body.repoPath : undefined, + reviewRequired: body.review === true, + }) }) } if (p === '/api/workspaces' && req.method === 'POST') { @@ -2666,7 +2765,8 @@ export function serve(ctx: Ctx, port = 0): Promise<ServerHandle> { const repoId = (body.repoId as string | undefined) ?? q.get('repo') ?? undefined const checkoutId = (body.checkoutId as string | undefined) ?? q.get('checkout') ?? undefined const file = (body.file as string | undefined) ?? q.get('file') ?? undefined - const workspaceId = ((body.workspace as string | undefined) ?? q.get('workspace') ?? undefined) + const workspaceId = requiredIntelWorkspace(db, String(body.workspace ?? q.get('workspace') ?? ws ?? '')) + if (workspaceId === null) return json(res, { ok: false, error: 'workspace is required for context when zero or multiple workspaces are registered' }, 400) return json(res, { ok: true, result: intel.getSymbolContext(db, name, { repoId, checkoutId, file, workspaceId }) }) } @@ -2677,9 +2777,20 @@ export function serve(ctx: Ctx, port = 0): Promise<ServerHandle> { const direction = ((body.direction ?? q.get('direction') ?? 'both') as 'upstream' | 'downstream' | 'both') const maxDepth = Number(body.maxDepth ?? q.get('maxDepth') ?? 3) const repoId = (body.repoId as string | undefined) ?? q.get('repo') ?? undefined + const checkoutId = (body.checkoutId as string | undefined) ?? q.get('checkout') ?? undefined const file = (body.file as string | undefined) ?? q.get('file') ?? undefined - const workspaceId = ((body.workspace as string | undefined) ?? q.get('workspace') ?? undefined) - return json(res, { ok: true, result: intel.getBlastRadius(db, target, { direction, maxDepth, repoId, file, workspaceId }) }) + const symbolIdRaw = body.symbolId ?? q.get('symbolId') + const symbolId = symbolIdRaw === undefined || symbolIdRaw === null ? undefined : Number(symbolIdRaw) + if (symbolId !== undefined && (!Number.isSafeInteger(symbolId) || symbolId < 1)) { + return json(res, { ok: false, error: 'symbolId must be a positive integer' }, 400) + } + const workspaceId = requiredIntelWorkspace(db, String(body.workspace ?? q.get('workspace') ?? ws ?? '')) + if (workspaceId === null) return json(res, { ok: false, error: 'workspace is required for impact when zero or multiple workspaces are registered' }, 400) + const result = intel.getBlastRadius(db, { name: target, file, id: symbolId }, { direction, maxDepth, repoId, checkoutId, file, workspaceId }) + if (result.status === 'ambiguous') { + return json(res, { ok: false, error: 'ambiguous impact target; provide a file or symbol id', candidates: result.candidates }, 409) + } + return json(res, { ok: true, result }) } if (p === '/api/intel/entry-points') { @@ -2708,10 +2819,11 @@ export function serve(ctx: Ctx, port = 0): Promise<ServerHandle> { const limit = Number(body.limit ?? q.get('limit') ?? 25) const repoId = (body.repoId as string | undefined) ?? q.get('repo') ?? undefined const checkoutId = (body.checkoutId as string | undefined) ?? q.get('checkout') ?? undefined - const workspaceId = ((body.workspace as string | undefined) ?? ws) || undefined + const workspaceId = requiredIntelWorkspace(db, String(body.workspace ?? q.get('workspace') ?? ws ?? '')) + if (workspaceId === null) return json(res, { ok: false, error: 'workspace is required for query when zero or multiple workspaces are registered' }, 400) return json(res, { ok: true, - ...(workspaceId === undefined || workspaceId === '' ? {} : indexStateOf(db, workspaceId)), + ...indexStateOf(db, workspaceId), result: intel.conceptSearch(db, query, { limit, repoId, checkoutId, workspaceId }), }) } @@ -2729,11 +2841,15 @@ export function serve(ctx: Ctx, port = 0): Promise<ServerHandle> { const repoId = (body.repoId as string | undefined) ?? q.get('repo') ?? undefined const checkoutPath = (body.checkoutPath as string | undefined) ?? q.get('path') ?? undefined const diffText = (body.diffText as string | undefined) - return json(res, { - ok: true, - ...indexStateOf(db, workspaceId), - result: intel.detectChanges(db, { workspaceId, checkoutId, repoId, checkoutPath, diffText }), - }) + try { + return json(res, { + ok: true, + ...indexStateOf(db, workspaceId), + result: intel.detectChanges(db, { workspaceId, checkoutId, repoId, checkoutPath, diffText }), + }) + } catch (error) { + return json(res, { ok: false, error: error instanceof Error ? error.message : String(error) }, 400) + } } if (p === '/api/intel/cypher' && (req.method === 'POST' || req.method === 'GET')) { @@ -2741,7 +2857,11 @@ export function serve(ctx: Ctx, port = 0): Promise<ServerHandle> { const query = (body.query ?? body.dsl ?? q.get('q') ?? q.get('query')) as string | object if (!query) return json(res, { ok: false, error: 'query or dsl required' }, 400) const limit = Number(body.limit ?? q.get('limit') ?? 50) - return json(res, { ok: true, result: intel.executeCypherQuery(db, query, { limit }) }) + const workspaceId = requiredIntelWorkspace(db, String(body.workspace ?? q.get('workspace') ?? ws ?? '')) + if (workspaceId === null) { + return json(res, { ok: false, error: 'workspace is required for Cypher when zero or multiple workspaces are registered' }, 400) + } + return json(res, { ok: true, result: intel.executeCypherQuery(db, query, { limit, workspaceId }) }) } // An unknown API path is a 404 in JSON. Falling through to the UI shell diff --git a/src/setup/agent-protocol.ts b/src/setup/agent-protocol.ts new file mode 100644 index 0000000..d39e666 --- /dev/null +++ b/src/setup/agent-protocol.ts @@ -0,0 +1,272 @@ +import { + existsSync, readFileSync, readdirSync, writeFileSync, unlinkSync, +} from 'node:fs' +import { dirname, join, resolve, sep } from 'node:path' +import { homedir } from 'node:os' +import { resolveClientTargets, type ClientDefinition, type SetupEntry } from './clients.ts' + +export const START_MARKER = '<!-- plugbrain:agent-protocol:start -->' +export const END_MARKER = '<!-- plugbrain:agent-protocol:end -->' + +export class InvalidAgentMarkersError extends Error { + constructor(message: string) { + super(message) + this.name = 'InvalidAgentMarkersError' + } +} + +// Kept in sync with the fenced block in docs/agent-protocol.md. The installed +// CLI can therefore initialize a project without shipping the documentation. +export const AGENT_PROTOCOL_BLOCK = `## Working with PlugBrain + +You are one agent in a team coordinated by PlugBrain. Your agent id is \`<id>\`. + +1. **Start of every turn:** run \`plugbrain swarm turn <id> start\`. Read every + message it prints and acknowledge each one with + \`plugbrain swarm ack <id> <messageId>\`. If a task is offered to you, take it + with \`plugbrain swarm turn <id> start --claim\`. +2. **Before you write:** claim the paths you will change: + \`plugbrain swarm claim <id> <path>... --task <taskId>\`. If the claim is + refused, another agent owns the path. Message that agent + (\`plugbrain swarm send <agent> --subject "..." --body "..." --from <your id>\`) instead of + writing anyway. Set \`PLUGBRAIN_AGENT=<your id>\` once in your shell and every + message is signed with your id; a message without a sender is refused. +3. **Before tests, builds, installs or new worktrees:** run + \`plugbrain swarm admit test|build|install|worktree\`. Exit code 5 means the + machine has no room. Do not start the work; end the turn as \`blocked\`. +4. **Hand in completed work:** run + \`plugbrain swarm deliver <id> <taskId> --path <evidence> [--repo <worktree>]\`. + The evidence must be a non-empty file inside the workspace. The receipt stores + its SHA-256, your agent id, attempt, and source revision when available. Review + tasks (title starts with \`R-\` or \`--review\`) also need a verdict and reviewed commit hash. +5. **End of every turn,** with exactly one state: + \`plugbrain swarm turn <id> end --state <state> --summary "<one or two sentences>"\` + - \`needs-task\`: you are done and need new work + - \`awaiting-commit\`: a tested change is ready; wait for approval + - \`blocked\`: a real blocker, named in the summary + - \`paused\`: you were told to stop + Then release your claims: \`plugbrain swarm release <id> --task <taskId>\`. + Ending with \`needs-task\` without a delivery leaves the task claimed and visible + under \`plugbrain swarm board --next\`. +6. **Commit only after approval.** Approval arrives as a message with the + subject "Commit freigegeben". Commit, then end the turn with \`needs-task\`. +7. **Report limits, never secrets.** If your tool shows a quota, report the + number: \`plugbrain swarm quota <account> <remaining> percent|credits|requests|rpm|tokens\`. + Never put keys or tokens into commands, summaries, messages or notes.` + +const backupPrefix = '.plugbrain-agent-protocol-backup-' +const emptyBackup = '.plugbrain-agent-protocol-created' + +export type AgentFileTarget = 'AGENTS.md' | 'CLAUDE.md' | 'both' + +export function selectAgentFiles(root: string, target?: AgentFileTarget): string[] { + const base = resolve(root) + const agents = join(base, 'AGENTS.md') + const claude = join(base, 'CLAUDE.md') + if (target === 'both') return [agents, claude] + if (target === 'AGENTS.md') return [agents] + if (target === 'CLAUDE.md') return [claude] + const selected = [agents] + if (existsSync(claude)) { + const text = readFileSync(claude, 'utf8') + if (!/\bAGENTS\.md\b/i.test(text)) selected.push(claude) + } + return selected +} + +function markerRange(text: string): { start: number; end: number } | null { + const starts = text.split(START_MARKER).length - 1 + const ends = text.split(END_MARKER).length - 1 + if (starts === 0 && ends === 0) return null + if (starts !== 1 || ends !== 1) throw new InvalidAgentMarkersError('invalid or duplicate PlugBrain markers; repair the markers and retry') + const start = text.indexOf(START_MARKER) + const end = text.indexOf(END_MARKER) + if (end < start + START_MARKER.length) throw new InvalidAgentMarkersError('invalid PlugBrain marker order; repair the markers and retry') + return { start, end: end + END_MARKER.length } +} + +export function renderAgentFile(existing: string | null): { text: string; changed: boolean } { + const before = existing ?? '' + const current = markerRange(before) + const block = `${START_MARKER}\n${AGENT_PROTOCOL_BLOCK}\n${END_MARKER}` + let text: string + if (current) text = `${before.slice(0, current.start)}${block}${before.slice(current.end)}` + else text = `${before}${before.length > 0 && !before.endsWith('\n') ? '\n' : ''}${block}\n` + return { text, changed: text !== before } +} + +export function validateAgentFiles(files: string[], undo = false): void { + for (const file of files) { + if (!existsSync(file)) continue + const text = readFileSync(file, 'utf8') + if (undo) markerRange(text) + else renderAgentFile(text) + } +} + +function latestBackup(file: string): string | null { + const folder = dirname(file) + if (!existsSync(folder)) return null + const prefix = `${file.split(/[\\/]/).pop()}${backupPrefix}` + const files = readdirSync(folder).filter(name => name.startsWith(prefix)).sort() + return files.length ? join(folder, files[files.length - 1]!) : null +} + +function backupPath(file: string): string { + const stamp = new Date().toISOString().replace(/[:.]/g, '-') + return `${file}${backupPrefix}${stamp}` +} + +export interface AgentFileResult { + path: string + action: 'created' | 'updated' | 'unchanged' | 'dry-run' | 'undone' + before: string | null + after: string | null + backup: string | null +} + +export function manageAgentFile(file: string, options: { dryRun?: boolean; undo?: boolean } = {}): AgentFileResult { + const before = existsSync(file) ? readFileSync(file, 'utf8') : null + if (options.undo) { + const backup = latestBackup(file) + if (backup !== null) { + const saved = readFileSync(backup, 'utf8') + if (saved === '') { + if (before === null) return { path: file, action: 'unchanged', before, after: before, backup } + const range = markerRange(before) + if (!range) return { path: file, action: 'unchanged', before, after: before, backup } + const reduced = `${before.slice(0, range.start)}${before.slice(range.end)}` + if (options.dryRun) return { path: file, action: 'dry-run', before, after: reduced.trim() === '' ? null : reduced, backup } + if (reduced.trim() === '') { unlinkSync(file); return { path: file, action: 'undone', before, after: null, backup } } + writeFileSync(file, reduced) + return { path: file, action: 'undone', before, after: reduced, backup } + } + if (before !== null && markerRange(saved) === null && before === renderAgentFile(saved).text) { + if (options.dryRun) return { path: file, action: 'dry-run', before, after: saved, backup } + writeFileSync(file, saved) + return { path: file, action: 'undone', before, after: saved, backup } + } + if (before === null) return { path: file, action: 'unchanged', before, after: before, backup } + const range = markerRange(before) + if (!range) return { path: file, action: 'unchanged', before, after: before, backup } + const after = `${before.slice(0, range.start)}${before.slice(range.end)}` + if (options.dryRun) return { path: file, action: 'dry-run', before, after, backup } + writeFileSync(file, after) + return { path: file, action: 'undone', before, after, backup } + } + if (before === null) return { path: file, action: 'unchanged', before, after: before, backup: null } + const range = markerRange(before) + if (!range) return { path: file, action: 'unchanged', before, after: before, backup: null } + const after = `${before.slice(0, range.start)}${before.slice(range.end)}` + if (!options.dryRun) writeFileSync(file, after) + return { path: file, action: options.dryRun ? 'dry-run' : 'undone', before, after, backup: null } + } + const rendered = renderAgentFile(before) + if (!rendered.changed) return { path: file, action: 'unchanged', before, after: before, backup: null } + if (options.dryRun) return { path: file, action: 'dry-run', before, after: rendered.text, backup: null } + const backup = backupPath(file) + writeFileSync(backup, before ?? '') + writeFileSync(file, rendered.text) + return { path: file, action: before === null ? 'created' : 'updated', before, after: rendered.text, backup } +} + +export interface DoctorAgentRow { + client: string + installed: 'ok' | 'missing' + mcp: 'ok' | 'missing' | 'wrong' + daemon: 'ok' | 'missing' + workspace: 'ok' | 'missing' + agentBlock: 'ok' | 'missing' | 'wrong' + fix: string +} + +function parseEntry(def: ClientDefinition, text: string): SetupEntry | null { + if (def.format === 'json') { + try { + const root = JSON.parse(text) as Record<string, any> + const entry = root?.[def.mapKey]?.plugbrain + if (entry && typeof entry.command === 'string' && Array.isArray(entry.args)) return entry as SetupEntry + } catch { return null } + return null + } + if (def.format === 'toml') { + const match = text.match(/\[mcp_servers\.plugbrain\]([\s\S]*?)(?=\n\s*\[|$)/) + if (!match) return null + const command = match[1]!.match(/^command\s*=\s*"([^"]+)"/m)?.[1] + const args = match[1]!.match(/^args\s*=\s*\[([^\]]*)\]/m)?.[1] + ?.match(/"([^"]*)"/g)?.map(value => JSON.parse(value) as string) + return command && args ? { command, args } : null + } + const nested = text.match(new RegExp(`^${def.mapKey}:\\s*\\n([\\s\\S]*)`, 'm'))?.[1] + const block = nested?.match(/^ plugbrain:\s*\n((?:^ .*\n?)*)/m)?.[1] + const command = block?.match(/^ command:\s*(['"])(.*?)\1\s*$/m)?.[2] + const args = [...(block?.matchAll(/^ -\s*(['"])(.*?)\1\s*$/gm) ?? [])].map(m => m[2]!) + return command && args.length ? { command, args } : null +} + +function installedVersion(entry: SetupEntry | null, installRoot: string): boolean { + if (!entry || !existsSync(entry.command)) return false + const cli = entry.args.find(arg => /(?:plugbrain|cli)(?:\.m?js|\.exe)$/i.test(arg)) + if (!cli || !existsSync(cli) || cli.endsWith('.ts')) return false + const root = resolve(installRoot).toLocaleLowerCase() + const target = resolve(cli).toLocaleLowerCase() + return target.startsWith(`${root}${sep}`) || target.startsWith(`${root}/`) +} + +function blockStatus(root: string): DoctorAgentRow['agentBlock'] { + const files = [join(root, 'AGENTS.md'), join(root, 'CLAUDE.md')].filter(existsSync) + if (!files.length) return 'missing' + let seen = false + for (const file of files) { + const text = readFileSync(file, 'utf8') + try { + if (markerRange(text)) seen = true + } catch { return 'wrong' } + } + return seen ? 'ok' : 'missing' +} + +export async function doctorAgents(options: { + db: { prepare(sql: string): { all(): unknown[] } } + cwd?: string + home?: string + installRoot?: string + port?: number + fetcher?: typeof fetch +}): Promise<DoctorAgentRow[]> { + const cwd = resolve(options.cwd ?? process.cwd()) + const home = options.home ?? process.env.PLUGBRAIN_CONFIG_HOME ?? homedir() + const targets = resolveClientTargets(home) + const installRoot = options.installRoot ?? join(process.env.LOCALAPPDATA ?? join(homedir(), 'AppData', 'Local'), 'Programs', 'PlugBrain') + let daemon: DoctorAgentRow['daemon'] = 'missing' + try { + const response = await (options.fetcher ?? fetch)(`http://127.0.0.1:${options.port ?? 4310}/api/health`, { signal: AbortSignal.timeout(1500) }) + if (response.ok) daemon = 'ok' + } catch { /* doctor is read-only and never starts the daemon */ } + const workspaces = options.db.prepare('SELECT root FROM workspaces').all() as Array<{ root: string }> + const workspace = workspaces.some(row => { + const root = resolve(row.root) + return cwd === root || cwd.startsWith(root + sep) + }) ? 'ok' : 'missing' + const workspaceRoot = workspaces + .map(row => resolve(row.root)) + .filter(root => cwd === root || cwd.startsWith(root + sep)) + .sort((a, b) => b.length - a.length)[0] + const agents = blockStatus(workspaceRoot ?? cwd) + return targets.map(target => { + const installed = target.detected ? 'ok' : 'missing' + let mcp: DoctorAgentRow['mcp'] = 'missing' + if (target.detected && existsSync(target.path)) { + let entry: SetupEntry | null = null + try { entry = parseEntry(target.def, readFileSync(target.path, 'utf8')) } catch { /* malformed file is wrong */ } + mcp = entry === null ? 'wrong' : installedVersion(entry, installRoot) ? 'ok' : 'wrong' + } + let fix = 'plugbrain init' + if (installed !== 'ok') fix = `Install ${target.def.label}, then run: plugbrain setup ${target.def.id}` + else if (mcp !== 'ok') fix = `plugbrain setup ${target.def.id}` + else if (daemon !== 'ok') fix = 'plugbrain serve' + else if (workspace !== 'ok') fix = 'plugbrain init' + else if (agents !== 'ok') fix = 'plugbrain agents-file' + return { client: target.def.label, installed, mcp, daemon, workspace, agentBlock: agents, fix } + }) +} diff --git a/src/setup/workspace-from-cwd.ts b/src/setup/workspace-from-cwd.ts index 651cc43..0fdb8e8 100644 --- a/src/setup/workspace-from-cwd.ts +++ b/src/setup/workspace-from-cwd.ts @@ -99,6 +99,30 @@ export interface WorkspaceRecord { created: boolean } +/** + * The row that already names this folder, however it is spelled in the store. + * + * Identity is the derived id first; failing that, the same physical folder + * under a legacy spelling. Reading every root through `fold` (which runs + * `canonicalPath`) means an 8.3 or junction spelling written before the + * canonicalization fix is still recognized as this folder, so re-registering + * updates the row instead of colliding on the primary key or adding a twin. + */ +function existingWorkspaceRow( + db: DatabaseSync, absolute: string, id: string, +): { id: string } | null { + const byId = db.prepare('SELECT id FROM workspaces WHERE id = ?').get(id) as + { id: string } | undefined + if (byId !== undefined) return { id: String(byId.id) } + const folded = fold(absolute) + const rows = db.prepare('SELECT id, root FROM workspaces').all() as + Array<{ id: unknown; root: unknown }> + for (const row of rows) { + if (fold(String(row.root)) === folded) return { id: String(row.id) } + } + return null +} + /** * What `registerWorkspaceRoot` WOULD write, without writing it. * @@ -107,29 +131,35 @@ export interface WorkspaceRecord { */ export function planWorkspaceRoot(db: DatabaseSync, root: string, name?: string): WorkspaceRecord { const absolute = canonicalPath(root) - const id = workspaceIdFor(absolute) - const existing = db.prepare('SELECT id FROM workspaces WHERE root = ?').get(absolute) as - { id: string } | undefined + const derivedId = workspaceIdFor(absolute) + const existing = existingWorkspaceRow(db, absolute, derivedId) + const id = existing?.id ?? derivedId const label = name ?? basename(absolute) ?? id - return { id, name: label, created: existing === undefined } + return { id, name: label, created: existing === null } } /** * Register a folder as a workspace, idempotently. * - * Mirrors the CLI's `register`: same id derivation, same `ON CONFLICT(root)`, - * so a folder registered here and one registered by `plugbrain register` land - * on one row rather than two. + * Mirrors the CLI's `register`: same id derivation, and a spelling-idempotent + * upsert, so a folder registered here and one registered by `plugbrain + * register` land on one row rather than two, whatever spelling either used. */ export function registerWorkspaceRoot( db: DatabaseSync, root: string, name?: string, ): WorkspaceRecord { const absolute = canonicalPath(root) const record = planWorkspaceRoot(db, root, name) - db.prepare( - `INSERT INTO workspaces (id, name, root, created_at) VALUES (?, ?, ?, ?) - ON CONFLICT(root) DO UPDATE SET name = excluded.name` - ).run(record.id, record.name, absolute, new Date().toISOString()) + if (record.created) { + db.prepare( + 'INSERT INTO workspaces (id, name, root, created_at) VALUES (?, ?, ?, ?)' + ).run(record.id, record.name, absolute, new Date().toISOString()) + } else { + // Found by identity or by a legacy spelling: rewrite the root to its + // canonical form and keep the id, so identity is never renumbered. + db.prepare('UPDATE workspaces SET name = ?, root = ? WHERE id = ?') + .run(record.name, absolute, record.id) + } return record } diff --git a/src/swarm-cli.ts b/src/swarm-cli.ts index d1ca403..f3162dd 100644 --- a/src/swarm-cli.ts +++ b/src/swarm-cli.ts @@ -5,7 +5,7 @@ * Code), can run a command, so this is the one channel all of them share. It * works on the local store directly: a worker never needs the Core's HTTP key. * - * plugbrain swarm register <agent> --surface <s> --account <label> [--key <resource>] + * plugbrain swarm register <agent> --surface <s> --account <label> [--quota-pool <pool>] [--key <resource>] * [--model <m>] [--name <n>] [--worktree <path>]... [--takeover] * plugbrain swarm turn <agent> start [--claim] * plugbrain swarm turn <agent> end --state needs-task|awaiting-commit|blocked|paused [--summary <s>] @@ -14,26 +14,49 @@ * plugbrain swarm claim <agent> <path>... [--task <id>] [--ttl-min <n>] write lease, shown on the board * plugbrain swarm release <agent> [<path>...] [--task <id>] * plugbrain swarm board [--git] [--json] + * plugbrain swarm reap [--dry-run] [--repo <path>] [--target <branch>] [--apply] + * plugbrain swarm reap --auto on|off * plugbrain swarm send <agent> --subject <s> --body <b> [--from <agent>] - * plugbrain swarm enqueue <title> [--body <b>] [--to <agent>] [--plan <M00>] [--by <agent>] + * plugbrain swarm enqueue <title> [--body <b>] [--to <agent>] [--plan <M00>] [--by <agent>] [--after <taskId>] + * plugbrain swarm supersede <task> [--by <task>] --note <reason> + * plugbrain swarm reassign <task> --to <agent> + * plugbrain swarm priority <task> <n> * plugbrain swarm deliver <agent> <taskId> --path <evidence> hand in a claimed task's candidate + * plugbrain swarm wave-done <waveId> record a wave only when evidence is complete * plugbrain swarm approve <agent> [--note <n>] [--by <agent>] * plugbrain swarm resources [--json] * plugbrain swarm quota <account> <remaining> <percent|credits|requests|rpm|tokens> [--resets <iso>] [--note <n>] + * plugbrain swarm quota-pool set <pool> --max-concurrent <n> [--rpm <n>] | quota-pool show * plugbrain swarm admit <edit|test|index|build|install|worktree> exit 0 = room, 5 = no room + * plugbrain swarm watchdog [--json] [--dry-run] | watchdog silent-after <minutes> + * plugbrain swarm review-pool set <agent>... | review-pool auto on|off | review-pool show + * plugbrain swarm runner set <agent> --cmd <exe> [--model <m>] [--effort <e>] [--sandbox <s>] [--search] + * plugbrain swarm runner show <agent> + * plugbrain swarm run <agent> [--once|--status|--stop] refill the worker's queue headless * * Every command takes `--workspace <id>`; without it the single planet is used. */ import type { DatabaseSync } from 'node:sqlite' -import { AccessDenied, registerAgent } from './access.ts' -import { deliverTask, enqueueTask } from './queue.ts' +import { AccessDenied, requireWorkspace } from './access.ts' +import { enqueueTask, prioritizeTask, reassignTask, supersedeTask } from './queue.ts' +import { + ensureWaveDoneSchema, recordWaveDone, + type WaveManifestEvidence, type WaveTaskEvidence, +} from './coord/wave-done.ts' import { PLAN_REF, setPlanRef } from './plan.ts' +import { buildSwarmChronicle, formatSwarmChronicleMarkdown } from './coord/chronicle.ts' +import { getSwarmNextActions, type SwarmNextAction } from './coord/next-actions.ts' +import { currentTaskForTurnDelivery, deliverTaskAtTurnEnd, deliverTaskWithEvidence } from './coord/turn-delivery.ts' import { - acquireLease, admitWork, agentsBoard, approveCommit, confirmDelivery, ensureSwarmOpsSchema, hostSnapshot, listQuotas, - recordTurn, releaseLease, - registerSwarmAgent, registerWorkerProfile, reportQuota, retireWorker, sendMessage, + acquireLease, admitWork, agentsBoard, approveCommit, confirmDelivery, ensureIntegrator, ensureSwarmOpsSchema, + configureQuotaPool, getRunnerProfile, hostSnapshot, listQuotaPools, listQuotas, quotaPoolSummary, readReviewPool, reconcileWorkerRuns, recordTurn, releaseLease, + reapWorktrees, setReapAuto, + registerSwarmAgent, registerWorkerProfile, reportQuota, retireWorker, scanWatchdog, sendMessage, + setReviewAuto, setReviewPool, setRunnerProfile, setSilentAfterMinutes, startWorkerRun, stopWorkerRun, + assertActiveSupervisorAttempt, runSupervisorLoop, startSupervisor, stopSupervisor, + touchAgentContact, watchdogSettings, workerRunStatus, TURN_END_STATES, WORK_KINDS, WORKER_SURFACES, - type QuotaUnit, type SwarmBoard, type TurnEndState, type TurnPing, type WorkKind, type WorkerSurface, + type QuotaUnit, type RunnerProfile, type SwarmBoard, type TurnEndState, type TurnPing, type WorkerRun, type WorkKind, type WorkerSurface, } from './coord/index.ts' /** Swarm workers check in at turn boundaries, which can be hours apart. */ @@ -48,6 +71,14 @@ const flag = (args: string[], name: string): string | null => { return at === -1 ? null : args[at + 1] ?? null } +const numberFlag = (args: string[], name: string): number | undefined => { + const value = flag(args, name) + if (value === null) return undefined + const parsed = Number(value) + if (!Number.isFinite(parsed)) throw new AccessDenied(`${name} must be a finite number`) + return parsed +} + const flags = (args: string[], name: string): string[] => { const values: string[] = [] args.forEach((arg, index) => { @@ -70,8 +101,10 @@ const positionals = (args: string[], valued: string[]): string[] => { } const VALUED = [ - '--workspace', '--surface', '--account', '--key', '--model', '--name', '--worktree', '--state', '--summary', + '--workspace', '--surface', '--account', '--quota-pool', '--key', '--model', '--name', '--worktree', '--state', '--summary', '--deliver', '--since', '--subject', '--body', '--from', '--to', '--by', '--note', '--resets', '--task', '--ttl-min', '--plan', '--path', + '--repo', '--target', '--auto', '--after', + '--cmd', '--args', '--effort', '--sandbox', '--cwd', '--max-concurrent', '--rpm', ] function need(value: string | null | undefined, usage: string): string { @@ -79,11 +112,6 @@ function need(value: string | null | undefined, usage: string): string { return value } -function ensureIntegrator(db: DatabaseSync, workspaceId: string, id: string): void { - registerAgent(db, id, id === INTEGRATOR ? 'Integrator' : id) - db.prepare('UPDATE agents SET workspace_id = COALESCE(workspace_id, ?) WHERE id = ?').run(workspaceId, id) -} - function printPing(ping: TurnPing): void { console.log(`${ping.agentId}: ${ping.state}`) if (ping.inbox.length === 0) console.log(' keine neuen Nachrichten') @@ -100,10 +128,29 @@ function printPing(ping: TurnPing): void { if (ping.inbox.length > 0) console.log(' gelesen quittieren: plugbrain swarm ack <agent> <messageId>') } -function printBoard(board: SwarmBoard): void { +function printNextActions(actions: SwarmNextAction[]): void { + console.log('Als Nächstes') + if (actions.length === 0) console.log(' Keine offenen Handlungsvorschläge.') + for (const action of actions) { + console.log(`- [${action.priority}] ${action.title}: ${action.reason}`) + console.log(` ${action.command}`) + } +} + +function printBoard(board: SwarmBoard, nextActions: SwarmNextAction[]): void { const drives = board.host.drives.map(drive => `${drive.root} ${(drive.freeBytes / GB).toFixed(1)} GB frei`).join(', ') const ram = `${Math.round((board.host.memory.freeBytes / board.host.memory.totalBytes) * 100)} % RAM frei` console.log(`Fleet ${board.workspaceId} · ${board.agents.filter(row => !row.retired).length} Worker · ${drives} · ${ram}`) + const pending = board.queue.tasks.filter(task => task.state === 'pending').length + const claimed = board.queue.tasks.filter(task => task.state === 'claimed').length + const superseded = board.queue.tasks.filter(task => task.state === 'superseded').length + console.log(`Queue: ${pending} pending · ${claimed} claimed · ${superseded} superseded`) + for (const task of board.queue.tasks.filter(task => task.state === 'claimed')) { + console.log(` Claimed ${task.id} by ${task.claimedBy ?? 'unknown'}: ${task.title}`) + } + for (const change of board.queue.recentChanges.slice(0, 10)) { + console.log(` Queue ${change.operation} ${change.taskId} by ${change.byAgent}${change.note ? `: ${change.note}` : ''}`) + } for (const admission of board.admission) { if (!admission.allowed) console.log(` KEIN PLATZ für ${admission.kind}: ${admission.reasons.join('; ')}`) } @@ -113,6 +160,7 @@ function printBoard(board: SwarmBoard): void { console.log(`- ${row.id.padEnd(18)} ${(row.surface ?? '?').padEnd(11)} ${(row.account ?? '-').padEnd(20)} ` + `${(row.turnState ?? 'unbekannt').padEnd(15)} ungelesen ${row.unread}${flagsText}`) if (row.task) console.log(` Aufgabe ${row.task.id}: ${row.task.title}`) + if (row.silentMinutes !== null) console.log(` still? seit ${row.silentMinutes} min ohne Kontakt`) if (row.turnSummary) console.log(` zuletzt: ${row.turnSummary.split('\n')[0]}`) for (const lease of row.leases) console.log(` schreibt (Lease): ${lease.paths.join(', ')}`) for (const worktree of row.worktrees) { @@ -120,8 +168,59 @@ function printBoard(board: SwarmBoard): void { : worktree.branch === null ? '' : `${worktree.branch} @ ${worktree.head} · ${worktree.dirtyFiles} uncommitted` console.log(` Worktree ${worktree.path} ${detail}`) } + if (row.runner !== null) { + const runner = row.runner + const life = runner.alive + ? `pid ${runner.pid} running since ${runner.startedAt}` + : `pid ${runner.pid ?? '?'} gone${runner.endedAt === null ? '' : ` at ${runner.endedAt}`}${runner.endedReason === null ? '' : ` (${runner.endedReason})`}` + console.log(` runner: ${life}`) + if (runner.lastEventKind !== null) { + console.log(` last log event ${runner.lastEventAt ?? '?'} ${runner.lastEventKind}: ${runner.lastEventText ?? ''}`) + } + if (runner.blockedAt !== null) console.log(` runner died mid-turn; worker booked blocked at ${runner.blockedAt}`) + } } for (const overlap of board.overlaps) console.log(` ÜBERLAPPUNG ${overlap.worktree}: ${overlap.agents.join(', ')}`) + console.log() + printNextActions(nextActions) +} + +function printRunnerProfile(profile: RunnerProfile): void { + const parts = [`program ${profile.cmd}`] + if (profile.model !== null) parts.push(`model ${profile.model}`) + if (profile.effort !== null) parts.push(`effort ${profile.effort}`) + if (profile.sandbox !== null) parts.push(`sandbox ${profile.sandbox}`) + if (profile.search) parts.push('web search') + if (profile.cwd !== null) parts.push(`cwd ${profile.cwd}`) + console.log(`${profile.agentId}: ${parts.join(' · ')}`) + if (profile.args.length > 0) console.log(` args: ${profile.args.join(' ')}`) +} + +function printRunStatus(db: DatabaseSync, workspaceId: string, agentId: string, asJson: boolean): number { + const status = workerRunStatus(db, workspaceId, agentId) + if (asJson) { console.log(JSON.stringify(status, null, 2)); return 0 } + if (status.run === null) { + console.error(`no run recorded for ${agentId}`) + return 3 + } + const { run } = status + console.log(`${agentId}: ${run.alive ? `running (pid ${run.pid})` : 'not running'}`) + console.log(` run ${run.runId}`) + console.log(` started ${run.startedAt}`) + console.log(` cwd ${run.cwd}`) + console.log(` command ${run.commandText}`) + console.log(` log ${run.logPath}`) + if (run.lastDocPath !== null) console.log(` last msg ${run.lastDocPath}`) + if (run.endedAt !== null) console.log(` ended ${run.endedAt} (${run.endedReason ?? 'unknown'})`) + if (run.stoppedAt !== null) console.log(` stopped ${run.stoppedAt}`) + console.log(` last event ${run.lastEventAt ?? 'none'} ${run.lastEventKind ?? ''}${ + run.lastEventText === null ? '' : `: ${run.lastEventText}`}`) + console.log(` turn ${status.turnState ?? 'unset'}${status.turnStateAt === null ? '' : ` at ${status.turnStateAt}`}`) + if (status.tail.length > 0) { + console.log(' log tail:') + for (const line of status.tail) console.log(` ${line}`) + } + return 0 } export function runSwarmCli(db: DatabaseSync, args: string[], defaultWorkspace: () => string): number { @@ -129,7 +228,8 @@ export function runSwarmCli(db: DatabaseSync, args: string[], defaultWorkspace: // Every command may be the first one a fresh store sees: `enqueue` used to // fail with "no such column: workspace_id" until some `register` had widened // the agents table. - ensureSwarmOpsSchema(db) + if (step !== 'chronik') ensureSwarmOpsSchema(db) + assertActiveSupervisorAttempt(db) const workspaceId = flag(rest, '--workspace') ?? defaultWorkspace() const asJson = rest.includes('--json') const pos = positionals(rest, VALUED) @@ -151,6 +251,7 @@ export function runSwarmCli(db: DatabaseSync, args: string[], defaultWorkspace: agentId, surface, account: need(flag(rest, '--account'), usage), + quotaPool: flag(rest, '--quota-pool') ?? undefined, resourceKey: flag(rest, '--key') ?? undefined, model: flag(rest, '--model') ?? undefined, worktrees: flags(rest, '--worktree'), @@ -160,21 +261,117 @@ export function runSwarmCli(db: DatabaseSync, args: string[], defaultWorkspace: else console.log(`registriert: ${profile.agentId} (${profile.surface}, ${profile.account}${profile.resourceKey ? `, Schlüssel ${profile.resourceKey}` : ''})`) return 0 } + case 'runner': { + const usage = 'plugbrain swarm runner set <agent> --cmd <exe> [--model <m>] [--effort <e>] ' + + '[--sandbox bypass|workspace-write|read-only|danger-full-access] [--search] [--args "<template>"] [--cwd <dir>] ' + + '| plugbrain swarm runner show <agent>' + const action = need(pos[0], usage) + const agentId = need(pos[1], usage) + if (action === 'show') { + const profile = getRunnerProfile(db, agentId) + if (profile === null) { console.error(`no runner profile for ${agentId}`); return 3 } + if (asJson) console.log(JSON.stringify(profile, null, 2)) + else printRunnerProfile(profile) + return 0 + } + if (action !== 'set') throw new AccessDenied(`usage: ${usage}`) + // `--args` is one string so the template can hold spaces; it is split here + // rather than by a shell, which the Brain never involves. + const argTemplate = flag(rest, '--args') + const profile = setRunnerProfile(db, { + agentId, + cmd: need(flag(rest, '--cmd'), usage), + args: argTemplate === null ? undefined : argTemplate.split(/\s+/).filter(token => token !== ''), + model: flag(rest, '--model') ?? undefined, + effort: flag(rest, '--effort') ?? undefined, + sandbox: flag(rest, '--sandbox') ?? undefined, + search: rest.includes('--search'), + cwd: flag(rest, '--cwd') ?? undefined, + }) + if (asJson) console.log(JSON.stringify(profile, null, 2)) + else printRunnerProfile(profile) + return 0 + } + case 'run': { + const usage = 'plugbrain swarm run <agent> [--once|--status|--stop|--stop-supervisor] [--json]' + const agentId = need(pos[0], usage) + if (rest.includes('--supervisor-child')) throw new AccessDenied('supervisor child must use the internal async dispatch') + if (!rest.includes('--once') && !rest.includes('--status') && !rest.includes('--stop') && !rest.includes('--stop-supervisor')) { + const supervisor = startSupervisor(db, { + workspaceId, agentId, + idleMs: numberFlag(rest, '--idle-ms'), + maxIdleChecks: numberFlag(rest, '--max-idle-checks'), + maxAttempts: numberFlag(rest, '--max-attempts'), + pollMs: numberFlag(rest, '--poll-ms'), + }) + if (asJson) console.log(JSON.stringify(supervisor, null, 2)) + else console.log(`supervising ${agentId}: pid ${supervisor.pid} (${supervisor.supervisorId})`) + return 0 + } + if (rest.includes('--stop-supervisor')) { + stopSupervisor(db, agentId) + console.log(`supervisor stop requested for ${agentId}`) + return 0 + } + if (rest.includes('--status')) return printRunStatus(db, workspaceId, agentId, asJson) + if (rest.includes('--stop')) { + const run = stopWorkerRun(db, { agentId, workspaceId }) + // A worker that already ended its turn keeps that state; the stop only + // pauses one that was still working. + const turn = workerRunStatus(db, workspaceId, agentId).turnState + if (asJson) { console.log(JSON.stringify({ ...run, turnState: turn }, null, 2)); return 0 } + console.log(`stopped ${agentId} (pid ${run.pid ?? '?'}); turn ${turn ?? 'unset'}`) + return 0 + } + const run = startWorkerRun(db, { workspaceId, agentId }) + if (asJson) { console.log(JSON.stringify(run, null, 2)); return 0 } + console.log(`started ${agentId}: pid ${run.pid ?? '?'} (${run.runId})`) + console.log(` cwd ${run.cwd}`) + console.log(` command ${run.commandText}`) + console.log(` log ${run.logPath}`) + console.log(` prompt ${run.promptPath ?? '-'}`) + console.log(' the Brain watches it: plugbrain swarm run ' + agentId + ' --status') + return 0 + } case 'turn': { - const usage = 'plugbrain swarm turn <agent> start|end [--state needs-task|awaiting-commit|blocked|paused] [--summary <s>] [--claim]' + const usage = 'plugbrain swarm turn <agent> start|end [--state needs-task|awaiting-commit|blocked|paused] [--summary <s>] [--claim] [--deliver <evidence>] [--repo <worktree>] [--review]' const agentId = need(pos[0], usage) const phase = need(pos[1], usage) if (phase !== 'start' && phase !== 'end') throw new AccessDenied(`usage: ${usage}`) const state = flag(rest, '--state') as TurnEndState | null if (state !== null && !TURN_END_STATES.includes(state)) throw new AccessDenied(`unknown state: ${state} (${TURN_END_STATES.join(', ')})`) + const deliveryPath = flag(rest, '--deliver') + let deliveryTaskId: string | null = null + if (deliveryPath !== null) { + const endState = state ?? 'needs-task' + if (phase !== 'end' || (endState !== 'needs-task' && endState !== 'awaiting-commit')) { + throw new AccessDenied('--deliver is only valid when ending with needs-task or awaiting-commit') + } + if (deliveryPath.trim() === '') throw new AccessDenied('deliver evidence path cannot be empty') + deliveryTaskId = currentTaskForTurnDelivery(db, workspaceId, agentId) + } const ping = recordTurn(db, { workspaceId, agentId, phase, state: state ?? undefined, summary: flag(rest, '--summary') ?? undefined, claimNext: rest.includes('--claim'), }) - if (asJson) console.log(JSON.stringify(ping, null, 2)) - else printPing(ping) + let delivery: { taskId: string; evidence: string } | null = null + if (deliveryPath !== null) { + const task = deliverTaskAtTurnEnd(db, deliveryTaskId!, agentId, deliveryPath, flag(rest, '--summary') ?? undefined, { + workspaceId, + workspaceRoot: requireWorkspace(db, workspaceId).root, + repoPath: flag(rest, '--repo') ?? undefined, + reviewRequired: rest.includes('--review'), + }) + delivery = { taskId: task.id, evidence: task.delivered_path ?? deliveryPath } + ping.currentTask = null + } + if (asJson) console.log(JSON.stringify({ ...ping, ...(delivery === null ? {} : { delivery }) }, null, 2)) + else { + printPing(ping) + if (delivery) console.log(` GELIEFERT ${delivery.taskId}: ${delivery.evidence}`) + } return 0 } case 'retire': { @@ -193,6 +390,7 @@ export function runSwarmCli(db: DatabaseSync, args: string[], defaultWorkspace: agentId, taskId: flag(rest, '--task') ?? `task-${agentId}`, paths, symbols: [], mode: 'write', ttlMs: ttlMin === null ? 4 * 60 * 60_000 : Number(ttlMin) * 60_000, }) + touchAgentContact(db, agentId) if (!result.acquired) { const holder = result.conflict?.holder console.error(`refused: ${result.conflict?.path ?? paths.join(', ')} gehört ${holder?.agentId ?? '?'} (${holder?.taskId ?? '?'}): ${result.conflict?.reason ?? ''}`) @@ -208,35 +406,98 @@ export function runSwarmCli(db: DatabaseSync, args: string[], defaultWorkspace: const result = releaseLease(db, { agentId, workspaceId, taskId: flag(rest, '--task') ?? undefined, paths: paths.length > 0 ? paths : undefined, }) + touchAgentContact(db, agentId) console.log(result.released ? `freigegeben: ${result.count} Lease(s)` : 'nichts freizugeben') return 0 } case 'ack': { const usage = 'plugbrain swarm ack <agent> <messageId>' - const read = confirmDelivery(db, need(pos[1], usage), need(pos[0], usage)) + const agentId = need(pos[0], usage) + const read = confirmDelivery(db, need(pos[1], usage), agentId) + touchAgentContact(db, agentId) console.log(read ? 'quittiert' : 'nichts zu quittieren') return 0 } case 'board': { + const reaper = reapWorktrees(db, workspaceId) + const settled = reconcileWorkerRuns(db, workspaceId).filter(event => event.reason === 'ended-without-turn-end') const board = agentsBoard(db, workspaceId, { gitStatus: rest.includes('--git') }) - if (asJson) console.log(JSON.stringify(board, null, 2)) - else printBoard(board) + const nextActions = getSwarmNextActions(db, workspaceId) + const summary = { + eligible: reaper.candidates.filter(row => row.eligible).length, + retained: reaper.candidates.filter(row => !row.eligible).length, + reasons: reaper.candidates.filter(row => !row.eligible).map(row => ({ path: row.path, reason: row.reason })), + missing: reaper.missing, + } + if (rest.includes('--next')) { + if (asJson) console.log(JSON.stringify(nextActions, null, 2)) + else printNextActions(nextActions) + } else if (asJson) console.log(JSON.stringify({ ...board, nextActions, reaper: summary, settled }, null, 2)) + else { + for (const event of settled) console.log(`!! ${event.summary.split('\n')[0]}`) + printBoard(board, nextActions) + console.log(`Reaper: ${summary.eligible} reapable, ${summary.retained} retained`) + for (const row of summary.reasons) console.log(` retained ${row.path}: ${row.reason}`) + for (const row of reaper.missing) console.log(` missing ${row.path}${row.quarantinedAt ? ` (quarantine: ${row.quarantinedAt})` : ''}`) + } + return 0 + } + case 'reap': { + const auto = flag(rest, '--auto') + if (auto !== null) { + if (auto !== 'on' && auto !== 'off') throw new AccessDenied('usage: plugbrain swarm reap --auto on|off') + setReapAuto(db, workspaceId, auto === 'on') + console.log(`reap.auto ${auto}`) + return 0 + } + const result = reapWorktrees(db, workspaceId, { + repo: flag(rest, '--repo') ?? undefined, target: flag(rest, '--target') ?? undefined, + apply: rest.includes('--apply'), + }) + if (asJson) console.log(JSON.stringify(result, null, 2)) + else { + for (const row of result.candidates) console.log(`${row.eligible ? 'reapable' : 'retained'} ${row.path}: ${row.reason}`) + for (const row of result.removed) console.log(`removed ${row.path}; restore with: ${row.restoreCommand}`) + for (const row of result.missing) console.log(`missing ${row.path}${row.quarantinedAt ? ` (quarantine: ${row.quarantinedAt})` : ''}`) + if (result.pruneCommand) { + console.log(`prune dry run only: ${result.pruneCommand}`) + for (const line of result.pruneReport) console.log(` ${line}`) + } + } + return 0 + } + case 'chronik': { + const usage = 'plugbrain swarm chronik [--since <iso|2h>] [--json|--md]' + const chronicle = buildSwarmChronicle(db, workspaceId, { since: flag(rest, '--since') ?? undefined }) + if (rest.includes('--json')) console.log(JSON.stringify(chronicle, null, 2)) + else if (rest.includes('--md') || !rest.includes('--json')) console.log(formatSwarmChronicleMarkdown(chronicle)) + else throw new AccessDenied(`usage: ${usage}`) return 0 } case 'send': { const usage = 'plugbrain swarm send <agent> --subject <s> --body <b> [--from <agent>]' - const from = flag(rest, '--from') ?? INTEGRATOR + // The sender is the author of the message. Defaulting it to the integrator + // made every message in the inbox look like it came from the integrator, + // even when cx01, cx03 or nv03 wrote it. An unnamed sender is refused + // rather than misattributed. + const from = (flag(rest, '--from') ?? process.env.PLUGBRAIN_AGENT ?? '').trim() + if (from === '') { + console.error('refused: no sender — pass --from <agent> or set PLUGBRAIN_AGENT') + return 2 + } ensureIntegrator(db, workspaceId, from) const message = sendMessage(db, { workspaceId, fromAgent: from, toAgent: need(pos[0], usage), subject: need(flag(rest, '--subject'), usage), body: need(flag(rest, '--body'), usage), }) + touchAgentContact(db, from) console.log(`gesendet ${message.id}`) return 0 } case 'enqueue': { - const usage = 'plugbrain swarm enqueue <title> [--body <b>] [--to <agent>] [--plan <M00>] [--by <agent>]' + const usage = 'plugbrain swarm enqueue <title> [--body <b>] [--to <agent>] [--plan <M00>] [--by <agent>] [--after <taskId>]' const by = flag(rest, '--by') ?? INTEGRATOR + const afterTaskId = flag(rest, '--after') ?? undefined ensureIntegrator(db, workspaceId, by) const title = need(pos.join(' '), usage) // A master task named in the title counts as the link too, so a planner @@ -244,22 +505,151 @@ export function runSwarmCli(db: DatabaseSync, args: string[], defaultWorkspace: const planRef = flag(rest, '--plan') ?? PLAN_REF.exec(title)?.[1] ?? null const task = enqueueTask(db, workspaceId, { title, body: flag(rest, '--body') ?? '', - addressedTo: flag(rest, '--to') ?? undefined, requestedBy: by, + addressedTo: flag(rest, '--to') ?? undefined, requestedBy: by, afterTaskId, }) if (planRef !== null) setPlanRef(db, task.id, planRef) console.log(`eingereiht ${task.id}: ${task.title}${task.addressed_to ? ` → ${task.addressed_to}` : ''}` + + (afterTaskId === undefined ? '' : ` nach ${afterTaskId}`) + (planRef === null ? '' : ` [${planRef}]`)) return 0 } + case 'supersede': { + const usage = 'plugbrain swarm supersede <task> [--by <task>] --note <reason>' + const task = supersedeTask(db, workspaceId, need(pos[0], usage), { + byAgent: (process.env.PLUGBRAIN_AGENT ?? INTEGRATOR).trim(), + byTaskId: flag(rest, '--by') ?? undefined, + note: need(flag(rest, '--note'), usage), + }) + if (asJson) console.log(JSON.stringify(task, null, 2)) + else console.log(`superseded ${task.id}${task.superseded_by ? ` by ${task.superseded_by}` : ''}`) + return 0 + } + case 'reassign': { + const usage = 'plugbrain swarm reassign <task> --to <agent>' + const task = reassignTask(db, workspaceId, need(pos[0], usage), { + byAgent: (process.env.PLUGBRAIN_AGENT ?? INTEGRATOR).trim(), + addressedTo: need(flag(rest, '--to'), usage), + }) + if (asJson) console.log(JSON.stringify(task, null, 2)) + else console.log(`reassigned ${task.id} → ${task.addressed_to}`) + return 0 + } + case 'priority': { + const usage = 'plugbrain swarm priority <task> <n>' + const priority = Number(need(pos[1], usage)) + const task = prioritizeTask(db, workspaceId, need(pos[0], usage), { + byAgent: (process.env.PLUGBRAIN_AGENT ?? INTEGRATOR).trim(), priority, + }) + if (asJson) console.log(JSON.stringify(task, null, 2)) + else console.log(`priority ${task.id}: ${task.priority}`) + return 0 + } case 'deliver': { // The worker that holds a task hands in its candidate: the queue row // moves to `delivered` and points at the evidence. Acceptance is not // decided here — that is the review and the integrator's approval. - const usage = 'plugbrain swarm deliver <agent> <taskId> --path <evidence>' - const task = deliverTask(db, need(pos[1], usage), need(pos[0], usage), need(flag(rest, '--path'), usage)) + const usage = 'plugbrain swarm deliver <agent> <taskId> --path <evidence> [--repo <worktree>] [--review]' + const deliveredBy = need(pos[0], usage) + const task = deliverTaskWithEvidence(db, need(pos[1], usage), deliveredBy, need(flag(rest, '--path'), usage), undefined, { + workspaceId, + workspaceRoot: requireWorkspace(db, workspaceId).root, + repoPath: flag(rest, '--repo') ?? undefined, + reviewRequired: rest.includes('--review'), + }) + touchAgentContact(db, deliveredBy) console.log(`geliefert ${task.id}: ${task.title} → ${task.delivered_path}`) return 0 } + case 'wave-done': { + const usage = 'plugbrain swarm wave-done <waveId> [--json]' + const waveId = need(pos[0], usage) + const manifestRows = db.prepare(`SELECT source, authority_ref, confidence, payload + FROM trace_events WHERE workspace_id = ? AND type = 'wave.manifest' + ORDER BY occurred_at DESC, id DESC`).all(workspaceId) as + Array<{ source: string; authority_ref: string; confidence: string; payload: string }> + let manifest: WaveManifestEvidence | null = null + for (const row of manifestRows) { + const payload = JSON.parse(row.payload) as Record<string, unknown> + if (payload.waveId !== waveId) continue + manifest = { + source: row.source, + authorityRef: row.authority_ref, + confidence: row.confidence, + waveId, + taskIds: Array.isArray(payload.taskIds) ? payload.taskIds.filter((id): id is string => typeof id === 'string') : [], + } + break + } + const taskIds = manifest?.taskIds ?? [] + const evidence: WaveTaskEvidence[] = taskIds.map(taskId => { + const task = db.prepare(`SELECT id, title, state, claimed_by FROM queue_tasks + WHERE workspace_id = ? AND id = ?`).get(workspaceId, taskId) as + { id: string; title: string; state: string; claimed_by: string | null } | undefined + const delivery = db.prepare(`SELECT review_judgment, source_revision, reviewed_commit + FROM queue_deliveries WHERE task_id = ?`).get(taskId) as + { review_judgment: string | null; source_revision: string | null; reviewed_commit: string | null } | undefined + const reviews = db.prepare(`SELECT source, agent_id, authority_ref, confidence, payload + FROM trace_events WHERE workspace_id = ? AND task_id = ? AND type = 'review.completed' + ORDER BY occurred_at DESC, id DESC`).all(workspaceId, taskId) as + Array<{ source: string; agent_id: string | null; authority_ref: string; confidence: string; payload: string }> + const integration = db.prepare(`SELECT source, authority_ref, confidence, payload + FROM trace_events WHERE workspace_id = ? AND task_id = ? AND type = 'integration.accepted' + ORDER BY occurred_at DESC, id DESC LIMIT 1`).get(workspaceId, taskId) as + { source: string; authority_ref: string; confidence: string; payload: string } | undefined + const ownerDecision = db.prepare(`SELECT source, authority_ref, confidence, payload + FROM trace_events WHERE workspace_id = ? AND task_id = ? AND type = 'wave.owner-decision' + ORDER BY occurred_at DESC, id DESC LIMIT 1`).get(workspaceId, taskId) as + { source: string; authority_ref: string; confidence: string; payload: string } | undefined + const integratedPayload = integration ? JSON.parse(integration.payload) as Record<string, unknown> : null + const decisionPayload = ownerDecision ? JSON.parse(ownerDecision.payload) as Record<string, unknown> : null + const independentReview = reviews.map(review => ({ + source: review.source, + reviewerId: review.agent_id ?? '', + authorityRef: review.authority_ref, + confidence: review.confidence, + payload: JSON.parse(review.payload) as Record<string, unknown>, + })).find(review => review.reviewerId !== '' && review.reviewerId !== task?.claimed_by + && review.payload.verdict === 'PASS' && review.payload.commit === delivery?.source_revision + && review.confidence === 'authoritative' && review.authorityRef.trim() !== '') + return { + taskId, + title: task?.title ?? '(unknown task)', + state: task?.state ?? 'missing', + claimedBy: task?.claimed_by ?? null, + reviewJudgment: delivery?.review_judgment ?? null, + sourceRevision: delivery?.source_revision ?? null, + reviewedCommit: delivery?.reviewed_commit ?? null, + independentReview: independentReview ? { + source: independentReview.source, + reviewerId: independentReview.reviewerId, + authorityRef: independentReview.authorityRef, + confidence: independentReview.confidence, + judgment: String(independentReview.payload.verdict), + commit: String(independentReview.payload.commit), + } : null, + integration: integration ? { + source: integration.source, + authorityRef: integration.authority_ref, + confidence: integration.confidence, + commit: typeof integratedPayload?.commit === 'string' ? integratedPayload.commit : null, + } : null, + ownerDecision: ownerDecision ? { + source: ownerDecision.source, + authorityRef: ownerDecision.authority_ref, + confidence: ownerDecision.confidence, + decision: typeof decisionPayload?.decision === 'string' ? decisionPayload.decision : '', + } : null, + } + }) + ensureWaveDoneSchema(db) + const report = recordWaveDone(db, workspaceId, waveId, manifest, evidence) + if (asJson) console.log(JSON.stringify(report, null, 2)) + else { + console.log(`${report.status} ${report.waveId}${report.alreadyRecorded ? ' (bereits gemeldet)' : ''}`) + for (const blocker of report.blockers) console.log(`- ${blocker}`) + } + return report.status === 'DONE' ? 0 : 1 + } case 'approve': { const usage = 'plugbrain swarm approve <agent> [--note <n>] [--by <agent>]' const by = flag(rest, '--by') ?? INTEGRATOR @@ -271,8 +661,9 @@ export function runSwarmCli(db: DatabaseSync, args: string[], defaultWorkspace: case 'resources': { const host = hostSnapshot() const quotas = listQuotas(db) + const quotaPools = listQuotaPools(db) const admission = WORK_KINDS.map(kind => admitWork(kind, host)) - if (asJson) { console.log(JSON.stringify({ host, quotas, admission }, null, 2)); return 0 } + if (asJson) { console.log(JSON.stringify({ host, quotas, quotaPools, admission }, null, 2)); return 0 } for (const drive of host.drives) console.log(`${drive.root} ${(drive.freeBytes / GB).toFixed(1)} von ${(drive.totalBytes / GB).toFixed(0)} GB frei`) console.log(`RAM ${(host.memory.freeBytes / GB).toFixed(1)} von ${(host.memory.totalBytes / GB).toFixed(0)} GB frei`) for (const entry of admission) console.log(` ${entry.kind.padEnd(8)} ${entry.allowed ? 'ok' : `NEIN: ${entry.reasons.join('; ')}`}`) @@ -280,18 +671,37 @@ export function runSwarmCli(db: DatabaseSync, args: string[], defaultWorkspace: console.log(` Kontingent ${quota.account}: ${quota.remaining} ${quota.unit}${quota.exhausted ? ' (erschöpft)' : ''}` + `${quota.resetsAt ? `, zurück ${quota.resetsAt}` : ''} · ${quota.updatedAt}`) } + for (const pool of quotaPools) console.log(` Quota-Pool ${pool.id}: concurrency=${pool.maxConcurrent}, rpm=${pool.maxRequestsPerMinute ?? 'unlimited'}${pool.blockedUntil ? `, bis ${pool.blockedUntil}` : ''}`) return 0 } case 'quota': { const usage = 'plugbrain swarm quota <account> <remaining> <percent|credits|requests|rpm|tokens> [--resets <iso>] [--note <n>]' + const reportedBy = flag(rest, '--by') ?? INTEGRATOR const report = reportQuota(db, { account: need(pos[0], usage), remaining: Number(need(pos[1], usage)), unit: need(pos[2], usage) as QuotaUnit, resetsAt: flag(rest, '--resets') ?? undefined, note: flag(rest, '--note') ?? undefined, - reportedBy: flag(rest, '--by') ?? INTEGRATOR, + reportedBy, }) + touchAgentContact(db, reportedBy) console.log(`${report.account}: ${report.remaining} ${report.unit}${report.exhausted ? ' (erschöpft)' : ''}`) return 0 } + case 'quota-pool': { + const action = pos[0] + if (action === 'show') { + const pools = listQuotaPools(db) + const summaries = pools.map(pool => quotaPoolSummary(db, pool.id)!).filter(Boolean) + if (asJson) console.log(JSON.stringify(summaries, null, 2)) + else for (const pool of summaries) console.log(`${pool.id}: concurrency=${pool.maxConcurrent}, rpm=${pool.maxRequestsPerMinute ?? 'unlimited'}, active=${pool.activeReservations}, settled=${pool.settledAttempts}, usage=${pool.reportedUsage} across ${pool.attemptsWithUnknownUsage} unknown${pool.blockedUntil ? `, blocked until ${pool.blockedUntil}` : ''}`) + return 0 + } + if (action !== 'set') throw new AccessDenied('usage: plugbrain swarm quota-pool set <pool> --max-concurrent <n> [--rpm <n>] | quota-pool show') + const usage = 'plugbrain swarm quota-pool set <pool> --max-concurrent <n> [--rpm <n>]' + const pool = configureQuotaPool(db, { id: need(pos[1], usage), maxConcurrent: numberFlag(rest, '--max-concurrent') ?? 1, + maxRequestsPerMinute: numberFlag(rest, '--rpm') ?? null }) + console.log(`${pool.id}: concurrency=${pool.maxConcurrent}, rpm=${pool.maxRequestsPerMinute ?? 'unlimited'}`) + return 0 + } case 'admit': { const kind = need(pos[0], 'plugbrain swarm admit <kind>') as WorkKind if (!WORK_KINDS.includes(kind)) throw new AccessDenied(`unknown work kind: ${kind} (${WORK_KINDS.join(', ')})`) @@ -299,7 +709,70 @@ export function runSwarmCli(db: DatabaseSync, args: string[], defaultWorkspace: console.log(admission.allowed ? `ok: ${kind}` : `kein Platz für ${kind}: ${admission.reasons.join('; ')}`) return admission.allowed ? 0 : 5 } + case 'watchdog': { + const usage = 'plugbrain swarm watchdog [--json] [--dry-run] | plugbrain swarm watchdog silent-after <minutes>' + if (pos[0] === 'silent-after') { + const settings = setSilentAfterMinutes(db, workspaceId, Number(need(pos[1], usage))) + console.log(`Still-Erkennung: working ohne Kontakt ab ${settings.silentAfterMinutes} min`) + return 0 + } + const report = scanWatchdog(db, workspaceId, { alert: !rest.includes('--dry-run') }) + if (asJson) { console.log(JSON.stringify(report, null, 2)); return 0 } + console.log(`Watchdog ${report.workspaceId} · still ab ${report.silentAfterMinutes} min · ` + + `${report.silent.length} still · ${report.alerted.length} gemeldet · ${report.released.length} freigegeben`) + for (const row of report.silent) { + console.log(`- ${row.agentId} still seit ${row.minutes} min${row.alerted ? ' (jetzt gemeldet)' : ''}`) + } + for (const id of report.released) console.log(` freigegeben: ${id}`) + return 0 + } + case 'review-pool': { + const usage = 'plugbrain swarm review-pool set <agent>... | plugbrain swarm review-pool auto on|off | review-pool show' + const verb = pos[0] + if (verb === 'set') { + const pool = setReviewPool(db, workspaceId, pos.slice(1)) + console.log(pool.length === 0 ? 'Reviewer: keine' : `Reviewer: ${pool.map(entry => entry.agentId).join(', ')}`) + return 0 + } + if (verb === 'auto') { + const mode = need(pos[1], usage) + if (mode !== 'on' && mode !== 'off') throw new AccessDenied(`usage: ${usage}`) + const settings = setReviewAuto(db, workspaceId, mode === 'on') + console.log(`Review-Routing: ${settings.reviewAuto ? 'an' : 'aus'}`) + return 0 + } + if (verb === 'show' || verb === undefined) { + const settings = watchdogSettings(db, workspaceId) + const pool = readReviewPool(db, workspaceId) + if (asJson) { console.log(JSON.stringify({ ...settings, pool }, null, 2)); return 0 } + console.log(`Review-Routing: ${settings.reviewAuto ? 'an' : 'aus'} · Still-Erkennung ab ${settings.silentAfterMinutes} min`) + for (const entry of pool) { + console.log(`- ${entry.agentId} (${entry.account ?? 'Konto unbekannt'}${entry.retired ? ', abgemeldet' : ''})`) + } + return 0 + } + throw new AccessDenied(`usage: ${usage}`) + } default: - throw new AccessDenied('usage: plugbrain swarm <register|turn|ack|retire|claim|release|board|send|enqueue|approve|resources|quota|admit> …') + throw new AccessDenied('usage: plugbrain swarm <register|turn|ack|retire|claim|release|board|reap|chronik|send|enqueue|supersede|reassign|priority|deliver|wave-done|approve|resources|quota|admit|watchdog|review-pool|runner|run> …') } } + +/** Internal entry point used only by the detached supervisor child. */ +export async function runSwarmSupervisorChild( + db: DatabaseSync, args: string[], defaultWorkspace: () => string, +): Promise<number> { + const [step, ...rest] = args + const agentId = need(positionals(rest, VALUED)[0], 'internal supervisor needs an agent id') + if (step !== 'run' || !rest.includes('--supervisor-child')) throw new AccessDenied('invalid internal supervisor invocation') + const supervisorId = process.env.PLUGBRAIN_SUPERVISOR_ID + if (!supervisorId) throw new AccessDenied('internal supervisor identity is missing') + await runSupervisorLoop(db, { + workspaceId: flag(rest, '--workspace') ?? defaultWorkspace(), agentId, supervisorId, + idleMs: numberFlag(rest, '--idle-ms'), + maxIdleChecks: numberFlag(rest, '--max-idle-checks'), + maxAttempts: numberFlag(rest, '--max-attempts'), + pollMs: numberFlag(rest, '--poll-ms'), + }) + return 0 +} diff --git a/src/swarm-watch.ts b/src/swarm-watch.ts new file mode 100644 index 0000000..f3f548e --- /dev/null +++ b/src/swarm-watch.ts @@ -0,0 +1,350 @@ +import { mkdirSync, readFileSync, writeFileSync } from 'node:fs' +import { readdir, stat } from 'node:fs/promises' +import { join, resolve } from 'node:path' +import { admitWork, hostSnapshot } from './coord/resources.ts' +import { storeHome } from './index/runs.ts' + +export interface WatchEvent { + /** The server's monotonic event id, when the source provides one. */ + id?: string + type: string + data: unknown + timestamp: string +} + +export interface SwarmWatchOptions { + forAgent?: string + agents?: string[] + dirs?: string[] + timeoutMs: number + json?: boolean + subscribe: (listener: (event: WatchEvent) => void, onError: (error: Error) => void) => () => void + emit?: (events: WatchEvent[], timedOut: boolean) => void + now?: () => number + pollMs?: number + batchMs?: number + snapshotFiles?: (roots: string[]) => Promise<Map<string, string>> + sampleAdmission?: () => Record<string, boolean> +} + +const POLL_MS = 30_000 +const BATCH_MS = 5_000 +const WORK_KINDS = ['test', 'build', 'worktree'] as const + +function record(value: unknown): Record<string, unknown> { + return typeof value === 'object' && value !== null ? value as Record<string, unknown> : {} +} + +function watched(event: WatchEvent, options: SwarmWatchOptions): boolean { + const data = record(event.data) + if (event.type === 'file.changed' || event.type === 'admission.changed') return true + const agents = options.agents?.length ? options.agents : options.forAgent ? [options.forAgent] : null + if (event.type === 'agent.turn') { + return (agents === null || agents.includes(String(data.agentId))) && data.previousState !== data.state + } + if (event.type === 'message.sent' || event.type === 'message.delivered') { + return options.forAgent !== undefined && (data.toAgent === options.forAgent || data.agentId === options.forAgent) + } + if (event.type === 'task.enqueued') { + return options.forAgent !== undefined && (data.addressedTo === null || data.addressedTo === undefined || data.addressedTo === options.forAgent) + } + if (event.type === 'task.delivered') { + const actor = String(data.agentId ?? data.claimedBy ?? '') + return agents === null || agents.includes(actor) + } + if (event.type === 'watchdog.silent') { + return options.forAgent === undefined ? agents === null || agents.includes(String(data.agentId)) : data.agentId === options.forAgent + } + return false +} + +async function markdownSnapshot(roots: string[]): Promise<Map<string, string>> { + const found = new Map<string, string>() + const visit = async (directory: string): Promise<void> => { + let entries + try { entries = await readdir(directory, { withFileTypes: true }) } catch { return } + for (const entry of entries) { + const path = join(directory, entry.name) + if (entry.isDirectory()) await visit(path) + else if (entry.isFile() && entry.name.toLowerCase().endsWith('.md')) { + try { + const info = await stat(path) + found.set(path, `${info.mtimeMs}:${info.size}`) + } catch { /* A concurrently removed file is absent from this snapshot. */ } + } + } + } + await Promise.all(roots.map(root => visit(resolve(root)))) + return found +} + +function admissionSnapshot(): Record<string, boolean> { + const host = hostSnapshot() + return Object.fromEntries(WORK_KINDS.map(kind => [kind, admitWork(kind, host).allowed])) +} + +function humanSummary(events: WatchEvent[]): string { + return events.map(event => { + const data = record(event.data) + if (event.type === 'agent.turn') return `Turn ${String(data.agentId)}: ${String(data.previousState ?? 'unbekannt')} → ${String(data.state)}${data.summary ? ` — ${String(data.summary)}` : ''}` + if (event.type === 'message.sent' || event.type === 'message.delivered') return `Nachricht ${String(data.subject ?? data.messageId ?? '')} an ${String(data.toAgent ?? data.agentId ?? '')}` + if (event.type === 'task.enqueued') return `Neue Aufgabe ${String(data.taskId)}: ${String(data.title)}` + if (event.type === 'task.delivered') return `Aufgabe geliefert ${String(data.taskId)}: ${String(data.title)}${data.evidence ? ` · Beleg ${String(data.evidence)}` : ''}` + if (event.type === 'watchdog.silent') return `still? ${String(data.agentId)} seit ${String(data.minutes)} min ohne Kontakt` + if (event.type === 'file.changed') return `Markdown geändert: ${String(data.path)}` + if (event.type === 'admission.changed') return `Zulassung ${String(data.kind)}: ${String(data.allowed)}` + return `${event.type}` + }).join('\n') +} + +export function formatWatchResult(events: WatchEvent[], timedOut: boolean, json = false): string { + return json ? JSON.stringify({ events, timedOut }) : timedOut ? 'Zeitlimit ohne Ereignis.' : humanSummary(events) +} + +/** Wait for one relevant Brain event, while sparsely sampling files and host admission. */ +export async function runSwarmWatch(options: SwarmWatchOptions): Promise<{ code: 0 | 3 | 1; events: WatchEvent[]; timedOut: boolean }> { + const emit = options.emit ?? ((events, timedOut) => { + console.log(formatWatchResult(events, timedOut, options.json)) + if (timedOut && !options.json) { + const targets = options.forAgent ?? options.agents?.join(', ') ?? 'alle Worker' + const folders = options.dirs?.length ? `${options.dirs.length} Verzeichnis(se)` : 'keine Verzeichnisse' + console.log(`Lage: beobachtet wurden ${targets} und ${folders}; geprüft bis zum Zeitlimit von ${Math.ceil(options.timeoutMs / 60_000)} min.`) + } + }) + const now = options.now ?? Date.now + const pollMs = options.pollMs ?? POLL_MS + const batchMs = options.batchMs ?? BATCH_MS + const started = now() + const snapshotFiles = options.snapshotFiles ?? markdownSnapshot + const sampleAdmission = options.sampleAdmission ?? admissionSnapshot + let baselineFiles = await snapshotFiles(options.dirs ?? []) + let baselineAdmission = sampleAdmission() + + return await new Promise((resolveResult) => { + let finished = false + let batchTimer: ReturnType<typeof setTimeout> | undefined + // Declared before `subscribe` so a subscriber that fires synchronously can + // finish safely instead of touching these bindings in their temporal dead + // zone (a subscriber that ended the watch is cleaned up right after). + let timeoutTimer: ReturnType<typeof setTimeout> | undefined + let pollTimer: ReturnType<typeof setInterval> | undefined + let unsubscribe: () => void = () => undefined + const events: WatchEvent[] = [] + const cleanup = () => { + if (finished) return + finished = true + if (timeoutTimer !== undefined) clearTimeout(timeoutTimer) + if (pollTimer !== undefined) clearInterval(pollTimer) + if (batchTimer) clearTimeout(batchTimer) + unsubscribe() + } + const finish = (timedOut: boolean, code: 0 | 3 | 1) => { + if (finished) return + const resultEvents = events.splice(0) + cleanup() + emit(resultEvents, timedOut) + resolveResult({ code, events: resultEvents, timedOut }) + } + const seenMessageIds = new Set<string>() + const onEvent = (event: WatchEvent) => { + // One message must wake the observer once. The bus emits `message.sent` + // when it is written and `message.delivered` when it is picked up; keep + // the sent frame and drop the later delivered frame for the same id. + if (event.type === 'message.delivered') { + const messageId = record(event.data).messageId + if (typeof messageId === 'string' && seenMessageIds.has(messageId)) return + } + if (!watched(event, options)) return + if (event.type === 'message.sent') { + const messageId = record(event.data).id + if (typeof messageId === 'string') seenMessageIds.add(messageId) + } + events.push(event) + if (!batchTimer) batchTimer = setTimeout(() => finish(false, 0), batchMs) + } + const onError = (error: Error) => { + const event: WatchEvent = { type: 'watch.error', data: { message: error.message }, timestamp: new Date(now()).toISOString() } + events.push(event) + finish(false, 1) + } + unsubscribe = options.subscribe(onEvent, onError) + if (finished) unsubscribe() + timeoutTimer = setTimeout(() => finish(true, 3), Math.max(0, options.timeoutMs - (now() - started))) + pollTimer = setInterval(() => { + void (async () => { + const files = await snapshotFiles(options.dirs ?? []) + for (const [path, signature] of files) { + if (baselineFiles.get(path) !== signature) onEvent({ type: 'file.changed', data: { path }, timestamp: new Date(now()).toISOString() }) + } + baselineFiles = files + + const admission = sampleAdmission() + for (const kind of WORK_KINDS) { + if (admission[kind] !== baselineAdmission[kind]) onEvent({ type: 'admission.changed', data: { kind, previousAllowed: baselineAdmission[kind], allowed: admission[kind] }, timestamp: new Date(now()).toISOString() }) + } + baselineAdmission = admission + })().catch(error => onError(error instanceof Error ? error : new Error(String(error)))) + }, pollMs) + }) +} + +function parseDuration(value: string): number | null { + const match = /^(\d+)(ms|s|m|h)$/.exec(value) + if (!match) return null + const scale: Record<string, number> = { ms: 1, s: 1_000, m: 60_000, h: 3_600_000 } + return Number(match[1]) * scale[match[2]!]! +} + +function flagValue(args: string[], name: string): string | undefined { + const index = args.indexOf(name) + if (index >= 0) return args[index + 1] + return args.find(arg => arg.startsWith(`${name}=`))?.slice(name.length + 1) +} + +function listFlag(args: string[], name: string): string[] { + return args.flatMap((arg, index) => arg === name ? (args[index + 1] ?? '').split(',') : arg.startsWith(`${name}=`) ? arg.slice(name.length + 1).split(',') : []) + .map(item => item.trim()).filter(Boolean) +} + +export interface SseSubscribeOptions { + /** Cursor to resume from. A fresh process passes its persisted cursor here. */ + lastEventId?: string + /** How many consecutive reconnects before the stream is declared failed. */ + maxRetries?: number + baseBackoffMs?: number + maxBackoffMs?: number + /** Called with each event id so a caller can persist the resume cursor. */ + onCursor?: (id: string) => void +} + +/** Where a `swarm watch` run remembers the last event it saw, so a restart resumes. */ +export function watchCursorPath(): string { + return join(storeHome(), 'swarm-watch-cursor') +} + +export function loadWatchCursor(): string | null { + try { + const value = readFileSync(watchCursorPath(), 'utf8').trim() + return /^\d+$/.test(value) ? value : null + } catch { return null } +} + +export function saveWatchCursor(id: string): void { + if (!/^\d+$/.test(id)) return + try { + const home = storeHome() + mkdirSync(home, { recursive: true }) + writeFileSync(watchCursorPath(), id, 'utf8') + } catch { /* A cursor is a convenience; a failed write must not stop the watch. */ } +} + +function isNumericId(value: string | null | undefined): value is string { + return typeof value === 'string' && /^\d+$/.test(value) +} + +/** + * Subscribe to the Brain's SSE stream, reconnecting with bounded backoff. + * + * A reconnect sends the last id it saw as `Last-Event-ID`, so the server can + * replay what was missed while the watcher was away; a normal stream close is + * retried instead of aborting a long watch. `onError` fires only after the + * retry budget is spent. + */ +export function sseSubscribe( + url: string, + listener: (event: WatchEvent) => void, + onError: (error: Error) => void, + options: SseSubscribeOptions = {}, +): () => void { + const controller = new AbortController() + let closed = false + let lastEventId = isNumericId(options.lastEventId) ? options.lastEventId : undefined + const maxRetries = options.maxRetries ?? 5 + const baseBackoffMs = options.baseBackoffMs ?? 500 + const maxBackoffMs = options.maxBackoffMs ?? 5_000 + + const parseFrame = (block: string): void => { + let type = 'message' + let data = '' + let frameId: string | undefined + for (const line of block.split('\n')) { + if (line.startsWith('event:')) type = line.slice(6).trim() + else if (line.startsWith('data:')) data += line.slice(5).trim() + else if (line.startsWith('id:')) frameId = line.slice(3).trim() + } + if (isNumericId(frameId)) { + lastEventId = frameId + options.onCursor?.(frameId) + } + if (!data) return + try { listener({ id: frameId, type, data: JSON.parse(data) as unknown, timestamp: new Date().toISOString() }) } + catch { /* Ignore malformed or keepalive frames. */ } + } + + void (async () => { + let attempt = 0 + while (!closed) { + try { + const headers: Record<string, string> = { accept: 'text/event-stream' } + if (lastEventId) headers['Last-Event-ID'] = lastEventId + const response = await fetch(url, { headers, signal: controller.signal }) + if (!response.ok || !response.body) throw new Error(`Live-Eventquelle antwortete mit HTTP ${response.status}`) + attempt = 0 + const reader = response.body.getReader() + const decoder = new TextDecoder() + let buffer = '' + while (!closed) { + const { value, done } = await reader.read() + if (done) break + buffer += decoder.decode(value, { stream: true }) + let boundary = buffer.indexOf('\n\n') + while (boundary >= 0) { + const block = buffer.slice(0, boundary).replace(/\r/g, '') + buffer = buffer.slice(boundary + 2) + parseFrame(block) + boundary = buffer.indexOf('\n\n') + } + } + if (closed) return + throw new Error('Live-Eventquelle wurde geschlossen') + } catch (error) { + if (closed) return + if (error instanceof Error && error.name === 'AbortError') return + attempt += 1 + if (attempt > maxRetries) { + onError(error instanceof Error ? error : new Error(String(error))) + return + } + const delay = Math.min(maxBackoffMs, baseBackoffMs * 2 ** (attempt - 1)) + await new Promise<void>(resolveDelay => { + const timer = setTimeout(resolveDelay, delay) + if (typeof timer.unref === 'function') timer.unref() + }) + } + } + })() + return () => { closed = true; controller.abort() } +} + +export async function runSwarmWatchCli(args: string[]): Promise<number> { + const forAgent = flagValue(args, '--for') + const agents = listFlag(args, '--agents') + const dirs = listFlag(args, '--dirs') + const timeoutText = flagValue(args, '--timeout') ?? '20m' + const timeoutMs = parseDuration(timeoutText) + if (timeoutMs === null || timeoutMs < 0) { + console.error('usage: plugbrain swarm watch [--for <agent>] [--agents <id,…>] [--dirs <path,…>] [--timeout 20m] [--json]') + return 2 + } + const baseUrl = (process.env.PLUGBRAIN_URL ?? 'http://127.0.0.1:4310').replace(/\/$/, '') + const result = await runSwarmWatch({ + forAgent, agents, dirs, timeoutMs, json: args.includes('--json'), + subscribe: (listener, onError) => sseSubscribe(`${baseUrl}/api/live/events`, listener, onError, { + // Resume from the persisted cursor on a fresh process and update it as + // events arrive, so a restart does not lose the window in between. + lastEventId: loadWatchCursor() ?? undefined, + onCursor: saveWatchCursor, + }), + }) + return result.code +} diff --git a/src/trace.ts b/src/trace.ts index c403fe8..668d3b1 100644 --- a/src/trace.ts +++ b/src/trace.ts @@ -59,6 +59,8 @@ export const TRACE_EVENT_TYPES = [ 'integration.queued', 'integration.accepted', 'integration.rejected', + 'wave.manifest', + 'wave.owner-decision', 'worker.started', 'worker.heartbeat', 'worker.backoff', diff --git a/test/agent-protocol.test.ts b/test/agent-protocol.test.ts new file mode 100644 index 0000000..2f10e20 --- /dev/null +++ b/test/agent-protocol.test.ts @@ -0,0 +1,133 @@ +import { strict as assert } from 'node:assert' +import { test } from 'node:test' +import { spawnSync } from 'node:child_process' +import { mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { AGENT_PROTOCOL_BLOCK, doctorAgents, manageAgentFile, renderAgentFile, selectAgentFiles } from '../src/setup/agent-protocol.ts' + +const CLI = join(import.meta.dirname, '..', 'src', 'cli.ts') + +test('agent block is inserted, replaced, byte-preserving outside markers and undoable', () => { + const before = 'first\r\n\r\nlast\n' + const rendered = renderAgentFile(before) + assert.equal(rendered.changed, true) + assert.ok(rendered.text.startsWith(before), 'all original bytes stay before the appended managed block') + assert.ok(renderAgentFile(rendered.text).text.includes(AGENT_PROTOCOL_BLOCK)) + const replaced = renderAgentFile(rendered.text.replace('are one agent', 'are one worker')) + assert.ok(replaced.text.includes('are one agent')) + assert.ok(replaced.text.startsWith('first\r\n\r\n')) + assert.ok(replaced.text.startsWith(before)) + assert.throws(() => renderAgentFile('<!-- plugbrain:agent-protocol:start --> only'), /repair the markers/) + assert.throws(() => renderAgentFile('<!-- plugbrain:agent-protocol:start --><!-- plugbrain:agent-protocol:start --><!-- plugbrain:agent-protocol:end -->'), /duplicate/) + + const dir = mkdtempSync(join(tmpdir(), 'plugbrain-agent-file-')) + try { + const file = join(dir, 'AGENTS.md') + const first = manageAgentFile(file) + assert.equal(first.action, 'created') + assert.ok(first.backup) + const dry = manageAgentFile(file, { dryRun: true }) + assert.equal(dry.action, 'unchanged') + assert.equal(readFileSync(file, 'utf8'), first.after) + assert.equal(manageAgentFile(file, { undo: true }).action, 'undone') + } finally { rmSync(dir, { recursive: true, force: true }) } +}) + +test('dry-run and undo preserve existing content and select the expected files', () => { + const dir = mkdtempSync(join(tmpdir(), 'plugbrain-agent-target-')) + try { + const agents = join(dir, 'AGENTS.md') + const claude = join(dir, 'CLAUDE.md') + writeFileSync(agents, 'custom') + writeFileSync(claude, 'custom') + assert.deepEqual(selectAgentFiles(dir).map(p => p.split(/[\\/]/).pop()), ['AGENTS.md', 'CLAUDE.md']) + writeFileSync(claude, 'See AGENTS.md') + assert.deepEqual(selectAgentFiles(dir).map(p => p.split(/[\\/]/).pop()), ['AGENTS.md']) + const preview = manageAgentFile(agents, { dryRun: true }) + assert.equal(preview.action, 'dry-run') + assert.equal(readFileSync(agents, 'utf8'), 'custom') + manageAgentFile(agents) + const managed = readFileSync(agents, 'utf8') + const directoryBeforePreview = readdirSync(dir).sort() + const undoPreview = manageAgentFile(agents, { undo: true, dryRun: true }) + assert.equal(undoPreview.action, 'dry-run') + assert.equal(undoPreview.after, 'custom') + assert.equal(readFileSync(agents, 'utf8'), managed, 'undo --dry-run must not change the file') + assert.deepEqual(readdirSync(dir).sort(), directoryBeforePreview, 'undo --dry-run must not remove or create backups') + assert.equal(manageAgentFile(agents, { undo: true }).action, 'undone') + assert.equal(readFileSync(agents, 'utf8'), 'custom') + } finally { rmSync(dir, { recursive: true, force: true }) } +}) + +test('undo --dry-run keeps a newly created managed file and its marker backup', () => { + const dir = mkdtempSync(join(tmpdir(), 'plugbrain-agent-undo-created-')) + try { + const file = join(dir, 'AGENTS.md') + manageAgentFile(file) + const managed = readFileSync(file, 'utf8') + const filesBeforePreview = readdirSync(dir).sort() + const preview = manageAgentFile(file, { undo: true, dryRun: true }) + assert.equal(preview.action, 'dry-run') + assert.equal(preview.after, null) + assert.equal(readFileSync(file, 'utf8'), managed, 'undo --dry-run must not unlink the managed file') + assert.deepEqual(readdirSync(dir).sort(), filesBeforePreview, 'undo --dry-run must preserve the creation marker backup') + } finally { rmSync(dir, { recursive: true, force: true }) } +}) + +test('doctor reports missing, wrong and valid client state without writes', async () => { + const dir = mkdtempSync(join(tmpdir(), 'plugbrain-doctor-')) + const config = join(dir, '.claude.json') + const install = join(dir, 'Programs', 'PlugBrain') + const cli = join(install, 'plugbrain.js') + try { + mkdirSync(install, { recursive: true }) + mkdirSync(join(dir, 'project'), { recursive: true }) + writeFileSync(cli, '// fake installed CLI') + const db = { prepare: () => ({ all: () => [{ root: join(dir, 'project') }] }) } + const options = { + db, cwd: join(dir, 'project'), home: dir, installRoot: install, port: 9, + fetcher: async () => new Response('', { status: 503 }), + } + const initial = await doctorAgents(options) + const claude = initial.find(row => row.client === 'Claude Code')! + assert.equal(claude.installed, 'missing') + assert.equal(claude.mcp, 'missing') + assert.equal(claude.workspace, 'ok') + assert.equal(claude.agentBlock, 'missing') + assert.match(claude.fix, /install.*claude code.*plugbrain setup claude/i) + + writeFileSync(config, JSON.stringify({ mcpServers: { plugbrain: { command: process.execPath, args: [cli, 'mcp'] } } })) + const valid = await doctorAgents({ ...options, fetcher: async () => new Response('{}', { status: 200 }) }) + const good = valid.find(row => row.client === 'Claude Code')! + assert.equal(good.installed, 'ok') + assert.equal(good.mcp, 'ok') + assert.equal(good.daemon, 'ok') + assert.equal(readFileSync(config, 'utf8').includes('plugbrain'), true) + + writeFileSync(config, '{broken') + const wrong = await doctorAgents(options) + assert.equal(wrong.find(row => row.client === 'Claude Code')!.mcp, 'wrong') + assert.equal(readFileSync(config, 'utf8'), '{broken') + } finally { rmSync(dir, { recursive: true, force: true }) } +}) + +test('embedded agent protocol stays synchronized with the documentation block', () => { + const docs = readFileSync(join(import.meta.dirname, '..', 'docs', 'agent-protocol.md'), 'utf8') + const normalizedDocs = docs.replace(/\r\n/g, '\n') + assert.ok(normalizedDocs.includes(AGENT_PROTOCOL_BLOCK.replace(/\r\n/g, '\n').trim()), + 'docs/agent-protocol.md must contain the same block as the embedded CLI copy') +}) + +test('malformed agent markers produce a concise CLI error and exit code 2', () => { + const dir = mkdtempSync(join(tmpdir(), 'plugbrain-agent-invalid-markers-')) + try { + writeFileSync(join(dir, 'AGENTS.md'), '<!-- plugbrain:agent-protocol:start -->\nincomplete') + const result = spawnSync(process.execPath, ['--experimental-strip-types', CLI, 'agents-file'], { + cwd: dir, encoding: 'utf8', env: { ...process.env }, + }) + assert.equal(result.status, 2, result.stderr) + assert.match(result.stderr, /invalid or duplicate PlugBrain markers; repair the markers and retry/) + assert.doesNotMatch(result.stderr, /\n\s+at /, 'expected a one-line refusal without a stack trace') + } finally { rmSync(dir, { recursive: true, force: true }) } +}) diff --git a/test/api-swarm.test.ts b/test/api-swarm.test.ts new file mode 100644 index 0000000..b9f8c13 --- /dev/null +++ b/test/api-swarm.test.ts @@ -0,0 +1,74 @@ +import './helpers/isolated-home.ts' +import { strict as assert } from 'node:assert' +import { test } from 'node:test' +import { mkdtempSync, rmSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { openStore } from '../src/store/schema.ts' +import { serve, type ServerHandle } from '../src/server/api.ts' +import { registerWorkerProfile, recordTurn, approveCommit } from '../src/coord/swarm-ops.ts' +import { registerSwarmAgent } from '../src/coord/registry.ts' +import { sendMessage } from '../src/coord/inbox.ts' +import { enqueueTask } from '../src/queue.ts' + +test('swarm read routes require auth and return the real scoped coordination records', async () => { + const dir = mkdtempSync(join(tmpdir(), 'plugbrain-api-swarm-')) + const db = openStore(join(dir, 'brain.db')) + const workspaceId = 'ws-api-swarm' + const root = join(dir, 'workspace') + db.prepare('INSERT INTO workspaces (id, name, root, created_at) VALUES (?, ?, ?, ?)') + .run(workspaceId, 'Swarm API', root, new Date().toISOString()) + registerSwarmAgent(db, { + agentId: 'worker-a', name: 'Worker A', model: 'fixture-model', workspaceId, + }) + registerWorkerProfile(db, { agentId: 'worker-a', surface: 'other', account: 'fixture-account', model: 'fixture-model' }) + registerSwarmAgent(db, { agentId: 'integrator', name: 'Integrator', workspaceId }) + enqueueTask(db, workspaceId, { title: 'Timeline fixture', requestedBy: 'worker-a' }) + recordTurn(db, { workspaceId, agentId: 'worker-a', phase: 'start', claimNext: true }) + recordTurn(db, { workspaceId, agentId: 'worker-a', phase: 'end', state: 'awaiting-commit', summary: 'Fixture turn summary' }) + sendMessage(db, { workspaceId, fromAgent: 'worker-a', toAgent: 'integrator', subject: 'Handoff', body: 'Fixture message' }) + approveCommit(db, { workspaceId, agentId: 'worker-a', by: 'integrator' }) + + const authKey = 'fixture-' + 'x'.repeat(24) + const server: ServerHandle = await serve({ db, uiRoot: null, authKey, requireAuth: true }, 0) + const base = `http://127.0.0.1:${server.port}` + const routes = ['board', 'turns', 'messages', 'approvals', 'queue'] + try { + for (const route of routes) { + const response = await fetch(`${base}/api/swarm/${route}?workspace=${workspaceId}`) + assert.equal(response.status, 401, `${route} must reject a missing session`) + } + const unauthenticatedResources = await fetch(`${base}/api/resources`) + assert.equal(unauthenticatedResources.status, 401, 'host resources and quota reports require a session') + + const get = async (route: string) => { + const response = await fetch(`${base}/api/swarm/${route}?workspace=${workspaceId}`, { + headers: { Authorization: `Bearer ${authKey}` }, + }) + assert.equal(response.status, 200) + return response.json() as Promise<Record<string, any>> + } + const [board, turns, messages, approvals, queue] = await Promise.all(routes.map(get)) + const resourcesResponse = await fetch(`${base}/api/resources`, { headers: { Authorization: `Bearer ${authKey}` } }) + assert.equal(resourcesResponse.status, 200) + const resources = await resourcesResponse.json() as Record<string, any> + assert.equal(resources.ok, true) + assert.ok(Array.isArray(resources.host.drives)) + assert.ok(Array.isArray(resources.quotas)) + assert.equal(board.board.agents[0].id, 'worker-a') + assert.equal(board.board.agents[0].model, 'fixture-model') + assert.equal('resourceKey' in board.board.agents[0], false, 'resource labels stay private') + assert.equal(turns.historyAvailable, true) + assert.equal(turns.turns.length, 1) + assert.equal(turns.turns[0].state, 'awaiting-commit') + assert.equal(turns.turns[0].summary, 'Fixture turn summary') + assert.ok(messages.messages.some((message: { subject: string }) => message.subject === 'Handoff')) + assert.equal(approvals.approvals.length, 1) + assert.equal(approvals.approvals[0].agentId, 'worker-a') + assert.equal(queue.tasks.length, 1) + } finally { + await server.close() + db.close() + try { rmSync(dir, { recursive: true, force: true, maxRetries: 10 }) } catch { /* fixture cleanup */ } + } +}) diff --git a/test/coordination.test.ts b/test/coordination.test.ts index 160ebda..3e4d502 100644 --- a/test/coordination.test.ts +++ b/test/coordination.test.ts @@ -26,6 +26,7 @@ import { PassThrough } from 'node:stream' import { openStore } from '../src/store/schema.ts' import { serve, type ServerHandle } from '../src/server/api.ts' import { registerAgent } from '../src/access.ts' +import { enqueueTask } from '../src/queue.ts' import * as coord from '../src/coord/index.ts' import { McpServer, MCP_TOOLS } from '../src/mcp/server.ts' import { createAwarenessPort } from '../src/projections/awareness.ts' @@ -175,15 +176,11 @@ test('M4: two real agent processes interact, conflict 409, message handoff, and test('M4: dead agent (expired heartbeat) does not block claims or writes permanently', async () => { const f = await createCoordFixture() try { - const heartbeatTtlMs = 60_000 - - // Keep the live case comfortably inside its TTL; CI setup and lease work - // must not decide whether this agent is alive. + // Register agent dead-1 with short TTL (500ms) coord.registerSwarmAgent(f.db, { agentId: 'agent-dead-1', workspaceId: f.workspaceId, - taskId: 'task-dead-1', - heartbeatTtlMs, + heartbeatTtlMs: 500, }) // Acquire lease @@ -200,14 +197,9 @@ test('M4: dead agent (expired heartbeat) does not block claims or writes permane coord.registerSwarmAgent(f.db, { agentId: 'agent-alive-2', workspaceId: f.workspaceId, - taskId: 'task-alive-2', - heartbeatTtlMs, + heartbeatTtlMs: 60000, }) - const livePresence = coord.getAgentPresence(f.db, { agentId: 'agent-alive-2' }) - assert.equal(livePresence[0].presence, 'active') - assert.equal(livePresence[0].isExpired, false) - // Immediate attempt to claim by alive agent should conflict const blockedRes = coord.acquireLease(f.db, f.workspaceId, { agentId: 'agent-alive-2', @@ -218,11 +210,8 @@ test('M4: dead agent (expired heartbeat) does not block claims or writes permane assert.equal(blockedRes.acquired, false) assert.equal(blockedRes.conflict?.holder.agentId, 'agent-dead-1') - // Expire only the heartbeat value in the fixture database. Keep the TTL - // unchanged and avoid a wall-clock sleep that can expire during setup on CI. - const staleHeartbeat = new Date(Date.now() - heartbeatTtlMs - 1).toISOString() - f.db.prepare('UPDATE agents SET last_heartbeat = ? WHERE id = ?') - .run(staleHeartbeat, 'agent-dead-1') + // Wait 600ms for agent-dead-1 heartbeat to expire + await new Promise((r) => setTimeout(r, 600)) // Verify presence is dead const presences = coord.getAgentPresence(f.db, { agentId: 'agent-dead-1' }) @@ -397,7 +386,7 @@ test('M4: awareness pack reports dependency overlap when another task claims an } }) -test('M4: MCP server lists all 27 tools and executes tool calls over JSON-RPC', async () => { +test('M4: MCP server lists coordination tools and executes tool calls over JSON-RPC', async () => { const f = await createCoordFixture() try { const inStream = new PassThrough() @@ -444,13 +433,14 @@ test('M4: MCP server lists all 27 tools and executes tool calls over JSON-RPC', // the hygiene/machine lanes (git guard and hardware awareness). const listRes = await sendRpc({ id: 2, method: 'tools/list' }) const tools = listRes.result.tools as Array<{ name: string }> - assert.equal(tools.length, 27, `Expected 27 tools, found ${tools.length}`) + assert.equal(tools.length, MCP_TOOLS.length) const toolNames = tools.map((t) => t.name) const expected = [ 'ask', 'search', 'read', 'context_pack', 'query', 'context', 'impact', 'detect_changes', 'claim', 'release', 'awareness', 'inbox_read', 'message_send', 'heartbeat', 'cypher', 'rename_preview', 'swarm_turn', 'swarm_board', 'swarm_resources', + 'swarm_supersede', 'swarm_reassign', 'swarm_priority', 'plan', 'notes_search', 'notes_read', 'notes_query', 'notes_backlinks', 'hygiene', 'machine', 'repos', ] @@ -458,6 +448,27 @@ test('M4: MCP server lists all 27 tools and executes tool calls over JSON-RPC', assert.ok(toolNames.includes(exp), `Missing MCP tool: ${exp}`) } + coord.registerSwarmAgent(f.db, { agentId: 'mcp-tester', workspaceId: f.workspaceId }) + const obsolete = enqueueTask(f.db, f.workspaceId, { title: 'Obsolete task' }) + const replacement = enqueueTask(f.db, f.workspaceId, { title: 'Replacement task' }) + const routed = enqueueTask(f.db, f.workspaceId, { title: 'Routed task' }) + const queueCall = async (id: number, name: string, args: Record<string, unknown>) => { + const response = await sendRpc({ id, method: 'tools/call', params: { name, arguments: { ...args, authKey: f.authKey } } }) + return JSON.parse(response.result.content[0].text) + } + assert.equal((await queueCall(20, 'swarm_supersede', { + taskId: obsolete.id, byTaskId: replacement.id, note: 'replaced by current brief', byAgent: 'mcp-tester', + })).task.state, 'superseded') + assert.equal((await queueCall(21, 'swarm_reassign', { + taskId: routed.id, toAgent: 'mcp-tester', byAgent: 'mcp-tester', + })).task.addressed_to, 'mcp-tester') + assert.equal((await queueCall(22, 'swarm_priority', { + taskId: replacement.id, priority: 7, byAgent: 'mcp-tester', + })).task.priority, 7) + const queueBoard = await queueCall(23, 'swarm_board', {}) + assert.equal(queueBoard.board.queue.tasks.find((task: { id: string }) => task.id === obsolete.id)?.state, 'superseded') + assert.equal(queueBoard.board.queue.recentChanges.length, 3) + // 3. tools/call: heartbeat const hbRes = await sendRpc({ id: 3, @@ -544,7 +555,7 @@ test('M4: MCP server lists all 27 tools and executes tool calls over JSON-RPC', test('M4: SSE stream delivers live events for register, claim, and message', async () => { const f = await createCoordFixture() try { - const sseEvents: Array<{ type: string; data: any }> = [] + const sseEvents: Array<{ type: string; data: any; id?: string }> = [] // Connect to /api/live/events const controller = new AbortController() @@ -568,9 +579,10 @@ test('M4: SSE stream delivers live events for register, claim, and message', asy for (const block of blocks) { const eventMatch = block.match(/event:\s*([^\r\n]+)/) const dataMatch = block.match(/data:\s*([^\r\n]+)/) + const idMatch = block.match(/^id:\s*([^\r\n]+)/m) if (eventMatch && dataMatch) { try { - sseEvents.push({ type: eventMatch[1].trim(), data: JSON.parse(dataMatch[1].trim()) }) + sseEvents.push({ type: eventMatch[1].trim(), data: JSON.parse(dataMatch[1].trim()), id: idMatch?.[1]?.trim() }) } catch {} } } @@ -612,6 +624,40 @@ test('M4: SSE stream delivers live events for register, claim, and message', asy assert.ok(eventTypes.includes('agent.registered'), 'SSE should receive agent.registered') assert.ok(eventTypes.includes('claim.acquired'), 'SSE should receive claim.acquired') assert.ok(eventTypes.includes('message.sent'), 'SSE should receive message.sent') + assert.ok(sseEvents.every((e) => e.id !== undefined && /^\d+$/.test(e.id)), 'every frame carries a numeric id') + + // A reconnect with Last-Event-ID replays what the buffer still holds. No new + // activity happens on this second connection, so anything it receives is replay. + const replayController = new AbortController() + const replayRes = await fetch(`${f.baseUrl}/api/live/events`, { + headers: { 'Last-Event-ID': '0' }, + signal: replayController.signal, + }) + assert.equal(replayRes.status, 200) + const replayReader = replayRes.body?.getReader() + const replayDecoder = new TextDecoder() + const replayedIds: string[] = [] + const replayedTypes: string[] = [] + let replayText = '' + const replayDeadline = Date.now() + 4000 + while (Date.now() < replayDeadline && !replayedTypes.includes('message.sent')) { + const { done, value } = await replayReader!.read() + if (done) break + replayText += replayDecoder.decode(value, { stream: true }) + const frames = replayText.split('\n\n') + replayText = frames.pop() ?? '' + for (const frame of frames) { + const typeMatch = frame.match(/event:\s*([^\r\n]+)/) + const idMatch = frame.match(/^id:\s*([^\r\n]+)/m) + if (typeMatch && idMatch) { + replayedTypes.push(typeMatch[1].trim()) + replayedIds.push(idMatch[1].trim()) + } + } + } + replayController.abort() + assert.ok(replayedTypes.includes('message.sent'), 'Last-Event-ID 0 replays the buffered events') + assert.ok(replayedIds.every((id) => Number(id) > 0), 'replayed frames keep their original ids') } finally { await f.cleanup() } diff --git a/test/disk.test.ts b/test/disk.test.ts new file mode 100644 index 0000000..633a3f6 --- /dev/null +++ b/test/disk.test.ts @@ -0,0 +1,169 @@ +import assert from 'node:assert/strict' +import { mkdtemp, mkdir, rm, utimes, writeFile } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import test from 'node:test' +import type { CensusReport } from '../src/machine/git-census.ts' +import { openStore } from '../src/store/schema.ts' +import { diskRecommendations, diskWipeCheck } from '../src/disk/analysis.ts' +import { currentDiskScan, diskScanHistory, diskTree, largestDiskFiles, scanDisk } from '../src/disk/scan.ts' + +test('disk scan records directory totals, categories, lockfile duplicate groups and largest files', async () => { + const root = await mkdtemp(join(tmpdir(), 'plugbrain-disk-')) + const db = openStore(':memory:') + try { + for (const project of ['alpha', 'beta']) { + await mkdir(join(root, project, 'node_modules', 'pkg'), { recursive: true }) + await writeFile(join(root, project, 'package-lock.json'), '{"lockfileVersion":3}') + await writeFile(join(root, project, 'node_modules', 'pkg', 'data.bin'), Buffer.alloc(256, 65)) + } + await mkdir(join(root, 'cache'), { recursive: true }) + await mkdir(join(root, 'target'), { recursive: true }) + await mkdir(join(root, 'models'), { recursive: true }) + await mkdir(join(root, 'Downloads'), { recursive: true }) + await mkdir(join(root, 'Steam', 'steamapps'), { recursive: true }) + await mkdir(join(root, 'Music'), { recursive: true }) + await mkdir(join(root, 'Program Files'), { recursive: true }) + await mkdir(join(root, 'VMs'), { recursive: true }) + await writeFile(join(root, 'cache', 'entry'), 'cache') + await writeFile(join(root, 'target', 'artifact'), 'build') + await writeFile(join(root, 'models', 'model.gguf'), 'model') + await writeFile(join(root, 'Downloads', 'installer.exe'), 'installer') + await writeFile(join(root, 'VMs', 'machine.vhdx'), 'vm') + + const report = await scanDisk(db, { roots: [root], concurrency: 2 }) + assert.equal(report.running, false) + assert.equal(report.complete, true) + assert.equal(report.inaccessible, 0) + assert.equal(report.files, 9) + const top = diskTree(db, root) + assert.equal(top.find(row => row.name === 'alpha')?.category, 'other') + assert.equal(top.find(row => row.name === 'cache')?.category, 'cache') + assert.equal(top.find(row => row.name === 'target')?.category, 'build') + assert.equal(top.find(row => row.name === 'models')?.category, 'models') + assert.equal(top.find(row => row.name === 'Music')?.category, 'media') + assert.equal(top.find(row => row.name === 'Program Files')?.category, 'system') + assert.equal(top.find(row => row.name === 'Downloads')?.category, 'downloads') + assert.equal(top.find(row => row.name === 'VMs')?.category, 'virtual-machines') + assert.equal(diskTree(db, join(root, 'Steam')).find(row => row.name === 'steamapps')?.category, 'games') + const alphaDeps = diskTree(db, join(root, 'alpha')).find(row => row.name === 'node_modules')! + const betaDeps = diskTree(db, join(root, 'beta')).find(row => row.name === 'node_modules')! + assert.ok(alphaDeps.duplicateGroup) + assert.equal(alphaDeps.duplicateGroup, betaDeps.duplicateGroup) + assert.equal(alphaDeps.files, 1) + assert.equal(alphaDeps.bytes, 256) + assert.equal(largestDiskFiles(db, 1)[0]?.bytes, 256) + assert.equal(currentDiskScan(db)?.scanId, report.scanId) + const recommendations = diskRecommendations(db) + assert.equal(recommendations.recommendations.some(item => item.risk === 'recoverable'), true) + assert.equal(recommendations.recommendations.some(item => item.risk === 'review'), false) + assert.equal(recommendations.recommendations.some(item => item.risk === 'keep'), false) + assert.equal(recommendations.totalsGb.recoverable, recommendations.recommendations + .filter(item => item.risk === 'recoverable').reduce((sum, item) => sum + item.gb, 0)) + } finally { + db.close() + await rm(root, { recursive: true, force: true }) + } +}) + +test('recommendations separate recoverable, review and keep; wipe checklist catches unpushed refs and config paths', async () => { + const home = await mkdtemp(join(tmpdir(), 'plugbrain-disk-wipe-')) + const db = openStore(':memory:') + try { + await mkdir(join(home, '.ssh'), { recursive: true }) + await writeFile(join(home, '.ssh', 'config'), 'test-only') + for (const repo of ['stale-project', 'dirty-project']) { + await mkdir(join(home, repo, '.git'), { recursive: true }) + await writeFile(join(home, repo, '.git', 'HEAD'), 'ref: refs/heads/main') + } + const old = new Date(Date.now() - 400 * 86_400_000) + await utimes(join(home, 'stale-project', '.git', 'HEAD'), old, old) + await utimes(join(home, 'stale-project', '.git'), old, old) + await scanDisk(db, { roots: [home] }) + const census: CensusReport = { + scannedAt: new Date().toISOString(), roots: [home], complete: true, + repos: [ + { path: join(home, 'stale-project'), registered: false, branch: 'main', dirtyFiles: 0, untrackedFiles: 0, unpushed: [], worktrees: 0, orphanWorktrees: 0, stashes: 0, lastCommitDays: 400, gitSizeMb: 1, workTreeSizeMb: 0 }, + { path: join(home, 'dirty-project'), registered: false, branch: 'main', dirtyFiles: 0, untrackedFiles: 0, unpushed: [{ branch: 'local-only', ahead: 2, upstream: null }], worktrees: 0, orphanWorktrees: 0, stashes: 0, lastCommitDays: 0, gitSizeMb: 1, workTreeSizeMb: 0 }, + ], totals: { repos: 2, dirty: 0, unpushedBranches: 1, orphanWorktrees: 0 }, unavailable: [], + } + const recommendations = diskRecommendations(db, census) + assert.ok(recommendations.recommendations.some(item => item.risk === 'review')) + assert.ok(recommendations.recommendations.some(item => item.risk === 'keep')) + assert.equal(diskTree(db, join(home, 'stale-project')).find(row => row.name === '.git')?.category, 'source') + const checklist = diskWipeCheck(db, census, new Date(), home) + assert.ok(checklist.items.some(item => item.path.endsWith('dirty-project'))) + assert.ok(checklist.items.some(item => item.path.endsWith('.ssh'))) + assert.ok(checklist.totalGb >= 0) + } finally { + db.close() + await rm(home, { recursive: true, force: true }) + } +}) + +test('disk scan records inaccessible paths and does not follow symlinks', async () => { + const root = await mkdtemp(join(tmpdir(), 'plugbrain-disk-links-')) + const outside = await mkdtemp(join(tmpdir(), 'plugbrain-disk-outside-')) + const db = openStore(':memory:') + try { + await writeFile(join(outside, 'not-counted.bin'), Buffer.alloc(128)) + let linked = false + try { + const { symlink } = await import('node:fs/promises') + await symlink(outside, join(root, 'link'), process.platform === 'win32' ? 'junction' : 'dir') + linked = true + } catch { /* Windows developer mode may disable link creation; other checks still apply. */ } + const report = await scanDisk(db, { roots: [root, join(root, 'missing')] }) + assert.ok(report.inaccessible >= 1) + assert.equal(report.files, 0) + if (linked) assert.equal(Boolean(diskTree(db, root).find(row => row.name === 'link')?.reparsePoint), true) + } finally { + db.close() + await rm(root, { recursive: true, force: true }) + await rm(outside, { recursive: true, force: true }) + } +}) + +test('disk history retains completed snapshots and reports non-overlapping directory growth', async () => { + const root = await mkdtemp(join(tmpdir(), 'plugbrain-disk-history-')) + const db = openStore(':memory:') + try { + const growing = join(root, 'growing') + await mkdir(growing) + const file = join(growing, 'payload.bin') + await writeFile(file, Buffer.alloc(100)) + await scanDisk(db, { roots: [root], now: () => new Date('2026-09-27T10:00:00.000Z') }) + await writeFile(file, Buffer.alloc(300)) + await scanDisk(db, { roots: [root], now: () => new Date('2026-09-27T10:05:00.000Z') }) + + const history = diskScanHistory(db) + assert.equal(history.scans.length, 2) + assert.equal(history.scans.every(scan => scan.complete), true) + assert.equal(history.largestGrowth[0]?.path, growing) + assert.equal(history.largestGrowth[0]?.previousBytes, 100) + assert.equal(history.largestGrowth[0]?.bytes, 300) + assert.equal(history.largestGrowth[0]?.deltaBytes, 200) + } finally { + db.close() + await rm(root, { recursive: true, force: true }) + } +}) + +test('interrupted disk scans are marked incomplete and never publish partial recommendations', async () => { + const root = await mkdtemp(join(tmpdir(), 'plugbrain-disk-abort-')) + const db = openStore(':memory:') + try { + await writeFile(join(root, 'some-file'), 'metadata only') + const controller = new AbortController() + controller.abort() + const report = await scanDisk(db, { roots: [root], signal: controller.signal }) + assert.equal(report.running, false) + assert.equal(report.complete, false) + assert.match(report.errors.at(-1) ?? '', /interrupted/) + assert.equal(diskTree(db, root).length, 0) + assert.equal(diskRecommendations(db).recommendations.length, 0) + } finally { + db.close() + await rm(root, { recursive: true, force: true }) + } +}) diff --git a/test/durable-delivery.test.ts b/test/durable-delivery.test.ts new file mode 100644 index 0000000..cb424ff --- /dev/null +++ b/test/durable-delivery.test.ts @@ -0,0 +1,127 @@ +import './helpers/isolated-home.ts' +import { strict as assert } from 'node:assert' +import { test } from 'node:test' +import { mkdtempSync, rmSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { openStore } from '../src/store/schema.ts' +import { registerAgent } from '../src/access.ts' +import { claimNextTask, deliverTask, enqueueTask } from '../src/queue.ts' +import { syncDependencies } from '../src/coord/dependencies.ts' +import { ensureSwarmOpsSchema } from '../src/coord/swarm-ops.ts' +import { + confirmDelivery, getConsumerCursor, getMessageDeliveryState, markMessageProcessed, + readInbox, sendMessage, +} from '../src/coord/inbox.ts' + +const WS = 'ws-durable-test' + +function fixture() { + const dir = mkdtempSync(join(tmpdir(), 'plugbrain-durable-')) + const dbPath = join(dir, 'brain.db') + let db = openStore(dbPath) + db.prepare('INSERT INTO workspaces (id, name, root, created_at) VALUES (?, ?, ?, ?)') + .run(WS, 'durable-delivery-test', dir, new Date().toISOString()) + ensureSwarmOpsSchema(db) + for (const agent of ['sender', 'recipient-a', 'recipient-b', 'unrelated']) registerAgent(db, agent, agent) + return { + dir, + get db() { return db }, + restart() { db.close(); db = openStore(dbPath) }, + cleanup: () => { db.close(); rmSync(dir, { recursive: true, force: true }) }, + } +} + +test('durable message delivery deduplicates an event identity and persists receipt separately from processing', async () => { + const f = fixture() + try { + const first = sendMessage(f.db, { + workspaceId: WS, fromAgent: 'sender', toAgent: 'recipient-a', subject: 'handoff', body: 'first payload', + deliveryKey: 'event:task.delivered:task-a:attempt-1', + }) + const duplicate = sendMessage(f.db, { + workspaceId: WS, fromAgent: 'sender', toAgent: 'recipient-a', subject: 'duplicate', body: 'must not replace payload', + deliveryKey: 'event:task.delivered:task-a:attempt-1', + }) + assert.equal(duplicate.id, first.id) + assert.equal(f.db.prepare('SELECT COUNT(*) AS n FROM inbox_messages').get()!.n, 1) + + const received = await readInbox(f.db, { workspaceId: WS, agentId: 'recipient-a', unreadOnly: true }) + assert.equal(received.length, 1) + assert.equal(received[0]!.id, first.id) + + assert.equal(markMessageProcessed(f.db, first.id, 'recipient-a'), true) + assert.equal(getMessageDeliveryState(f.db, first.id, 'recipient-a')?.acknowledgedAt, null, + 'processing must not imply receipt ACK') + assert.equal(getConsumerCursor(f.db, WS, 'recipient-a'), null, + 'processing must not advance the consumer ACK cursor') + + assert.equal(confirmDelivery(f.db, first.id, 'recipient-a'), true) + const state = getMessageDeliveryState(f.db, first.id, 'recipient-a') + assert.ok(state?.acknowledgedAt) + assert.ok(state?.processedAt, 'the earlier processing record must remain independent') + assert.equal(getConsumerCursor(f.db, WS, 'recipient-a')?.messageId, first.id) + assert.equal((await readInbox(f.db, { workspaceId: WS, agentId: 'recipient-a', unreadOnly: true })).length, 0) + } finally { f.cleanup() } +}) + +test('consumer cursor advances contiguously, resumes after restart, and does not skip reordered ACKs', async () => { + const f = fixture() + try { + const first = sendMessage(f.db, { + workspaceId: WS, fromAgent: 'sender', toAgent: 'recipient-a', body: 'first', deliveryKey: 'event:first', + }) + const second = sendMessage(f.db, { + workspaceId: WS, fromAgent: 'sender', toAgent: 'recipient-a', body: 'second', deliveryKey: 'event:second', + }) + await readInbox(f.db, { workspaceId: WS, agentId: 'recipient-a' }) + assert.equal(confirmDelivery(f.db, second.id, 'recipient-a'), true) + assert.equal(getConsumerCursor(f.db, WS, 'recipient-a'), null, + 'an out-of-order ACK cannot skip an earlier unacknowledged message') + assert.equal(confirmDelivery(f.db, first.id, 'recipient-a'), true) + assert.equal(getConsumerCursor(f.db, WS, 'recipient-a')?.messageId, second.id) + + f.restart() + assert.equal(getConsumerCursor(f.db, WS, 'recipient-a')?.messageId, second.id, + 'the cursor must survive reopening the store') + assert.equal((await readInbox(f.db, { workspaceId: WS, agentId: 'recipient-a', afterCursor: true })).length, 0) + } finally { f.cleanup() } +}) + +test('broadcast delivery and ACK state are isolated per consumer', async () => { + const f = fixture() + try { + const message = sendMessage(f.db, { + workspaceId: WS, fromAgent: 'sender', channel: 'ops', body: 'fleet notice', deliveryKey: 'event:fleet-notice', + }) + assert.equal((await readInbox(f.db, { workspaceId: WS, agentId: 'recipient-a', unreadOnly: true })).length, 1) + assert.equal(confirmDelivery(f.db, message.id, 'recipient-a'), true) + assert.equal((await readInbox(f.db, { workspaceId: WS, agentId: 'recipient-a', unreadOnly: true })).length, 0) + assert.equal((await readInbox(f.db, { workspaceId: WS, agentId: 'unrelated', unreadOnly: true })).length, 1, + 'one consumer ACK must not hide a broadcast from other consumers') + } finally { f.cleanup() } +}) + +test('task delivery notifies only addressed dependent-task recipients once', () => { + const f = fixture() + try { + const predecessor = enqueueTask(f.db, WS, { title: 'produce shared result' }) + const dependentA = enqueueTask(f.db, WS, { title: 'review result A', addressedTo: 'recipient-a', afterTaskId: predecessor.id }) + const dependentB = enqueueTask(f.db, WS, { title: 'consume result B', addressedTo: 'recipient-b', afterTaskId: predecessor.id }) + enqueueTask(f.db, WS, { title: 'unrelated task', addressedTo: 'unrelated' }) + const claimed = claimNextTask(f.db, WS, 'sender') + assert.equal(claimed?.id, predecessor.id) + + deliverTask(f.db, predecessor.id, 'sender', 'review/result.md') + const messages = f.db.prepare(`SELECT to_agent, body FROM inbox_messages WHERE workspace_id = ? ORDER BY rowid`) + .all(WS) as Array<{ to_agent: string | null; body: string }> + assert.deepEqual(messages.map(message => message.to_agent).sort(), ['recipient-a', 'recipient-b']) + assert.ok(messages.every(message => message.body.includes(predecessor.id))) + + // Reconciliation on a repeated event must not notify again. + syncDependencies(f.db, WS) + assert.equal(f.db.prepare('SELECT COUNT(*) AS n FROM inbox_messages WHERE workspace_id = ?').get(WS)!.n, 2) + assert.equal(dependentA.addressed_to, 'recipient-a') + assert.equal(dependentB.addressed_to, 'recipient-b') + } finally { f.cleanup() } +}) diff --git a/test/fixtures/agent-b.ts b/test/fixtures/agent-b.ts index ee6f99e..1aca27f 100644 --- a/test/fixtures/agent-b.ts +++ b/test/fixtures/agent-b.ts @@ -142,6 +142,7 @@ async function run() { // 6. Deliver the queue task const deliverRes = await post('/api/queue/deliver', { + workspace: ws, taskId: claimedQueueTask.id, agentId: 'agent-b', deliveredPath: 'src/x.ts', diff --git a/test/fixtures/fake-worker.mjs b/test/fixtures/fake-worker.mjs new file mode 100644 index 0000000..01b7b52 --- /dev/null +++ b/test/fixtures/fake-worker.mjs @@ -0,0 +1,91 @@ +#!/usr/bin/env node +/** + * A stand-in for a CLI worker in the runner tests. + * + * `swarm run` must start a real process, so the tests need a real process to + * start — but starting Codex would spend the owner's quota and needs a login. + * This script IS the worker: it writes the same JSONL shape `codex exec --json` + * writes to stdout, and it talks to the same brain store through the same CLI, + * so the whole loop (process, log, turn end, board) is exercised for real. + * + * Modes: + * ok - check in, claim, write the last message, end the turn, exit 0 + * die - write two events and exit 3 without ever ending the turn + * hang - write one event and stay alive until the Brain stops it + * + * Everything is passed in, nothing is hard-coded: the argv template that + * `swarm runner set --args` stores is what points this script at the log, the + * prompt file and the CLI. + */ +import { spawn, spawnSync } from 'node:child_process' +import { readFileSync, writeFileSync } from 'node:fs' + +const argv = process.argv.slice(2) +const value = (name) => { + const at = argv.indexOf(name) + return at === -1 ? null : argv[at + 1] ?? null +} + +const mode = value('--mode') ?? 'ok' +const agent = value('--agent') +const workspace = value('--workspace') +const cli = value('--cli') +const promptFile = value('--prompt-file') +const lastDoc = value('--last') + +const emit = (event) => { process.stdout.write(`${JSON.stringify(event)}\n`) } + +const brain = (...args) => { + const result = spawnSync( + process.execPath, + ['--experimental-strip-types', '--no-warnings', cli, ...args], + { encoding: 'utf8', timeout: 60_000 }, + ) + return { status: result.status, out: result.stdout ?? '', err: result.stderr ?? '' } +} + +const firstLine = (() => { + if (promptFile === null) return 'no prompt file was passed' + try { return readFileSync(promptFile, 'utf8').split('\n')[0] } catch { return 'prompt file was unreadable' } +})() + +if (mode === 'ok') { + emit({ type: 'item.completed', item: { type: 'agent_message', text: `prompt: ${firstLine}` } }) + const start = brain('swarm', 'turn', agent, 'start', '--claim', '--workspace', workspace) + emit({ + type: 'item.completed', + item: { type: 'command_execution', command: `plugbrain swarm turn ${agent} start --claim`, exit_code: start.status }, + }) + if (lastDoc !== null) writeFileSync(lastDoc, 'fake worker finished its turn\n') + const end = brain('swarm', 'turn', agent, 'end', '--state', 'needs-task', '--summary', 'fake worker done', '--workspace', workspace) + emit({ + type: 'item.completed', + item: { type: 'command_execution', command: `plugbrain swarm turn ${agent} end --state needs-task`, exit_code: end.status }, + }) + emit({ type: 'turn.completed', usage: { input_tokens: 1, output_tokens: 2 } }) + process.exit(0) +} + +if (mode === 'die') { + const start = brain('swarm', 'turn', agent, 'start', '--claim', '--workspace', workspace) + emit({ + type: 'item.completed', + item: { type: 'command_execution', command: `plugbrain swarm turn ${agent} start --claim`, exit_code: start.status }, + }) + emit({ type: 'item.completed', item: { type: 'agent_message', text: 'about to die without a turn end' } }) + emit({ type: 'item.completed', item: { type: 'command_execution', command: 'node --run build', exit_code: 1 } }) + // Let the runner record this process's OS identity before simulating a mid-turn crash. + // Otherwise a busy Windows host can observe a process that exited before launch finished. + await new Promise(resolve => setTimeout(resolve, 500)) + process.exit(3) +} + +if (mode === 'tree') { + const child = spawn(process.execPath, ['-e', 'setInterval(() => {}, 1000)'], { stdio: 'ignore' }) + if (lastDoc !== null) writeFileSync(lastDoc, String(child.pid ?? '')) + emit({ type: 'item.completed', item: { type: 'agent_message', text: 'parent and child are running' } }) + setInterval(() => {}, 1000) +} + +emit({ type: 'item.completed', item: { type: 'agent_message', text: 'hanging on purpose' } }) +setInterval(() => {}, 1000) diff --git a/test/fleet-automation.test.ts b/test/fleet-automation.test.ts new file mode 100644 index 0000000..aaebfee --- /dev/null +++ b/test/fleet-automation.test.ts @@ -0,0 +1,188 @@ +import './helpers/isolated-home.ts' +import { strict as assert } from 'node:assert' +import { test } from 'node:test' +import { mkdtempSync, rmSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { openStore } from '../src/store/schema.ts' +import { enqueueTask } from '../src/queue.ts' +import * as coord from '../src/coord/index.ts' +import { handleFleetAutomationEvent } from '../src/coord/fleet-automation.ts' + +function fixture() { + const dir = mkdtempSync(join(tmpdir(), 'plugbrain-ap03-')) + const db = openStore(join(dir, 'brain.db')) + const workspaceId = 'ws-ap03' + db.prepare('INSERT INTO workspaces (id, name, root, created_at) VALUES (?, ?, ?, ?)') + .run(workspaceId, 'AP-03', dir, new Date().toISOString()) + const register = (agentId: string, account: string) => { + coord.registerSwarmAgent(db, { agentId, workspaceId }) + const model = agentId === 'reviewer' ? 'claude-sonnet-4' : agentId === 'integrator' ? 'gemini-2.5-pro' : 'gpt-6-luna' + coord.registerWorkerProfile(db, { agentId, surface: 'codex-app', account, model }) + } + register('author', 'account-author') + register('same-account', 'account-author') + register('reviewer', 'account-reviewer') + register('integrator', 'account-integrator') + coord.setReviewPool(db, workspaceId, ['same-account', 'reviewer']) + coord.setReviewAuto(db, workspaceId, true) + return { dir, db, workspaceId, register, close: () => { db.close(); rmSync(dir, { recursive: true, force: true }) } } +} + +const rows = (db: ReturnType<typeof openStore>, workspaceId: string) => db.prepare( + 'SELECT id, title, body, addressed_to, state FROM queue_tasks WHERE workspace_id = ? ORDER BY rowid', +).all(workspaceId) as unknown as Array<{ id: string; title: string; body: string; addressed_to: string | null; state: string }> + +test('task delivery queues one independent review and repeated events are idempotent', () => { + const f = fixture() + try { + const source = enqueueTask(f.db, f.workspaceId, { title: 'Implement feature', addressedTo: 'author' }) + f.db.prepare("UPDATE queue_tasks SET state = 'delivered', claimed_by = 'author', claimed_at = ?, delivered_path = 'result.md' WHERE id = ?").run(new Date().toISOString(), source.id) + const event = { id: '100', type: 'task.delivered', data: { taskId: source.id } } + const first = handleFleetAutomationEvent(f.db, f.workspaceId, event) + const repeated = handleFleetAutomationEvent(f.db, f.workspaceId, event) + assert.equal(first.action, 'review-queued') + assert.equal(repeated.action, 'duplicate') + const review = rows(f.db, f.workspaceId).filter(row => row.title.startsWith('Review:')) + assert.equal(review.length, 1) + assert.equal(review[0]!.addressed_to, 'reviewer') + assert.match(review[0]!.body, /Author: author/) + } finally { f.close() } +}) + +test('delivery with review routing disabled raises a lead decision instead of disappearing', () => { + const f = fixture() + try { + coord.setReviewAuto(f.db, f.workspaceId, false) + const source = enqueueTask(f.db, f.workspaceId, { title: 'Implement feature', addressedTo: 'author' }) + f.db.prepare("UPDATE queue_tasks SET state = 'delivered', claimed_by = 'author', claimed_at = ? WHERE id = ?") + .run(new Date().toISOString(), source.id) + const event = { id: '110', type: 'task.delivered', data: { taskId: source.id } } + assert.equal(handleFleetAutomationEvent(f.db, f.workspaceId, event).action, 'lead-decision-required') + assert.equal(handleFleetAutomationEvent(f.db, f.workspaceId, event).action, 'duplicate') + assert.equal(rows(f.db, f.workspaceId).filter(row => row.title.startsWith('Review:')).length, 0) + assert.equal(f.db.prepare('SELECT COUNT(*) AS n FROM inbox_messages WHERE workspace_id = ? AND to_agent = ?') + .get(f.workspaceId, 'integrator')?.n, 1) + } finally { f.close() } +}) + +test('review PASS creates an integration task for the integrator', () => { + const f = fixture() + try { + const source = enqueueTask(f.db, f.workspaceId, { title: 'Implement feature', addressedTo: 'author' }) + f.db.prepare("UPDATE queue_tasks SET state = 'delivered', claimed_by = 'author', claimed_at = ? WHERE id = ?").run(new Date().toISOString(), source.id) + handleFleetAutomationEvent(f.db, f.workspaceId, { id: '101', type: 'task.delivered', data: { taskId: source.id } }) + const review = rows(f.db, f.workspaceId).find(row => row.title.startsWith('Review:'))! + f.db.prepare("UPDATE queue_tasks SET state = 'delivered', claimed_by = 'reviewer' WHERE id = ?").run(review.id) + f.db.prepare("INSERT INTO queue_deliveries (task_id, delivered_by, delivery_attempt, delivered_path, delivered_sha256, review_judgment, reviewed_commit, delivered_at) VALUES (?, 'reviewer', 1, 'review.md', 'hash', 'PASS', 'aabbcc', ?)") + .run(review.id, new Date().toISOString()) + const result = handleFleetAutomationEvent(f.db, f.workspaceId, { id: '102', type: 'task.delivered', data: { taskId: review.id } }) + assert.equal(result.action, 'integration-queued') + const integration = rows(f.db, f.workspaceId).find(row => row.title.startsWith('Integrate:')) + assert.equal(integration?.addressed_to, 'integrator') + assert.match(integration?.body ?? '', /reviewer/) + } finally { f.close() } +}) + +test('a model-family change after routing blocks an automatic integration PASS', () => { + const f = fixture() + try { + const source = enqueueTask(f.db, f.workspaceId, { title: 'Review-family drift', addressedTo: 'author' }) + f.db.prepare("UPDATE queue_tasks SET state = 'delivered', claimed_by = 'author', claimed_at = ?, delivered_path = 'result.md' WHERE id = ?") + .run(new Date().toISOString(), source.id) + assert.equal(handleFleetAutomationEvent(f.db, f.workspaceId, { + id: 'drift-source', type: 'task.delivered', data: { taskId: source.id }, + }).action, 'review-queued') + + const review = rows(f.db, f.workspaceId).find(row => row.title.startsWith('Review:'))! + f.db.prepare("UPDATE agents SET model = 'gpt-5.4' WHERE id = 'reviewer'").run() + f.db.prepare("UPDATE queue_tasks SET state = 'delivered', claimed_by = 'reviewer', claimed_at = ? WHERE id = ?") + .run(new Date(Date.now() + 1000).toISOString(), review.id) + f.db.prepare("INSERT INTO queue_deliveries (task_id, delivered_by, delivery_attempt, delivered_path, delivered_sha256, review_judgment, reviewed_commit, delivered_at) VALUES (?, 'reviewer', 1, 'review.md', 'hash', 'PASS', 'aabbcc', ?)") + .run(review.id, new Date().toISOString()) + + const result = handleFleetAutomationEvent(f.db, f.workspaceId, { + id: 'drift-review', type: 'task.delivered', data: { taskId: review.id }, + }) + assert.equal(result.action, 'lead-decision-required') + assert.equal(rows(f.db, f.workspaceId).some(row => row.title.startsWith('Integrate:')), false) + assert.equal((f.db.prepare(`SELECT state FROM fleet_automation_cases WHERE workspace_id = ? AND source_task_id = ?`) + .get(f.workspaceId, source.id) as { state: string }).state, 'awaiting-lead') + } finally { f.close() } +}) + +test('blocking FAIL routes no more than two fix rounds to the author', () => { + const f = fixture() + try { + const source = enqueueTask(f.db, f.workspaceId, { title: 'Implement feature', addressedTo: 'author' }) + f.db.prepare("UPDATE queue_tasks SET state = 'delivered', claimed_by = 'author', claimed_at = ? WHERE id = ?").run(new Date().toISOString(), source.id) + handleFleetAutomationEvent(f.db, f.workspaceId, { id: '201', type: 'task.delivered', data: { taskId: source.id } }) + let review = rows(f.db, f.workspaceId).find(row => row.title.startsWith('Review:'))! + for (let round = 1; round <= 3; round++) { + f.db.prepare("UPDATE queue_tasks SET state = 'delivered', claimed_by = 'reviewer', claimed_at = ? WHERE id = ?").run(new Date(Date.now() + round * 1000).toISOString(), review.id) + f.db.prepare("INSERT OR REPLACE INTO queue_deliveries (task_id, delivered_by, delivery_attempt, delivered_path, delivered_sha256, review_judgment, reviewed_commit, delivered_at) VALUES (?, 'reviewer', 1, 'review.md', 'hash', 'FAIL', 'aabbcc', ?)") + .run(review.id, new Date().toISOString()) + const result = handleFleetAutomationEvent(f.db, f.workspaceId, { id: String(201 + round), type: 'task.delivered', data: { taskId: review.id } }) + if (round < 3) { + assert.equal(result.action, 'fix-queued') + const fix = rows(f.db, f.workspaceId).find(row => row.title.includes(`round ${round} of 2`))! + f.db.prepare("UPDATE queue_tasks SET state = 'delivered', claimed_by = 'author', claimed_at = ? WHERE id = ?").run(new Date(Date.now() + round * 1000 + 1).toISOString(), fix.id) + assert.equal(handleFleetAutomationEvent(f.db, f.workspaceId, { + id: `fix-${round}`, type: 'task.delivered', data: { taskId: fix.id }, + }).action, 'review-queued') + review = rows(f.db, f.workspaceId).filter(row => row.title.startsWith('Review:')).at(-1)! + } else assert.equal(result.action, 'lead-decision-required') + } + const fixes = rows(f.db, f.workspaceId).filter(row => row.title.startsWith('Fix:')) + assert.equal(fixes.length, 2) + assert.ok(fixes.every(row => row.addressed_to === 'author')) + } finally { f.close() } +}) + +test('ordinary silence is ignored and a blocked turn with held work wakes the lead once', () => { + const f = fixture() + try { + const task = enqueueTask(f.db, f.workspaceId, { title: 'Blocked task', addressedTo: 'author' }) + const quiet = handleFleetAutomationEvent(f.db, f.workspaceId, { + id: '301', type: 'watchdog.silent', data: { agentId: 'author' }, + }) + assert.equal(quiet.action, 'ignored') + assert.equal(handleFleetAutomationEvent(f.db, f.workspaceId, { + id: '301', type: 'watchdog.silent', data: { agentId: 'author' }, + }).action, 'duplicate') + f.db.prepare("UPDATE queue_tasks SET state = 'claimed', claimed_by = 'author', claimed_at = ? WHERE id = ?").run(new Date().toISOString(), task.id) + const blocked = handleFleetAutomationEvent(f.db, f.workspaceId, { + id: '302', type: 'agent.turn', data: { agentId: 'author', state: 'blocked', taskId: task.id }, + }) + assert.equal(blocked.action, 'lead-decision-required') + assert.equal(handleFleetAutomationEvent(f.db, f.workspaceId, { + id: '302', type: 'agent.turn', data: { agentId: 'author', state: 'blocked', taskId: task.id }, + }).action, 'duplicate') + const messages = f.db.prepare('SELECT subject FROM inbox_messages WHERE workspace_id = ? AND to_agent = ?') + .all(f.workspaceId, 'integrator') as Array<{ subject: string }> + assert.equal(messages.length, 1) + } finally { f.close() } +}) + +test('watchdog silence wakes the lead only when work is held and does so once per quiet case', () => { + const f = fixture() + try { + const task = enqueueTask(f.db, f.workspaceId, { title: 'Stalled task', addressedTo: 'author' }) + const now = new Date('2026-09-27T12:00:00.000Z') + f.db.prepare("UPDATE agents SET turn_state = 'working', turn_state_at = ?, last_contact_at = NULL WHERE id = 'author'") + .run(new Date(now.getTime() - 60 * 60_000).toISOString()) + assert.deepEqual(coord.scanWatchdog(f.db, f.workspaceId, { now }).alerted, []) + assert.equal(rows(f.db, f.workspaceId).filter(row => row.state === 'claimed').length, 0) + const claimAt = new Date(now.getTime() - 60 * 60_000).toISOString() + f.db.prepare("UPDATE queue_tasks SET state = 'claimed', claimed_by = 'author', claimed_at = ? WHERE id = ?") + .run(claimAt, task.id) + f.db.prepare("UPDATE agents SET turn_state = 'working', turn_state_at = ?, last_contact_at = NULL, silence_alerted_at = NULL WHERE id = 'author'") + .run(claimAt) + assert.deepEqual(coord.scanWatchdog(f.db, f.workspaceId, { now }).alerted, ['author']) + assert.deepEqual(coord.scanWatchdog(f.db, f.workspaceId, { now: new Date(now.getTime() + 1_000) }).alerted, []) + const messages = f.db.prepare('SELECT subject FROM inbox_messages WHERE workspace_id = ? AND to_agent = ?') + .all(f.workspaceId, 'integrator') as Array<{ subject: string }> + assert.equal(messages.length, 1) + assert.match(messages[0]!.subject, /Stalled task/) + } finally { f.close() } +}) diff --git a/test/gp2-brain-ui-e2e.test.ts b/test/gp2-brain-ui-e2e.test.ts index a9ac733..410df08 100644 --- a/test/gp2-brain-ui-e2e.test.ts +++ b/test/gp2-brain-ui-e2e.test.ts @@ -44,6 +44,30 @@ test('GP-2: a human can complete standalone knowledge work through the real Brai const openError = await page.locator('.brain-vault__error').allTextContents() assert.deepEqual(openError, [], `opening fixture vault failed: ${openError.join(' ')}`) await page.waitForFunction('new URL(location.href).searchParams.has("workspace")') + await page.setViewportSize({ width: 390, height: 768 }) + await page.getByRole('button', { name: 'Menü' }).click() + await page.locator('#mobile-menu').getByRole('button', { name: 'Fragen' }).click() + await page.keyboard.press('Escape') + await page.setViewportSize({ width: 1024, height: 768 }) + const menu = page.getByRole('button', { name: 'Menü' }) + await menu.click() + assert.equal(await menu.getAttribute('aria-expanded'), 'true') + await page.locator('#mobile-menu').getByRole('button', { name: 'Repos wählen' }).waitFor() + await page.getByRole('textbox', { name: 'Code und Notizen durchsuchen' }).focus() + assert.equal(await menu.getAttribute('aria-expanded'), 'false', 'outside focus closes the menu') + await menu.click() + await page.keyboard.press('Escape') + assert.equal(await menu.getAttribute('aria-expanded'), 'false', 'Escape closes the menu') + await menu.click() + const topbarBounds = await page.locator('.pb-topbar').boundingBox() + assert.ok(topbarBounds && topbarBounds.x + topbarBounds.width <= 1024) + await page.locator('#mobile-menu').getByRole('button', { name: 'Suche (Strg+K)' }).click() + await page.route('**/api/agent/search', route => route.fulfill({ status: 503, body: 'unavailable' })) + await page.locator('.search-input').fill('GP2-Suche') + await page.locator('.search-submit-btn').click() + await page.locator('.search-error-alert').getByText('Code-Suche nicht erreichbar', { exact: false }).waitFor() + assert.equal(await page.locator('.search-results-summary').innerText().then(text => text.includes('Keine Treffer für')), false) + await page.unroute('**/api/agent/search') await page.getByRole('button', { name: 'Wissen' }).waitFor() await page.getByRole('button', { name: 'Wissen' }).click() await page.locator('.notes-workbench').waitFor() diff --git a/test/intel.test.ts b/test/intel.test.ts index cd6cad0..d38c252 100644 --- a/test/intel.test.ts +++ b/test/intel.test.ts @@ -11,6 +11,7 @@ import { execFileSync } from 'node:child_process' import { DatabaseSync } from 'node:sqlite' import { openStore } from '../src/store/schema.ts' import { serve, type ServerHandle } from '../src/server/api.ts' +import { McpServer } from '../src/mcp/server.ts' import * as intel from '../src/intel/index.ts' interface IntelFixture { @@ -120,6 +121,11 @@ test('M3: getSymbolContext returns 360-degree view (callers, callees, execution assert.ok(res.processes.length > 0) assert.match(res.processes[0].label, /sendQuery -> handleRequest -> dispatchRequest/) + fix.db.prepare(`INSERT INTO edges (workspace_id, kind, src_symbol, src_file, raw_target, resolved, line) + VALUES (?, 'calls', 12, 2, 'unknownExternal', 0, 18)`).run(fix.workspaceId) + const unresolved = intel.getSymbolContext(fix.db, 'handleRequest', { workspaceId: fix.workspaceId }) + assert.ok(unresolved.outgoing.calls.some(call => call.rawTarget === 'unknownExternal')) + // 2. Negative case: symbol not found const missing = intel.getSymbolContext(fix.db, 'nonExistentSymbol') assert.equal(missing.status, 'not_found') @@ -158,6 +164,88 @@ test('M3: getBlastRadius calculates upstream and downstream impact with depth an } }) +test('M3: import fallback reports only the named symbol from its resolved module', async () => { + const fix = await createIntelFixture() + try { + fix.db.prepare(`INSERT INTO files (id, workspace_id, path, repo_id, checkout_id, ext, size, mtime) + VALUES (4, ?, 'src/target-user.ts', 'repo-engine', 'co-engine', '.ts', 10, '2026-09-17T00:00:00Z'), + (5, ?, 'src/other-user.ts', 'repo-engine', 'co-engine', '.ts', 10, '2026-09-17T00:00:00Z'), + (6, ?, 'src/unrelated-user.ts', 'repo-engine', 'co-engine', '.ts', 10, '2026-09-17T00:00:00Z'), + (7, ?, 'other/gateway.ts', 'repo-engine', 'co-engine', '.ts', 10, '2026-09-17T00:00:00Z'), + (8, ?, 'src/barrel.ts', 'repo-engine', 'co-engine', '.ts', 10, '2026-09-17T00:00:00Z'), + (9, ?, 'src/barrel-user.ts', 'repo-engine', 'co-engine', '.ts', 10, '2026-09-17T00:00:00Z'), + (10, ?, 'src/shadow-barrel.ts', 'repo-engine', 'co-engine', '.ts', 10, '2026-09-17T00:00:00Z'), + (11, ?, 'src/shadow-user.ts', 'repo-engine', 'co-engine', '.ts', 10, '2026-09-17T00:00:00Z')` + ).run(...Array(8).fill(fix.workspaceId)) + fix.db.prepare(`INSERT INTO symbols (id, file_id, name, kind, line, end_line, exported, container) + VALUES (14, 1, 'startGateway', 'method', 60, 65, 0, 'PrivateGateway'), + (15, 10, 'startGateway', 'function', 1, 1, 1, null)`).run() + fix.db.prepare(`INSERT INTO file_imports (workspace_id, file_id, specifier, local_name, imported_name, line) + VALUES (?, 4, './gateway', 'start', 'startGateway', 1), + (?, 4, './gateway', 'startAgain', 'startGateway', 2), + (?, 5, './gateway', 'dispatchRequest', 'dispatchRequest', 1), + (?, 6, '../other/gateway', 'startGateway', 'startGateway', 1), + (?, 8, './gateway', '*', '*', 1), + (?, 9, './barrel', 'startGateway', 'startGateway', 1), + (?, 10, './gateway', '*', '*', 1), + (?, 11, './shadow-barrel', 'startGateway', 'startGateway', 1)` + ).run(...Array(8).fill(fix.workspaceId)) + fix.db.prepare(`INSERT INTO edges (workspace_id, kind, src_file, dst_file, raw_target, resolved, line) + VALUES (?, 'imports', 4, 1, './gateway', 1, 1), + (?, 'imports', 5, 1, './gateway', 1, 1), + (?, 'imports', 6, 7, '../other/gateway', 1, 1), + (?, 'imports', 8, 1, './gateway', 1, 1), + (?, 'imports', 9, 8, './barrel', 1, 1), + (?, 'imports', 10, 1, './gateway', 1, 1), + (?, 'imports', 11, 10, './shadow-barrel', 1, 1)` + ).run(...Array(7).fill(fix.workspaceId)) + + const result = intel.getBlastRadius(fix.db, { name: 'startGateway', id: 10 }, { + workspaceId: fix.workspaceId, direction: 'upstream', maxDepth: 2, + }) + assert.equal(result.status, 'found') + const imports = result.nodes[1].filter(node => node.relationType === 'imports') + assert.deepEqual(imports.map(node => ({ id: node.id, file: node.file, kind: node.kind })).sort((a, b) => a.id - b.id), [ + { id: -8, file: 'src/barrel.ts', kind: 'file' }, + { id: -4, file: 'src/target-user.ts', kind: 'file' }, + ]) + assert.deepEqual(result.nodes[2].filter(node => node.relationType === 'imports').map(node => node.file), + ['src/barrel-user.ts']) + + const privateMethod = intel.getBlastRadius(fix.db, { name: 'startGateway', id: 14 }, { + workspaceId: fix.workspaceId, direction: 'upstream', maxDepth: 1, + }) + assert.deepEqual(privateMethod.nodes[1].filter(node => node.relationType === 'imports'), []) + + fix.db.prepare(`INSERT INTO symbols (id, file_id, name, kind, line, end_line, exported, container) + VALUES (16, 9, 'useBarrel', 'function', 2, 3, 1, null)`).run() + fix.db.prepare(`INSERT INTO edges (workspace_id, kind, src_symbol, src_file, raw_target, resolved, line) + VALUES (?, 'calls', 16, 9, 'startGateway', 0, 2)`).run(fix.workspaceId) + const calledThroughBarrel = intel.getBlastRadius(fix.db, { name: 'startGateway', id: 10 }, { + workspaceId: fix.workspaceId, direction: 'upstream', maxDepth: 2, + }) + assert.ok(calledThroughBarrel.nodes[1].some(node => node.name === 'useBarrel')) + assert.deepEqual(calledThroughBarrel.nodes[2].filter(node => node.relationType === 'imports'), [], + 'a caller already represents the importing file at a shallower depth') + + fix.db.prepare(`INSERT INTO symbols (id, file_id, name, kind, line, end_line, exported, container) + VALUES (17, 4, 'lateCaller', 'function', 3, 4, 1, null), + (18, 4, 'downstreamCallee', 'function', 5, 6, 1, null)`).run() + fix.db.prepare(`INSERT INTO edges (workspace_id, kind, src_symbol, src_file, dst_symbol, dst_file, resolved, line) + VALUES (?, 'calls', 17, 4, 11, 1, 1, 3), + (?, 'calls', 10, 1, 18, 4, 1, 5)`).run(fix.workspaceId, fix.workspaceId) + const mixed = intel.getBlastRadius(fix.db, { name: 'startGateway', id: 10 }, { + workspaceId: fix.workspaceId, direction: 'both', maxDepth: 2, + }) + assert.ok(mixed.nodes[1].some(node => node.id === -4 && node.relationType === 'imports'), + 'a direct import remains visible beside later upstream and same-depth downstream symbols') + assert.ok(mixed.nodes[1].some(node => node.name === 'downstreamCallee')) + assert.ok(mixed.nodes[2].some(node => node.name === 'lateCaller')) + } finally { + await fix.cleanup() + } +}) + test('M3: conceptSearch searches across symbols, notes, and call flows', async () => { const fix = await createIntelFixture() try { @@ -319,3 +407,84 @@ test('M3: HTTP API routes /api/intel/* serve real data', async () => { await fix.cleanup() } }) + +test('HTTP Cypher requires an unambiguous workspace and never reads another workspace', async () => { + const fix = await createIntelFixture() + try { + const second = 'ws-intel-private' + fix.db.prepare('INSERT INTO workspaces (id, name, root, created_at) VALUES (?, ?, ?, ?)') + .run(second, 'Private', join(fix.dir, 'private'), new Date().toISOString()) + fix.db.prepare(`INSERT INTO files (id, workspace_id, path, ext, size, mtime) + VALUES (90, ?, 'src/private.ts', '.ts', 30, '2026-09-30T00:00:00Z')`).run(second) + fix.db.prepare(`INSERT INTO symbols (id, file_id, name, kind, line, end_line, exported) + VALUES (90, 90, 'privateOnly', 'function', 1, 1, 1)`).run() + + const ask = (workspace?: string, query = 'MATCH (n:Function) RETURN n.name') => fetch(`${fix.baseUrl}/api/intel/cypher`, { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ query, ...(workspace ? { workspace } : {}) }), + }) + const missing = await ask() + assert.equal(missing.status, 400) + const first = await ask(fix.workspaceId) + assert.equal(first.status, 200) + const firstBody = await first.json() as { result: { rows: Array<Record<string, unknown>> } } + assert.equal(JSON.stringify(firstBody.result.rows).includes('privateOnly'), false) + const privateResponse = await ask(second) + assert.equal(privateResponse.status, 200) + const privateBody = await privateResponse.json() as { result: { rows: Array<Record<string, unknown>> } } + assert.equal(JSON.stringify(privateBody.result.rows).includes('privateOnly'), true) + assert.equal(JSON.stringify(privateBody.result.rows).includes('startGateway'), false) + const hostile = "MATCH (n:Function) WHERE n.name = 'x') OR 1=1 OR (1=1 RETURN n.name" + const hostileHttp = await ask(fix.workspaceId, hostile) + assert.equal(hostileHttp.status, 200) + const hostileHttpBody = await hostileHttp.json() as { result: { rowCount: number; rows: Array<Record<string, unknown>> } } + assert.equal(hostileHttpBody.result.rowCount, 0) + assert.equal(JSON.stringify(hostileHttpBody).includes('privateOnly'), false) + const hostileMcp = await new McpServer({ db: fix.db, workspaceId: fix.workspaceId }) + .executeTool('cypher', { workspaceId: fix.workspaceId, query: hostile }) + assert.equal((hostileMcp.result as { rowCount: number }).rowCount, 0) + assert.equal(JSON.stringify(hostileMcp).includes('privateOnly'), false) + const dslInjection = JSON.stringify({ where: { name: 'startGateway' }, + limit: "(SELECT CASE WHEN EXISTS(SELECT 1 FROM symbols s JOIN files f ON f.id=s.file_id WHERE f.workspace_id='ws-intel-private' AND s.name='privateOnly') THEN 1 ELSE 0 END)" }) + const dslHttp = await ask(fix.workspaceId, dslInjection) + // HTTP supplies an explicit numeric outer limit, which overrides the DSL value. + const dslHttpBody = await dslHttp.json() as { result: { rowCount: number; rows: unknown[] } } + assert.equal(dslHttpBody.result.rowCount, 1) + assert.equal(JSON.stringify(dslHttpBody).includes('privateOnly'), false) + const hostileLimitHttp = await fetch(`${fix.baseUrl}/api/intel/cypher`, { + method: 'POST', headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ query: dslInjection, workspace: fix.workspaceId, limit: '(SELECT 1)' }), + }) + assert.equal((await hostileLimitHttp.json() as { result: { rowCount: number } }).result.rowCount, 0) + const dslMcp = await new McpServer({ db: fix.db, workspaceId: fix.workspaceId }) + .executeTool('cypher', { workspaceId: fix.workspaceId, query: dslInjection }) + assert.equal((dslMcp.result as { rowCount: number }).rowCount, 0) + } finally { + await fix.cleanup() + } +}) + +test('HTTP impact refuses an ambiguous name and accepts an explicit symbol ID', async () => { + const fix = await createIntelFixture() + try { + fix.db.prepare(`INSERT INTO symbols (id, file_id, name, kind, line, end_line, exported) + VALUES (91, 3, 'startGateway', 'function', 60, 61, 1)`).run() + const ask = (symbolId?: number) => fetch(`${fix.baseUrl}/api/intel/impact`, { + method: 'POST', headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ target: 'startGateway', ...(symbolId ? { symbolId } : {}) }), + }) + const ambiguous = await ask() + assert.equal(ambiguous.status, 409) + const body = await ambiguous.json() as { ok: boolean; candidates: Array<{ id: number }> } + assert.equal(body.ok, false) + assert.deepEqual(body.candidates.map(row => row.id), [10, 91]) + const selected = await ask(10) + assert.equal(selected.status, 200) + const selectedBody = await selected.json() as { result: { target: { id: number }; status: string } } + assert.equal(selectedBody.result.target.id, 10) + assert.equal(selectedBody.result.status, 'found') + } finally { + await fix.cleanup() + } +}) diff --git a/test/llm-client.test.ts b/test/llm-client.test.ts new file mode 100644 index 0000000..6ad6fc3 --- /dev/null +++ b/test/llm-client.test.ts @@ -0,0 +1,188 @@ +import assert from 'node:assert/strict' +import test from 'node:test' +import { + createLlmClient, + environmentCredentialResolver, + loadLlmProviders, + LlmAuthError, + LlmQuotaError, +} from '../src/llm/client.ts' + +function jsonResponse(body: unknown, status = 200, headers?: Record<string, string>): Response { + return new Response(JSON.stringify(body), { status, headers: { 'content-type': 'application/json', ...headers } }) +} + +const providerKeys = ['NVIDIA_API_KEY_01', 'NVIDIA_API_KEY_02'] as const + +test('provider configuration keeps only environment variable names, never values', () => { + const providers = loadLlmProviders({ + GEMINI_API_KEY_02: 'gemini-value', + GEMINI_API_KEY: 'ignored-single-key', + NVIDIA_API_KEY_01: 'nvidia-value', + }) + + assert.deepEqual(providers.map(provider => provider.id), ['gemini', 'nvidia']) + assert.deepEqual(providers[0]?.keyEnvNames, ['GEMINI_API_KEY_02']) + assert.deepEqual(providers[1]?.keyEnvNames, ['NVIDIA_API_KEY_01']) + assert.equal(providers[0]?.baseUrl, 'https://generativelanguage.googleapis.com/v1beta/openai') + assert.equal(providers[1]?.baseUrl, 'https://integrate.api.nvidia.com/v1') + assert.equal(JSON.stringify(providers).includes('nvidia-value'), false) +}) + +test('credential pooling is delegated to an injected resolver and usage includes token cost', async () => { + const seenKeys: string[] = [] + const usage: unknown[] = [] + let selection = 0 + const client = createLlmClient({ + providers: [{ + id: 'nvidia', baseUrl: 'https://example.invalid/v1', keyEnvNames: providerKeys, + pricing: { inputUsdPerMillion: 2, outputUsdPerMillion: 6 }, + }], + resolveCredential: () => { + const keyName = providerKeys[selection++ % providerKeys.length]! + return { keyName, value: `value-${selection}` } + }, + fetchImpl: async (_input, init) => { + seenKeys.push(new Headers(init?.headers).get('authorization') ?? '') + return jsonResponse({ + choices: [{ message: { role: 'assistant', content: 'hello' } }], + usage: { prompt_tokens: 1000, completion_tokens: 500 }, + }) + }, + onUsage: event => { usage.push(event) }, + }) + + const first = await client.complete({ provider: 'nvidia', model: 'model-a', messages: [{ role: 'user', content: 'one' }] }) + const second = await client.complete({ provider: 'nvidia', model: 'model-a', messages: [{ role: 'user', content: 'two' }] }) + + assert.equal(first.content, 'hello') + assert.equal(second.content, 'hello') + assert.deepEqual(seenKeys, ['Bearer value-1', 'Bearer value-2']) + assert.deepEqual(usage[0], { + provider: 'nvidia', model: 'model-a', inputTokens: 1000, outputTokens: 500, + costUsd: 0.005, + }) +}) + +test('429 honors Retry-After and clamps the wait to the configured retry bound', async () => { + let calls = 0 + const sleeps: number[] = [] + const client = createLlmClient({ + providers: [{ id: 'gemini', baseUrl: 'https://example.invalid/v1', keyEnvNames: ['GEMINI_API_KEY_01'] }], + resolveCredential: () => ({ keyName: 'GEMINI_API_KEY_01', value: 'synthetic-value' }), + maxRetries: 1, + maxRetryDelayMs: 500, + fetchImpl: async () => { + calls += 1 + return calls === 1 + ? jsonResponse({ error: { message: 'try later' } }, 429, { 'retry-after': '2' }) + : jsonResponse({ choices: [{ message: { content: 'ok' } }] }) + }, + sleep: async milliseconds => { sleeps.push(milliseconds) }, + }) + + const response = await client.complete({ provider: 'gemini', model: 'flash', messages: [{ role: 'user', content: 'hi' }] }) + assert.equal(response.content, 'ok') + assert.equal(calls, 2) + assert.deepEqual(sleeps, [500]) +}) + +test('exhausted quota and auth errors are distinct and auth failures do not retry', async () => { + let quotaCalls = 0 + const quotaClient = createLlmClient({ + providers: [{ id: 'nvidia', baseUrl: 'https://example.invalid/v1', keyEnvNames: ['NVIDIA_API_KEY_01'] }], + resolveCredential: () => ({ keyName: 'NVIDIA_API_KEY_01', value: 'synthetic-value' }), + maxRetries: 1, + fetchImpl: async () => { quotaCalls += 1; return jsonResponse({}, 429) }, + sleep: async () => {}, + }) + await assert.rejects( + quotaClient.complete({ provider: 'nvidia', model: 'model', messages: [] }), + error => error instanceof LlmQuotaError, + ) + assert.equal(quotaCalls, 2) + + let authCalls = 0 + const authClient = createLlmClient({ + providers: [{ id: 'nvidia', baseUrl: 'https://example.invalid/v1', keyEnvNames: ['NVIDIA_API_KEY_01'] }], + resolveCredential: () => ({ keyName: 'NVIDIA_API_KEY_01', value: 'synthetic-value' }), + maxRetries: 3, + fetchImpl: async () => { authCalls += 1; return jsonResponse({}, 401) }, + }) + await assert.rejects( + authClient.complete({ provider: 'nvidia', model: 'model', messages: [] }), + error => error instanceof LlmAuthError, + ) + assert.equal(authCalls, 1) +}) + +test('timeout retries are bounded and failures never include credential values', async () => { + let calls = 0 + const waits: number[] = [] + const client = createLlmClient({ + providers: [{ id: 'gemini', baseUrl: 'https://example.invalid/v1', keyEnvNames: ['GEMINI_API_KEY_01'] }], + resolveCredential: () => ({ keyName: 'GEMINI_API_KEY_01', value: 'synthetic-value' }), + maxRetries: 1, + retryBaseMs: 10, + maxRetryDelayMs: 20, + fetchImpl: async () => { calls += 1; throw new DOMException('aborted', 'TimeoutError') }, + sleep: async milliseconds => { waits.push(milliseconds) }, + }) + await assert.rejects( + client.complete({ provider: 'gemini', model: 'flash', messages: [] }), + error => error instanceof Error && !error.message.includes('synthetic-value'), + ) + assert.equal(calls, 2) + assert.deepEqual(waits, [10]) +}) + +test('invalid provider or key names fail closed; multiple env keys require QUOTA resolver', async () => { + assert.throws(() => createLlmClient({ + providers: [{ id: 'gemini', baseUrl: 'https://example.invalid/v1', keyEnvNames: ['NVIDIA_API_KEY_01'] }, + ] as never, + }), /invalid key name configuration/) + assert.throws(() => createLlmClient({ + providers: [{ id: 'gemini', baseUrl: 'https://example.invalid/v1', keyEnvNames: ['GEMINI_API_KEY_01'], pricing: { inputUsdPerMillion: -1, outputUsdPerMillion: 1 } }], + }), /invalid pricing configuration/) + + const client = createLlmClient({ + providers: [{ id: 'gemini', baseUrl: 'https://example.invalid/v1', keyEnvNames: ['GEMINI_API_KEY_01'] }], + resolveCredential: () => ({ keyName: 'NVIDIA_API_KEY_01', value: 'synthetic-value' }), + fetchImpl: async () => jsonResponse({ choices: [{ message: { content: 'unreachable' } }] }), + }) + await assert.rejects( + client.complete({ provider: 'unknown' as never, model: 'flash', messages: [] }), + /not configured/, + ) + await assert.rejects(client.complete({ provider: 'gemini', model: 'flash', messages: [] }), /invalid credential/) + + const resolver = environmentCredentialResolver({ + GEMINI_API_KEY_01: 'value-one', + GEMINI_API_KEY_02: 'value-two', + }) + assert.throws( + () => resolver('gemini', ['GEMINI_API_KEY_01', 'GEMINI_API_KEY_02']), + /QUOTA-01 credential pool is required/, + ) +}) + +test('provider endpoints require HTTPS before a bearer credential can be sent', () => { + assert.throws(() => createLlmClient({ + providers: [{ id: 'nvidia', baseUrl: 'http://provider.example/v1', keyEnvNames: ['NVIDIA_API_KEY_01'] }], + }), /invalid base URL/) +}) + +test('usage stays explicit when pricing or token counts are unavailable', async () => { + const usage: unknown[] = [] + const client = createLlmClient({ + providers: [{ id: 'gemini', baseUrl: 'https://example.invalid/v1', keyEnvNames: ['GEMINI_API_KEY_01'] }], + resolveCredential: () => ({ keyName: 'GEMINI_API_KEY_01', value: 'synthetic-value' }), + fetchImpl: async () => jsonResponse({ choices: [{ message: { content: 'ok' } }], usage: { total_tokens: 4 } }), + onUsage: event => { usage.push(event) }, + }) + + await client.complete({ provider: 'gemini', model: 'flash', messages: [{ role: 'user', content: 'hi' }] }) + assert.deepEqual(usage[0], { + provider: 'gemini', model: 'flash', inputTokens: null, outputTokens: null, costUsd: null, + }) +}) diff --git a/test/mcp-parity.test.ts b/test/mcp-parity.test.ts index fe03c5d..fa398fd 100644 --- a/test/mcp-parity.test.ts +++ b/test/mcp-parity.test.ts @@ -6,6 +6,7 @@ import { tmpdir } from 'node:os' import { join } from 'node:path' import { openStore } from '../src/store/schema.ts' import { McpServer } from '../src/mcp/server.ts' +import { setPlanetIndexSelection } from '../src/planet.ts' function fixture() { const dir = mkdtempSync(join(tmpdir(), 'plugbrain-mcp-parity-')) @@ -25,6 +26,7 @@ function fixture() { (id, planet_id, repo_id, name, path, rel_prefix, branch, head, revision, is_primary, seen_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)`) .run(checkoutId, planetId, repoId, 'PlugBrain-Core', join(dir, 'brain-corpus'), 'Code/PlugBrain-Core', 'main', 'abc123', 'rev-abc123', now) + setPlanetIndexSelection(db, workspaceId, [checkoutId]) db.prepare(`INSERT INTO workspace_index_state (workspace_id, generation, git_head, file_count, symbol_count, edge_count, unresolved_count, ambiguous_count) VALUES (?, 7, 'abc123', 2, 2, 1, 0, 0)`) diff --git a/test/model-family.test.ts b/test/model-family.test.ts new file mode 100644 index 0000000..dead5e4 --- /dev/null +++ b/test/model-family.test.ts @@ -0,0 +1,17 @@ +import './helpers/isolated-home.ts' +import { strict as assert } from 'node:assert' +import { test } from 'node:test' +import { resolveModelFamily } from '../src/coord/model-family.ts' + +test('registered model IDs resolve to a recognized model family', () => { + assert.deepEqual(resolveModelFamily(' GPT-6-Luna '), { family: 'openai', modelId: 'gpt-6-luna' }) + assert.deepEqual(resolveModelFamily('claude-sonnet-4'), { family: 'anthropic', modelId: 'claude-sonnet-4' }) + assert.deepEqual(resolveModelFamily('gemini-2.5-pro'), { family: 'google', modelId: 'gemini-2.5-pro' }) +}) + +test('missing and unknown or stale deployment model labels do not infer a family', () => { + assert.deepEqual(resolveModelFamily(null), { family: null, modelId: null, reason: 'missing-model' }) + assert.deepEqual(resolveModelFamily('custom-deployment-2026-09'), { + family: null, modelId: 'custom-deployment-2026-09', reason: 'unknown-model', + }) +}) diff --git a/test/path-auflagen.test.ts b/test/path-auflagen.test.ts new file mode 100644 index 0000000..f2e6527 --- /dev/null +++ b/test/path-auflagen.test.ts @@ -0,0 +1,252 @@ +/** + * PATH-02 — the follow-ups left by the R-PATH01 review of "one folder, one + * spelling". + * + * A1 a checkout is matched through `canonicalPath`, not a raw string compare, + * so a junction/8.3 spelling of the same directory still selects it. + * A2 the workspace upsert is spelling-idempotent: a legacy root written in + * another spelling is recognized and rewritten in place instead of + * colliding on the primary key or becoming a second row. + * A3 `isNoisePath` memoizes its verdict, so an event burst does not run a + * realpath per event. + * + * Everything runs against temporary stores and temporary repositories; the + * owner's store, config and live brain are never touched. + */ +import { strict as assert } from 'node:assert' +import { execFileSync } from 'node:child_process' +import { mkdirSync, mkdtempSync, rmSync, symlinkSync, writeFileSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join, sep } from 'node:path' +import { test } from 'node:test' +import type { DatabaseSync } from 'node:sqlite' +import { openStore } from '../src/store/schema.ts' +import { + canonicalPath, discoverCheckouts, listPlanet, registerPlanet, + setPlanetIndexSelection, workspaceIdFor, +} from '../src/planet.ts' +import { detectChanges } from '../src/intel/changes.ts' +import { isNoisePath } from '../src/daemon.ts' +import { generationCache } from '../src/store/count-cache.ts' +import { findRegisteredWorkspace, registerWorkspaceRoot } from '../src/setup/workspace-from-cwd.ts' + +const HAS_GIT = ((): boolean => { + try { execFileSync('git', ['--version'], { stdio: 'ignore' }); return true } catch { return false } +})() +const skipGit = HAS_GIT ? false : 'git is not available on this machine' + +const git = (cwd: string, args: string[]): string => + execFileSync('git', ['-c', 'user.email=brain@test', '-c', 'user.name=brain', + '-c', 'commit.gpgsign=false', ...args], { + cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true, + }).trim() + +function gitInit(path: string): void { + mkdirSync(path, { recursive: true }) + git(path, ['init', '-q']) +} + +/** A real, committed git repository under `codeDir`. */ +function makeRepo(codeDir: string, name: string, files: Record<string, string>): string { + const root = join(codeDir, name) + mkdirSync(root, { recursive: true }) + git(root, ['init', '-q']) + for (const [rel, content] of Object.entries(files)) { + const abs = join(root, rel) + mkdirSync(join(abs, '..'), { recursive: true }) + writeFileSync(abs, content) + } + git(root, ['add', '.']) + git(root, ['commit', '-q', '-m', 'init']) + git(root, ['branch', '-M', 'main']) + return root +} + +interface Fixture { dir: string; db: DatabaseSync; cleanup: () => void } + +function fixture(name: string): Fixture { + const dir = mkdtempSync(join(tmpdir(), `plugbrain-${name}-`)) + const db = openStore(join(dir, 'brain.db')) + return { + dir, + db, + cleanup: () => { try { db.close() } catch { /* already closed */ } rmSync(dir, { recursive: true, force: true }) }, + } +} + +/** A workspace row exactly as a pre-fix writer would have left it. */ +function insertLegacyWorkspace(db: DatabaseSync, id: string, name: string, root: string): void { + db.prepare('INSERT INTO workspaces (id, name, root, created_at) VALUES (?, ?, ?, ?)') + .run(id, name, root, new Date().toISOString()) +} + +const workspaceCount = (db: DatabaseSync): number => + Number((db.prepare('SELECT COUNT(*) AS n FROM workspaces').get() as { n: number }).n) + +const storedRoot = (db: DatabaseSync, id: string): string | null => + (db.prepare('SELECT root FROM workspaces WHERE id = ?').get(id) as { root: string } | undefined)?.root ?? null + +/** + * A spelling of `path` that names the same directory but is not the canonical + * string: a trailing `.` segment, which `resolve`/`canonicalPath` folds away. + * It is exactly the shape a pre-fix `WHERE root = ?` lookup failed to match. + */ +const legacySpelling = (path: string): string => `${path}${sep}.` + +// ───────────────────────────────────────────────────────────────────────────── +// A1 — checkout match is canonical, not a raw string compare +// ───────────────────────────────────────────────────────────────────────────── + +test('A1: a checkout is selected through a junction spelling, not only its raw path', + { skip: skipGit }, () => { + const fx = fixture('path-a1') + try { + const planetRoot = join(fx.dir, 'plugpt') + const main = makeRepo(join(planetRoot, 'Code'), 'alpha', { 'src/a.ts': 'export const a = 1\n' }) + const registered = registerPlanet(fx.db, planetRoot) + const selected = discoverCheckouts(join(registered.root, 'Code'), registered.planetId) + .map(checkout => checkout.checkoutId) + setPlanetIndexSelection(fx.db, registered.workspaceId, selected) + + const checkout = listPlanet(fx.db, registered.workspaceId).checkouts + .find(row => canonicalPath(row.path) === canonicalPath(main)) + assert.ok(checkout, 'the checkout must be persisted') + + // One and the same directory, reached under a junction name. The raw + // compare cannot see that; `canonicalPath` can. + const alias = join(fx.dir, 'alpha-alias') + symlinkSync(checkout!.path, alias, 'junction') + + const throughAlias = detectChanges(fx.db, { + workspaceId: registered.workspaceId, + checkoutPath: alias, + diffText: 'diff --git a/src/a.ts b/src/a.ts\n', + }) + assert.equal(throughAlias.checkoutId, checkout!.id, + 'a junction spelling of the checkout path must select the persisted checkout') + + // No behaviour change for a caller that already passes the canonical path. + const direct = detectChanges(fx.db, { + workspaceId: registered.workspaceId, + checkoutPath: checkout!.path, + diffText: 'diff --git a/src/a.ts b/src/a.ts\n', + }) + assert.equal(direct.checkoutId, checkout!.id) + } finally { + fx.cleanup() + } + }) + +// ───────────────────────────────────────────────────────────────────────────── +// A2 — spelling-idempotent workspace upsert +// ───────────────────────────────────────────────────────────────────────────── + +test('A2: re-registering a legacy root that carries a git marker updates in place', () => { + const fx = fixture('path-a2-marker') + try { + const repo = join(fx.dir, 'repo') + gitInit(repo) + // A pinned identity makes the legacy row carry TODAY'S id, so a raw INSERT + // collides on the primary key instead of on `root`. + const pinned = 'ws-0123456789ab' + writeFileSync(join(repo, '.git', 'plugbrain-workspace-id'), `${pinned}\n`) + assert.equal(workspaceIdFor(repo), pinned, 'the marker pins the derived id') + insertLegacyWorkspace(fx.db, pinned, 'legacy', legacySpelling(repo)) + + const record = registerWorkspaceRoot(fx.db, repo) // must not throw + assert.equal(record.created, false) + assert.equal(record.id, pinned, 'identity is never renumbered') + assert.equal(workspaceCount(fx.db), 1) + assert.equal(storedRoot(fx.db, pinned), canonicalPath(repo)) + } finally { + fx.cleanup() + } +}) + +test('A2: a legacy root without a marker is folded in, never duplicated', () => { + const fx = fixture('path-a2-nomarker') + try { + const repo = join(fx.dir, 'repo') + gitInit(repo) + const legacyId = 'ws-ffffffffffff' + assert.notEqual(workspaceIdFor(repo), legacyId, 'no marker, so the derived id differs') + insertLegacyWorkspace(fx.db, legacyId, 'legacy', legacySpelling(repo)) + + // Reading already folds legacy spellings onto the canonical folder. + assert.equal(findRegisteredWorkspace(fx.db, repo)?.id, legacyId) + + const record = registerWorkspaceRoot(fx.db, repo) + assert.equal(record.created, false) + assert.equal(workspaceCount(fx.db), 1, 'the same folder must not become a second row') + assert.equal(storedRoot(fx.db, legacyId), canonicalPath(repo)) + } finally { + fx.cleanup() + } +}) + +test('A2: registering through a junction alias stays one canonical row', { skip: skipGit }, () => { + const fx = fixture('path-a2-junction') + try { + const repo = join(fx.dir, 'repo') + gitInit(repo) + const first = registerWorkspaceRoot(fx.db, repo) + + const alias = join(fx.dir, 'repo-alias') + symlinkSync(repo, alias, 'junction') + const second = registerWorkspaceRoot(fx.db, alias) + + assert.equal(second.id, first.id) + assert.equal(second.created, false) + assert.equal(workspaceCount(fx.db), 1) + assert.equal(storedRoot(fx.db, first.id), canonicalPath(repo)) + } finally { + fx.cleanup() + } +}) + +test('A2: an already canonical root is still updated, not inserted twice', () => { + const fx = fixture('path-a2-canonical') + try { + const repo = join(fx.dir, 'repo') + mkdirSync(repo, { recursive: true }) + const first = registerWorkspaceRoot(fx.db, repo, 'first') + const second = registerWorkspaceRoot(fx.db, repo, 'second') + + assert.equal(second.id, first.id) + assert.equal(second.created, false) + assert.equal(second.name, 'second') + assert.equal(workspaceCount(fx.db), 1) + assert.equal(storedRoot(fx.db, first.id), canonicalPath(repo)) + } finally { + fx.cleanup() + } +}) + +// ───────────────────────────────────────────────────────────────────────────── +// A3 — the noise-path verdict is memoized +// ───────────────────────────────────────────────────────────────────────────── + +test('A3: isNoisePath memoizes its verdict, bounded, keyed by store home', () => { + const cache = generationCache('daemon-noise') + cache.clear() + try { + const home = join(tmpdir(), 'plugbrain-noise-home') + const inside = join(home, 'runs', 'state.json') + + assert.equal(isNoisePath(inside, home), true) + assert.equal(cache.size, 1, 'the store verdict is memoized') + assert.equal(isNoisePath(inside, home), true) + assert.equal(cache.size, 1, 'a repeated path does not grow the memo') + + // A different store home must never read the first home's answer. + const other = join(tmpdir(), 'plugbrain-noise-other') + assert.equal(isNoisePath(inside, other), false) + assert.equal(cache.size, 2) + + // The memo stays bounded under an event burst. + for (let i = 0; i < 100; i++) isNoisePath(join(home, 'runs', `f${i}.json`), home) + assert.ok(cache.size <= 64, 'the memo must not grow without bound') + } finally { + cache.clear() + } +}) diff --git a/test/plan-decompose.test.ts b/test/plan-decompose.test.ts new file mode 100644 index 0000000..b6d0d61 --- /dev/null +++ b/test/plan-decompose.test.ts @@ -0,0 +1,71 @@ +import { strict as assert } from 'node:assert' +import { mkdtempSync, rmSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { test } from 'node:test' +import { openStore } from '../src/store/schema.ts' +import { ensureAgent } from '../src/access.ts' +import { enqueueTask, ensureQueueSchema } from '../src/queue.ts' +import { decomposeLedgerTarget, applyDecomposedTask, type DecompositionLedger } from '../src/plan-decompose.ts' + +const ledger: DecompositionLedger = { + gates: [{ + id: 'R1-TEST-001', title: 'Ship a safe change', category: 'tests', + brief: 'Add regression coverage for the referenced gate.', + acceptance: ['A failing case is reproduced before the fix.', 'All relevant checks pass.'], + reviewerRole: 'independent-reviewer', dependsOn: ['R0-BASE-001'], evidence: ['evidence/R1-TEST-001.md'], + }], + requirements: [{ id: 'REQ-UNKNOWN', title: 'Unknown work', category: 'experimental', brief: 'Do work.', acceptance: ['Done'], reviewerRole: 'reviewer', evidence: ['proof.md'] }], +} + +test('decompose emits an executable spec for allowlisted categories with evidence and dependency refs', () => { + const result = decomposeLedgerTarget(ledger, 'R1-TEST-001') + assert.equal(result.state, 'executable') + assert.equal(result.spec?.category, 'tests') + assert.deepEqual(result.spec?.dependsOn, ['R0-BASE-001']) + assert.deepEqual(result.spec?.evidence, ['evidence/R1-TEST-001.md']) +}) + +test('unknown categories stay waiting-for-lead', () => { + const result = decomposeLedgerTarget(ledger, 'REQ-UNKNOWN') + assert.equal(result.state, 'waiting-for-lead') + assert.match(result.reason, /allowlist/i) +}) + +test('missing evidence blocks executable status without inventing proof', () => { + const noEvidence = { ...ledger, gates: [{ ...ledger.gates![0]!, evidence: [] as string[] }] } + const result = decomposeLedgerTarget(noEvidence, 'R1-TEST-001') + assert.equal(result.state, 'waiting-for-lead') + assert.match(result.reason, /evidence/i) + assert.deepEqual(result.spec?.evidence, []) +}) + +test('dependencies must resolve to existing queue tasks before apply', () => { + const root = mkdtempSync(join(tmpdir(), 'plugbrain-decompose-')) + const dbPath = join(root, 'brain.sqlite') + const db = openStore(dbPath) + try { + ensureQueueSchema(db) + db.exec("INSERT INTO workspaces (id, name, root, created_at) VALUES ('ws', 'fixture', '/', '2026-01-01')") + ensureAgent(db, 'lead') + const result = decomposeLedgerTarget(ledger, 'R1-TEST-001') + assert.throws(() => applyDecomposedTask(db, 'ws', result, 'lead'), /dependency/i) + assert.equal((db.prepare('SELECT COUNT(*) AS n FROM queue_tasks').get() as { n: number }).n, 0) + } finally { db.close(); rmSync(root, { recursive: true, force: true }) } +}) + +test('repeating an apply for the same source is idempotent', () => { + const root = mkdtempSync(join(tmpdir(), 'plugbrain-decompose-')) + const db = openStore(join(root, 'brain.sqlite')) + try { + ensureQueueSchema(db) + db.exec("INSERT INTO workspaces (id, name, root, created_at) VALUES ('ws', 'fixture', '/', '2026-01-01')") + ensureAgent(db, 'lead') + const noDependencies = { ...ledger, gates: [{ ...ledger.gates![0]!, dependsOn: [] as string[] }] } + const result = decomposeLedgerTarget(noDependencies, 'R1-TEST-001') + const first = applyDecomposedTask(db, 'ws', result, 'lead') + const second = applyDecomposedTask(db, 'ws', result, 'lead') + assert.equal(first.id, second.id) + assert.equal((db.prepare('SELECT COUNT(*) AS n FROM queue_tasks').get() as { n: number }).n, 1) + } finally { db.close(); rmSync(root, { recursive: true, force: true }) } +}) diff --git a/test/planet-prune.test.ts b/test/planet-prune.test.ts index ef8402f..5e122bb 100644 --- a/test/planet-prune.test.ts +++ b/test/planet-prune.test.ts @@ -25,6 +25,12 @@ import { } from '../src/planet.ts' import { compactStore, planPrune, prunePlanet } from '../src/index/prune.ts' import { readRunState } from '../src/index/runs.ts' +import { conceptSearch } from '../src/intel/query.ts' +import { getSymbolContext } from '../src/intel/context.ts' +import { getBlastRadius } from '../src/intel/impact.ts' +import { detectChanges } from '../src/intel/changes.ts' +import { executeCypherQuery } from '../src/intel/cypher.ts' +import { searchNotes } from '../src/notes/search.ts' const home = mkdtempSync(join(tmpdir(), 'plugbrain-prune-home-')) process.env.PLUGBRAIN_HOME = home @@ -104,6 +110,51 @@ function fixture(name: string): Fixture { } } +test('Intel query and context hide deselected checkout rows before a reindex', { skip: skipGit }, () => { + const fx = fixture('intel-selection') + try { + assert.throws(() => detectChanges(fx.db, { workspaceId: fx.ws }), /checkout is ambiguous/) + assert.throws(() => detectChanges(fx.db, { workspaceId: fx.ws, checkoutId: 'missing' }), /checkout is not in the active selection/) + const betaFile = fx.db.prepare("SELECT id FROM files WHERE checkout_id = ? AND path LIKE '%/src/core.ts'").get(fx.beta) as { id: number } + fx.db.prepare('INSERT INTO note_body (workspace_id, file_id, path, text) VALUES (?, ?, ?, ?)') + .run(fx.ws, betaFile.id, 'Code/beta/src/core.ts', 'betaHiddenPhrase only in an old code note body') + fx.db.prepare(`INSERT INTO files (id, workspace_id, path, ext, size, mtime) + VALUES (900, ?, 'Code/legacy/src/secret.md', '.md', 40, '2026-09-30T00:00:00Z')`).run(fx.ws) + fx.db.prepare('INSERT INTO note_body (workspace_id, file_id, path, text) VALUES (?, ?, ?, ?)') + .run(fx.ws, 900, 'Code/legacy/src/secret.md', 'legacyHiddenPhrase from a pre-Planet index') + const betaDiff = 'diff --git a/Code/beta/src/core.ts b/Code/beta/src/core.ts\n@@ -1,1 +1,1 @@\n+changed\n' + assert.equal(detectChanges(fx.db, { workspaceId: fx.ws, checkoutId: fx.alpha, diffText: betaDiff }).changedSymbols.length, 0) + assert.ok(detectChanges(fx.db, { workspaceId: fx.ws, checkoutId: fx.beta, diffText: betaDiff }).changedSymbols.some(change => change.name === 'betaCore')) + assert.ok(conceptSearch(fx.db, 'betaCore', { workspaceId: fx.ws }).symbols.length > 0) + assert.equal(getSymbolContext(fx.db, 'betaCore', { workspaceId: fx.ws }).status, 'found') + assert.equal(getBlastRadius(fx.db, 'betaCore', { workspaceId: fx.ws, checkoutId: fx.beta }).status, 'found') + assert.equal(getBlastRadius(fx.db, 'betaCore', { workspaceId: fx.ws, checkoutId: fx.alpha }).status, 'not_found') + assert.equal(executeCypherQuery(fx.db, "MATCH (n:Function) WHERE n.name = 'betaCore' RETURN n.name", { workspaceId: fx.ws }).rowCount, 1) + setPlanetIndexSelection(fx.db, fx.ws, [fx.alpha]) + // The old Beta index rows still exist. Read-time selection must hide them. + assert.ok(n(fx.db, 'SELECT COUNT(*) AS n FROM files WHERE checkout_id = ?', fx.beta) > 0) + assert.equal(conceptSearch(fx.db, 'betaCore', { workspaceId: fx.ws }).symbols.length, 0) + assert.equal(getSymbolContext(fx.db, 'betaCore', { workspaceId: fx.ws }).status, 'not_found') + assert.equal(getBlastRadius(fx.db, 'betaCore', { workspaceId: fx.ws }).status, 'not_found') + assert.equal(getBlastRadius(fx.db, 'betaCore', { workspaceId: fx.ws, checkoutId: fx.beta }).status, 'not_found') + assert.equal(searchNotes(fx.db, fx.ws, 'betaHiddenPhrase').total, 0) + assert.equal(searchNotes(fx.db, fx.ws, 'legacyHiddenPhrase').total, 0) + assert.equal(conceptSearch(fx.db, 'legacyHiddenPhrase', { workspaceId: fx.ws }).notes.length, 0) + assert.ok(searchNotes(fx.db, fx.ws, 'roadmap').total > 0) + const staleBeta = fx.db.prepare("SELECT s.id FROM symbols s WHERE s.name = 'betaCore'").get() as { id: number } + assert.equal(getBlastRadius(fx.db, { name: 'alphaCore', id: staleBeta.id }, { workspaceId: fx.ws }).status, 'not_found') + assert.equal(executeCypherQuery(fx.db, "MATCH (n:Function) WHERE n.name = 'betaCore' RETURN n.name", { workspaceId: fx.ws }).rowCount, 0) + assert.equal(executeCypherQuery(fx.db, { where: { name: 'betaCore' } }, { workspaceId: fx.ws }).rowCount, 0) + const graph = executeCypherQuery(fx.db, 'MATCH (a:Symbol)-[:CALLS]->(b:Symbol) RETURN a.name, b.name', { workspaceId: fx.ws }) + assert.ok(graph.rows.every(row => !JSON.stringify(row).includes('beta'))) + assert.equal(executeCypherQuery(fx.db, 'MATCH (r:Repo) RETURN count(r)', { workspaceId: fx.ws }).rows[0].count, 1) + assert.ok(conceptSearch(fx.db, 'alphaCore', { workspaceId: fx.ws }).symbols.length > 0) + assert.equal(getBlastRadius(fx.db, 'alphaCore', { workspaceId: fx.ws }).status, 'found') + } finally { + fx.cleanup() + } +}) + /** Every row that belongs to one checkout, table by table. */ function footprint(db: DatabaseSync, ws: string, checkoutId: string): Record<string, number> { const files = `SELECT id FROM files WHERE workspace_id = '${ws}' AND checkout_id = '${checkoutId}'` diff --git a/test/planet-selection-api.test.ts b/test/planet-selection-api.test.ts index 9302ca8..0928945 100644 --- a/test/planet-selection-api.test.ts +++ b/test/planet-selection-api.test.ts @@ -212,11 +212,9 @@ test('Intel status and change detection are scoped to the requested Planet works assert.throws(() => detectChanges(db, {} as never), /workspaceId is required/) const bDiff = 'diff --git a/src/b.ts b/src/b.ts\n@@ -1 +1 @@\n-export const beta = false\n+export const beta = true\n' - const crossPlanet = detectChanges(db, { + assert.throws(() => detectChanges(db, { workspaceId: a.workspaceId, checkoutId: bId, diffText: bDiff, - }) - assert.equal(crossPlanet.changedFiles, 0) - assert.equal(crossPlanet.checkoutId, null) + }), /checkout is not in the active selection/) handle = await serve({ db, uiRoot: null }, 0) const base = `http://127.0.0.1:${handle.port}` @@ -237,11 +235,10 @@ test('Intel status and change detection are scoped to the requested Planet works body: JSON.stringify({ checkoutId: bId, diffText: bDiff }), }, ) - assert.equal(crossResponse.status, 200) - const crossBody = await crossResponse.json() as { ok: boolean; result: { changedFiles: number; checkoutId: string | null } } - assert.equal(crossBody.ok, true) - assert.equal(crossBody.result.changedFiles, 0) - assert.equal(crossBody.result.checkoutId, null) + assert.equal(crossResponse.status, 400) + const crossBody = await crossResponse.json() as { ok: boolean; error: string } + assert.equal(crossBody.ok, false) + assert.match(crossBody.error, /checkout is not in the active selection/) const ambiguousChanges = await fetch(`${base}/api/intel/detect-changes`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ diffText: bDiff }), diff --git a/test/planet.test.ts b/test/planet.test.ts index 0060e76..ed22751 100644 --- a/test/planet.test.ts +++ b/test/planet.test.ts @@ -289,14 +289,11 @@ test('a planet inventories every checkout but indexes only a persisted explicit assert.equal(status.checkouts, 1) assert.equal(status.files, 1, 'status excludes stale graph rows from deselected checkouts') assert.equal(status.dirtyCheckouts, 0, 'unselected dirt cannot make the active Brain stale') - const changes = detectChanges(fx.db, { workspaceId: fx.workspaceId, checkoutPath: historic }) - assert.equal(changes.checkoutId, null) - assert.equal(changes.changedFiles, 0, 'explicit historical path is not a selection bypass') - const changesById = detectChanges(fx.db, { + assert.throws(() => detectChanges(fx.db, { workspaceId: fx.workspaceId, checkoutPath: historic }), + /checkout is not in the active selection/, 'explicit historical path is not a selection bypass') + assert.throws(() => detectChanges(fx.db, { workspaceId: fx.workspaceId, checkoutId: old!.id, diffText: 'diff --git a/src/x.ts b/src/x.ts', - }) - assert.equal(changesById.checkoutId, null) - assert.equal(changesById.changedFiles, 0, 'explicit historical id is not a selection bypass') + }), /checkout is not in the active selection/, 'explicit historical id is not a selection bypass') // A restart reads the same persisted vector. No live daemon or production // store is involved: this is a temporary test database only. diff --git a/test/plugin-manifest.test.ts b/test/plugin-manifest.test.ts index b2b5c19..dd76066 100644 --- a/test/plugin-manifest.test.ts +++ b/test/plugin-manifest.test.ts @@ -18,7 +18,7 @@ const readJson = (rel: string): Record<string, unknown> => test('plugin.json is a complete, consistent manifest', () => { const manifest = readJson('.claude-plugin/plugin.json') assert.equal(manifest.name, 'plugbrain') - assert.match(String(manifest.version), /^\d+\.\d+\.\d+$/) + assert.match(String(manifest.version), /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/) assert.ok(String(manifest.description).length > 10, 'description must say what the plugin does') assert.equal(manifest.license, 'MIT') assert.ok(manifest.author, 'author is required') diff --git a/test/queue-e2e.test.ts b/test/queue-e2e.test.ts index fa3f6f6..494e469 100644 --- a/test/queue-e2e.test.ts +++ b/test/queue-e2e.test.ts @@ -24,8 +24,9 @@ */ import './helpers/isolated-home.ts' import { strict as assert } from 'node:assert' +import { createHash } from 'node:crypto' import { test } from 'node:test' -import { mkdtempSync, rmSync } from 'node:fs' +import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' import { DatabaseSync } from 'node:sqlite' @@ -57,9 +58,12 @@ async function getInbox(base: string): Promise<Reply> { test('B4: Q-chain E2E — enqueue, claim (HTTP + turn --claim), restart, deliver, idempotent double deliver, existing receipt', async () => { const dir = mkdtempSync(join(tmpdir(), 'plugbrain-qe2e-')) const dbFile = join(dir, 'brain.db') + const workspaceRoot = join(dir, 'ws') + mkdirSync(join(workspaceRoot, 'out'), { recursive: true }) + writeFileSync(join(workspaceRoot, 'out', 'result.md'), 'Q-chain delivery proof.\n') let db: DatabaseSync = openStore(dbFile) db.prepare('INSERT INTO workspaces (id, name, root, created_at) VALUES (?, ?, ?, ?)') - .run('ws-qe2e', 'Q-chain E2E', join(dir, 'ws'), new Date().toISOString()) + .run('ws-qe2e', 'Q-chain E2E', workspaceRoot, new Date().toISOString()) let handle: ServerHandle = await serve({ db, uiRoot: null, authKey: 'qe2e-token' }, 0) let base = `http://127.0.0.1:${handle.port}` @@ -113,17 +117,31 @@ test('B4: Q-chain E2E — enqueue, claim (HTTP + turn --claim), restart, deliver assert.strictEqual(restarted.claimed_by, 'worker-b') // ── 4. Worker liefert ──────────────────────────────────────────────── + const missingEvidence = await post(base, '/api/queue/deliver', { + workspace: 'ws-qe2e', taskId: task.id, agentId: 'worker-b', deliveredPath: 'out/missing.md', + }) + assert.strictEqual(missingEvidence.status, 403) + assert.match(String(missingEvidence.data.error), /does not exist/i) const deliver = await post(base, '/api/queue/deliver', { - taskId: task.id, agentId: 'worker-b', deliveredPath: 'out/result.md', + workspace: 'ws-qe2e', taskId: task.id, agentId: 'worker-b', deliveredPath: 'out/result.md', repoPath: process.cwd(), }) assert.strictEqual(deliver.status, 200, `deliver: ${JSON.stringify(deliver.data)}`) const delivered = deliver.data.task as { state: string; delivered_path: string | null; updated_at: string } assert.strictEqual(delivered.state, 'delivered') assert.strictEqual(delivered.delivered_path, 'out/result.md') + const deliveryReceipt = db.prepare(`SELECT delivered_by, delivery_attempt, source_revision, delivered_path, delivered_sha256 + FROM queue_deliveries WHERE task_id = ?`).get(task.id) as { + delivered_by: string; delivery_attempt: number; source_revision: string | null; delivered_path: string; delivered_sha256: string + } + assert.equal(deliveryReceipt.delivered_by, 'worker-b') + assert.equal(deliveryReceipt.delivery_attempt, 1) + assert.match(deliveryReceipt.source_revision, /^[0-9a-f]{40}$/i) + assert.equal(deliveryReceipt.delivered_path, 'out/result.md') + assert.equal(deliveryReceipt.delivered_sha256, createHash('sha256').update('Q-chain delivery proof.\n').digest('hex')) // ── 5. Doppeltes deliver-Event, das idempotent bleiben muss ───────── const deliverAgain = await post(base, '/api/queue/deliver', { - taskId: task.id, agentId: 'worker-b', deliveredPath: 'out/result.md', + workspace: 'ws-qe2e', taskId: task.id, agentId: 'worker-b', deliveredPath: 'out/result.md', repoPath: process.cwd(), }) assert.strictEqual(deliverAgain.status, 403, 'a duplicate deliver must be refused, not silently re-applied') assert.strictEqual(deliverAgain.data.ok, false) diff --git a/test/queue.test.ts b/test/queue.test.ts index 64efc75..0194949 100644 --- a/test/queue.test.ts +++ b/test/queue.test.ts @@ -17,8 +17,10 @@ import { tmpdir } from 'node:os' import { join } from 'node:path' import { openStore } from '../src/store/schema.ts' import { registerAgent } from '../src/access.ts' +import { ensureLeaseSchema } from '../src/coord/leases.ts' import { claimNextTask, deliverTask, enqueueTask, listQueue, queueDepth, + listQueueEvents, prioritizeTask, reassignTask, supersedeTask, } from '../src/queue.ts' const WS = 'ws-queue-test' @@ -72,6 +74,79 @@ test('claiming is first-in-first-out, so nothing starves', () => { } finally { f.cleanup() } }) +test('queue operations are audited, visible, and affect future offers', () => { + const f = fixture() + try { + const replaced = enqueueTask(f.db, WS, { title: 'old task' }) + const ordinary = enqueueTask(f.db, WS, { title: 'ordinary task' }) + const urgent = enqueueTask(f.db, WS, { title: 'urgent task' }) + + const superseded = supersedeTask(f.db, WS, replaced.id, { + byAgent: 'agent-nvidia', byTaskId: urgent.id, note: 'replaced by the current brief', + }) + assert.equal(superseded.state, 'superseded') + assert.equal(superseded.superseded_by, urgent.id) + assert.equal(reassignTask(f.db, WS, ordinary.id, { byAgent: 'agent-nvidia', addressedTo: 'agent-gemini' }).addressed_to, 'agent-gemini') + assert.equal(prioritizeTask(f.db, WS, urgent.id, { byAgent: 'agent-nvidia', priority: 8 }).priority, 8) + + assert.equal(claimNextTask(f.db, WS, 'agent-nvidia')?.id, urgent.id) + assert.equal(claimNextTask(f.db, WS, 'agent-nvidia'), null, 'reassigned work is not offered to another worker') + assert.equal(claimNextTask(f.db, WS, 'agent-gemini')?.id, ordinary.id) + + const rows = listQueue(f.db, WS) + assert.equal(rows.find(row => row.id === replaced.id)?.state, 'superseded') + assert.equal(rows.find(row => row.id === urgent.id)?.priority, 8) + const events = listQueueEvents(f.db, WS, { taskId: replaced.id }) + assert.deepEqual(events.map(event => event.operation), ['supersede']) + assert.equal(events[0]?.by_agent, 'agent-nvidia') + assert.equal(events[0]?.note, 'replaced by the current brief') + assert.equal(listQueueEvents(f.db, WS).length, 3) + } finally { f.cleanup() } +}) + +test('a stale claim can be atomically rerouted, but an active lease blocks queue mutation', () => { + const f = fixture() + try { + const claimed = enqueueTask(f.db, WS, { title: 'claimed task' }) + assert.ok(claimNextTask(f.db, WS, 'agent-nvidia')) + const rerouted = reassignTask(f.db, WS, claimed.id, { byAgent: 'agent-gemini', addressedTo: 'agent-gemini' }) + assert.equal(rerouted.state, 'pending') + assert.equal(rerouted.claimed_by, null) + assert.equal(rerouted.claimed_at, null) + assert.equal(rerouted.addressed_to, 'agent-gemini') + assert.equal(listQueueEvents(f.db, WS, { taskId: claimed.id })[0]?.old_value, 'claimed:agent-nvidia') + assert.equal(claimNextTask(f.db, WS, 'agent-gemini')?.id, claimed.id) + const superseded = supersedeTask(f.db, WS, claimed.id, { + byAgent: 'agent-nvidia', note: 'duplicate without a started lease or worktree', + }) + assert.equal(superseded.state, 'superseded') + assert.equal(superseded.claimed_by, null) + assert.equal(listQueueEvents(f.db, WS, { taskId: claimed.id })[0]?.old_value, 'claimed:agent-gemini') + + const leased = enqueueTask(f.db, WS, { title: 'leased task' }) + assert.ok(claimNextTask(f.db, WS, 'agent-nvidia')) + ensureLeaseSchema(f.db) + f.db.prepare(`INSERT INTO leases + (id, workspace_id, agent_id, task_id, paths_json, symbols_json, mode, epoch, created_at, expires_at, released_at, ttl_ms) + VALUES (?, ?, ?, ?, '[]', '[]', 'write', 1, ?, ?, NULL, 60000)`) + .run('lease-active', WS, 'agent-nvidia', leased.id, new Date().toISOString(), new Date(Date.now() + 60000).toISOString()) + assert.throws(() => supersedeTask(f.db, WS, leased.id, { byAgent: 'agent-gemini', note: 'obsolete' }), /active lease/i) + assert.equal(listQueue(f.db, WS).find(row => row.id === leased.id)?.state, 'claimed') + } finally { f.cleanup() } +}) + +test('queue mutations reject invalid addressees, bad priorities, and cross-workspace tasks', () => { + const f = fixture() + try { + const pending = enqueueTask(f.db, WS, { title: 'pending task' }) + f.db.prepare('INSERT INTO workspaces (id, name, root, created_at) VALUES (?, ?, ?, ?)') + .run('ws-other', 'other', join(f.dir, 'other'), new Date().toISOString()) + assert.throws(() => reassignTask(f.db, WS, pending.id, { byAgent: 'agent-gemini', addressedTo: 'missing-agent' }), /agent/i) + assert.throws(() => prioritizeTask(f.db, WS, pending.id, { byAgent: 'agent-gemini', priority: 1.5 }), /integer/i) + assert.throws(() => supersedeTask(f.db, 'ws-other', pending.id, { byAgent: 'agent-gemini', note: 'obsolete' }), /another workspace/i) + } finally { f.cleanup() } +}) + test('an addressed task is claimable only by its addressee', () => { const f = fixture() try { diff --git a/test/quota-pools.test.ts b/test/quota-pools.test.ts new file mode 100644 index 0000000..d3e7bf9 --- /dev/null +++ b/test/quota-pools.test.ts @@ -0,0 +1,44 @@ +import './helpers/isolated-home.ts' +import { strict as assert } from 'node:assert' +import { test } from 'node:test' +import { mkdtempSync, rmSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { openStore } from '../src/store/schema.ts' +import { configureQuotaPool, noteQuotaRateLimit, reserveQuota, settleQuota } from '../src/coord/quota-pools.ts' + +function fixture() { + const dir = mkdtempSync(join(tmpdir(), 'plugbrain-quota-pool-')) + const db = openStore(join(dir, 'brain.db')) + return { db, cleanup: () => { db.close(); rmSync(dir, { recursive: true, force: true }) } } +} + +test('pool reservations share concurrency and rpm limits and usage settlement remains separate', () => { + const f = fixture() + try { + configureQuotaPool(f.db, { id: 'nvidia:project-a', maxConcurrent: 1, maxRequestsPerMinute: 2 }) + const first = reserveQuota(f.db, { poolId: 'nvidia:project-a', attemptId: 'task-a:1' }) + assert.equal(first.allowed, true) + assert.equal(reserveQuota(f.db, { poolId: 'nvidia:project-a', attemptId: 'task-b:1' }).reason, 'concurrency') + const settled = settleQuota(f.db, { reservationId: first.reservation!.id, outcome: 'clean' }) + assert.equal(settled.usage, null, 'unknown usage must not be represented as zero') + assert.equal(settled.settledAt !== null, true) + const second = reserveQuota(f.db, { poolId: 'nvidia:project-a', attemptId: 'task-b:1' }) + assert.equal(second.allowed, true, 'separate key aliases configured to this project share the same pool') + settleQuota(f.db, { reservationId: second.reservation!.id, outcome: 'quota' }) + assert.equal(reserveQuota(f.db, { poolId: 'nvidia:project-a', attemptId: 'task-c:1' }).reason, 'rpm') + } finally { f.cleanup() } +}) + +test('Retry-After cooldown is pool-wide and capped; attempt settlement is idempotent', () => { + const f = fixture() + try { + const now = new Date('2026-09-27T03:00:00.000Z') + configureQuotaPool(f.db, { id: 'freebuff:freebucks', maxConcurrent: 2, now }) + const reservation = reserveQuota(f.db, { poolId: 'freebuff:freebucks', attemptId: 'task-a:1', now }) + const settled = settleQuota(f.db, { reservationId: reservation.reservation!.id, outcome: 'quota', now }) + assert.equal(noteQuotaRateLimit(f.db, 'freebuff:freebucks', 60_000_000, { now }), '2026-09-27T03:30:00.000Z') + assert.equal(reserveQuota(f.db, { poolId: 'freebuff:freebucks', attemptId: 'task-b:1', now }).reason, 'cooldown') + assert.equal(settleQuota(f.db, { reservationId: settled.id, outcome: 'duplicate', usage: 4, now }).usage, null) + } finally { f.cleanup() } +}) diff --git a/test/setup-init.test.ts b/test/setup-init.test.ts index f46bee3..9c64d92 100644 --- a/test/setup-init.test.ts +++ b/test/setup-init.test.ts @@ -9,7 +9,7 @@ import { strict as assert } from 'node:assert' import { test } from 'node:test' import { execFileSync, spawn } from 'node:child_process' -import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs' +import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' import { dirname, join } from 'node:path' import type { ChildProcess } from 'node:child_process' @@ -75,6 +75,11 @@ test('S2: init registers once, indexes, and enrolls a detected client', () => { assert.match(first, /generation/) assert.match(first, /clients:/) assert.match(first, /Claude Code/) + assert.match(first, /agent instructions:/) + const agentFile = readFileSync(join(f.repo, 'AGENTS.md'), 'utf8') + assert.match(agentFile, /<!-- plugbrain:agent-protocol:start -->/) + assert.match(agentFile, /<!-- plugbrain:agent-protocol:end -->/) + assert.match(agentFile, /agent id is `<id>`/) assert.doesNotMatch(first, /PlugBrain-Core--setup/, 'the entry must name this CLI, not the owner install') const enrolled = JSON.parse(readFileSync(join(f.home, '.claude.json'), 'utf8')) @@ -136,8 +141,11 @@ test('S5: init --dry-run reports but writes neither a workspace nor a client', ( assert.match(out, /would register workspace/) assert.match(out, /would index/) assert.match(out, /would change/) + assert.match(out, /agent instructions:/) + assert.match(out, /dry-run/) assert.equal(readFileSync(join(f.home, '.claude.json'), 'utf8'), '{}\n') + assert.equal(existsSync(join(f.repo, 'AGENTS.md')), false) const db = openStore(join(f.home, 'plugbrain.db')) try { const rows = db.prepare('SELECT id FROM workspaces').all() as Array<{ id: string }> @@ -150,6 +158,21 @@ test('S5: init --dry-run reports but writes neither a workspace nor a client', ( } }) +test('doctor --agents --json is read-only when no Brain store exists', () => { + const f = makeFixture() + try { + const env = { ...tempEnv(f.home), PLUGBRAIN_DOCTOR_PORT: '9' } + const out = runCli(['doctor', '--agents', '--json'], env, f.repo) + const rows = JSON.parse(out) as Array<{ client: string; fix: string }> + assert.equal(rows.length, 7) + assert.ok(rows.every(row => typeof row.fix === 'string' && row.fix.length > 0)) + assert.equal(existsSync(join(f.home, 'plugbrain.db')), false) + assert.deepEqual(readdirSync(f.home), []) + } finally { + f.cleanup() + } +}) + test('S4: init then mcp over stdio answers tools/list and finds the symbol', async () => { const f = makeFixture() const env = tempEnv(f.home) diff --git a/test/swarm-chronicle.test.ts b/test/swarm-chronicle.test.ts new file mode 100644 index 0000000..b4f4b55 --- /dev/null +++ b/test/swarm-chronicle.test.ts @@ -0,0 +1,193 @@ +import './helpers/isolated-home.ts' +import { strict as assert } from 'node:assert' +import { test } from 'node:test' +import { spawnSync } from 'node:child_process' +import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { fileURLToPath } from 'node:url' +import { openStore } from '../src/store/schema.ts' +import { workspaceIdFor } from '../src/planet.ts' +import { acquireLease, registerSwarmAgent, releaseLease, sendMessage } from '../src/coord/index.ts' +import { recordTurn, registerWorkerProfile } from '../src/coord/swarm-ops.ts' +import { reportQuota } from '../src/coord/resources.ts' +import { claimNextTask, deliverTask, enqueueTask, ensureQueueSchema } from '../src/queue.ts' +import { buildSwarmChronicle, formatSwarmChronicleMarkdown } from '../src/coord/chronicle.ts' +import { getSwarmNextActions } from '../src/coord/next-actions.ts' + +const WS = 'ws-swarm-chronicle' +const GB = 1024 ** 3 +const roomy = { + measuredAt: new Date().toISOString(), + drives: [{ root: 'C:\\', freeBytes: 120 * GB, totalBytes: 900 * GB }], + memory: { freeBytes: 12 * GB, totalBytes: 32 * GB }, + cpuBusyFraction: 0, +} +const CLI = fileURLToPath(new URL('../src/cli.ts', import.meta.url)) + +function fixture() { + const dir = mkdtempSync(join(tmpdir(), 'plugbrain-chronicle-')) + const db = openStore(join(dir, 'brain.db')) + db.prepare('INSERT INTO workspaces (id, name, root, created_at) VALUES (?, ?, ?, ?)') + .run(WS, 'chronicle-demo', join(dir, 'root'), new Date().toISOString()) + const worker = (id: string, account = 'owner:chatgpt') => { + registerSwarmAgent(db, { agentId: id, name: id, workspaceId: WS }) + return registerWorkerProfile(db, { agentId: id, surface: 'freebuff', account }) + } + return { dir, db, worker, cleanup: () => { db.close(); rmSync(dir, { recursive: true, force: true }) } } +} + +test('swarm chronicle orders stored queue, message, lease, and quota events and names missing turn history', () => { + const f = fixture() + try { + f.worker('worker-a') + registerSwarmAgent(f.db, { agentId: 'integrator', workspaceId: WS }) + const task = enqueueTask(f.db, WS, { title: 'Build chronicle', requestedBy: 'integrator' }) + const credentialLikeTitle = 'Imported record sk-' + 'x'.repeat(36) + enqueueTask(f.db, WS, { title: credentialLikeTitle, requestedBy: 'integrator' }) + assert.ok(claimNextTask(f.db, WS, 'worker-a')) + deliverTask(f.db, task.id, 'worker-a', 'closeout/chronicle.md', 'Finished the chronicle implementation') + sendMessage(f.db, { workspaceId: WS, fromAgent: 'integrator', toAgent: 'worker-a', subject: 'Review', body: 'Please review' }) + recordTurn(f.db, { workspaceId: WS, agentId: 'worker-a', phase: 'start' }) + const lease = acquireLease(f.db, WS, { agentId: 'worker-a', taskId: task.id, paths: ['src/a.ts'] }) + assert.equal(lease.acquired, true) + if (lease.lease) releaseLease(f.db, { agentId: 'worker-a', workspaceId: WS, leaseId: lease.lease.id }) + reportQuota(f.db, { account: 'owner:chatgpt', remaining: 10, unit: 'percent', reportedBy: 'integrator' }) + + const chronicle = buildSwarmChronicle(f.db, WS, { since: '2h' }) + for (const type of ['task.enqueued', 'task.claimed', 'task.delivered', 'message.sent', 'message.delivered', + 'lease.acquired', 'lease.released', 'quota.reported']) { + assert.ok(chronicle.events.some(event => event.type === type), `missing ${type}`) + } + assert.equal(chronicle.events.find(event => event.type === 'task.delivered')?.summary, 'Finished the chronicle implementation') + assert.ok(chronicle.events.some(event => event.type === 'task.enqueued' && event.title === 'Imported record [REDACTED]')) + assert.equal(JSON.stringify(chronicle).includes('sk-' + 'x'.repeat(36)), false) + assert.deepEqual(chronicle.events.map(event => Date.parse(event.at)), + [...chronicle.events.map(event => Date.parse(event.at))].sort((a, b) => a - b)) + assert.ok((chronicle.events.find(event => event.type === 'task.claimed')?.waitMs ?? -1) >= 0) + assert.ok(chronicle.missingSources.some(source => /turn/i.test(source))) + } finally { f.cleanup() } +}) + +test('swarm chronicle caps source reads and reports omitted history', () => { + const f = fixture() + try { + ensureQueueSchema(f.db) + const now = new Date().toISOString() + const insert = f.db.prepare(`INSERT INTO queue_tasks + (id, workspace_id, title, body, state, created_at, updated_at) + VALUES (?, ?, ?, '', 'pending', ?, ?)`) + for (let index = 0; index < 520; index += 1) { + insert.run(`task-cap-${index}`, WS, `Task ${index}`, now, now) + } + const chronicle = buildSwarmChronicle(f.db, WS, { since: '365d' }) + assert.ok(chronicle.events.length <= 1000) + assert.equal(chronicle.truncated, true) + assert.equal(chronicle.sourceRowCapReached, true) + assert.match(formatSwarmChronicleMarkdown(chronicle), /Weitere Ereignisse ausgelassen/) + } finally { f.cleanup() } +}) + +test('swarm next actions suggest review, offer, delivery, integrator inbox, disk, and quota work by urgency', () => { + const f = fixture() + try { + f.worker('review-waiter') + f.worker('idle-worker') + f.worker('stale-holder') + registerSwarmAgent(f.db, { agentId: 'integrator', workspaceId: WS }) + recordTurn(f.db, { workspaceId: WS, agentId: 'review-waiter', phase: 'end', state: 'awaiting-commit', summary: 'Ready' }) + recordTurn(f.db, { workspaceId: WS, agentId: 'idle-worker', phase: 'end', state: 'needs-task', summary: 'Free' }) + const held = enqueueTask(f.db, WS, { title: 'Finish implementation' }) + assert.ok(claimNextTask(f.db, WS, 'stale-holder')) + recordTurn(f.db, { workspaceId: WS, agentId: 'stale-holder', phase: 'end', state: 'needs-task', summary: 'Done' }) + enqueueTask(f.db, WS, { title: 'Next queued work' }) + sendMessage(f.db, { workspaceId: WS, fromAgent: 'review-waiter', toAgent: 'integrator', subject: 'Need review', body: 'Ready' }) + reportQuota(f.db, { account: 'owner:chatgpt', remaining: 0, unit: 'percent', reportedBy: 'integrator' }) + + const tightHost = { ...roomy, drives: [{ root: 'C:\\', freeBytes: 3 * GB, totalBytes: 900 * GB }] } + const actions = getSwarmNextActions(f.db, WS, { host: tightHost }) + const ids = actions.map(action => action.id) + for (const id of ['review:review-waiter', 'offer:idle-worker', `deliver:${held.id}`, 'inbox:integrator', 'admission:test', 'quota:owner:chatgpt']) { + assert.ok(ids.includes(id), `missing ${id}`) + } + assert.deepEqual(actions.map(action => action.priority), [...actions.map(action => action.priority)].sort((a, b) => a - b)) + assert.ok(actions.every(action => action.command.trim().length > 0 && action.reason.trim().length > 0)) + } finally { f.cleanup() } +}) + +test('swarm next actions omit rules whose conditions are absent', () => { + const f = fixture() + try { + f.worker('quiet-worker') + f.worker('reviewer') + f.worker('other-worker') + f.worker('holder') + f.worker('needs-no-match') + registerSwarmAgent(f.db, { agentId: 'integrator', workspaceId: WS }) + recordTurn(f.db, { workspaceId: WS, agentId: 'reviewer', phase: 'end', state: 'awaiting-commit' }) + enqueueTask(f.db, WS, { title: 'Review reviewer change', addressedTo: 'quiet-worker' }) + recordTurn(f.db, { workspaceId: WS, agentId: 'holder', phase: 'start' }) + const held = enqueueTask(f.db, WS, { title: 'Held task', addressedTo: 'holder' }) + assert.ok(claimNextTask(f.db, WS, 'holder')) + recordTurn(f.db, { workspaceId: WS, agentId: 'needs-no-match', phase: 'end', state: 'needs-task' }) + enqueueTask(f.db, WS, { title: 'Not for this worker', addressedTo: 'other-worker' }) + sendMessage(f.db, { workspaceId: WS, fromAgent: 'reviewer', toAgent: 'integrator', subject: 'Already handled', body: 'Read' }) + f.db.prepare('UPDATE inbox_messages SET read_at = ? WHERE to_agent = ?').run(new Date().toISOString(), 'integrator') + reportQuota(f.db, { account: 'owner:chatgpt', remaining: 50, unit: 'percent', reportedBy: 'integrator' }) + const actions = getSwarmNextActions(f.db, WS, { host: roomy }) + assert.deepEqual(actions, []) + assert.equal(f.db.prepare('SELECT state FROM queue_tasks WHERE id = ?').get(held.id)?.state, 'claimed') + } finally { f.cleanup() } +}) + +test('turn end delivers a claimed task only when --deliver is supplied', () => { + const home = mkdtempSync(join(tmpdir(), 'plugbrain-turn-deliver-')) + const root = join(home, 'root') + mkdirSync(root) + const workspace = workspaceIdFor(root) + const run = (...args: string[]) => { + const result = spawnSync(process.execPath, ['--experimental-strip-types', '--no-warnings', CLI, ...args], { + env: { ...process.env, PLUGBRAIN_HOME: home, PLUGBRAIN_NO_DAEMON: '1' }, + encoding: 'utf8', timeout: 60_000, + }) + return { code: result.status, out: result.stdout, err: result.stderr } + } + try { + assert.equal(run('register', root, 'swarm-deliver').code, 0) + for (const agent of ['worker-deliver', 'worker-keep']) { + const registration = run('swarm', 'register', agent, '--surface', 'freebuff', '--account', 'owner:chatgpt', '--workspace', workspace) + assert.equal(registration.code, 0, registration.err || registration.out) + } + const deliveredTask = run('swarm', 'enqueue', 'Delivered work', '--to', 'worker-deliver', '--workspace', workspace) + assert.equal(deliveredTask.code, 0, deliveredTask.err) + const keptTask = run('swarm', 'enqueue', 'Still claimed work', '--to', 'worker-keep', '--workspace', workspace) + assert.equal(keptTask.code, 0, keptTask.err) + assert.equal(run('swarm', 'turn', 'worker-deliver', 'start', '--claim', '--workspace', workspace).code, 0) + assert.equal(run('swarm', 'turn', 'worker-keep', 'start', '--claim', '--workspace', workspace).code, 0) + mkdirSync(join(root, 'closeout'), { recursive: true }) + writeFileSync(join(root, 'closeout', 'DELIVER.md'), 'Finished.\n') + const delivered = run('swarm', 'turn', 'worker-deliver', 'end', '--state', 'awaiting-commit', '--summary', 'Finished', '--deliver', 'closeout/DELIVER.md', '--workspace', workspace, '--json') + assert.equal(delivered.code, 0, delivered.err) + const kept = run('swarm', 'turn', 'worker-keep', 'end', '--state', 'needs-task', '--summary', 'No evidence yet', '--workspace', workspace, '--json') + assert.equal(kept.code, 0, kept.err) + const chronicle = run('swarm', 'chronik', '--since', '2h', '--workspace', workspace, '--json') + assert.equal(chronicle.code, 0, chronicle.err) + const events = (JSON.parse(chronicle.out) as { events: Array<{ type: string; title?: string; evidence?: string; summary?: string }> }).events + assert.ok(events.some(event => event.type === 'task.delivered' && event.evidence === 'closeout/DELIVER.md' && event.summary === 'Finished')) + assert.ok(!events.some(event => event.type === 'task.delivered' && event.title === 'Still claimed work')) + const next = run('swarm', 'board', '--next', '--workspace', workspace, '--json') + assert.equal(next.code, 0, next.err) + const actions = JSON.parse(next.out) as Array<{ id: string }> + const keptId = /eingereiht (task-[0-9a-f-]+)/.exec(keptTask.out)?.[1] + assert.ok(keptId) + assert.ok(actions.some(action => action.id === `deliver:${keptId}`)) + assert.ok(!actions.some(action => action.id === `deliver:${/eingereiht (task-[0-9a-f-]+)/.exec(deliveredTask.out)?.[1]}`)) + const board = run('swarm', 'board', '--workspace', workspace, '--json') + assert.equal(board.code, 0, board.err) + assert.ok(Array.isArray((JSON.parse(board.out) as { nextActions?: unknown }).nextActions)) + const markdown = run('swarm', 'chronik', '--since', '2h', '--workspace', workspace) + assert.equal(markdown.code, 0, markdown.err) + assert.match(markdown.out, /^# Swarm-Chronik/) + assert.match(markdown.out, /Turn-Verlauf erst ab Version mit Turn-Historie/) + } finally { rmSync(home, { recursive: true, force: true }) } +}) diff --git a/test/swarm-cli.test.ts b/test/swarm-cli.test.ts index b423544..11ba62d 100644 --- a/test/swarm-cli.test.ts +++ b/test/swarm-cli.test.ts @@ -4,12 +4,16 @@ */ import { strict as assert } from 'node:assert' import { test } from 'node:test' -import { spawnSync } from 'node:child_process' -import { mkdirSync, mkdtempSync, rmSync } from 'node:fs' +import { execFileSync, spawnSync } from 'node:child_process' +import { createHash } from 'node:crypto' +import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' import { fileURLToPath } from 'node:url' import { workspaceIdFor } from '../src/planet.ts' +import { openStore } from '../src/store/schema.ts' +import { AGENT_PROTOCOL_BLOCK } from '../src/setup/agent-protocol.ts' +import { deliverTaskWithEvidence } from '../src/coord/turn-delivery.ts' const CLI = fileURLToPath(new URL('../src/cli.ts', import.meta.url)) @@ -22,6 +26,7 @@ function brain() { env: { ...process.env, PLUGBRAIN_HOME: home, PLUGBRAIN_NO_DAEMON: '1' }, encoding: 'utf8', timeout: 60_000, + windowsHide: true, }) return { code: result.status, out: result.stdout, err: result.stderr } } @@ -43,13 +48,14 @@ test('a worker registers, gets pinged at its turn end, claims work and shows on assert.match(clash.err, /already carried by nv01/) assert.equal(b.run('swarm', 'enqueue', 'Lint', 'ratchet', '--workspace', b.ws).code, 0) - assert.equal(b.run('swarm', 'send', 'nv01', '--subject', 'Hallo', '--body', 'Bitte starten', '--workspace', b.ws).code, 0) + assert.equal(b.run('swarm', 'send', 'nv01', '--subject', 'Hallo', '--body', 'Bitte starten', '--from', 'cx03', '--workspace', b.ws).code, 0) const end = b.run('swarm', 'turn', 'nv01', 'end', '--state', 'needs-task', '--summary', 'R1 fertig', '--workspace', b.ws, '--json') assert.equal(end.code, 0, end.err) - const ping = JSON.parse(end.out) as { state: string; inbox: Array<{ id: string; subject: string }>; nextTask: { title: string } | null } + const ping = JSON.parse(end.out) as { state: string; inbox: Array<{ id: string; subject: string; fromAgent: string }>; nextTask: { title: string } | null } assert.equal(ping.state, 'needs-task') assert.deepEqual(ping.inbox.map(message => message.subject), ['Hallo']) + assert.equal(ping.inbox[0]?.fromAgent, 'cx03', 'the sender is the worker that wrote it, not the integrator') assert.equal(ping.nextTask?.title, 'Lint ratchet') const start = b.run('swarm', 'turn', 'nv01', 'start', '--claim', '--workspace', b.ws) @@ -67,6 +73,37 @@ test('a worker registers, gets pinged at its turn end, claims work and shows on } finally { b.cleanup() } }) +test('queue supersede, reassign and priority commands persist audit history and show on the board', () => { + const b = brain() + try { + for (const id of ['cx11', 'nv01']) { + assert.equal(b.run('swarm', 'register', id, '--surface', 'other', '--account', 'test', '--workspace', b.ws).code, 0) + } + const old = /eingereiht (task-[0-9a-f-]+)/.exec(b.run('swarm', 'enqueue', 'Old task', '--workspace', b.ws).out)?.[1] + const next = /eingereiht (task-[0-9a-f-]+)/.exec(b.run('swarm', 'enqueue', 'Replacement', '--workspace', b.ws).out)?.[1] + const routed = /eingereiht (task-[0-9a-f-]+)/.exec(b.run('swarm', 'enqueue', 'Routed', '--workspace', b.ws).out)?.[1] + assert.ok(old && next && routed) + + assert.equal(b.run('swarm', 'supersede', old, '--by', next, '--note', 'replaced brief', '--workspace', b.ws).code, 0) + assert.equal(b.run('swarm', 'reassign', routed, '--to', 'nv01', '--workspace', b.ws).code, 0) + assert.equal(b.run('swarm', 'priority', next, '10', '--workspace', b.ws).code, 0) + + const board = JSON.parse(b.run('swarm', 'board', '--workspace', b.ws, '--json').out) as { + queue: { tasks: Array<{ id: string; state: string; priority: number; addressedTo: string | null; supersededBy: string | null }>; recentChanges: Array<{ taskId: string; operation: string }> } + } + assert.equal(board.queue.tasks.find(task => task.id === old)?.state, 'superseded') + assert.equal(board.queue.tasks.find(task => task.id === old)?.supersededBy, next) + assert.equal(board.queue.tasks.find(task => task.id === routed)?.addressedTo, 'nv01') + assert.equal(board.queue.tasks.find(task => task.id === next)?.priority, 10) + assert.deepEqual(board.queue.recentChanges.map(change => change.operation), ['priority', 'reassign', 'supersede']) + + const claimed = JSON.parse(b.run('swarm', 'turn', 'cx11', 'start', '--claim', '--workspace', b.ws, '--json').out) as { + claimedTask: { id: string } | null + } + assert.equal(claimed.claimedTask?.id, next, 'priority controls order and superseded work is never offered') + } finally { b.cleanup() } +}) + test('a worker hands in its claimed task with the evidence path, and only the holder can', () => { const b = brain() try { @@ -76,12 +113,162 @@ test('a worker hands in its claimed task with the evidence path, and only the ho assert.equal(b.run('swarm', 'enqueue', 'M15: Overlays', '--to', 'wf-m15', '--workspace', b.ws).code, 0) const start = b.run('swarm', 'turn', 'wf-m15', 'start', '--claim', '--workspace', b.ws, '--json') const taskId = (JSON.parse(start.out) as { claimedTask: { id: string } }).claimedTask.id - const stranger = b.run('swarm', 'deliver', 'wf-m16', taskId, '--path', 'x/DONE.md', '--workspace', b.ws) + const evidenceDir = join(b.home, 'root', 'closeout', 'M15') + mkdirSync(evidenceDir, { recursive: true }) + const evidence = join(evidenceDir, 'DONE.md') + writeFileSync(evidence, 'Implementation complete.\n') + const stranger = b.run('swarm', 'deliver', 'wf-m16', taskId, '--path', evidence, '--workspace', b.ws) assert.equal(stranger.code, 3) assert.match(stranger.err, /held by wf-m15/) - const delivered = b.run('swarm', 'deliver', 'wf-m15', taskId, '--path', 'closeout/M15/DONE.md', '--workspace', b.ws) + const repo = join(b.home, 'repo') + mkdirSync(repo) + execFileSync('git', ['init', '-b', 'main', repo], { windowsHide: true }) + execFileSync('git', ['-C', repo, 'config', 'user.email', 'delivery@test.invalid'], { windowsHide: true }) + execFileSync('git', ['-C', repo, 'config', 'user.name', 'Delivery Test'], { windowsHide: true }) + writeFileSync(join(repo, 'source.txt'), 'source\n') + execFileSync('git', ['-C', repo, 'add', '.'], { windowsHide: true }) + execFileSync('git', ['-C', repo, 'commit', '-m', 'source revision'], { windowsHide: true }) + const sourceRevision = execFileSync('git', ['-C', repo, 'rev-parse', 'HEAD'], { encoding: 'utf8', windowsHide: true }).trim() + const delivered = b.run('swarm', 'deliver', 'wf-m15', taskId, '--path', evidence, '--repo', repo, '--workspace', b.ws) assert.equal(delivered.code, 0, delivered.err) - assert.match(delivered.out, /geliefert task-[0-9a-f-]+: M15: Overlays → closeout\/M15\/DONE\.md/) + assert.match(delivered.out, /geliefert task-[0-9a-f-]+: M15: Overlays/) + const db = openStore(join(b.home, 'plugbrain.db')) + try { + const receipt = db.prepare('SELECT task_id, delivered_by, delivery_attempt, source_revision, delivered_path, delivered_sha256 FROM queue_deliveries WHERE task_id = ?') + .get(taskId) as { task_id: string; delivered_by: string; delivery_attempt: number; source_revision: string; delivered_path: string; delivered_sha256: string } + assert.deepEqual({ ...receipt }, { + task_id: taskId, + delivered_by: 'wf-m15', + delivery_attempt: 1, + source_revision: sourceRevision, + delivered_path: 'closeout/M15/DONE.md', + delivered_sha256: createHash('sha256').update('Implementation complete.\n').digest('hex'), + }) + } finally { db.close() } + } finally { b.cleanup() } +}) + +test('delivery rejects missing, empty, outside-workspace, and incomplete review evidence', () => { + const b = brain() + try { + for (const [agent, title] of [['reviewer-missing', 'Missing artifact'], ['reviewer-review', 'R- Review result']] as const) { + assert.equal(b.run('swarm', 'register', agent, '--surface', 'other', '--account', 'test', '--workspace', b.ws).code, 0) + const enqueue = b.run('swarm', 'enqueue', title, '--to', agent, '--workspace', b.ws) + const taskId = /eingereiht (task-[0-9a-f-]+)/.exec(enqueue.out)?.[1] + assert.ok(taskId) + assert.equal(b.run('swarm', 'turn', agent, 'start', '--claim', '--workspace', b.ws).code, 0) + const missing = b.run('swarm', 'deliver', agent, taskId, '--path', 'missing.md', '--workspace', b.ws) + assert.equal(missing.code, 3) + assert.match(missing.err, /does not exist/i) + const emptyPath = join(b.home, 'root', 'empty.md') + writeFileSync(emptyPath, '') + const empty = b.run('swarm', 'deliver', agent, taskId, '--path', emptyPath, '--workspace', b.ws) + assert.equal(empty.code, 3) + assert.match(empty.err, /empty/i) + const outside = join(b.home, 'outside.md') + writeFileSync(outside, 'outside workspace') + const rejected = b.run('swarm', 'deliver', agent, taskId, '--path', outside, '--workspace', b.ws) + assert.equal(rejected.code, 3) + assert.match(rejected.err, /workspace/i) + if (title.startsWith('R-')) { + const review = join(b.home, 'root', 'review.md') + writeFileSync(review, `This review says it is not PASS.\nReviewed commit: \`${'a'.repeat(40)}\`\n`) + const incomplete = b.run('swarm', 'deliver', agent, taskId, '--path', review, '--workspace', b.ws) + assert.equal(incomplete.code, 3) + assert.match(incomplete.err, /review.*judgment.*commit hash/i) + writeFileSync(review, `Verdict: PASS\nReviewed commit: \`${'a'.repeat(40)}\`\n`) + const complete = b.run('swarm', 'deliver', agent, taskId, '--path', review, '--workspace', b.ws) + assert.equal(complete.code, 0, complete.err) + const db = openStore(join(b.home, 'plugbrain.db')) + try { + const receipt = db.prepare('SELECT review_judgment, reviewed_commit FROM queue_deliveries WHERE task_id = ?').get(taskId) as + { review_judgment: string; reviewed_commit: string } + assert.equal(receipt.review_judgment, 'PASS') + assert.equal(receipt.reviewed_commit, 'a'.repeat(40)) + } finally { db.close() } + } + } + } finally { b.cleanup() } +}) + +test('delivery cannot use a task from a different selected workspace', () => { + const b = brain() + const db = openStore(join(b.home, 'plugbrain.db')) + try { + db.prepare('INSERT INTO workspaces (id, name, root, created_at) VALUES (?, ?, ?, ?)') + .run('ws-other', 'Other workspace', join(b.home, 'other'), new Date().toISOString()) + const queued = b.run('swarm', 'enqueue', 'Scoped task', '--workspace', b.ws) + const taskId = /eingereiht (task-[0-9a-f-]+)/.exec(queued.out)?.[1] + assert.ok(taskId) + const evidence = join(b.home, 'root', 'evidence.md') + writeFileSync(evidence, 'evidence\n') + assert.throws(() => deliverTaskWithEvidence(db, taskId, 'some-agent', evidence, undefined, { + workspaceId: 'ws-other', workspaceRoot: join(b.home, 'other'), + }), /unknown task/i) + } finally { db.close(); b.cleanup() } +}) + +test('swarm delivery is documented in the generated agent block and CLI reference', () => { + const protocol = readFileSync(new URL('../docs/agent-protocol.md', import.meta.url), 'utf8') + const cliHelp = readFileSync(new URL('../docs/cli.md', import.meta.url), 'utf8') + const commandHelp = readFileSync(new URL('../src/cli.ts', import.meta.url), 'utf8') + for (const text of [protocol, AGENT_PROTOCOL_BLOCK]) { + assert.match(text, /plugbrain swarm deliver <id> <taskId> --path/) + assert.match(text, /needs-task.*or.*awaiting-commit/s) + } + assert.match(cliHelp, /swarm deliver <agent> <taskId> --path/) + assert.match(commandHelp, /swarm <register\|turn\|ack\|board\|chronik\|send\|enqueue\|deliver/) +}) + +test('waiting tasks, the reviewer pool and the watchdog cycle all work through the CLI', () => { + const b = brain() + try { + for (const [id, account, model] of [ + ['cx01', 'owner:chatgpt', 'gpt-6-luna'], + ['nv03', 'nvidia:key-03', 'claude-sonnet-4'], + ['nv04', 'nvidia:key-04', 'gemini-2.5-pro'], + ] as const) { + assert.equal(b.run('swarm', 'register', id, '--surface', 'freebuff', '--account', account, '--model', model, '--workspace', b.ws).code, 0) + } + + // A task with --after stays out of reach until its predecessor arrives. + const basis = b.run('swarm', 'enqueue', 'Basis', '--to', 'cx01', '--workspace', b.ws) + assert.equal(basis.code, 0, basis.err) + const basisId = /eingereiht (task-[0-9a-f-]+)/.exec(basis.out)?.[1] + assert.ok(basisId, basis.out) + assert.equal(b.run('swarm', 'enqueue', 'Nachlauf', '--to', 'nv03', '--after', basisId, '--workspace', b.ws).code, 0) + + const blocked = b.run('swarm', 'turn', 'nv03', 'start', '--claim', '--workspace', b.ws, '--json') + assert.equal(blocked.code, 0, blocked.err) + assert.equal((JSON.parse(blocked.out) as { claimedTask: unknown }).claimedTask, null) + + const claimed = b.run('swarm', 'turn', 'cx01', 'start', '--claim', '--workspace', b.ws, '--json') + const claimedTask = (JSON.parse(claimed.out) as { claimedTask: { id: string; title: string } }).claimedTask + assert.equal(claimedTask.title, 'Basis') + + // The reviewer pool routes a finished turn to another account. + assert.match(b.run('swarm', 'review-pool', 'set', 'nv03', 'nv04', '--workspace', b.ws).out, /Reviewer: nv03, nv04/) + assert.match(b.run('swarm', 'review-pool', 'auto', 'on', '--workspace', b.ws).out, /Review-Routing: an/) + assert.equal(b.run('swarm', 'turn', 'cx01', 'end', '--state', 'awaiting-commit', '--summary', 'fertig', '--workspace', b.ws).code, 0) + + const waiting = b.run('swarm', 'turn', 'nv03', 'start', '--claim', '--workspace', b.ws, '--json') + assert.equal((JSON.parse(waiting.out) as { claimedTask: { title: string } }).claimedTask.title, 'Nachlauf') + const review = b.run('swarm', 'turn', 'nv04', 'start', '--claim', '--workspace', b.ws, '--json') + assert.equal((JSON.parse(review.out) as { claimedTask: unknown }).claimedTask, null, + 'a review addressed to nv03 is not offered to nv04') + + // The workspace threshold is a setting, and the scan reads it. + assert.match(b.run('swarm', 'watchdog', 'silent-after', '30', '--workspace', b.ws).out, /ab 30 min/) + const scan = JSON.parse(b.run('swarm', 'watchdog', '--workspace', b.ws, '--json').out) as + { silentAfterMinutes: number; silent: unknown[]; alerted: unknown[] } + assert.equal(scan.silentAfterMinutes, 30) + assert.deepEqual(scan.silent, []) + assert.deepEqual(scan.alerted, []) + const show = JSON.parse(b.run('swarm', 'review-pool', 'show', '--workspace', b.ws, '--json').out) as + { reviewAuto: boolean; silentAfterMinutes: number; pool: Array<{ agentId: string }> } + assert.equal(show.reviewAuto, true) + assert.equal(show.silentAfterMinutes, 30) + assert.deepEqual(show.pool.map(entry => entry.agentId), ['nv03', 'nv04']) } finally { b.cleanup() } }) @@ -96,6 +283,12 @@ test('quotas, resources and admission are available to every worker', () => { assert.deepEqual(parsed.quotas.map(row => row.account), ['owner:chatgpt']) assert.deepEqual(parsed.admission.map(row => row.kind), ['edit', 'test', 'index', 'build', 'install', 'worktree']) assert.equal(b.run('swarm', 'admit', 'edit', '--workspace', b.ws).code, 0) + const setPool = b.run('swarm', 'quota-pool', 'set', 'nvidia:project-a', '--max-concurrent', '3', '--rpm', '20', '--workspace', b.ws) + assert.equal(setPool.code, 0, setPool.err) + assert.match(b.run('swarm', 'quota-pool', 'show', '--workspace', b.ws).out, /nvidia:project-a: concurrency=3, rpm=20/) + const register = b.run('swarm', 'register', 'nv01', '--surface', 'freebuff', '--account', 'nvidia:key-01', + '--quota-pool', 'nvidia:project-a', '--workspace', b.ws) + assert.equal(register.code, 0, register.err) const bad = b.run('swarm', 'quota', 'nvidia:key-02', '5', 'rpm', '--note', 'nvapi-' + 'x'.repeat(36), '--workspace', b.ws) assert.equal(bad.code, 3) assert.match(bad.err, /looks like a credential/) @@ -119,3 +312,25 @@ test('workers claim the paths they write, and a second writer is refused until r assert.equal(b.run('swarm', 'claim', 'o4-mission', 'packages/plug/swarm/src/client-lane-executor.ts', '--workspace', b.ws).code, 0) } finally { b.cleanup() } }) + +test('swarm reap defaults to a read-only plan and persists the automatic setting', () => { + const b = brain() + try { + const repo = join(b.home, 'reap-repo') + mkdirSync(repo) + execFileSync('git', ['init', '-b', 'main', repo], { windowsHide: true }) + execFileSync('git', ['-C', repo, 'config', 'user.email', 'cli-reaper@test.invalid'], { windowsHide: true }) + execFileSync('git', ['-C', repo, 'config', 'user.name', 'CLI Reaper Test'], { windowsHide: true }) + writeFileSync(join(repo, 'base.txt'), 'base\n') + execFileSync('git', ['-C', repo, 'add', '.'], { windowsHide: true }) + execFileSync('git', ['-C', repo, 'commit', '-m', 'base'], { windowsHide: true }) + const preview = b.run('swarm', 'reap', '--repo', repo, '--workspace', b.ws, '--json') + assert.equal(preview.code, 0, preview.err) + const result = JSON.parse(preview.out) as { dryRun: boolean; removed: unknown[]; candidates: Array<{ reason: string }> } + assert.equal(result.dryRun, true) + assert.equal(result.removed.length, 0) + assert.match(result.candidates[0]?.reason ?? '', /primary checkout/) + assert.match(b.run('swarm', 'reap', '--auto', 'on', '--workspace', b.ws).out, /reap.auto on/) + assert.match(b.run('swarm', 'reap', '--auto', 'off', '--workspace', b.ws).out, /reap.auto off/) + } finally { b.cleanup() } +}) diff --git a/test/swarm-ops.test.ts b/test/swarm-ops.test.ts index a7eadde..1ca16ba 100644 --- a/test/swarm-ops.test.ts +++ b/test/swarm-ops.test.ts @@ -260,7 +260,10 @@ test('the HTTP API carries the same contract: profile, turn, board, approval and assert.deepEqual(row?.attention, ['awaiting-commit']) assert.equal((await post('/api/agent/approve-commit', { agentId: 'agy-sonnet', by: 'integrator', workspaceId: WS })).status, 200) - const resources = await (await fetch(`${base}/api/resources`)).json() as { admission: unknown[] } + assert.equal((await fetch(`${base}/api/resources`)).status, 401) + const resources = await (await fetch(`${base}/api/resources`, { + headers: { Authorization: `Bearer ${authKey}` }, + })).json() as { admission: unknown[] } assert.equal(resources.admission.length, 6) } finally { await handle.close() diff --git a/test/swarm-runner.test.ts b/test/swarm-runner.test.ts new file mode 100644 index 0000000..dc66cd3 --- /dev/null +++ b/test/swarm-runner.test.ts @@ -0,0 +1,502 @@ +/** + * `swarm run` end to end: the Brain starts a CLI worker itself and watches it. + * + * The worker is `test/fixtures/fake-worker.mjs`, not Codex — starting Codex + * would spend the owner's quota and needs a login. Everything else is real: a + * detached OS process, a JSONL log on disk, a store in a temp home, and the + * board reading the run back. The fake even checks in and ends its turn through + * the same CLI a Codex tab would call, so the turn semantics under test are the + * ones the fleet actually has. + */ +// First, before anything that could open a store: this test process must never +// read the owner's live brain home. +import './helpers/isolated-home.ts' +import { strict as assert } from 'node:assert' +import { test } from 'node:test' +import { DatabaseSync } from 'node:sqlite' +import { spawnSync } from 'node:child_process' +import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { fileURLToPath } from 'node:url' +import { workspaceIdFor } from '../src/planet.ts' +import { buildRunnerArgv, discoverProtocolDocs, isOwnedProcess, renderRunnerPrompt, summarizeLogEvent, type RunnerProfile } from '../src/coord/runner.ts' +import { assertActiveSupervisorAttempt, classifySupervisorFailure } from '../src/coord/supervisor.ts' + +const CLI = fileURLToPath(new URL('../src/cli.ts', import.meta.url)) +const FAKE = fileURLToPath(new URL('./fixtures/fake-worker.mjs', import.meta.url)) +const nativeRunnerOnly = process.platform === 'win32' ? {} : { skip: 'native runner requires Windows process-tree identity' } + +interface Brain { + home: string + root: string + ws: string + run: (...args: string[]) => { code: number | null; out: string; err: string } + cleanup: () => void +} + +function brain(): Brain { + const home = mkdtempSync(join(tmpdir(), 'plugbrain-swarm-run-')) + const root = join(home, 'root') + // The workspace configuration the prompt is derived from: a coordination + // folder with the protocol, a lane's rules and the Brain's own wrapper. + mkdirSync(join(root, 'koordination', 'bin'), { recursive: true }) + writeFileSync(join(root, 'koordination', 'BRAIN-PROTOKOLL.md'), '# protocol\n') + writeFileSync(join(root, 'koordination', 'bin', 'plugbrain'), '#!/bin/sh\n') + // Two lanes on purpose: the newer one sorts EARLIER by name, so a comparison + // of full paths would pick the wrong rules. + mkdirSync(join(root, 'koordination', 'closeout', 'lane-new-20260101', 'briefs'), { recursive: true }) + writeFileSync(join(root, 'koordination', 'closeout', 'lane-new-20260101', 'briefs', '_REGELN.md'), '# rules of the new lane\n') + mkdirSync(join(root, 'koordination', 'closeout', 'zulu-old-20251231', 'briefs'), { recursive: true }) + writeFileSync(join(root, 'koordination', 'closeout', 'zulu-old-20251231', 'briefs', '_REGELN.md'), '# rules of the old lane\n') + const run = (...args: string[]) => { + const result = spawnSync(process.execPath, ['--experimental-strip-types', '--no-warnings', CLI, ...args], { + env: { ...process.env, PLUGBRAIN_HOME: home, PLUGBRAIN_NO_DAEMON: '1' }, + encoding: 'utf8', + timeout: 120_000, + }) + return { code: result.status, out: result.stdout ?? '', err: result.stderr ?? '' } + } + assert.equal(run('register', root, 'swarm-runner').code, 0) + return { home, root, ws: workspaceIdFor(root), run, cleanup: () => rmSync(home, { recursive: true, force: true }) } +} + +/** The `--args` template that points the fake worker at the real store. */ +const fakeArgs = (b: Brain, mode: string): string => + `${FAKE} --mode ${mode} --agent fake-01 --workspace ${b.ws} --cli ${CLI} ` + + `--prompt-file {promptFile} --last {last}` + +interface RunJson { + pid: number | null + runId: string + alive: boolean + logPath: string + lastDocPath: string | null + promptPath: string | null + commandText: string + endedAt: string | null + endedReason: string | null + stoppedAt: string | null + lastEventKind: string | null + lastEventText: string | null +} + +interface StatusJson { run: RunJson | null; turnState: string | null; tail: string[] } + +const delay = (ms: number) => new Promise<void>(resolve => { setTimeout(resolve, ms) }) + +/** Poll `--status` (which settles dead runs) until the run has an end. */ +async function settle(b: Brain, agent: string, ms = 90_000): Promise<StatusJson> { + const deadline = Date.now() + ms + for (;;) { + const status = b.run('swarm', 'run', agent, '--status', '--workspace', b.ws, '--json') + assert.equal(status.code, 0, status.err) + const parsed = JSON.parse(status.out) as StatusJson + if (parsed.run !== null && parsed.run.endedAt !== null) return parsed + if (Date.now() > deadline) assert.fail(`run never settled: ${status.out}`) + await delay(200) + } +} + +interface BoardJson { + agents: Array<{ id: string; turnState: string; turnSummary: string | null; unread: number; runner: { pid: number; alive: boolean; lastEventKind: string } | null }> + settled: Array<{ agentId: string; reason: string; messageId: string | null; summary: string }> +} + +/** + * Poll the board itself until it has booked the worker. + * + * Deliberately not `--status`: the board is the path under test here, and it is + * the call that has to notice a process that never came back. + */ +async function settleBoard(b: Brain, agent: string, ms = 90_000): Promise<BoardJson> { + const deadline = Date.now() + ms + for (;;) { + const board = b.run('swarm', 'board', '--workspace', b.ws, '--json') + assert.equal(board.code, 0, board.err) + const parsed = JSON.parse(board.out) as BoardJson + if (parsed.agents.find(row => row.id === agent)?.turnState === 'blocked') return parsed + if (Date.now() > deadline) assert.fail(`the board never booked the dead run: ${board.out}`) + await delay(200) + } +} + +test('a runner profile is stored and read back, and a bad sandbox is refused', () => { + const b = brain() + try { + assert.equal(b.run('swarm', 'register', 'cx01', '--surface', 'other', '--account', 'owner:chatgpt', '--workspace', b.ws).code, 0) + const set = b.run('swarm', 'runner', 'set', 'cx01', '--cmd', 'codex', '--model', 'gpt-6-luna', + '--effort', 'high', '--sandbox', 'bypass', '--search', '--workspace', b.ws, '--json') + assert.equal(set.code, 0, set.err) + const profile = JSON.parse(set.out) as RunnerProfile + assert.equal(profile.cmd, 'codex') + assert.equal(profile.model, 'gpt-6-luna') + assert.equal(profile.effort, 'high') + assert.equal(profile.sandbox, 'bypass') + assert.equal(profile.search, true) + + const show = b.run('swarm', 'runner', 'show', 'cx01', '--workspace', b.ws, '--json') + assert.equal(show.code, 0, show.err) + assert.deepEqual(JSON.parse(show.out), profile) + + const bad = b.run('swarm', 'runner', 'set', 'cx01', '--cmd', 'codex', '--sandbox', 'yolo', '--workspace', b.ws) + assert.equal(bad.code, 3) + assert.match(bad.err, /unknown sandbox: yolo/) + + const unknown = b.run('swarm', 'runner', 'show', 'nobody', '--workspace', b.ws) + assert.equal(unknown.code, 3) + assert.match(unknown.err, /no runner profile for nobody/) + } finally { b.cleanup() } +}) + +test('runner profiles reject credential-like args, unknown commands, and cwd outside the workspace', () => { + const b = brain() + try { + assert.equal(b.run('swarm', 'register', 'cx01', '--surface', 'other', '--account', 'owner:chatgpt', '--workspace', b.ws).code, 0) + const credential = 'ghp_' + 'x'.repeat(36) + const secret = b.run('swarm', 'runner', 'set', 'cx01', '--cmd', 'node', '--args', `--token ${credential}`, '--workspace', b.ws) + assert.notEqual(secret.code, 0) + assert.doesNotMatch(secret.err + secret.out, new RegExp(credential)) + + const badCommand = b.run('swarm', 'runner', 'set', 'cx01', '--cmd', 'powershell.exe', '--workspace', b.ws) + assert.notEqual(badCommand.code, 0) + assert.match(badCommand.err, /not an allowed worker command/) + const pathCommand = b.run('swarm', 'runner', 'set', 'cx01', '--cmd', 'C:\\Temp\\codex.exe', '--workspace', b.ws) + assert.notEqual(pathCommand.code, 0) + assert.match(pathCommand.err, /known executable name from PATH/) + + const outsidePath = join(b.home, 'outside') + mkdirSync(outsidePath) + const outside = b.run('swarm', 'runner', 'set', 'cx01', '--cmd', 'node', '--cwd', outsidePath, '--workspace', b.ws) + assert.notEqual(outside.code, 0) + assert.match(outside.err, /outside registered workspace/i) + } finally { b.cleanup() } +}) + +test('swarm run starts the worker detached, keeps its log, and the board shows pid and last event', nativeRunnerOnly, async () => { + const b = brain() + try { + assert.equal(b.run('swarm', 'register', 'fake-01', '--surface', 'other', '--account', 'test:fake', '--workspace', b.ws).code, 0) + const set = b.run('swarm', 'runner', 'set', 'fake-01', '--cmd', 'node', '--args', fakeArgs(b, 'ok'), '--workspace', b.ws) + assert.equal(set.code, 0, set.err) + + const started = b.run('swarm', 'run', 'fake-01', '--once', '--workspace', b.ws, '--json') + assert.equal(started.code, 0, started.err) + const run = JSON.parse(started.out) as RunJson + assert.ok(Number.isInteger(run.pid) && (run.pid ?? 0) > 0, `expected a pid, got ${started.out}`) + assert.match(run.logPath, /runs[\\/]workers[\\/]fake-01-\d{8}-\d{9}\.jsonl$/) + assert.ok(run.commandText.includes(FAKE)) + + const status = await settle(b, 'fake-01') + assert.equal(status.run?.alive, false) + // The worker ended its own turn, so the end of the process is not a failure. + assert.equal(status.run?.endedReason, 'turn-end') + assert.equal(status.turnState, 'needs-task') + assert.ok(existsSync(status.run!.logPath), 'the log file must exist') + const log = readFileSync(status.run!.logPath, 'utf8') + assert.match(log, /"turn\.completed"/) + assert.match(log, /"command_execution"/) + + const lastDoc = status.run!.lastDocPath! + assert.match(readFileSync(lastDoc, 'utf8'), /fake worker finished its turn/) + + // The prompt is derived from the workspace configuration, not hard-coded. + const prompt = readFileSync(status.run!.promptPath!, 'utf8') + assert.ok(!prompt.includes('{{'), 'every placeholder must be filled') + assert.match(prompt, /fake-01/) + assert.match(prompt, /BRAIN-PROTOKOLL\.md/) + assert.match(prompt, /_REGELN\.md/) + assert.match(prompt, /koordination[\\/]bin[\\/]plugbrain/) + + const board = JSON.parse(b.run('swarm', 'board', '--workspace', b.ws, '--json').out) as { + agents: Array<{ id: string; turnState: string; runner: { pid: number; alive: boolean; lastEventKind: string; lastEventText: string } | null }> + settled: unknown[] + } + const row = board.agents.find(agent => agent.id === 'fake-01') + assert.equal(row?.turnState, 'needs-task') + assert.ok((row?.runner?.pid ?? 0) > 0) + assert.equal(row?.runner?.alive, false) + assert.equal(row?.runner?.lastEventKind, 'turn.completed') + assert.match(row?.runner?.lastEventText ?? '', /DONE usage/) + assert.deepEqual(board.settled, []) + } finally { b.cleanup() } +}) + +test('a process that dies mid-turn is booked blocked, with the log tail and a message to the integrator', nativeRunnerOnly, async () => { + const b = brain() + try { + assert.equal(b.run('swarm', 'register', 'fake-01', '--surface', 'other', '--account', 'test:fake', '--workspace', b.ws).code, 0) + assert.equal(b.run('swarm', 'runner', 'set', 'fake-01', '--cmd', 'node', '--args', fakeArgs(b, 'die'), '--workspace', b.ws).code, 0) + const started = b.run('swarm', 'run', 'fake-01', '--once', '--workspace', b.ws) + assert.equal(started.code, 0, started.err) + + const board = await settleBoard(b, 'fake-01') + const status = JSON.parse(b.run('swarm', 'run', 'fake-01', '--status', '--workspace', b.ws, '--json').out) as StatusJson + assert.equal(status.run?.alive, false) + assert.equal(status.run?.endedReason, 'process-gone') + assert.equal(status.turnState, 'blocked') + assert.equal(board.settled.length, 1) + assert.equal(board.settled[0]!.agentId, 'fake-01') + assert.equal(board.settled[0]!.reason, 'ended-without-turn-end') + assert.match(board.settled[0]!.messageId ?? '', /^msg-/) + assert.match(board.settled[0]!.summary, /without a turn end/) + + const worker = board.agents.find(agent => agent.id === 'fake-01') + assert.equal(worker?.turnState, 'blocked') + assert.match(worker?.turnSummary ?? '', /about to die without a turn end/) + + // The integrator was registered on demand and holds the report unread. + const integrator = board.agents.find(agent => agent.id === 'integrator') + assert.equal(integrator?.unread, 1) + } finally { b.cleanup() } +}) + +test('blocked booking, run latch, and notification reconcile atomically and retry exactly once', nativeRunnerOnly, async () => { + const b = brain() + let db: DatabaseSync | null = null + try { + assert.equal(b.run('swarm', 'register', 'fake-01', '--surface', 'other', '--account', 'test:fake', '--workspace', b.ws).code, 0) + assert.equal(b.run('swarm', 'runner', 'set', 'fake-01', '--cmd', 'node', '--args', fakeArgs(b, 'die'), '--workspace', b.ws).code, 0) + const started = b.run('swarm', 'run', 'fake-01', '--once', '--workspace', b.ws) + assert.equal(started.code, 0, started.err) + db = new DatabaseSync(join(b.home, 'plugbrain.db')) + const run = db.prepare("SELECT pid, process_started_at FROM worker_runs WHERE agent_id = 'fake-01'") + .get() as { pid: number; process_started_at: string } + const exitDeadline = Date.now() + 15_000 + while (isOwnedProcess(run.pid, run.process_started_at)) { + if (Date.now() >= exitDeadline) assert.fail('fake worker did not exit before blocked booking') + await delay(50) + } + db.exec(`CREATE TRIGGER fail_blocked_booking BEFORE UPDATE OF turn_state ON agents + WHEN NEW.turn_state = 'blocked' BEGIN SELECT RAISE(ABORT, 'simulated crash before blocked booking'); END`) + const failed = b.run('swarm', 'board', '--workspace', b.ws, '--json') + assert.notEqual(failed.code, 0) + const afterFailure = db.prepare("SELECT ended_at, blocked_at FROM worker_runs WHERE agent_id = 'fake-01'").get() as { ended_at: string | null; blocked_at: string | null } + assert.equal(afterFailure.ended_at, null, 'the run latch rolls back with the failed turn update') + assert.equal(afterFailure.blocked_at, null) + assert.equal(Number((db.prepare("SELECT COUNT(*) AS n FROM inbox_messages WHERE to_agent = 'integrator'").get() as { n: number }).n), 0) + db.exec('DROP TRIGGER fail_blocked_booking') + db.close() + db = null + + const settled = await settleBoard(b, 'fake-01') + assert.equal(settled.settled.length, 1) + const verify = new DatabaseSync(join(b.home, 'plugbrain.db')) + try { + assert.equal(Number((verify.prepare("SELECT COUNT(*) AS n FROM inbox_messages WHERE to_agent = 'integrator'").get() as { n: number }).n), 1) + assert.equal(Number((verify.prepare("SELECT COUNT(*) AS n FROM worker_runs WHERE agent_id = 'fake-01' AND blocked_at IS NOT NULL").get() as { n: number }).n), 1) + } finally { verify.close() } + } finally { db?.close(); b.cleanup() } +}) + +test('swarm run --stop ends a hanging worker and pauses its turn', nativeRunnerOnly, async () => { + const b = brain() + try { + assert.equal(b.run('swarm', 'register', 'fake-01', '--surface', 'other', '--account', 'test:fake', '--workspace', b.ws).code, 0) + assert.equal(b.run('swarm', 'runner', 'set', 'fake-01', '--cmd', 'node', '--args', fakeArgs(b, 'hang'), '--workspace', b.ws).code, 0) + const started = b.run('swarm', 'run', 'fake-01', '--once', '--workspace', b.ws, '--json') + assert.equal(started.code, 0, started.err) + const run = JSON.parse(started.out) as RunJson + + // Give the process a moment to write its first event, then stop it. + const deadline = Date.now() + 30_000 + while (Date.now() < deadline && !existsSync(run.logPath)) await delay(100) + const stopped = b.run('swarm', 'run', 'fake-01', '--stop', '--workspace', b.ws) + assert.equal(stopped.code, 0, stopped.err) + assert.match(stopped.out, /stopped fake-01 \(pid \d+\); turn paused/) + + const status = JSON.parse(b.run('swarm', 'run', 'fake-01', '--status', '--workspace', b.ws, '--json').out) as StatusJson + assert.equal(status.run?.alive, false) + assert.equal(status.run?.stoppedAt !== null, true) + assert.equal(status.run?.endedReason, 'stopped') + assert.equal(status.turnState, 'paused') + // A deliberately stopped worker is not a failure: no blocked booking. + assert.ok(!readFileSync(status.run!.logPath, 'utf8').includes('spawn_error')) + } finally { b.cleanup() } +}) + +test('swarm run --stop kills only the verified worker process tree', nativeRunnerOnly, async () => { + const b = brain() + try { + assert.equal(b.run('swarm', 'register', 'fake-01', '--surface', 'other', '--account', 'test:fake', '--workspace', b.ws).code, 0) + assert.equal(b.run('swarm', 'runner', 'set', 'fake-01', '--cmd', 'node', '--args', fakeArgs(b, 'tree'), '--workspace', b.ws).code, 0) + const started = b.run('swarm', 'run', 'fake-01', '--once', '--workspace', b.ws, '--json') + assert.equal(started.code, 0, started.err) + const run = JSON.parse(started.out) as RunJson + const deadline = Date.now() + 10_000 + let childPid = 0 + while (Date.now() < deadline) { + try { childPid = Number(readFileSync(run.lastDocPath!, 'utf8')); if (childPid > 0) break } catch { /* child not started yet */ } + await delay(50) + } + assert.ok(childPid > 0, 'fake worker must start its child') + const stopped = b.run('swarm', 'run', 'fake-01', '--stop', '--workspace', b.ws) + assert.equal(stopped.code, 0, stopped.err) + assert.throws(() => process.kill(childPid, 0), 'the descendant must not survive the stop') + } finally { b.cleanup() } +}) + +test('swarm run refuses a worker without a profile and refuses a second live run', nativeRunnerOnly, async () => { + const b = brain() + try { + assert.equal(b.run('swarm', 'register', 'fake-01', '--surface', 'other', '--account', 'test:fake', '--workspace', b.ws).code, 0) + const missing = b.run('swarm', 'run', 'fake-01', '--once', '--workspace', b.ws) + assert.equal(missing.code, 3) + assert.match(missing.err, /no runner profile for fake-01/) + + assert.equal(b.run('swarm', 'runner', 'set', 'fake-01', '--cmd', 'node', '--args', fakeArgs(b, 'hang'), '--workspace', b.ws).code, 0) + assert.equal(b.run('swarm', 'run', 'fake-01', '--once', '--workspace', b.ws).code, 0) + const second = b.run('swarm', 'run', 'fake-01', '--once', '--workspace', b.ws) + assert.equal(second.code, 3) + assert.match(second.err, /already has a live run/) + assert.equal(b.run('swarm', 'run', 'fake-01', '--stop', '--workspace', b.ws).code, 0) + + // A missing program is a refusal, not a run that dies silently. + const disallowed = b.run('swarm', 'runner', 'set', 'fake-01', '--cmd', 'no-such-program-xyz', '--workspace', b.ws) + assert.equal(disallowed.code, 3) + assert.match(disallowed.err, /not an allowed worker command/) + } finally { b.cleanup() } +}) + +test('the Codex command line and the log summarizer are what they claim to be', () => { + const codex: RunnerProfile = { + agentId: 'cx01', cmd: 'codex', args: [], model: 'gpt-6-luna', effort: 'high', sandbox: 'bypass', + search: true, cwd: null, updatedAt: '2026-09-26T00:00:00.000Z', + } + const context = { prompt: 'DO IT', promptPath: 'p.md', logPath: 'l.jsonl', lastDocPath: 'last.md', cwd: 'C:/w' } + const plan = buildRunnerArgv(codex, context) + assert.deepEqual(plan.args, [ + 'exec', '-m', 'gpt-6-luna', '-c', 'model_reasoning_effort=high', + '--dangerously-bypass-approvals-and-sandbox', '-c', 'tools.web_search=true', + '--json', '-o', 'last.md', 'DO IT', + ]) + assert.equal(plan.viaComspec, false) + assert.ok(plan.commandText.includes('p.md'), 'the prompt is shown as its file, never as its text') + + assert.throws(() => buildRunnerArgv({ ...codex, cmd: 'C:\\worker.cmd' }, context), /batch shims are not supported/i) + assert.equal(isOwnedProcess(process.pid, '2000-01-01T00:00:00.000Z'), false, 'a reused pid with a different start time is not owned') + + const sandboxed = buildRunnerArgv({ ...codex, sandbox: 'workspace-write', search: false }, context) + assert.deepEqual(sandboxed.args, [ + 'exec', '-m', 'gpt-6-luna', '-c', 'model_reasoning_effort=high', '--sandbox', 'workspace-write', + '--json', '-o', 'last.md', 'DO IT', + ]) + + // A template token that resolves to nothing is dropped, so an unset model + // cannot leave a flag without its value behind. + const template = buildRunnerArgv( + { ...codex, cmd: 'node', args: ['worker.mjs', '--last={last}', '--prompt', '{prompt}', '{model}'], model: null }, + context, + ) + assert.deepEqual(template.args, ['worker.mjs', '--last=last.md', '--prompt', 'DO IT']) + + assert.deepEqual(summarizeLogEvent('{"type":"item.completed","item":{"type":"command_execution","command":"npm test","exit_code":0}}'), + { at: null, kind: 'command_execution', text: 'CMD npm test -> exit 0' }) + assert.deepEqual(summarizeLogEvent('{"type":"turn.completed","timestamp":"2026-09-26T10:00:00Z","usage":{"input_tokens":1}}'), + { at: '2026-09-26T10:00:00Z', kind: 'turn.completed', text: 'DONE usage {"input_tokens":1}' }) + assert.deepEqual(summarizeLogEvent('not json at all'), { at: null, kind: 'raw', text: 'not json at all' }) + assert.equal(summarizeLogEvent(' '), null) +}) + +test('the prompt names the protocol documents the workspace actually has', () => { + const b = brain() + try { + const docs = discoverProtocolDocs(b.root) + assert.match(docs.rulesPath ?? '', /lane-new-20260101/, 'the newest lane wins, not the last name') + const prompt = renderRunnerPrompt({ agentId: 'x1', workspaceRoot: b.root, docs }) + assert.ok(!prompt.includes('{{')) + assert.match(prompt, /worker x1/) + assert.match(prompt, /BRAIN-PROTOKOLL\.md/) + assert.match(prompt, /lane-new-20260101[\\/]briefs[\\/]_REGELN\.md/) + assert.ok(!prompt.includes('zulu-old-20251231')) + + const bare = renderRunnerPrompt({ + agentId: 'x2', workspaceRoot: b.root, + docs: { coordinationDir: null, protocolPath: null, rulesPath: null }, + }) + assert.ok(!bare.includes('{{')) + assert.match(bare, /Read in full: the brief named by your task\./) + } finally { b.cleanup() } +}) + +test('swarm run supervisor claims the next task, runs a fake worker and stops for a lead decision after the retry limit', nativeRunnerOnly, async () => { + const b = brain() + let db: DatabaseSync | null = null + let supervisorPid: number | null = null + try { + assert.equal(b.run('swarm', 'register', 'fake-01', '--surface', 'other', '--account', 'test:fake', '--workspace', b.ws).code, 0) + assert.equal(b.run('swarm', 'enqueue', 'supervised task', '--to', 'fake-01', '--workspace', b.ws).code, 0) + assert.equal(b.run('swarm', 'runner', 'set', 'fake-01', '--cmd', 'node', '--args', fakeArgs(b, 'die'), '--workspace', b.ws).code, 0) + const launched = b.run('swarm', 'run', 'fake-01', '--workspace', b.ws, + '--max-attempts', '2', '--max-idle-checks', '1', '--idle-ms', '50', '--poll-ms', '50', '--json') + assert.equal(launched.code, 0, launched.err) + const supervisor = JSON.parse(launched.out) as { pid: number; supervisorId: string } + supervisorPid = supervisor.pid + assert.ok(supervisor.pid > 0) + + const deadline = Date.now() + 30_000 + let state: { active: number; last_failure: string | null } | undefined + while (Date.now() < deadline) { + db ??= new DatabaseSync(join(b.home, 'plugbrain.db')) + state = db.prepare("SELECT active, last_failure FROM worker_supervisors WHERE agent_id = 'fake-01'").get() as + { active: number; last_failure: string | null } | undefined + if (state?.active === 0) break + await delay(100) + } + assert.equal(state?.active, 0, 'supervisor stops after a bounded unsuccessful attempt') + assert.equal(state?.last_failure, 'crash') + const attempts = db!.prepare("SELECT attempt, attempt_token, outcome, ended_at FROM worker_task_attempts WHERE agent_id = 'fake-01' ORDER BY attempt") + .all() as Array<{ attempt: number; attempt_token: string; outcome: string; ended_at: string | null }> + assert.equal(attempts.length, 2, 'the crash is retried once, then reaches the configured bound') + assert.equal(attempts[0]?.outcome, 'crash') + assert.equal(attempts[0]?.ended_at !== null, true) + assert.equal(attempts[1]?.outcome, 'crash') + const reservations = db!.prepare('SELECT pool_id, attempt_id, settled_at, usage, outcome FROM quota_reservations ORDER BY reserved_at') + .all() as Array<{ pool_id: string; attempt_id: string; settled_at: string | null; usage: number | null; outcome: string | null }> + assert.equal(reservations.length, 2, 'every launched attempt reserves one shared account-pool slot') + assert.deepEqual(reservations.map(row => row.pool_id), ['test:fake', 'test:fake']) + assert.ok(reservations.every(row => row.settled_at !== null && row.usage === null && row.outcome === 'crash')) + const promptPath = (db!.prepare("SELECT prompt_path FROM worker_runs WHERE agent_id = 'fake-01'").get() as { prompt_path: string }).prompt_path + const assignedPrompt = readFileSync(promptPath, 'utf8') + assert.match(assignedPrompt, /Assigned queue task \(already atomically claimed\)/) + assert.match(assignedPrompt, /supervised task/) + assert.doesNotMatch(assignedPrompt, /swarm turn fake-01 start --claim/) + const task = db!.prepare("SELECT state, claimed_by FROM queue_tasks WHERE title = 'supervised task'").get() as + { state: string; claimed_by: string | null } | undefined + assert.equal(task?.state, 'pending') + assert.equal(task?.claimed_by, null) + const leadMessage = db!.prepare("SELECT COUNT(*) AS n FROM inbox_messages WHERE to_agent = 'integrator'").get() as { n: number } + assert.equal(Number(leadMessage.n), 1) + const oldToken = process.env.PLUGBRAIN_ATTEMPT_TOKEN + const oldAgent = process.env.PLUGBRAIN_SUPERVISED_AGENT + try { + process.env.PLUGBRAIN_ATTEMPT_TOKEN = attempts[0]!.attempt_token + process.env.PLUGBRAIN_SUPERVISED_AGENT = 'fake-01' + assert.throws(() => assertActiveSupervisorAttempt(db!), /stale supervisor attempt is fenced/) + } finally { + if (oldToken === undefined) delete process.env.PLUGBRAIN_ATTEMPT_TOKEN + else process.env.PLUGBRAIN_ATTEMPT_TOKEN = oldToken + if (oldAgent === undefined) delete process.env.PLUGBRAIN_SUPERVISED_AGENT + else process.env.PLUGBRAIN_SUPERVISED_AGENT = oldAgent + } + assert.equal(classifySupervisorFailure({ exitCode: 1, output: 'HTTP 429: rate limit; Retry-After: 2' }), 'quota') + assert.equal(classifySupervisorFailure({ exitCode: 1, output: 'authentication failed' }), 'auth') + assert.equal(classifySupervisorFailure({ exitCode: 3, output: 'worker exited' }), 'crash') + const exitDeadline = Date.now() + 10_000 + for (;;) { + try { process.kill(supervisor.pid, 0) } catch { break } + if (Date.now() >= exitDeadline) assert.fail(`supervisor process ${supervisor.pid} did not exit`) + await delay(50) + } + supervisorPid = null + } finally { + if (supervisorPid !== null) { + const exitDeadline = Date.now() + 10_000 + while (Date.now() < exitDeadline) { + try { process.kill(supervisorPid, 0); await delay(50) } catch { break } + } + } + db?.close() + b.cleanup() + } +}) diff --git a/test/swarm-watch.test.ts b/test/swarm-watch.test.ts new file mode 100644 index 0000000..e705950 --- /dev/null +++ b/test/swarm-watch.test.ts @@ -0,0 +1,199 @@ +import assert from 'node:assert/strict' +import test from 'node:test' +import { createServer } from 'node:http' +import type { AddressInfo } from 'node:net' +import { openStore } from '../src/store/schema.ts' +import { CoordEventBus, coordEvents } from '../src/coord/events.ts' +import { registerSwarmAgent } from '../src/coord/registry.ts' +import { registerWorkerProfile, recordTurn } from '../src/coord/swarm-ops.ts' +import { formatWatchResult, runSwarmWatch, sseSubscribe, type WatchEvent } from '../src/swarm-watch.ts' + +const event = (type: string, data: unknown): WatchEvent => ({ type, data, timestamp: '2026-09-26T20:00:00.000Z' }) + +function subscribeAfter(...events: WatchEvent[]) { + return (listener: (value: WatchEvent) => void) => { + const timer = setTimeout(() => events.forEach(listener), 1) + return () => clearTimeout(timer) + } +} + +test('swarm watch wakes for turn, targeted message, queued task, and still readings', async () => { + const examples = [ + event('agent.turn', { agentId: 'a1', previousState: 'working', state: 'blocked', summary: 'stuck' }), + event('message.sent', { toAgent: 'a1', subject: 'hello' }), + event('task.enqueued', { taskId: 'task-1', title: 'review', addressedTo: 'a1' }), + event('watchdog.silent', { agentId: 'a1', minutes: 46 }), + ] + for (const value of examples) { + let result: WatchEvent[] = [] + const output = await runSwarmWatch({ + forAgent: 'a1', timeoutMs: 200, batchMs: 2, pollMs: 100, + subscribe: subscribeAfter(value), emit: events => { result = events }, + sampleAdmission: () => ({ test: true, build: true, worktree: true }), + snapshotFiles: async () => new Map(), + }) + assert.equal(output.code, 0, value.type) + assert.deepEqual(result.map(row => row.type), [value.type]) + } +}) + +test('swarm watch filters unchanged and unobserved turns', async () => { + const events = [ + event('agent.turn', { agentId: 'a1', previousState: 'working', state: 'working' }), + event('agent.turn', { agentId: 'a2', previousState: 'working', state: 'needs-task' }), + ] + const result = await runSwarmWatch({ + forAgent: 'a1', timeoutMs: 12, pollMs: 100, + subscribe: subscribeAfter(...events), emit: () => undefined, + sampleAdmission: () => ({ test: true, build: true, worktree: true }), + snapshotFiles: async () => new Map(), + }) + assert.equal(result.code, 3) + assert.deepEqual(result.events, []) +}) + +test('swarm watch reports new and changed Markdown paths', async () => { + let scans = 0 + const result = await runSwarmWatch({ + dirs: ['ignored-by-injected-scanner'], timeoutMs: 300, pollMs: 5, batchMs: 25, + subscribe: () => () => undefined, + snapshotFiles: async () => ++scans === 1 ? new Map([['a.md', 'one']]) : new Map([['a.md', 'two'], ['b.md', 'one']]), + sampleAdmission: () => ({ test: true, build: true, worktree: true }), + }) + assert.deepEqual(result.events.filter(row => row.type === 'file.changed').map(row => (row.data as { path: string }).path), ['a.md', 'b.md']) +}) + +test('swarm watch reports admission changes and batches arrivals for one five-second window', async () => { + let samples = 0 + let emitted = 0 + const result = await runSwarmWatch({ + timeoutMs: 300, pollMs: 5, batchMs: 25, + subscribe: listener => { + setTimeout(() => listener(event('agent.turn', { agentId: 'a1', previousState: 'working', state: 'needs-task' })), 1) + setTimeout(() => listener(event('agent.turn', { agentId: 'a2', previousState: 'working', state: 'blocked' })), 3) + return () => undefined + }, + sampleAdmission: () => ({ test: ++samples === 1, build: true, worktree: true }), + snapshotFiles: async () => new Map(), + emit: () => { emitted += 1 }, + }) + assert.equal(emitted, 1) + assert.deepEqual(result.events.map(row => row.type), ['agent.turn', 'agent.turn', 'admission.changed']) + assert.equal((result.events[2]!.data as { kind: string }).kind, 'test') +}) + +test('swarm watch receives turn transitions emitted from a test Brain store', async () => { + const db = openStore(':memory:') + const workspaceId = 'ws-swarm-watch-test' + db.prepare('INSERT INTO workspaces (id, name, root, created_at) VALUES (?, ?, ?, ?)') + .run(workspaceId, 'Swarm Watch Test', 'C:/fixture/swarm-watch', new Date().toISOString()) + registerSwarmAgent(db, { agentId: 'watch-fixture', name: 'Watch Fixture', workspaceId }) + registerWorkerProfile(db, { agentId: 'watch-fixture', surface: 'other', account: 'fixture' }) + try { + const watching = runSwarmWatch({ + forAgent: 'watch-fixture', timeoutMs: 200, batchMs: 2, pollMs: 100, + subscribe: listener => coordEvents.onLive(payload => listener({ id: String(payload.id), type: payload.type, data: payload.data, timestamp: payload.timestamp })), + snapshotFiles: async () => new Map(), + sampleAdmission: () => ({ test: true, build: true, worktree: true }), + emit: () => undefined, + }) + setTimeout(() => recordTurn(db, { workspaceId, agentId: 'watch-fixture', phase: 'start' }), 1) + const result = await watching + assert.equal(result.code, 0) + assert.equal(result.events[0]?.type, 'agent.turn') + const payload = result.events[0]!.data as Record<string, unknown> + assert.equal(payload.previousState, null) + assert.equal(payload.state, 'working') + } finally { + db.close() + } +}) + +test('swarm watch timeout is exit 3 and JSON output has a stable machine-readable envelope', async () => { + const result = await runSwarmWatch({ + timeoutMs: 5, pollMs: 100, subscribe: () => () => undefined, + sampleAdmission: () => ({ test: true, build: true, worktree: true }), + snapshotFiles: async () => new Map(), emit: () => undefined, + }) + assert.equal(result.code, 3) + assert.equal(result.timedOut, true) + assert.deepEqual(JSON.parse(formatWatchResult(result.events, result.timedOut, true)), { events: [], timedOut: true }) +}) + +test('swarm watch wakes once for a message, not again when it is delivered', async () => { + const emitted: WatchEvent[] = [] + const result = await runSwarmWatch({ + forAgent: 'a1', timeoutMs: 200, batchMs: 2, pollMs: 100, + subscribe: subscribeAfter( + event('message.sent', { id: 'm-1', toAgent: 'a1', subject: 'review' }), + event('message.delivered', { messageId: 'm-1', agentId: 'a1' }), + ), + emit: events => { emitted.push(...events) }, + snapshotFiles: async () => new Map(), + sampleAdmission: () => ({ test: true, build: true, worktree: true }), + }) + assert.equal(result.code, 0) + assert.deepEqual(result.events.map(row => row.type), ['message.sent']) + assert.deepEqual(emitted.map(row => row.type), ['message.sent']) +}) + +test('swarm watch wakes when the watched agent delivers a task', async () => { + const result = await runSwarmWatch({ + forAgent: 'a1', timeoutMs: 200, batchMs: 2, pollMs: 100, + subscribe: subscribeAfter(event('task.delivered', { taskId: 't-1', title: 'build reaper', agentId: 'a1', evidence: 'review/R-BR3.md' })), + emit: () => undefined, + snapshotFiles: async () => new Map(), + sampleAdmission: () => ({ test: true, build: true, worktree: true }), + }) + assert.equal(result.code, 0) + assert.equal(result.events[0]?.type, 'task.delivered') + assert.match(formatWatchResult(result.events, false), /Aufgabe geliefert t-1/) +}) + +test('the event bus assigns monotonic ids and replays events after a cursor', () => { + const bus = new CoordEventBus() + const first = bus.emitLive('one', { n: 1 }) + const second = bus.emitLive('two', { n: 2 }) + assert.equal(second.id, first.id + 1) + assert.deepEqual(bus.replaySince(String(first.id)).map(row => row.type), ['two']) + assert.deepEqual(bus.replaySince(undefined).map(row => row.type), ['one', 'two']) + assert.deepEqual(bus.replaySince(String(second.id)), []) + assert.equal(bus.latestEventId(), second.id) +}) + +test('sseSubscribe reconnects with bounded backoff and resumes from Last-Event-ID', async () => { + const cursors: string[] = [] + const lastEventIds: Array<string | undefined> = [] + let connections = 0 + const server = createServer((req, res) => { + connections += 1 + lastEventIds.push(req.headers['last-event-id'] as string | undefined) + res.writeHead(200, { 'Content-Type': 'text/event-stream' }) + if (connections === 1) { + res.write('id: 7\nevent: agent.turn\ndata: {"agentId":"a1","previousState":"working","state":"blocked"}\n\n') + res.end() + } else { + res.write('id: 8\nevent: agent.turn\ndata: {"agentId":"a1","previousState":"blocked","state":"needs-task"}\n\n') + } + }) + await new Promise<void>(resolve => server.listen(0, '127.0.0.1', () => resolve())) + const port = (server.address() as AddressInfo).port + try { + const result = await runSwarmWatch({ + forAgent: 'a1', timeoutMs: 4000, batchMs: 60, pollMs: 60_000, + subscribe: (listener, onError) => sseSubscribe(`http://127.0.0.1:${port}/live`, listener, onError, { + baseBackoffMs: 20, maxBackoffMs: 50, maxRetries: 5, onCursor: id => cursors.push(id), + }), + snapshotFiles: async () => new Map(), + sampleAdmission: () => ({ test: true, build: true, worktree: true }), + emit: () => undefined, + }) + assert.equal(result.code, 0, 'the second event after a reconnect still wakes the watcher') + assert.equal(connections, 2, 'a normally closed stream is retried') + assert.equal(lastEventIds[1], '7', 'the reconnect resumes from the last seen id') + assert.deepEqual(cursors, ['7', '8']) + assert.deepEqual(result.events.map(row => row.id), ['7', '8']) + } finally { + server.close() + } +}) diff --git a/test/ui-turns-e2e.test.ts b/test/ui-turns-e2e.test.ts new file mode 100644 index 0000000..3d09f9c --- /dev/null +++ b/test/ui-turns-e2e.test.ts @@ -0,0 +1,57 @@ +import './helpers/isolated-home.ts' +import { strict as assert } from 'node:assert' +import { test } from 'node:test' +import { mkdirSync, mkdtempSync, rmSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { chromium } from 'playwright-core' +import { openStore } from '../src/store/schema.ts' +import { serve } from '../src/server/api.ts' +import { registerSwarmAgent } from '../src/coord/registry.ts' +import { registerWorkerProfile, recordTurn } from '../src/coord/swarm-ops.ts' + +test('Turns renders an authenticated fixture timeline and opens turn details', async () => { + const dir = mkdtempSync(join(tmpdir(), 'plugbrain-ui-turns-')) + const db = openStore(join(dir, 'brain.db')) + const workspaceId = 'ws-ui-turns' + db.prepare('INSERT INTO workspaces (id, name, root, created_at) VALUES (?, ?, ?, ?)') + .run(workspaceId, 'UI Turns Fixture', join(dir, 'workspace'), new Date().toISOString()) + registerSwarmAgent(db, { agentId: 'fixture-worker', name: 'Fixture Worker', model: 'fixture-model', workspaceId }) + registerWorkerProfile(db, { agentId: 'fixture-worker', surface: 'other', account: 'fixture-account', model: 'fixture-model' }) + recordTurn(db, { workspaceId, agentId: 'fixture-worker', phase: 'start' }) + db.prepare('UPDATE swarm_turn_history SET started_at = ? WHERE agent_id = ?') + .run(new Date(Date.now() - 31 * 60_000).toISOString(), 'fixture-worker') + db.prepare('UPDATE agents SET turn_state_at = ?, last_heartbeat = ? WHERE id = ?') + .run(new Date(Date.now() - 31 * 60_000).toISOString(), new Date(Date.now() - 31 * 60_000).toISOString(), 'fixture-worker') + + const authKey = 'fixture-ui-' + 'x'.repeat(24) + const server = await serve({ db, uiRoot: join(process.cwd(), 'ui-dist'), authKey, requireAuth: true }, 0) + const browser = await chromium.launch({ headless: true }) + try { + const page = await browser.newPage({ viewport: { width: 1440, height: 1000 }, colorScheme: 'dark' }) + const evidenceDir = process.env.PLUGBRAIN_UI_MESH_EVIDENCE_DIR ?? dir + mkdirSync(evidenceDir, { recursive: true }) + await page.goto(`http://127.0.0.1:${server.port}/?view=turns&workspace=${workspaceId}`, { waitUntil: 'networkidle' }) + await page.getByRole('heading', { name: 'Turns' }).waitFor() + await page.getByText('Fixture Worker').waitFor() + const block = page.getByRole('button', { name: /Fixture Worker, Läuft, still\?/ }) + await block.waitFor() + await block.click() + await page.getByText('Keine aktiven Claims.').waitFor() + await page.getByText('Beginn', { exact: true }).waitFor() + await page.locator('.turns-still-label').getByText(/still\? seit 31 min/).waitFor() + await page.screenshot({ path: join(evidenceDir, 'candidate-turns-fixture.png'), fullPage: true }) + assert.equal(await page.locator('.turns-block[data-state="running"]').count(), 1) + assert.equal(await page.locator('.turns-block[data-still="true"]').count(), 1) + assert.equal(await page.getByRole('button', { name: '0 ungelesen' }).getAttribute('aria-pressed'), 'false') + await page.getByRole('button', { name: '0 ungelesen' }).click() + await page.getByText('Keine registrierten Worker in diesem Workspace.').waitFor() + await page.getByRole('button', { name: '0 ungelesen' }).click() + await block.waitFor() + } finally { + await browser.close() + await server.close() + db.close() + try { rmSync(dir, { recursive: true, force: true, maxRetries: 10 }) } catch { /* fixture cleanup */ } + } +}) diff --git a/test/watchdog.test.ts b/test/watchdog.test.ts new file mode 100644 index 0000000..76f6b4b --- /dev/null +++ b/test/watchdog.test.ts @@ -0,0 +1,445 @@ +/** + * BR-2a: the watchdog — still detection, task dependencies, review routing and + * the sender of a message. + * + * Every case here runs on an injected clock: the timestamps are written into + * the store and handed to the reading as `now`, so nothing waits 45 real + * minutes for a worker to look quiet. + */ +import './helpers/isolated-home.ts' +import { strict as assert } from 'node:assert' +import { test } from 'node:test' +import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { openStore } from '../src/store/schema.ts' +import * as coord from '../src/coord/index.ts' +import { enqueueTask } from '../src/queue.ts' +import { addTaskDependency } from '../src/coord/dependencies.ts' +import { runSwarmCli } from '../src/swarm-cli.ts' + +interface Fixture { + dir: string + db: ReturnType<typeof openStore> + workspaceId: string + cleanup: () => void +} + +function createFixture(): Fixture { + const dir = mkdtempSync(join(tmpdir(), 'plugbrain-watchdog-')) + const db = openStore(join(dir, 'brain.db')) + const workspaceId = 'ws-watchdog' + db.prepare('INSERT INTO workspaces (id, name, root, created_at) VALUES (?, ?, ?, ?)') + .run(workspaceId, 'Watchdog Test', dir, new Date().toISOString()) + return { dir, db, workspaceId, cleanup: () => { db.close(); rmSync(dir, { recursive: true, force: true }) } } +} + +/** A host snapshot that is never out of room — admission is not under test here. */ +const HOST: coord.HostSnapshot = { + measuredAt: new Date().toISOString(), + drives: [], + memory: { freeBytes: 1e12, totalBytes: 1e12 }, + cpuBusyFraction: null, +} + +function register(f: Fixture, agentId: string, account: string, model?: string): void { + coord.registerSwarmAgent(f.db, { agentId, workspaceId: f.workspaceId, heartbeatTtlMs: 6 * 60 * 60_000 }) + coord.registerWorkerProfile(f.db, { agentId, surface: 'freebuff', account, ...(model === undefined ? {} : { model }) }) +} + +const minutesAgo = (now: Date, minutes: number): Date => new Date(now.getTime() - minutes * 60_000) +const minutesAfter = (now: Date, minutes: number): Date => new Date(now.getTime() + minutes * 60_000) + +/** Pretend the worker started its turn (and last touched the Brain) at that time. */ +function backdate(f: Fixture, agentId: string, iso: string): void { + f.db.prepare('UPDATE agents SET turn_state_at = ?, last_contact_at = ? WHERE id = ?').run(iso, iso, agentId) +} + +const messagesTo = (f: Fixture, agentId: string): Array<{ subject: string; from_agent: string; body: string }> => + f.db.prepare('SELECT subject, from_agent, body FROM inbox_messages WHERE workspace_id = ? AND to_agent = ? ORDER BY created_at') + .all(f.workspaceId, agentId) as unknown as Array<{ subject: string; from_agent: string; body: string }> + +const queueTask = (f: Fixture, id: string): { id: string; title: string; addressed_to: string | null; state: string; claimed_by: string | null } => + f.db.prepare('SELECT id, title, addressed_to, state, claimed_by FROM queue_tasks WHERE id = ?').get(id) as unknown as + { id: string; title: string; addressed_to: string | null; state: string; claimed_by: string | null } + +const reviewTaskCount = (f: Fixture): number => + Number((f.db.prepare(`SELECT COUNT(*) AS n FROM queue_tasks WHERE workspace_id = ? AND title LIKE 'Review:%'`) + .get(f.workspaceId) as { n: number }).n) + +test('BR-2a: a working worker without contact is still, and the board shows the minutes', () => { + const f = createFixture() + try { + register(f, 'w-quiet', 'freebuff:quiet') + register(f, 'w-fresh', 'nvidia:key-01') + // The clock is injected in the readings below; the store timestamps are + // written relative to the real now because the board reads it itself. + const now = new Date() + coord.recordTurn(f.db, { workspaceId: f.workspaceId, agentId: 'w-quiet', phase: 'start' }) + coord.recordTurn(f.db, { workspaceId: f.workspaceId, agentId: 'w-fresh', phase: 'start' }) + backdate(f, 'w-quiet', minutesAgo(now, 50.5).toISOString()) + backdate(f, 'w-fresh', minutesAgo(now, 10).toISOString()) + + const silent = coord.silentWorkers(f.db, f.workspaceId, { now }) + assert.deepEqual(silent.map(reading => [reading.agentId, reading.minutes]), [['w-quiet', 50]], + 'only the worker past the threshold and without contact is still') + + // A claim five minutes ago is Brain contact: the worker is not still. + coord.touchAgentContact(f.db, 'w-quiet', minutesAgo(now, 5)) + assert.equal(coord.silentWorkers(f.db, f.workspaceId, { now }).length, 0, + 'contact inside the working turn clears the reading') + + // The board carries the same reading, with minutes, for whoever looks. + backdate(f, 'w-quiet', minutesAgo(new Date(), 50.5).toISOString()) + const board = coord.agentsBoard(f.db, f.workspaceId, { host: HOST }) + const quiet = board.agents.find(agent => agent.id === 'w-quiet') + const fresh = board.agents.find(agent => agent.id === 'w-fresh') + assert.equal(quiet?.silentMinutes, 50) + assert.ok(quiet?.attention.includes('silent')) + assert.equal(fresh?.silentMinutes, null) + + // The threshold is a per-workspace setting. + coord.setSilentAfterMinutes(f.db, f.workspaceId, 60) + assert.equal(coord.silentWorkers(f.db, f.workspaceId, { now }).length, 0, + 'a 50-minute silence is not still once the workspace says 60 minutes') + assert.equal(coord.watchdogSettings(f.db, f.workspaceId).silentAfterMinutes, 60) + } finally { + f.cleanup() + } +}) + +test('BR-2a: the integrator is told once per case, and again only after new contact', () => { + const f = createFixture() + try { + register(f, 'w-quiet', 'freebuff:quiet') + const now = new Date('2026-09-26T20:00:00.000Z') + coord.recordTurn(f.db, { workspaceId: f.workspaceId, agentId: 'w-quiet', phase: 'start' }) + backdate(f, 'w-quiet', minutesAgo(now, 50).toISOString()) + const task = enqueueTask(f.db, f.workspaceId, { title: 'Quiet worker task', addressedTo: 'w-quiet' }) + f.db.prepare("UPDATE queue_tasks SET state = 'claimed', claimed_by = 'w-quiet', claimed_at = ? WHERE id = ?") + .run(minutesAgo(now, 50).toISOString(), task.id) + + // A dry run observes without telling anybody. + const dry = coord.scanWatchdog(f.db, f.workspaceId, { now, alert: false }) + assert.equal(dry.silent.length, 1) + assert.equal(dry.alerted.length, 0) + assert.equal(messagesTo(f, 'integrator').length, 0) + + const first = coord.scanWatchdog(f.db, f.workspaceId, { now }) + assert.deepEqual(first.alerted, ['w-quiet']) + assert.equal(first.silent[0]?.alerted, true) + const told = messagesTo(f, 'integrator') + assert.equal(told.length, 1) + assert.match(told[0]!.subject, /Silent work needs decision: Quiet worker task/) + assert.match(told[0]!.body, /holding task/) + + // The same case is never reported twice. + const second = coord.scanWatchdog(f.db, f.workspaceId, { now }) + assert.deepEqual(second.alerted, []) + assert.equal(messagesTo(f, 'integrator').length, 1, 'no repeat while the case is the same') + + // Contact opens no new case while the worker stays in touch… + coord.touchAgentContact(f.db, 'w-quiet', minutesAfter(now, 5)) + assert.deepEqual(coord.scanWatchdog(f.db, f.workspaceId, { now }).alerted, []) + assert.equal(messagesTo(f, 'integrator').length, 1) + + // …but going quiet again after that contact is a new case. + const later = minutesAfter(now, 60) + const third = coord.scanWatchdog(f.db, f.workspaceId, { now: later }) + assert.deepEqual(third.alerted, ['w-quiet']) + assert.equal(messagesTo(f, 'integrator').length, 2) + } finally { + f.cleanup() + } +}) + +test('BR-2a: silence notice and case latch commit together', () => { + const f = createFixture() + try { + register(f, 'w-quiet', 'freebuff:quiet') + const now = new Date('2026-09-26T20:00:00.000Z') + coord.recordTurn(f.db, { workspaceId: f.workspaceId, agentId: 'w-quiet', phase: 'start' }) + backdate(f, 'w-quiet', minutesAgo(now, 50).toISOString()) + const task = enqueueTask(f.db, f.workspaceId, { title: 'Quiet worker task', addressedTo: 'w-quiet' }) + f.db.prepare("UPDATE queue_tasks SET state = 'claimed', claimed_by = 'w-quiet', claimed_at = ? WHERE id = ?") + .run(minutesAgo(now, 50).toISOString(), task.id) + f.db.exec(`CREATE TRIGGER fail_silence_latch BEFORE UPDATE OF silence_alerted_at ON agents + BEGIN SELECT RAISE(ABORT, 'simulated crash before latch'); END`) + assert.throws(() => coord.scanWatchdog(f.db, f.workspaceId, { now }), /simulated crash/) + assert.equal(messagesTo(f, 'integrator').length, 0, 'the message rolls back with the failed latch') + assert.equal((f.db.prepare("SELECT silence_alerted_at FROM agents WHERE id = 'w-quiet'").get() as { silence_alerted_at: string | null }).silence_alerted_at, null) + f.db.exec('DROP TRIGGER fail_silence_latch') + assert.deepEqual(coord.scanWatchdog(f.db, f.workspaceId, { now }).alerted, ['w-quiet']) + assert.equal(messagesTo(f, 'integrator').length, 1) + } finally { f.cleanup() } +}) + +test('BR-2a: unknown reviewer accounts do not count as independent', () => { + const f = createFixture() + try { + register(f, 'w-author', 'freebuff:freebucks') + register(f, 'w-unknown', 'unknown') + f.db.prepare("UPDATE agents SET account = NULL WHERE id = 'w-unknown'").run() + coord.setReviewPool(f.db, f.workspaceId, ['w-unknown']) + coord.setReviewAuto(f.db, f.workspaceId, true) + assert.equal(coord.routeReviewForAuthor(f.db, f.workspaceId, 'w-author').routed, false) + assert.equal(reviewTaskCount(f), 0) + } finally { f.cleanup() } +}) + +test('AP03: same model family with different accounts is not independent', () => { + const f = createFixture() + try { + register(f, 'w-author', 'account-a', 'gpt-6-luna') + register(f, 'w-reviewer', 'account-b', 'gpt-5.4') + coord.setReviewPool(f.db, f.workspaceId, ['w-reviewer']) + coord.setReviewAuto(f.db, f.workspaceId, true) + + const result = coord.routeReviewForAuthor(f.db, f.workspaceId, 'w-author') + assert.equal(result.routed, false) + assert.match(result.reason, /known different model family/) + assert.equal(reviewTaskCount(f), 0) + } finally { f.cleanup() } +}) + +test('AP03: different registered model families route, while unknown and changed metadata fail closed', () => { + const f = createFixture() + try { + register(f, 'w-author', 'account-a', 'gpt-6-luna') + register(f, 'w-reviewer', 'account-b', 'claude-sonnet-4') + coord.setReviewPool(f.db, f.workspaceId, ['w-reviewer']) + coord.setReviewAuto(f.db, f.workspaceId, true) + + const routed = coord.routeReviewForAuthor(f.db, f.workspaceId, 'w-author') + assert.equal(routed.routed, true) + const queued = f.db.prepare('SELECT body FROM queue_tasks WHERE id = ?').get(routed.taskId) as { body: string } + assert.match(queued.body, /openai \(gpt-6-luna\) -> anthropic \(claude-sonnet-4\)/) + + f.db.prepare("UPDATE agents SET model = 'custom-deployment-model' WHERE id = 'w-reviewer'").run() + assert.deepEqual(coord.resolveReviewIndependence(f.db, 'w-author', 'w-reviewer'), { + independent: false, reason: 'unknown-model', + }) + + f.db.prepare("UPDATE agents SET model = 'gpt-6-pro' WHERE id = 'w-reviewer'").run() + assert.deepEqual(coord.resolveReviewIndependence(f.db, 'w-author', 'w-reviewer'), { + independent: false, reason: 'same-model-family', + }) + } finally { f.cleanup() } +}) + +test('AP03: conflicting current agent and runner model metadata is treated as stale', () => { + const f = createFixture() + try { + register(f, 'w-author', 'account-a', 'gpt-6-luna') + register(f, 'w-reviewer', 'account-b', 'claude-sonnet-4') + coord.setRunnerProfile(f.db, { agentId: 'w-reviewer', cmd: 'node', model: 'gpt-6-luna' }) + assert.deepEqual(coord.resolveReviewIndependence(f.db, 'w-author', 'w-reviewer'), { + independent: false, reason: 'stale-model-metadata', + }) + coord.setReviewPool(f.db, f.workspaceId, ['w-reviewer']) + coord.setReviewAuto(f.db, f.workspaceId, true) + assert.equal(coord.routeReviewForAuthor(f.db, f.workspaceId, 'w-author').routed, false) + assert.equal(reviewTaskCount(f), 0) + } finally { f.cleanup() } +}) + +test('BR-2a: --after blocks a task until its predecessor is delivered, then tells the addressee', () => { + const f = createFixture() + try { + register(f, 'w-one', 'freebuff:one') + register(f, 'w-two', 'nvidia:key-01') + const cli = (args: string[]): number => runSwarmCli(f.db, [...args, '--workspace', f.workspaceId], () => f.workspaceId) + + assert.equal(cli(['enqueue', 'Basis', '--to', 'w-one']), 0) + const basis = f.db.prepare(`SELECT id FROM queue_tasks WHERE title = 'Basis'`).get() as { id: string } + assert.equal(cli(['enqueue', 'Darauf aufbauen', '--to', 'w-two', '--after', basis.id]), 0) + const dependent = f.db.prepare(`SELECT id FROM queue_tasks WHERE title = 'Darauf aufbauen'`).get() as { id: string } + + const first = coord.recordTurn(f.db, { workspaceId: f.workspaceId, agentId: 'w-one', phase: 'start', claimNext: true }) + assert.equal(first.claimedTask?.id, basis.id) + + // Blocked: no offer and no claim while the predecessor is still running. + const blocked = coord.recordTurn(f.db, { workspaceId: f.workspaceId, agentId: 'w-two', phase: 'start', claimNext: true }) + assert.equal(blocked.claimedTask, null) + assert.equal(blocked.nextTask, null, 'a blocked task must not be offered either') + assert.equal(messagesTo(f, 'w-two').length, 0) + + // Delivery is the moment it arrives — with exactly one notice. + mkdirSync(join(f.dir, 'review'), { recursive: true }) + writeFileSync(join(f.dir, 'review', 'BASIS.md'), 'Basis delivery evidence.\n') + assert.equal(cli(['deliver', 'w-one', basis.id, '--path', 'review/BASIS.md']), 0) + const told = messagesTo(f, 'w-two') + assert.equal(told.length, 1) + assert.match(told[0]!.subject, /Deine Aufgabe ist jetzt frei/) + assert.match(told[0]!.body, /Darauf aufbauen/) + assert.equal(coord.syncDependencies(f.db, f.workspaceId).length, 0, 'the release is not announced again') + assert.equal(messagesTo(f, 'w-two').length, 1) + + const claimed = coord.recordTurn(f.db, { workspaceId: f.workspaceId, agentId: 'w-two', phase: 'start', claimNext: true }) + assert.equal(claimed.claimedTask?.id, dependent.id) + assert.equal(queueTask(f, dependent.id).state, 'claimed') + } finally { + f.cleanup() + } +}) + +test('BR-2a: dependencies reject cross-workspace predecessors and cycles', () => { + const f = createFixture() + try { + const otherWorkspace = 'ws-other' + const otherRoot = join(f.dir, 'other') + const first = enqueueTask(f.db, f.workspaceId, { title: 'first' }) + const second = enqueueTask(f.db, f.workspaceId, { title: 'second' }) + f.db.prepare('INSERT INTO workspaces (id, name, root, created_at) VALUES (?, ?, ?, ?)') + .run(otherWorkspace, 'Other', otherRoot, new Date().toISOString()) + const foreignTask = enqueueTask(f.db, otherWorkspace, { title: 'foreign' }) + assert.throws(() => addTaskDependency(f.db, first.id, foreignTask.id), /same workspace/) + addTaskDependency(f.db, second.id, first.id) + assert.throws(() => addTaskDependency(f.db, first.id, second.id), /cycle/) + const legacyTime = new Date().toISOString() + f.db.prepare('INSERT INTO task_dependencies (task_id, after_task_id, created_at) VALUES (?, ?, ?)') + .run(first.id, foreignTask.id, legacyTime) + coord.syncDependencies(f.db, f.workspaceId) + assert.equal(queueTask(f, first.id).state, 'cancelled', 'legacy cross-workspace edges are failed closed too') + assert.match((f.db.prepare('SELECT body FROM queue_tasks WHERE id = ?').get(first.id) as { body: string }).body, /another workspace/i) + + const legacyA = enqueueTask(f.db, f.workspaceId, { title: 'legacy cycle A' }) + const legacyB = enqueueTask(f.db, f.workspaceId, { title: 'legacy cycle B' }) + const now = new Date().toISOString() + f.db.prepare('INSERT INTO task_dependencies (task_id, after_task_id, created_at) VALUES (?, ?, ?)') + .run(legacyA.id, legacyB.id, now) + f.db.prepare('INSERT INTO task_dependencies (task_id, after_task_id, created_at) VALUES (?, ?, ?)') + .run(legacyB.id, legacyA.id, now) + coord.syncDependencies(f.db, f.workspaceId) + assert.equal(queueTask(f, legacyA.id).state, 'cancelled') + assert.equal(queueTask(f, legacyB.id).state, 'cancelled') + } finally { f.cleanup() } +}) + +test('BR-2a: a deleted predecessor becomes a visible cancelled task', () => { + const f = createFixture() + try { + register(f, 'w-waiter', 'freebuff:waiter') + const predecessor = enqueueTask(f.db, f.workspaceId, { title: 'predecessor' }) + const dependent = enqueueTask(f.db, f.workspaceId, { title: 'dependent', addressedTo: 'w-waiter', afterTaskId: predecessor.id }) + f.db.prepare('DELETE FROM queue_tasks WHERE id = ?').run(predecessor.id) + coord.syncDependencies(f.db, f.workspaceId) + const task = f.db.prepare('SELECT state, body FROM queue_tasks WHERE id = ?').get(dependent.id) as { state: string; body: string } + assert.equal(task.state, 'cancelled') + assert.match(task.body, /predecessor .* no longer exists/i) + assert.match(messagesTo(f, 'w-waiter')[0]?.body ?? '', /no longer exists/i) + } finally { f.cleanup() } +}) + +test('BR-2a: a turn ended with awaiting-commit frees what waits on it — blocked does not', () => { + const f = createFixture() + try { + register(f, 'w-holder', 'freebuff:holder') + register(f, 'w-waiter', 'gemini:key-02') + const cli = (args: string[]): number => runSwarmCli(f.db, [...args, '--workspace', f.workspaceId], () => f.workspaceId) + + assert.equal(cli(['enqueue', 'Vorlauf', '--to', 'w-holder']), 0) + const pre = f.db.prepare(`SELECT id FROM queue_tasks WHERE title = 'Vorlauf'`).get() as { id: string } + assert.equal(cli(['enqueue', 'Nachlauf', '--to', 'w-waiter', '--after', pre.id]), 0) + const after = f.db.prepare(`SELECT id FROM queue_tasks WHERE title = 'Nachlauf'`).get() as { id: string } + + assert.equal(coord.recordTurn(f.db, { workspaceId: f.workspaceId, agentId: 'w-holder', phase: 'start', claimNext: true }).claimedTask?.id, pre.id) + + // A blocked holder is not an arrived predecessor: the task stays out of reach. + coord.recordTurn(f.db, { workspaceId: f.workspaceId, agentId: 'w-holder', phase: 'end', state: 'blocked', summary: 'waiting for the owner' }) + assert.equal(coord.recordTurn(f.db, { workspaceId: f.workspaceId, agentId: 'w-waiter', phase: 'start', claimNext: true }).claimedTask, null) + assert.equal(messagesTo(f, 'w-waiter').length, 0) + + // awaiting-commit is: the holder has done all it can do alone. + const ended = coord.recordTurn(f.db, { workspaceId: f.workspaceId, agentId: 'w-holder', phase: 'end', state: 'awaiting-commit', summary: 'ready' }) + assert.deepEqual(ended.state, 'awaiting-commit') + assert.equal(messagesTo(f, 'w-waiter').length, 1) + assert.equal(coord.recordTurn(f.db, { workspaceId: f.workspaceId, agentId: 'w-waiter', phase: 'start', claimNext: true }).claimedTask?.id, after.id) + assert.equal(queueTask(f, after.id).claimed_by, 'w-waiter') + } finally { + f.cleanup() + } +}) + +test('BR-2a: review routing picks a reviewer with another account, once, and only when on', () => { + const f = createFixture() + try { + register(f, 'w-author', 'freebuff:freebucks', 'gpt-6-luna') + register(f, 'nv-review', 'nvidia:key-01', 'claude-sonnet-4') + register(f, 'fb-review', 'freebuff:freebucks', 'gpt-5.4') + register(f, 'owner-review', 'owner:chatgpt', 'gemini-2.5-pro') + register(f, 'w-author-2', 'gemini:key-03', 'gemini-2.5-pro') + register(f, 'w-author-3', 'freebuff:freebucks', 'gpt-6-luna') + + const cli = (args: string[]): number => runSwarmCli(f.db, [...args, '--workspace', f.workspaceId], () => f.workspaceId) + // The first entry shares the author's account and must be skipped. + assert.equal(cli(['review-pool', 'set', 'fb-review', 'nv-review', 'owner-review']), 0) + assert.equal(cli(['review-pool', 'auto', 'on']), 0) + + assert.equal(cli(['enqueue', 'Cx01 Aufgabe', '--to', 'w-author']), 0) + const authored = f.db.prepare(`SELECT id FROM queue_tasks WHERE title = 'Cx01 Aufgabe'`).get() as { id: string } + coord.recordTurn(f.db, { workspaceId: f.workspaceId, agentId: 'w-author', phase: 'start', claimNext: true }) + coord.recordTurn(f.db, { workspaceId: f.workspaceId, agentId: 'w-author', phase: 'end', state: 'awaiting-commit', summary: 'fertig' }) + + const reviews = f.db.prepare(`SELECT id, title, addressed_to, body FROM queue_tasks WHERE workspace_id = ? AND title LIKE 'Review:%'`) + .all(f.workspaceId) as unknown as Array<{ id: string; title: string; addressed_to: string; body: string }> + assert.equal(reviews.length, 1) + assert.equal(reviews[0]!.addressed_to, 'nv-review', 'a review goes to a different account and model family') + assert.match(reviews[0]!.title, /Review: Cx01 Aufgabe/) + assert.match(reviews[0]!.body, /w-author/) + assert.equal(queueTask(f, reviews[0]!.id).state, 'pending') + + // The same finished turn does not pile up review tasks. + coord.recordTurn(f.db, { workspaceId: f.workspaceId, agentId: 'w-author', phase: 'end', state: 'awaiting-commit', summary: 'fertig' }) + assert.equal(reviewTaskCount(f), 1) + + // Switched off, nothing is routed. + assert.equal(cli(['review-pool', 'auto', 'off']), 0) + assert.equal(cli(['enqueue', 'Zweite Aufgabe', '--to', 'w-author-2']), 0) + coord.recordTurn(f.db, { workspaceId: f.workspaceId, agentId: 'w-author-2', phase: 'start', claimNext: true }) + coord.recordTurn(f.db, { workspaceId: f.workspaceId, agentId: 'w-author-2', phase: 'end', state: 'awaiting-commit', summary: 'fertig' }) + assert.equal(reviewTaskCount(f), 1) + + // A pool in which every remaining reviewer shares the account routes nothing. + assert.equal(cli(['review-pool', 'set', 'fb-review']), 0) + assert.equal(cli(['review-pool', 'auto', 'on']), 0) + assert.equal(cli(['enqueue', 'Dritte Aufgabe', '--to', 'w-author-3']), 0) + coord.recordTurn(f.db, { workspaceId: f.workspaceId, agentId: 'w-author-3', phase: 'start', claimNext: true }) + coord.recordTurn(f.db, { workspaceId: f.workspaceId, agentId: 'w-author-3', phase: 'end', state: 'awaiting-commit', summary: 'fertig' }) + assert.equal(reviewTaskCount(f), 1, 'the same account cannot give an independent review') + } finally { + f.cleanup() + } +}) + +test('BR-2a: send without a sender is refused (exit 2) instead of crediting the integrator', () => { + const f = createFixture() + const previous = process.env.PLUGBRAIN_AGENT + try { + register(f, 'w-sender', 'freebuff:sender') + register(f, 'w-recipient', 'owner:fleet') + const cli = (args: string[]): number => runSwarmCli(f.db, [...args, '--workspace', f.workspaceId], () => f.workspaceId) + + delete process.env.PLUGBRAIN_AGENT + const code = cli(['send', 'w-recipient', '--subject', 'Hallo', '--body', 'ohne Absender']) + assert.equal(code, 2) + assert.equal(messagesTo(f, 'w-recipient').length, 0, 'the refused message must not be stored') + + process.env.PLUGBRAIN_AGENT = 'w-sender' + assert.equal(cli(['send', 'w-recipient', '--subject', 'Hallo', '--body', 'aus der Umgebung']), 0) + const fromEnv = f.db.prepare(`SELECT from_agent FROM inbox_messages WHERE body = 'aus der Umgebung'`).get() as { from_agent: string } + assert.equal(fromEnv.from_agent, 'w-sender') + + assert.equal(cli(['send', 'w-recipient', '--subject', 'Hallo', '--body', 'mit Flag', '--from', 'nv-lane']), 0) + const fromFlag = f.db.prepare(`SELECT from_agent FROM inbox_messages WHERE body = 'mit Flag'`).get() as { from_agent: string } + assert.equal(fromFlag.from_agent, 'nv-lane', '--from wins over the environment') + + // The sender is contact: a message is proof the process is alive. + const contact = f.db.prepare('SELECT last_contact_at FROM agents WHERE id = ?').get('w-sender') as { last_contact_at: string | null } + assert.notEqual(contact.last_contact_at, null) + } finally { + if (previous === undefined) delete process.env.PLUGBRAIN_AGENT + else process.env.PLUGBRAIN_AGENT = previous + f.cleanup() + } +}) diff --git a/test/wave-done.test.ts b/test/wave-done.test.ts new file mode 100644 index 0000000..97afae5 --- /dev/null +++ b/test/wave-done.test.ts @@ -0,0 +1,124 @@ +import assert from 'node:assert/strict' +import { DatabaseSync } from 'node:sqlite' +import test from 'node:test' +import { coordEvents } from '../src/coord/events.ts' +import { + ensureWaveDoneSchema, recordWaveDone, type WaveManifestEvidence, type WaveTaskEvidence, +} from '../src/coord/wave-done.ts' + +const evidence = (overrides: Partial<WaveTaskEvidence> = {}): WaveTaskEvidence => ({ + taskId: 'task-a', + title: 'A task', + state: 'delivered', + claimedBy: 'worker-a', + reviewJudgment: 'PASS', + sourceRevision: 'abc123', + reviewedCommit: 'abc123', + independentReview: { + source: 'operator', reviewerId: 'reviewer-b', authorityRef: 'operator:review:abc123', + confidence: 'authoritative', judgment: 'PASS', commit: 'abc123', + }, + integration: { + source: 'git', authorityRef: 'git:commit:abc123', confidence: 'authoritative', commit: 'abc123', + }, + ownerDecision: null, + ...overrides, +}) + +const database = (): DatabaseSync => { + const db = new DatabaseSync(':memory:') + ensureWaveDoneSchema(db) + return db +} + +const manifest = (waveId: string, taskIds: string[] = ['task-a']): WaveManifestEvidence => ({ + waveId, taskIds, source: 'operator', authorityRef: `operator:manifest:${waveId}`, confidence: 'authoritative', +}) + +const record = (db: DatabaseSync, waveId: string, tasks: WaveTaskEvidence[]) => + recordWaveDone(db, 'ws-test', waveId, manifest(waveId, tasks.map(task => task.taskId)), tasks) + +test('wave done is recorded and announced only when every task has delivery, independent review, and integration proof', () => { + const db = database() + try { + const before = coordEvents.latestEventId() + const result = record(db, 'wave-3', [evidence()]) + assert.equal(result.status, 'DONE') + assert.equal(result.blockers.length, 0) + assert.equal(coordEvents.latestEventId(), before + 1) + assert.equal(coordEvents.replaySince(before)[0]?.type, 'WAVE-DONE') + } finally { db.close() } +}) + +test('an open review prevents a completion event', () => { + const db = database() + try { + const before = coordEvents.latestEventId() + const result = record(db, 'wave-open-review', [evidence({ reviewJudgment: null, reviewedCommit: null })]) + assert.equal(result.status, 'BLOCKED') + assert.ok(result.blockers.some(blocker => blocker.includes('review'))) + assert.equal(coordEvents.latestEventId(), before) + } finally { db.close() } +}) + +test('a review from the task owner is not independent', () => { + const db = database() + try { + const result = record(db, 'wave-self-review', [evidence({ + independentReview: { + source: 'operator', reviewerId: 'worker-a', authorityRef: 'operator:review:self', + confidence: 'authoritative', judgment: 'PASS', commit: 'abc123', + }, + })]) + assert.equal(result.status, 'BLOCKED') + assert.ok(result.blockers.some(blocker => blocker.includes('independent review'))) + } finally { db.close() } +}) + +test('missing integration proof prevents a completion event', () => { + const db = database() + try { + const before = coordEvents.latestEventId() + const result = record(db, 'wave-no-merge', [evidence({ integration: null })]) + assert.equal(result.status, 'BLOCKED') + assert.ok(result.blockers.some(blocker => blocker.includes('integration'))) + assert.equal(coordEvents.latestEventId(), before) + } finally { db.close() } +}) + +test('an explicit authoritative owner decision can resolve the integration requirement', () => { + const db = database() + try { + const result = record(db, 'wave-decision', [evidence({ + integration: null, + ownerDecision: { source: 'operator', authorityRef: 'operator:decision:42', confidence: 'authoritative', decision: 'defer integration' }, + })]) + assert.equal(result.status, 'DONE') + assert.equal(result.tasks[0]?.resolution, 'owner-decision') + } finally { db.close() } +}) + +test('a duplicate trigger returns the first report without emitting another event', () => { + const db = database() + try { + const first = record(db, 'wave-idempotent', [evidence()]) + const afterFirst = coordEvents.latestEventId() + const second = record(db, 'wave-idempotent', [evidence()]) + assert.equal(second.status, 'DONE') + assert.equal(second.eventId, first.eventId) + assert.equal(coordEvents.latestEventId(), afterFirst) + } finally { db.close() } +}) + +test('a missing or incomplete authoritative manifest blocks completion', () => { + const db = database() + try { + const missing = recordWaveDone(db, 'ws-test', 'wave-no-manifest', null, [evidence()]) + assert.equal(missing.status, 'BLOCKED') + assert.ok(missing.blockers.some(blocker => blocker.includes('manifest'))) + + const omitted = recordWaveDone(db, 'ws-test', 'wave-omitted-task', manifest('wave-omitted-task', ['task-a', 'task-b']), [evidence()]) + assert.equal(omitted.status, 'BLOCKED') + assert.ok(omitted.blockers.some(blocker => blocker.includes('task-b'))) + } finally { db.close() } +}) diff --git a/test/worktree-reaper.test.ts b/test/worktree-reaper.test.ts new file mode 100644 index 0000000..4ce1cc5 --- /dev/null +++ b/test/worktree-reaper.test.ts @@ -0,0 +1,169 @@ +import './helpers/isolated-home.ts' +import { strict as assert } from 'node:assert' +import { test } from 'node:test' +import { execFileSync } from 'node:child_process' +import { existsSync, mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { openStore } from '../src/store/schema.ts' +import { registerSwarmAgent, acquireLease } from '../src/coord/index.ts' +import { registerWorkerProfile, recordTurn } from '../src/coord/swarm-ops.ts' +import { isReapAutoEnabled, reapWorktrees, setReapAuto, synchronizeMissingWorktrees } from '../src/coord/reaper.ts' + +function git(cwd: string, ...args: string[]): string { + return execFileSync('git', ['-C', cwd, ...args], { encoding: 'utf8' }).trim() +} + +const samePath = (a: string, b: string): boolean => { + const real = (path: string) => { + const value = realpathSync.native(path) + return process.platform === 'win32' ? value.toLowerCase() : value + } + return real(a) === real(b) +} + +function fixture() { + const dir = mkdtempSync(join(tmpdir(), 'plugbrain-reaper-')) + const repo = join(dir, 'repo') + mkdirSync(repo) + git(dir, 'init', '-b', 'main', repo) + git(repo, 'config', 'user.email', 'reaper@test.invalid') + git(repo, 'config', 'user.name', 'Reaper Test') + writeFileSync(join(repo, 'base.txt'), 'base\n') + git(repo, 'add', '.') + git(repo, 'commit', '-m', 'base') + const db = openStore(join(dir, 'brain.db')) + const ws = 'ws-reaper-test' + db.prepare('INSERT INTO workspaces (id, name, root, created_at) VALUES (?, ?, ?, ?)') + .run(ws, 'reaper-test', dir, new Date().toISOString()) + const addMerged = (name: string) => { + const path = join(dir, name) + git(repo, 'worktree', 'add', '-b', name, path) + writeFileSync(join(path, `${name}.txt`), name) + git(path, 'add', '.') + git(path, 'commit', '-m', name) + git(repo, 'merge', '--no-ff', name, '-m', `merge ${name}`) + return path + } + return { dir, repo, db, ws, addMerged, cleanup: () => { db.close(); rmSync(dir, { recursive: true, force: true }) } } +} + +test('dry-run is read only, and apply removes a merged clean tree with a recovery receipt', () => { + const f = fixture() + try { + const path = f.addMerged('clean') + const preview = reapWorktrees(f.db, f.ws, { repo: f.repo }) + assert.equal(existsSync(path), true) + assert.equal(preview.removed.length, 0) + assert.equal(preview.candidates.find(row => samePath(row.path, path))?.eligible, true) + const applied = reapWorktrees(f.db, f.ws, { repo: f.repo, apply: true }) + assert.equal(existsSync(path), false) + assert.equal(applied.removed.length, 1) + assert.match(applied.removed[0]?.restoreCommand ?? '', /git worktree add/) + assert.ok(applied.removed[0]?.commit) + git(f.repo, 'worktree', 'add', path, 'clean') + assert.equal(existsSync(path), true, 'the recorded branch and path restore the removed worktree') + } finally { f.cleanup() } +}) + +test('dirty and untracked merged worktrees stay in place with distinct reasons', () => { + const f = fixture() + try { + const dirty = f.addMerged('dirty') + const untracked = f.addMerged('untracked') + writeFileSync(join(dirty, 'base.txt'), 'changed\n') + writeFileSync(join(untracked, 'extra.txt'), 'new\n') + const result = reapWorktrees(f.db, f.ws, { repo: f.repo, apply: true }) + assert.equal(result.removed.length, 0) + assert.match(result.candidates.find(row => samePath(row.path, dirty))?.reason ?? '', /uncommitted: 1 file/) + assert.match(result.candidates.find(row => samePath(row.path, untracked))?.reason ?? '', /untracked/) + } finally { f.cleanup() } +}) + +test('unmerged and locked worktrees remain in place', () => { + const f = fixture() + try { + const path = join(f.dir, 'unmerged') + git(f.repo, 'worktree', 'add', '-b', 'unmerged', path) + writeFileSync(join(path, 'new.txt'), 'new') + git(path, 'add', '.') + git(path, 'commit', '-m', 'unmerged') + const locked = f.addMerged('locked') + git(f.repo, 'worktree', 'lock', locked, '--reason', 'test hold') + const result = reapWorktrees(f.db, f.ws, { repo: f.repo, apply: true }) + assert.equal(existsSync(path), true) + assert.equal(existsSync(locked), true) + assert.match(result.candidates.find(row => samePath(row.path, path))?.reason ?? '', /not merged/) + assert.match(result.candidates.find(row => samePath(row.path, locked))?.reason ?? '', /locked/) + } finally { f.cleanup() } +}) + +test('an active worker lease prevents reaping its bound worktree', () => { + const f = fixture() + try { + const path = f.addMerged('leased') + registerSwarmAgent(f.db, { agentId: 'worker', workspaceId: f.ws }) + registerWorkerProfile(f.db, { agentId: 'worker', surface: 'other', account: 'owner:chatgpt', worktrees: [path] }) + acquireLease(f.db, f.ws, { agentId: 'worker', taskId: 'reaper-test', paths: ['leased/leased.txt'] }) + recordTurn(f.db, { workspaceId: f.ws, agentId: 'worker', phase: 'end', state: 'needs-task' }) + const result = reapWorktrees(f.db, f.ws, { repo: f.repo, apply: true }) + assert.equal(existsSync(path), true) + assert.match(result.candidates.find(row => samePath(row.path, path))?.reason ?? '', /active lease/) + } finally { f.cleanup() } +}) + +test('missing checkouts stay bound and return to present when their path reappears', () => { + const f = fixture() + try { + const missingPath = join(f.dir, 'moved-checkout') + f.db.prepare('INSERT INTO planets (id, workspace_id, name, root, created_at) VALUES (?, ?, ?, ?, ?)') + .run('planet-reaper', f.ws, 'reaper-test', f.dir, new Date().toISOString()) + f.db.prepare('INSERT INTO repos (id, planet_id, name, common_dir, remote_url, created_at) VALUES (?, ?, ?, ?, ?, ?)') + .run('repo-reaper', 'planet-reaper', 'repo', null, null, new Date().toISOString()) + f.db.prepare(`INSERT INTO checkouts (id, planet_id, repo_id, name, path, rel_prefix, seen_at) + VALUES (?, ?, ?, ?, ?, ?, ?)`).run('checkout-reaper', 'planet-reaper', 'repo-reaper', 'moved-checkout', missingPath, 'Code/moved-checkout', new Date().toISOString()) + registerSwarmAgent(f.db, { agentId: 'missing-worker', workspaceId: f.ws }) + registerWorkerProfile(f.db, { agentId: 'missing-worker', surface: 'other', account: 'owner:chatgpt', worktrees: [missingPath] }) + const preview = synchronizeMissingWorktrees(f.db, f.ws) + assert.equal(preview.some(row => row.path === missingPath), true) + assert.equal((f.db.prepare('SELECT disk_state FROM checkouts WHERE id = ?').get('checkout-reaper') as { disk_state: string }).disk_state, 'present') + const missing = synchronizeMissingWorktrees(f.db, f.ws, true) + assert.equal(missing.some(row => row.path === missingPath), true) + const row = f.db.prepare('SELECT disk_state, retired_at FROM checkouts WHERE id = ?').get('checkout-reaper') as { disk_state: string; retired_at: string | null } + assert.equal(row.disk_state, 'fehlt') + assert.equal(row.retired_at, null) + assert.deepEqual(JSON.parse((f.db.prepare('SELECT worktrees FROM agents WHERE id = ?').get('missing-worker') as { worktrees: string }).worktrees), [missingPath]) + mkdirSync(missingPath) + assert.deepEqual(synchronizeMissingWorktrees(f.db, f.ws, true), []) + assert.equal((f.db.prepare('SELECT disk_state FROM checkouts WHERE id = ?').get('checkout-reaper') as { disk_state: string }).disk_state, 'present') + } finally { f.cleanup() } +}) + +test('dry-run reports missing checkout rows without changing the registry', () => { + const f = fixture() + try { + const missingPath = join(f.dir, 'not-on-disk') + f.db.prepare('INSERT INTO planets (id, workspace_id, name, root, created_at) VALUES (?, ?, ?, ?, ?)') + .run('planet-reaper-preview', f.ws, 'reaper-test', f.dir, new Date().toISOString()) + f.db.prepare('INSERT INTO repos (id, planet_id, name, common_dir, remote_url, created_at) VALUES (?, ?, ?, ?, ?, ?)') + .run('repo-reaper-preview', 'planet-reaper-preview', 'repo', null, null, new Date().toISOString()) + f.db.prepare(`INSERT INTO checkouts (id, planet_id, repo_id, name, path, rel_prefix, seen_at) + VALUES (?, ?, ?, ?, ?, ?, ?)`).run('checkout-reaper-preview', 'planet-reaper-preview', 'repo-reaper-preview', 'missing', missingPath, 'Code/missing', new Date().toISOString()) + const result = reapWorktrees(f.db, f.ws, { repo: f.repo }) + assert.equal(result.missing.some(row => row.path === missingPath), true) + const row = f.db.prepare('SELECT disk_state, retired_at FROM checkouts WHERE id = ?').get('checkout-reaper-preview') as { disk_state: string; retired_at: string | null } + assert.equal(row.disk_state, 'present') + assert.equal(row.retired_at, null) + } finally { f.cleanup() } +}) + +test('automatic reap stays off until the workspace setting is explicitly enabled', () => { + const f = fixture() + try { + assert.equal(isReapAutoEnabled(f.db, f.ws), false) + setReapAuto(f.db, f.ws, true) + assert.equal(isReapAutoEnabled(f.db, f.ws), true) + setReapAuto(f.db, f.ws, false) + assert.equal(isReapAutoEnabled(f.db, f.ws), false) + } finally { f.cleanup() } +}) diff --git a/ui-dist/assets/index-BPzNdedk.css b/ui-dist/assets/index-BPzNdedk.css new file mode 100644 index 0000000..f0a1680 --- /dev/null +++ b/ui-dist/assets/index-BPzNdedk.css @@ -0,0 +1 @@ +@font-face{font-family:Geist Variable;font-style:normal;font-weight:100 900;font-display:swap;src:url(/fonts/geist-latin-wght.woff2) format("woff2")}@font-face{font-family:Geist Mono Variable;font-style:normal;font-weight:100 900;font-display:swap;src:url(/fonts/geist-mono-latin-wght.woff2) format("woff2")}:root,:root[data-theme=dark]{--bg: #070908;--panel: #0D1210;--raised: #131A17;--hover: #1A2320;--ink: #e7e8eb;--muted: #9b9ea6;--faint: #6c6f77;--line: #1F2B25;--line-soft: #16201B;--line-strong: #2C3D34;--accent: #10b981;--accent-hover: #34d399;--accent-ink: #06101f;--accent-soft: rgb(16 185 129 / .12);--pos: #10b981;--neg: #ef4444;--warn: #eab308;--info: #06b6d4;--halo: #000;color-scheme:dark}:root[data-theme=light]{--bg: #f6f6f3;--panel: #fcfcfa;--raised: #efefec;--hover: #e8e8e4;--ink: #16171a;--muted: #585b62;--faint: #8e9097;--line: #e0dfdb;--line-soft: #ecebe7;--line-strong: #cfcec9;--accent: #2f62d0;--accent-hover: #2552b5;--accent-ink: #ffffff;--accent-soft: rgb(47 98 208 / .1);--pos: #0f7b4f;--neg: #c2352b;--warn: #b4690e;--info: #2563eb;--halo: #fff;color-scheme:light}:root{--r-sm: 5px;--r: 7px;--r-lg: 10px;--sans: "Geist Variable", ui-sans-serif, -apple-system, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;--mono: "Geist Mono Variable", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;--ease: cubic-bezier(.2, 0, 0, 1);--dur-fast: .12s;--dur: .18s;--dur-slow: .26s;--fast: var(--dur-fast) var(--ease);--slow: var(--dur-slow) var(--ease);--fs-xs: 11px;--fs-sm: 12px;--fs: 13px;--fs-md: 14px;--fs-lg: 16px;--fs-xl: 20px;--s1: 4px;--s2: 8px;--s3: 12px;--s4: 16px;--s5: 20px;--s6: 24px;--s8: 32px;--z-toolbar: 10;--z-panel: 20;--z-overlay: 40;--z-modal: 100;--shadow-sm: none;--shadow-lg: none;--pad: 20px;--gap: 14px;--border: var(--line);--fg: var(--ink);--card-bg: var(--panel);--font-mono: var(--mono);--ok: var(--pos);--bad: var(--neg)}*{box-sizing:border-box;margin:0;padding:0}html,body,#root{height:100%;overflow:hidden}body{background:var(--bg);color:var(--ink);font:var(--fs)/1.5 var(--sans);font-feature-settings:"cv11","ss01";-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}h1,h2,h3{font-weight:560;letter-spacing:-.015em;line-height:1.25}h1{font-size:16px}h2{font-size:14px}.eyebrow{font-size:10.5px;font-weight:500;letter-spacing:.09em;text-transform:uppercase;color:var(--faint)}.num,.kpi-val,td.n,th.n{font-family:var(--mono);font-variant-numeric:tabular-nums;font-feature-settings:"tnum","zero"}.dim{color:var(--muted)}.faint{color:var(--faint)}.pos{color:var(--pos)}.neg{color:var(--neg)}.warn{color:var(--warn)}.panel{background:var(--panel);border:1px solid var(--line);border-radius:var(--r)}.panel-head{display:flex;align-items:center;gap:10px;padding:12px var(--pad);border-bottom:1px solid var(--line-soft);min-height:44px}.panel-head h2{font-size:13px;font-weight:550}.panel-head .spacer{flex:1}.panel-body{padding:var(--pad)}.btn{font:inherit;font-size:12.5px;font-weight:500;color:var(--ink);background:var(--panel);border:1px solid var(--line);border-radius:var(--r-sm);padding:5px 11px;cursor:pointer;transition:background var(--fast),border-color var(--fast),color var(--fast);white-space:nowrap}.btn:hover{background:var(--raised);border-color:var(--faint)}.btn:active{transform:translateY(.5px)}.btn.primary{background:var(--accent);border-color:var(--accent);color:var(--accent-ink)}.btn.primary:hover{filter:brightness(1.08)}.btn:focus-visible,.seg>*:focus-visible,select:focus-visible,input:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.seg{position:relative;display:inline-flex;background:var(--raised);border:1px solid var(--line);border-radius:var(--r-sm);padding:2px;gap:2px}.seg>*{position:relative;z-index:1;font:inherit;font-size:12px;font-weight:500;color:var(--muted);background:none;border:0;border-radius:3px;padding:4px 10px;cursor:pointer;transition:color var(--fast)}.seg>*.on{color:var(--ink)}.seg>*:hover{color:var(--ink)}.seg .pill{position:absolute;z-index:0;top:2px;bottom:2px;background:var(--panel);border-radius:3px;box-shadow:0 0 0 1px var(--line);transition:transform var(--slow),width var(--slow);pointer-events:none}select,input[type=text],input[type=search],input[type=number],input[type=date]{font:inherit;font-size:12.5px;color:var(--ink);background:var(--panel);border:1px solid var(--line);border-radius:var(--r-sm);padding:5px 9px;outline:none;transition:border-color var(--fast)}select:hover,input:hover{border-color:var(--faint)}select{cursor:pointer}.chip{font-size:11px;font-weight:500;color:var(--muted);background:var(--raised);border:1px solid var(--line);border-radius:99px;padding:2px 9px;cursor:pointer;transition:all var(--fast)}.chip.on{color:var(--accent);border-color:var(--accent);background:var(--accent-soft)}.tag{font-size:10.5px;font-weight:500;font-family:var(--mono);border-radius:3px;padding:1.5px 6px;border:1px solid currentColor;opacity:.9}table{width:100%;border-collapse:collapse;font-size:12.5px}thead th{position:sticky;top:0;z-index:2;background:var(--panel);text-align:left;font-size:10.5px;font-weight:500;letter-spacing:.07em;text-transform:uppercase;color:var(--faint);padding:9px var(--pad);border-bottom:1px solid var(--line);white-space:nowrap;-webkit-user-select:none;user-select:none}thead th.sortable{cursor:pointer}thead th.sortable:hover{color:var(--ink)}thead th .caret{opacity:0;margin-left:4px;transition:opacity var(--fast)}thead th[data-dir] .caret{opacity:1;color:var(--accent)}tbody td{padding:9px var(--pad);border-bottom:1px solid var(--line-soft)}tbody tr{transition:background var(--fast)}tbody tr:hover{background:var(--raised)}tbody tr:last-child td{border-bottom:0}td.n,th.n{text-align:right}[data-reveal]{opacity:0;transform:translateY(6px)}[data-reveal].in{opacity:1;transform:none;transition:opacity .42s var(--ease) calc(var(--i, 0) * 28ms),transform .42s var(--ease) calc(var(--i, 0) * 28ms)}@keyframes kit-pulse{0%{opacity:1}50%{opacity:.25}to{opacity:1}}.live-dot{width:6px;height:6px;border-radius:50%;background:var(--pos);animation:kit-pulse 2.2s var(--ease) infinite}@media (prefers-reduced-motion: reduce){*,*:before,*:after{animation-duration:.001ms!important;transition-duration:.001ms!important}[data-reveal]{opacity:1;transform:none}}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{background:var(--line);border-radius:99px;border:3px solid var(--bg)}::-webkit-scrollbar-thumb:hover{background:var(--faint)}::-webkit-scrollbar-track{background:transparent}.kit-theme{width:30px;height:30px;display:grid;place-items:center;background:none;border:1px solid transparent;border-radius:var(--r-sm);color:var(--muted);cursor:pointer;flex:none;transition:color var(--fast),background var(--fast),border-color var(--fast)}.kit-theme:hover{color:var(--ink);background:var(--raised)}.kit-theme svg{width:15px;height:15px}.kit-theme-float{position:fixed;right:16px;bottom:16px;z-index:9998;width:34px;height:34px;background:var(--panel);border-color:var(--line)}.kit-theme-float:hover{background:var(--raised)}.pb-status,.pb-pill,[class*=status-chip]{font-family:var(--mono);text-transform:uppercase;letter-spacing:.08em}.pb-mobile-nav{display:none}.pb-mobile-menu{position:absolute;top:100%;right:0;background:var(--panel);border:1px solid var(--line);border-radius:var(--r);padding:var(--s1);z-index:var(--z-modal);box-shadow:0 4px 16px #0006;min-width:190px;max-height:min(75vh,540px);overflow-y:auto}.pb-mobile-menu[hidden]{display:none}.pb-mobile-menu .pb-tab-menu-item{display:block;width:100%;text-align:left;padding:8px 16px;border:none;background:transparent;cursor:pointer;color:var(--ink);font-size:var(--fs)}.pb-mobile-menu .pb-tab-menu-item:hover{background:var(--hover)}@media (max-width: 1180px){.pb-tabs--desktop{display:none}.pb-mobile-nav{display:block;position:relative}}.pb-tab-group{position:relative;display:inline-flex;align-items:stretch}.pb-tab-group-menu{display:none;position:absolute;top:100%;left:0;background:var(--panel);border:1px solid var(--line);border-radius:var(--r);padding:var(--s1);z-index:var(--z-modal);box-shadow:0 4px 16px #0006}.pb-tab-group:hover .pb-tab-group-menu,.pb-tab-group[data-active=true] .pb-tab-group-menu{display:block}.pb-tab-menu-item{display:block;width:100%;text-align:left;padding:8px 16px;border:none;background:transparent;cursor:pointer;color:var(--ink);font-size:var(--fs);white-space:nowrap}.pb-tab-menu-item:hover{background:var(--hover)}.pb-view-header{display:flex;align-items:center;justify-content:space-between;padding:12px 20px;border-bottom:1px solid var(--line);background:var(--panel);flex:none;gap:var(--s3)}.pb-view-header h2{margin:0;font-size:var(--fs-md);font-weight:600;color:var(--ink);white-space:nowrap}.pb-view-header p{margin:0;font-size:var(--fs-sm);color:var(--muted);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.brain-empty{padding:90px 24px;color:var(--ink)}.atlas-app{height:100%!important;margin-top:0}.atlas-app>aside{display:grid;grid-template-rows:auto auto auto minmax(0,1fr) auto;overflow:hidden}.atlas-app>aside .listwrap{min-height:0}.atlas-app>aside .foot{flex:none}.brain-vault{position:fixed;z-index:40;top:30px;left:0;right:0;padding:14px 16px 12px;box-sizing:border-box;background:var(--panel);color:var(--ink);border-bottom:1px solid color-mix(in srgb,currentColor 18%,transparent);display:flex;flex-direction:column;gap:8px}.brain-vault__row{display:flex;gap:8px}.brain-vault__row--tools{margin-top:2px}.brain-vault__path{flex:1 1 auto;min-width:0;font:inherit;font-family:Geist Mono Variable,ui-monospace,monospace;font-size:12px;color:var(--ink);background:color-mix(in srgb,var(--ink) 6%,transparent);border:1px solid color-mix(in srgb,currentColor 24%,transparent);border-radius:4px;padding:6px 9px}.brain-vault__path:focus-visible{outline:2px solid currentColor;outline-offset:1px}.brain-vault__open,.brain-vault__reindex{flex:none;font:inherit;font-size:12px;letter-spacing:.03em;padding:6px 12px;cursor:pointer;border-radius:4px;border:1px solid currentColor}.brain-vault__open{color:var(--accent-ink);background:var(--accent);border-color:var(--accent);font-weight:600}.brain-vault__open:disabled{opacity:.35;cursor:default}.brain-vault__reindex{color:currentColor;background:transparent;opacity:.7}.brain-vault__reindex:hover:not(:disabled){opacity:1}.brain-vault__error{color:var(--neg);font-size:12px;margin:0}.brain-vault__progress{color:var(--ink);font-size:12px;margin:0;font-family:Geist Mono Variable,ui-monospace,monospace}.brain-vault__done{color:var(--pos);font-size:12px;margin:0}.brain-landing{max-width:680px;height:100%;overflow:auto;margin:0 auto;padding:96px 24px 48px;color:var(--ink)}.brain-landing__title{font-size:28px;font-weight:600;letter-spacing:-.01em;margin:0 0 10px;color:var(--ink)}.brain-landing__lead{font-size:14px;line-height:1.6;opacity:.85;margin:0 0 22px}.brain-first-run{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;margin:0 0 22px;padding:0;list-style:none;counter-reset:steps}.brain-first-run li{counter-increment:steps;padding:12px;border:1px solid var(--border);border-radius:6px;background:#ffffff06;display:grid;gap:5px;font-size:12px}.brain-first-run strong:before{content:counter(steps) ". ";color:var(--info)}.brain-first-run span{color:var(--faint);line-height:1.4}.brain-shortcuts dl{margin:0}.brain-shortcuts dl div{display:grid;grid-template-columns:120px 1fr;gap:12px;padding:9px 0;border-top:1px solid var(--border)}.brain-shortcuts dd{margin:0;color:var(--faint)}.brain-shortcuts kbd{display:inline-block;margin-right:4px;padding:2px 6px;border:1px solid var(--border);border-radius:3px;font:12px var(--font-mono, monospace);color:var(--ink)}@media (max-width: 700px){.brain-first-run{grid-template-columns:1fr}}.brain-landing .brain-vault{position:static;background:color-mix(in srgb,var(--ink) 4%,transparent);border:1px solid color-mix(in srgb,currentColor 16%,transparent);border-radius:8px}.brain-vault__known{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-top:18px;font-size:12px;opacity:.85}.brain-vault__known-item{font:inherit;font-size:12px;color:currentColor;cursor:pointer;background:transparent;border:1px solid color-mix(in srgb,currentColor 22%,transparent);border-radius:999px;padding:4px 10px;opacity:.8;transition:opacity .14s ease,background-color .14s ease}.brain-vault__known-item:hover{opacity:1;background:color-mix(in srgb,currentColor 10%,transparent)}.brain-vault__known-item em{font-style:normal;opacity:.55}.notes-workbench{position:fixed;top:52px;right:0;bottom:0;left:0;display:grid;grid-template-columns:minmax(230px,29%) 1fr;background:var(--bg);z-index:2}.notes-sidebar{min-width:0;height:100%;display:flex;flex-direction:column;border-right:1px solid var(--border);background:var(--panel);overflow:hidden}.notes-sidebar__head,.notes-create,.notes-search,.notes-tag-drawer{flex:none}.notes-sidebar__head{display:flex;justify-content:space-between;align-items:center;padding:10px 12px;border-bottom:1px solid var(--border)}.notes-sidebar button,.notes-editor button,.notes-editor a{font:inherit;font-size:12px}.notes-sidebar__head button,.notes-editor .primary{background:var(--accent);color:var(--accent-ink);border:0;border-radius:5px;padding:6px 9px;cursor:pointer}.notes-create{display:flex;gap:6px;padding:8px 12px;border-bottom:1px solid var(--border);background:var(--panel)}.notes-create select{flex:1 1 auto;min-width:0;font:inherit;font-size:12px;background:var(--raised);color:var(--ink);border:1px solid var(--line);border-radius:4px;padding:5px 8px;cursor:pointer}.notes-create select:focus-visible{outline:2px solid var(--accent)}.notes-create button{flex:none;font:inherit;font-size:12px;font-weight:500;background:var(--accent);color:var(--accent-ink);border:0;border-radius:4px;padding:5px 12px;cursor:pointer;transition:background-color var(--dur-fast) var(--ease)}.notes-create button:hover:not(:disabled){background:var(--accent-hover)}.notes-search{display:flex;gap:6px;padding:8px 12px;border-bottom:1px solid var(--border)}.notes-search input{flex:1 1 auto;min-width:0;font:inherit;font-size:12px;background:var(--raised);color:var(--ink);border:1px solid var(--line);border-radius:4px;padding:5px 8px}.notes-search input:focus-visible{outline:2px solid var(--accent)}.notes-search button{flex:none;font:inherit;font-size:12px;font-weight:500;background:var(--raised);color:var(--ink);border:1px solid var(--line);border-radius:4px;padding:5px 12px;cursor:pointer;transition:background-color var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease)}.notes-search button:hover:not(:disabled){background:var(--hover);border-color:var(--line-strong)}.notes-tags{display:flex;gap:5px;flex-wrap:wrap}.notes-tags button{background:transparent;color:var(--ink);border:1px solid var(--border);border-radius:99px;padding:2px 7px;font-size:11px;cursor:pointer}.notes-tags .on{border-color:var(--accent);color:var(--accent-hover)}.notes-list,.notes-results{overflow-y:auto;min-height:0}.notes-list{flex:1 1 0}.notes-list button,.notes-results button{display:block;width:100%;text-align:left;color:var(--ink);background:transparent;border:0;border-bottom:1px solid var(--border);padding:10px 12px;cursor:pointer}.notes-list button.on{background:var(--hover);border-left:3px solid var(--accent)}.notes-list strong,.notes-list span,.notes-list small,.notes-results strong,.notes-results span,.notes-results small{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.notes-list span,.notes-list small,.notes-results span,.notes-results small{color:var(--faint);font-size:11px;margin-top:3px}.notes-editor{min-width:0;height:100%;display:flex;flex-direction:column;overflow:hidden}.notes-editor__head{flex:none;display:flex;justify-content:space-between;align-items:center;gap:12px;padding:10px 14px;border-bottom:1px solid var(--border);min-width:0}.notes-editor__head strong,.notes-editor__head span,.notes-editor__head small{display:block}.notes-editor__head .notes-editor__title,.notes-editor__head>div:first-child{min-width:0;flex:1 1 auto;overflow:hidden}.notes-editor__head strong{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13.5px}.notes-editor__head span{font-family:var(--mono);color:var(--faint);font-size:11px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.notes-editor__head small{color:var(--muted);font-size:10.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.notes-editor__head>div:last-child{display:flex;gap:6px;align-items:center;flex-shrink:0}.notes-editor__head a,.notes-editor__head button{color:var(--ink);background:transparent;border:1px solid var(--border);border-radius:5px;padding:5px 8px;text-decoration:none;white-space:nowrap}.notes-editor__head button:disabled{opacity:.45}.notes-frontmatter-bar{flex:none}.notes-content-grid{flex:1 1 50%;min-height:50%;overflow:hidden}.notes-editor textarea{resize:none;height:100%;min-height:0;width:100%;box-sizing:border-box;padding:16px;color:var(--ink);background:var(--bg);border:0;outline:0;font:13px/1.65 var(--mono)}.notes-meta-bar{flex:none;border-top:1px solid var(--border);background:var(--panel)}.notes-meta-toggle{width:100%;display:flex;justify-content:space-between;align-items:center;padding:5px 12px;background:transparent;border:0;color:var(--ink);cursor:pointer;font-size:11px;font-weight:600;letter-spacing:.04em;text-align:left}.notes-meta-toggle:hover{background:var(--hover)}.notes-meta-counts{color:var(--faint);font-size:11px;font-weight:400;font-family:var(--mono)}.notes-editor__meta{flex:0 1 auto;max-height:min(220px,35vh);display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:1px;background:var(--line);border-top:1px solid var(--border);overflow:hidden}.notes-editor__meta section{min-height:80px;max-height:min(220px,35vh);padding:8px 12px;background:var(--panel);overflow-y:auto;overflow-x:hidden}.notes-editor__meta h3{margin:0 0 6px;font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--faint);position:sticky;top:0;background:var(--panel);padding-bottom:2px;z-index:1}.notes-editor__meta button,.notes-editor__meta a,.notes-editor__meta span{display:block;margin:4px 0;color:var(--ink);background:none;border:0;padding:0;text-align:left;text-decoration:none;cursor:pointer;font-size:12px}.notes-editor__meta a{color:var(--accent)}.notes-conflict,.notes-notice{flex:none;margin:0;padding:8px 16px;font-size:12px}.notes-conflict{background:color-mix(in srgb,var(--neg) 15%,transparent);color:var(--neg)}.notes-empty{padding:24px;color:var(--faint)}.notes-empty--initial{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:48px 24px;text-align:center;gap:var(--s2)}.notes-empty__icon{width:36px;height:36px;color:var(--faint);margin-bottom:var(--s2)}.notes-empty--initial h3{margin:0;font-size:var(--fs-lg);font-weight:600;color:var(--ink)}.notes-empty--initial p{margin:0 0 var(--s2);font-size:var(--fs);color:var(--muted);max-width:440px;line-height:1.5}.notes-empty-list{padding:24px 16px;text-align:center;display:flex;flex-direction:column;align-items:center;gap:var(--s2)}.notes-empty-list p{margin:0;font-size:var(--fs-sm);color:var(--muted)}@media (max-width: 860px){.notes-editor__head{align-items:flex-start;flex-direction:column;gap:8px;padding:8px 12px}.notes-editor__head .notes-editor__title,.notes-editor__head>div:first-child{width:100%}.notes-editor__actions{width:100%;justify-content:flex-start;flex-wrap:wrap;gap:6px}}@media (max-width: 780px){.notes-workbench{display:flex!important;flex-direction:column!important;grid-template-columns:none!important;position:relative!important;height:100%!important;overflow-y:auto!important;overflow-x:hidden!important;width:100%!important;max-width:100vw!important}.notes-sidebar{max-height:220px!important;flex:none!important;width:100%!important;border-right:none!important;border-bottom:1px solid var(--border)!important;overflow-y:auto!important}.notes-sidebar__head,.notes-create{flex-wrap:wrap;gap:6px}.notes-create select{flex:1 1 120px}.notes-search{flex-wrap:wrap;gap:6px}.notes-editor{flex:1 1 auto!important;width:100%!important;min-height:400px!important;height:auto!important;display:flex!important;flex-direction:column!important;overflow:visible!important}.notes-content-grid{display:flex!important;flex-direction:column!important;grid-template-columns:none!important;height:auto!important;min-height:300px!important;overflow:visible!important;width:100%!important}.notes-editor-pane{border-right:none!important;width:100%!important;min-height:250px!important;box-sizing:border-box}.notes-editor textarea{min-height:220px!important}.notes-preview-pane{border-right:none!important;width:100%!important;min-height:200px!important;box-sizing:border-box}.notes-backlinks-pane{border-left:none!important;border-top:1px solid var(--border)!important;width:100%!important;min-width:0!important;max-height:220px!important;overflow-y:auto!important;box-sizing:border-box}.notes-frontmatter-bar{flex-wrap:wrap!important;gap:4px!important;padding:6px 10px!important}.notes-editor__meta{grid-template-columns:1fr!important}}.notes-save-status{font-family:var(--mono, monospace);text-transform:uppercase;font-size:11px;letter-spacing:.06em;padding:3px 8px;border-radius:4px;font-weight:600}.notes-save-status--saved{background:color-mix(in srgb,var(--pos) 20%,transparent);color:var(--pos)}.notes-save-status--saving{background:color-mix(in srgb,var(--info) 20%,transparent);color:var(--info)}.notes-save-status--dirty{background:color-mix(in srgb,var(--warn) 20%,transparent);color:var(--warn)}.notes-save-status--conflict{background:color-mix(in srgb,var(--neg) 20%,transparent);color:var(--neg)}.notes-results__section-head{padding:6px 12px;font-size:10px;text-transform:uppercase;letter-spacing:.08em;color:var(--faint);background:color-mix(in srgb,var(--ink) 3%,transparent);font-weight:600}.notes-results__sym-row{display:flex;align-items:center;justify-content:space-between}.notes-results__sym-kind{font-size:10px;padding:1px 5px;border-radius:3px;background:color-mix(in srgb,var(--info) 15%,transparent);color:var(--info);font-family:var(--mono)}.brain-note{position:fixed;left:50%;top:64px;transform:translate(-50%);z-index:30;max-width:min(680px,92vw);padding:8px 14px;font-size:13px;line-height:1.45;text-align:center;border:1px solid currentColor;border-radius:8px;background:color-mix(in srgb,canvas 88%,transparent)}.queue{display:flex;flex-direction:column;gap:18px;padding:18px 22px;height:100%;overflow:hidden}.queue__figures{display:flex;gap:34px;flex:0 0 auto}.queue__figure{display:flex;flex-direction:column;gap:2px}.queue__figure strong{font-size:30px;font-weight:600;line-height:1;font-variant-numeric:tabular-nums}.queue__figure span{font-size:10px;letter-spacing:.14em;opacity:.55}.queue__figure--hot strong{color:var(--warn)}.queue__list{list-style:none;margin:0;padding:0;overflow-y:auto;display:flex;flex-direction:column;gap:1px}.queue__row{display:grid;grid-template-columns:78px minmax(0,1fr) auto auto auto;gap:12px;align-items:baseline;padding:7px 10px;border-radius:5px;background:color-mix(in oklab,currentColor 4%,transparent)}.queue__row--delivered{opacity:.5}.queue__state{font-size:9.5px;letter-spacing:.12em;opacity:.6}.queue__row--pending .queue__state{color:var(--info);opacity:.9}.queue__row--claimed .queue__state{color:var(--pos);opacity:.9}.queue__title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.queue__holder,.queue__path{font-size:11px;opacity:.6}.queue__stale{font-size:9.5px;letter-spacing:.1em;color:var(--warn);border:1px solid currentColor;border-radius:3px;padding:1px 5px}.brain-modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;background:#000000bf;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);z-index:10000;display:grid;place-items:center}.brain-modal{background:var(--bg);border:1px solid var(--border);border-radius:8px;width:90%;max-width:520px;padding:24px;box-shadow:0 20px 40px #00000080}.brain-modal__header{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px}.brain-modal__header h3{margin:0;font-size:16px}.brain-modal__close{background:transparent;border:none;color:var(--faint);font-size:18px;cursor:pointer}.brain-modal__field{margin-bottom:16px}.brain-modal__field label{display:block;font-size:12px;margin-bottom:6px;color:var(--faint)}.brain-modal__input{width:100%;padding:8px 12px;background:var(--card-bg);border:1px solid var(--border);border-radius:4px;color:var(--fg);font-size:13px;box-sizing:border-box}.brain-modal__actions{display:flex;justify-content:flex-end;gap:12px;margin-top:24px}.brain-selection-modal{max-width:640px}.brain-selection-modal__hint{color:var(--faint);font-size:12px;line-height:1.5;margin:0 0 14px}.brain-selection-list{display:grid;gap:7px;max-height:min(42vh,360px);overflow:auto;margin:0;padding:10px;border:1px solid var(--border);border-radius:5px}.brain-selection-list legend{color:var(--faint);font-size:11px;padding:0 4px}.brain-selection-list label{display:flex;align-items:flex-start;gap:8px;padding:7px 8px;border-radius:4px;cursor:pointer}.brain-selection-list label:hover{background:var(--card-bg)}.brain-selection-list label.is-retired{cursor:not-allowed;opacity:.52}.brain-selection-list input{margin-top:3px}.brain-selection-list span{display:grid;gap:2px;min-width:0}.brain-selection-list strong{font:600 12px var(--font-mono, ui-monospace, monospace);color:var(--fg)}.brain-selection-list small{font:10px var(--font-mono, ui-monospace, monospace);color:var(--faint);overflow-wrap:anywhere}.workbench-split{display:flex;height:100%;width:100%}.workbench-pane--side{width:380px;min-width:320px;max-width:500px;border-right:1px solid var(--border);height:100%;overflow-y:auto;background:var(--bg);display:flex;flex-direction:column}.workbench-pane--main{flex:1;height:100%;overflow:hidden;background:var(--card-bg);display:flex;flex-direction:column}@media (max-width: 860px){.workbench-split{flex-direction:column}.workbench-pane--side{width:100%;min-width:0;max-width:100%;max-height:40vh;border-right:none;border-bottom:1px solid var(--border)}.workbench-pane--main{width:100%;flex:1;min-height:50vh}}.atlas-source-overlay{position:absolute;top:16px;bottom:16px;right:16px;width:55%;min-width:500px;background:var(--bg);border:1px solid var(--border);border-radius:8px;box-shadow:0 12px 36px #0009;z-index:500;overflow:hidden;display:flex;flex-direction:column}.source-container{display:flex;flex-direction:column;height:100%;width:100%;background:var(--bg);color:var(--fg)}.source-header{display:flex;align-items:center;justify-content:space-between;padding:10px 16px;background:var(--card-bg);border-bottom:1px solid var(--border);flex-shrink:0;gap:12px}.source-header__meta{display:flex;align-items:center;gap:8px;overflow:hidden;flex-wrap:wrap}.source-header__path{font-weight:600;font-size:13px;color:var(--info);white-space:nowrap}.source-badge{font-size:11px;padding:2px 8px;border-radius:4px;border:1px solid var(--border);background:var(--raised);white-space:nowrap}.source-badge--lang{color:var(--pos);border-color:color-mix(in srgb,var(--pos) 30%,transparent)}.source-badge--git{color:var(--warn);border-color:color-mix(in srgb,var(--warn) 30%,transparent)}.source-badge--agent{display:inline-flex;align-items:center;gap:6px}.source-badge--agent i{display:inline-block;width:7px;height:7px;border-radius:50%}.source-badge--highlight{background:var(--raised);color:var(--ink);border:1px dashed var(--line-strong);font-weight:600}.source-close-btn{background:transparent;border:none;color:var(--faint);font-size:16px;cursor:pointer;padding:4px 8px;border-radius:4px}.source-close-btn:hover{background:var(--hover);color:var(--ink)}.source-body{flex:1;overflow:auto;position:relative;background:var(--bg)}.source-code-view{min-width:100%}.source-table{width:100%;border-collapse:collapse;font-size:12.5px;line-height:1.55}.source-line-row{vertical-align:top}.source-line-row:hover{background:var(--hover)}.source-line-row--highlight{background:var(--accent-soft)!important}.source-line-num{width:44px;min-width:44px;padding:0 10px;text-align:right;color:var(--faint);-webkit-user-select:none;user-select:none;border-right:1px solid var(--line);background:var(--panel);position:sticky;left:0}.source-line-row--highlight .source-line-num{color:var(--accent);font-weight:700}.source-line-code{padding:0 14px;white-space:pre;color:var(--ink)}.source-line-code pre{margin:0;font-family:inherit;white-space:pre}.source-container--error{background:var(--bg)}.source-error-box{padding:40px 24px;text-align:center;max-width:500px;margin:40px auto;background:color-mix(in srgb,var(--neg) 8%,transparent);border:1px solid color-mix(in srgb,var(--neg) 30%,transparent);border-radius:8px}.source-error-icon{font-size:36px;margin-bottom:12px}.source-error-box h3{color:var(--neg);margin:0 0 8px;font-size:16px}.source-error-msg{color:var(--ink);font-size:13px;margin-bottom:16px}.source-error-details{background:var(--panel);padding:10px;border-radius:4px;font-size:11px;color:var(--faint);text-align:left}.source-placeholder{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:var(--faint);text-align:center;padding:24px}.source-placeholder__icon{font-size:48px;margin-bottom:16px;opacity:.6}.explorer-view{display:flex;flex-direction:column;height:100%}.explorer-header{padding:12px 16px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between}.explorer-title{display:flex;align-items:center;gap:8px;font-size:13px}.explorer-stats{font-size:11px;color:var(--faint)}.explorer-search{padding:8px 12px;border-bottom:1px solid var(--border);position:relative}.explorer-search__input{width:100%;padding:6px 28px 6px 10px;background:var(--card-bg);border:1px solid var(--border);border-radius:4px;color:var(--fg);font-size:12px;box-sizing:border-box}.explorer-search__clear{position:absolute;right:18px;top:13px;background:transparent;border:none;color:var(--faint);cursor:pointer;font-size:12px}.explorer-tree{flex:1;overflow-y:auto;padding:6px 0}.tree-actions{display:flex;gap:6px;padding:0 16px 8px}.tree-action-btn{background:#ffffff0d;border:1px solid var(--border);color:var(--faint);font-size:11px;padding:3px 8px;border-radius:4px;cursor:pointer;transition:all .15s ease}.tree-action-btn:hover{background:#ffffff1a;color:var(--ink)}.tree-chevron{font-size:8px;color:var(--faint);width:12px;text-align:center;display:inline-block;-webkit-user-select:none;user-select:none}.tree-item{display:flex;align-items:center;gap:6px;padding:4px 10px;cursor:pointer;font-size:12.5px;-webkit-user-select:none;user-select:none}.tree-item:hover{background:#ffffff0d}.tree-item--active{background:var(--hover)!important;color:var(--accent);font-weight:500}.tree-icon{font-size:13px;flex-shrink:0}.tree-label{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tree-badge{font-size:10px;padding:1px 5px;border-radius:3px;background:var(--raised);color:var(--faint)}.tree-agent-dot{width:6px;height:6px;border-radius:50%;flex-shrink:0}.search-view{display:flex;flex-direction:column;height:100%}.search-view__header{padding:12px 16px;border-bottom:1px solid var(--border)}.search-view__title{display:flex;align-items:center;gap:8px;font-size:13px;flex-wrap:wrap}.search-view__endpoint{font-size:10.5px;background:var(--raised);padding:1px 6px;border-radius:3px;color:var(--faint)}.search-form{padding:12px 16px;border-bottom:1px solid var(--border)}.search-input-group{display:flex;gap:8px}.search-input{flex:1;padding:7px 12px;background:var(--card-bg);border:1px solid var(--border);border-radius:4px;color:var(--fg);font-size:13px}.search-submit-btn{background:var(--accent);color:var(--accent-ink);border:none;padding:7px 14px;border-radius:4px;cursor:pointer;font-weight:500;font-size:12px}.search-submit-btn:disabled{opacity:.5;cursor:not-allowed}.search-error-alert{margin:12px 16px;padding:10px 14px;background:color-mix(in srgb,var(--neg) 15%,transparent);border:1px solid var(--neg);border-radius:4px;color:var(--neg);font-size:12px}.search-results{flex:1;overflow-y:auto;padding:12px 16px}.search-results-summary{font-size:12px;color:var(--faint);margin-bottom:12px}.search-hits-list{display:flex;flex-direction:column;gap:8px}.search-hit-card{display:block;width:100%;text-align:left;font:inherit;color:var(--fg);background:var(--card-bg);border:1px solid var(--border);border-radius:6px;padding:10px 12px;cursor:pointer;transition:border-color .15s}.search-hit-card:hover{border-color:var(--accent);background:var(--hover)}.search-hit-card__head{display:flex;align-items:center;gap:8px;margin-bottom:4px}.search-hit-name{font-weight:600;font-size:13px;color:var(--fg)}.search-hit-kind{font-size:10.5px;padding:1px 6px;border-radius:3px;background:var(--raised);color:var(--info)}.search-hit-line{font-size:11px;color:var(--faint);margin-left:auto}.search-hit-path{display:block;min-width:0;font-size:11.5px;color:var(--faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.search-hit-snippet{display:block;font-size:12px;line-height:1.45;color:var(--fg);opacity:.85;margin:2px 0 0;overflow-wrap:anywhere}.pack-view{display:flex;flex-direction:column;height:100%}.pack-view__header{padding:12px 16px;border-bottom:1px solid var(--border)}.pack-view__title{display:flex;align-items:center;gap:8px;font-size:13px}.pack-view__endpoint{font-size:10.5px;background:#ffffff0f;padding:1px 6px;border-radius:3px;color:var(--faint)}.pack-form{padding:12px 16px;border-bottom:1px solid var(--border)}.pack-form__field label{display:block;font-size:12px;margin-bottom:6px;color:var(--faint)}.pack-input-row{display:flex;gap:8px}.pack-input{flex:1;padding:7px 12px;background:var(--card-bg);border:1px solid var(--border);border-radius:4px;color:var(--fg);font-size:13px}.pack-create-btn{background:var(--accent);color:var(--accent-ink);border:none;padding:7px 14px;border-radius:4px;cursor:pointer;font-weight:500;font-size:12px}.pack-create-btn:disabled{opacity:.5;cursor:not-allowed}.pack-error-alert{margin:12px 16px;padding:10px 14px;background:color-mix(in srgb,var(--neg) 15%,transparent);border:1px solid var(--neg);border-radius:4px;color:var(--neg);font-size:12px}.pack-details{flex:1;overflow-y:auto;padding:12px 16px}.pack-card{background:var(--card-bg);border:1px solid var(--border);border-radius:8px;padding:16px}.pack-card__header{display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--border);padding-bottom:12px;margin-bottom:12px}.pack-card__meta{display:flex;align-items:center;gap:8px}.pack-id{font-weight:700;color:var(--accent)}.pack-badge{font-size:11px;padding:2px 6px;border-radius:4px;background:var(--raised)}.pack-card__staleness{display:flex;align-items:center;gap:8px}.pack-staleness-badge{font-size:12px;font-weight:600;padding:3px 8px;border-radius:4px}.pack-staleness-badge--fresh{background:color-mix(in srgb,var(--pos) 20%,transparent);color:var(--pos)}.pack-staleness-badge--stale{background:color-mix(in srgb,var(--neg) 20%,transparent);color:var(--neg)}.pack-staleness-btn{background:transparent;border:1px solid var(--border);color:var(--fg);padding:3px 8px;border-radius:4px;font-size:11px;cursor:pointer}.pack-stale-warning{background:color-mix(in srgb,var(--neg) 15%,transparent);border:1px solid var(--neg);border-radius:4px;padding:8px 12px;color:var(--neg);font-size:12px;margin-bottom:16px}.pack-sources-section h4{margin:0 0 10px;font-size:13px;color:var(--faint)}.pack-sources-list{display:flex;flex-direction:column;gap:6px}.pack-source-item{padding:8px 10px;background:#ffffff08;border:1px solid var(--border);border-radius:4px;cursor:pointer;transition:border-color .15s}.pack-source-item:hover{border-color:var(--accent);background:var(--hover)}.pack-source-path{font-size:12px;font-weight:600;color:var(--info);margin-bottom:2px}.pack-source-why{font-size:11.5px;color:var(--faint)}.pack-body-toggle{margin-top:16px;padding-top:12px;border-top:1px solid var(--border)}.pack-toggle-raw-btn{background:transparent;border:none;color:var(--accent);font-size:12px;cursor:pointer;padding:0}.pack-raw-markdown{margin-top:10px;background:var(--panel);padding:12px;border-radius:4px;font-size:11.5px;color:var(--ink);max-height:250px;overflow-y:auto}.pack-raw-markdown pre{margin:0;white-space:pre-wrap}.brain-empty--offline-box{text-align:center;max-width:480px;padding:32px;background:color-mix(in srgb,var(--neg) 8%,transparent);border:1px solid color-mix(in srgb,var(--neg) 25%,transparent);border-radius:8px}.offline-icon{font-size:42px;margin-bottom:12px}.brain-empty--offline-box h3{color:var(--neg);margin:0 0 8px}.brain-empty--offline-box p{font-size:13px;color:var(--neg);margin-bottom:16px}.notes-create{display:flex;gap:6px;padding:8px 10px;flex-wrap:wrap}.notes-create select{min-width:0;flex:1 1 90px}.knowledge-graph{height:100%;min-height:0;display:flex;flex-direction:column;padding:18px;gap:10px;outline:none}.knowledge-graph:focus-visible{box-shadow:inset 0 0 0 2px var(--accent)}.knowledge-graph__head{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.knowledge-graph__head h2{margin:0}.knowledge-graph__head p{color:var(--faint);margin:3px 0;font-size:12px}.knowledge-graph__head label{display:grid;gap:3px;font-size:11px;color:var(--faint)}.knowledge-graph__coverage{color:var(--faint);font-size:12px;margin:0}.knowledge-graph__body{min-height:0;flex:1;display:grid;grid-template-columns:minmax(260px,1fr) minmax(220px,.72fr);gap:12px}.knowledge-graph__list{overflow:auto;margin:0;padding:0;list-style:none;border:1px solid var(--border);border-radius:6px}.knowledge-graph__list button{width:100%;display:grid;grid-template-columns:10px 1fr auto;gap:9px;text-align:left;padding:9px 11px;border:0;border-bottom:1px solid var(--border);background:transparent;color:inherit;cursor:pointer}.knowledge-graph__list button:hover,.knowledge-graph__list button.on{background:#60a5fa1f}.knowledge-graph__list i{width:8px;height:8px;border-radius:99px;margin-top:5px}.knowledge-graph__list small,.knowledge-graph__list b{color:var(--faint);font-size:11px}.knowledge-graph__list span{display:grid;gap:2px}.knowledge-graph__empty{padding:16px;color:var(--faint)}.knowledge-graph__inspector{border:1px solid var(--border);border-radius:6px;padding:14px;overflow:auto}.knowledge-graph__inspector h3{margin-top:0}.knowledge-graph__inspector code{display:block;color:var(--accent);overflow-wrap:anywhere;margin:6px 0 10px}.knowledge-graph__inspector dl{margin:0 0 12px}.knowledge-graph__inspector dl div{display:grid;grid-template-columns:82px 1fr;gap:8px;padding:5px 0;border-top:1px solid var(--border)}.knowledge-graph__inspector dt{color:var(--faint)}.knowledge-graph__inspector dd{margin:0;overflow-wrap:anywhere}.knowledge-graph__inspector section{margin-top:16px;display:grid;gap:5px}.knowledge-graph__inspector section button{text-align:left}@media (max-width: 800px){.knowledge-graph__body{grid-template-columns:1fr}.knowledge-graph__inspector{max-height:280px}}.markdown-preview{color:var(--ink);font-family:var(--sans);font-size:13.5px;line-height:1.68;word-break:break-word}.markdown-preview p{margin:0 0 12px}.markdown-preview p:last-child{margin-bottom:0}.markdown-preview .md-empty{color:var(--faint);font-style:italic}.md-heading{color:var(--ink);font-weight:600;letter-spacing:-.02em;margin:18px 0 10px;line-height:1.3}.md-heading:first-child{margin-top:0}.md-heading--1{font-size:20px;border-bottom:1px solid var(--line);padding-bottom:6px}.md-heading--2{font-size:17px;border-bottom:1px solid var(--line-soft);padding-bottom:4px}.md-heading--3{font-size:15px}.md-heading--4{font-size:13.5px}.md-heading--5{font-size:12.5px}.md-heading--6{font-size:12px;color:var(--muted)}.md-hr{border:0;border-top:1px solid var(--line);margin:18px 0}.md-callout{border-radius:6px;padding:10px 14px;margin:14px 0}.md-callout-header{display:flex;align-items:center;gap:8px;font-family:var(--mono);font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;margin-bottom:6px}.md-callout-glyph{font-size:13px;line-height:1}.md-callout-content{font-size:13px;line-height:1.55}.md-callout-content p{margin:0 0 6px}.md-callout-content p:last-child{margin-bottom:0}.md-blockquote{border-left:3px solid var(--line-strong);padding:6px 12px;margin:12px 0;color:var(--muted);background:#ffffff05;border-radius:0 4px 4px 0}.md-blockquote p{margin:0 0 6px}.md-blockquote p:last-child{margin-bottom:0}.md-table-wrap{overflow-x:auto;margin:14px 0;border:1px solid var(--line);border-radius:6px;background:var(--panel)}.md-table{width:100%;border-collapse:collapse;font-size:12px}.md-table th{background:var(--raised);padding:8px 12px;border-bottom:1px solid var(--line);color:var(--muted);font-weight:600;text-transform:uppercase;font-size:10.5px;letter-spacing:.06em}.md-table td{padding:7px 12px;border-bottom:1px solid var(--line-soft);color:var(--ink)}.md-table tr:hover td{background:var(--hover)}.md-table tr:last-child td{border-bottom:0}.md-checklist{list-style:none;padding-left:0;margin:10px 0}.md-checklist-item{display:flex;align-items:flex-start;gap:8px;margin:4px 0;font-size:13px}.md-checklist-item.is-checked .md-checklist-label{text-decoration:line-through;color:var(--faint)}.md-checkbox{margin-top:3px;accent-color:var(--accent);cursor:pointer}.md-ul,.md-ol{margin:10px 0;padding-left:22px}.md-ul li,.md-ol li{margin:3px 0}.md-inline-code{font-family:var(--mono);font-size:11.5px;padding:1.5px 5px;border-radius:4px;background:#ffffff0f;color:var(--info);border:1px solid rgba(255,255,255,.08)}.md-code-link{display:inline-flex;align-items:center;gap:4px;background:color-mix(in srgb,var(--accent) 8%,transparent);border:1px solid color-mix(in srgb,var(--accent) 25%,transparent);border-radius:4px;padding:1px 6px;color:var(--accent-hover);cursor:pointer;font-size:11.5px;font-family:var(--mono);transition:all var(--fast)}.md-code-link:hover{background:color-mix(in srgb,var(--accent) 18%,transparent);border-color:var(--accent)}.md-code-link code{color:inherit;background:transparent;padding:0;border:0}.md-code-link__glyph{font-size:10px;opacity:.75}.md-wikilink{display:inline-flex;align-items:center;gap:2px;background:color-mix(in srgb,var(--info) 8%,transparent);border:1px solid color-mix(in srgb,var(--info) 20%,transparent);border-radius:4px;padding:1px 5px;color:var(--info);cursor:pointer;font-size:12px;transition:all var(--fast)}.md-wikilink:hover{background:color-mix(in srgb,var(--info) 18%,transparent);border-color:var(--info);color:var(--ink)}.md-wikilink__brackets{color:var(--faint);font-family:var(--mono);font-size:11px}.md-wikilink__arrow{font-size:9px;opacity:.7;margin-left:2px}.md-link{color:var(--info);text-decoration:underline;text-underline-offset:3px}.md-link:hover{color:var(--accent-hover)}.md-code-block{margin:14px 0;border-radius:6px;border:1px solid var(--line);background:var(--panel);overflow:hidden}.md-code-header{display:flex;align-items:center;justify-content:space-between;padding:5px 12px;background:var(--raised);border-bottom:1px solid var(--line);font-family:var(--mono);font-size:10.5px;letter-spacing:.06em;text-transform:uppercase}.md-code-lang{color:var(--muted);font-weight:600}.md-code-copy{background:transparent;border:1px solid var(--line);color:var(--muted);border-radius:4px;padding:2px 7px;font-size:10.5px;cursor:pointer;transition:all var(--fast)}.md-code-copy:hover{color:var(--ink);border-color:var(--accent)}.md-code-pre{margin:0;padding:10px 0;overflow-x:auto;font-family:var(--mono);font-size:12px;line-height:1.6}.md-code-line{display:flex;padding:0 12px}.md-code-line:hover{background:#ffffff05}.md-line-num{width:32px;flex:none;color:var(--faint);-webkit-user-select:none;user-select:none;text-align:right;padding-right:12px;font-size:11px}.md-line-code{flex:1;white-space:pre}.md-tok--keyword{color:var(--info);font-weight:500}.md-tok--string{color:var(--pos)}.md-tok--comment{color:var(--faint);font-style:italic}.md-tok--number{color:var(--warn)}.md-tok--type{color:var(--accent)}.md-tok--fn{color:var(--info)}.md-tok--text{color:var(--ink)}.notes-view-mode-toggle{display:inline-flex;background:var(--raised);border:1px solid var(--line);border-radius:6px;padding:2px}.notes-toggle-btn{background:transparent;border:0;color:var(--muted);border-radius:4px;padding:3px 9px;font-size:11px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;cursor:pointer;transition:all var(--fast)}.notes-toggle-btn.on{background:var(--accent);color:var(--accent-ink);font-weight:700}.notes-frontmatter-bar{display:flex;align-items:center;gap:6px;flex-wrap:wrap;padding:6px 16px;background:var(--raised);border-bottom:1px solid var(--line)}.notes-fm-pill{font-family:var(--mono);font-size:10px;letter-spacing:.08em;text-transform:uppercase;padding:2px 7px;border-radius:4px;font-weight:600}.notes-fm-pill--entscheidung{background:color-mix(in srgb,var(--pos) 15%,transparent);color:var(--pos);border:1px solid color-mix(in srgb,var(--pos) 30%,transparent)}.notes-fm-pill--widerspruch{background:color-mix(in srgb,var(--warn) 15%,transparent);color:var(--warn);border:1px solid color-mix(in srgb,var(--warn) 30%,transparent)}.notes-fm-pill--notiz,.notes-fm-pill--stand{background:color-mix(in srgb,var(--muted) 15%,transparent);color:var(--muted);border:1px solid color-mix(in srgb,var(--muted) 30%,transparent)}.notes-fm-btn{display:inline-flex;align-items:center;gap:4px;font-family:var(--mono);font-size:10.5px;letter-spacing:.04em;padding:2px 7px;border-radius:4px;background:transparent;cursor:pointer;transition:all var(--fast)}.notes-fm-btn--link{color:var(--info);border:1px solid color-mix(in srgb,var(--info) 30%,transparent);background:color-mix(in srgb,var(--info) 8%,transparent)}.notes-fm-btn--link:hover{background:color-mix(in srgb,var(--info) 18%,transparent);border-color:var(--info)}.notes-fm-btn--code{color:var(--pos);border:1px solid color-mix(in srgb,var(--pos) 30%,transparent);background:color-mix(in srgb,var(--pos) 8%,transparent)}.notes-fm-btn--code:hover{background:color-mix(in srgb,var(--pos) 18%,transparent);border-color:var(--pos)}.notes-fm-btn--rev{color:var(--accent);border:1px solid color-mix(in srgb,var(--accent) 30%,transparent);background:color-mix(in srgb,var(--accent) 8%,transparent)}.notes-fm-btn--rev:hover{background:color-mix(in srgb,var(--accent) 18%,transparent);border-color:var(--accent)}.notes-tag-drawer{padding:8px 12px;border-bottom:1px solid var(--border)}.notes-tag-drawer__head{display:flex;align-items:center;justify-content:space-between;margin-bottom:6px}.notes-tag-drawer__toggle{background:transparent;border:0;color:var(--muted);cursor:pointer;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;padding:0;display:flex;align-items:center;gap:4px}.notes-tag-drawer__toggle:hover{color:var(--ink)}.notes-tag-drawer__active{display:flex;align-items:center;gap:4px}.notes-tag-chip{font-size:10.5px;font-family:var(--mono);background:var(--accent-soft);color:var(--accent-hover);border:1px solid color-mix(in srgb,var(--accent) 30%,transparent);border-radius:99px;padding:1px 6px}.notes-tag-clear{background:transparent;border:0;color:var(--faint);cursor:pointer;padding:0 2px;font-size:10px}.notes-tag-clear:hover{color:var(--neg)}.notes-tag-drawer__body{display:flex;flex-direction:column;gap:6px;margin-top:6px}.notes-tag-search{width:100%;box-sizing:border-box;font-size:11.5px;padding:4px 8px;border-radius:4px;border:1px solid var(--line);background:var(--raised);color:var(--ink)}.notes-tag-drawer__list{max-height:140px;overflow-y:auto;display:flex;flex-wrap:wrap;gap:4px;padding-top:4px}.notes-tags--compact{display:flex;flex-wrap:wrap;gap:4px}.notes-tags-more{background:transparent;border:1px dashed var(--line);color:var(--faint);border-radius:99px;padding:2px 6px;font-size:10.5px;cursor:pointer}.notes-tags-more:hover{color:var(--ink);border-color:var(--muted)}.notes-overflow-dropdown{position:absolute;top:100%;right:0;margin-top:4px;background:var(--panel);border:1px solid var(--line);border-radius:var(--r);padding:4px;z-index:var(--z-modal);min-width:180px;box-shadow:0 8px 24px #00000080;display:flex;flex-direction:column;gap:2px}.notes-overflow-dropdown button,.notes-overflow-dropdown a{display:block;width:100%;text-align:left;padding:7px 10px;background:transparent;border:0;border-radius:var(--r-sm);color:var(--ink);font-size:var(--fs-sm);cursor:pointer;text-decoration:none;box-sizing:border-box}.notes-overflow-dropdown button:hover:not(:disabled),.notes-overflow-dropdown a:hover{background:var(--hover);color:var(--ink)}.notes-overflow-dropdown button:disabled{opacity:.45;cursor:default}.brain-quick-switcher{max-width:540px;display:flex;flex-direction:column;gap:var(--s2)}.quick-switcher-input-wrap{width:100%}.quick-switcher-input-wrap input{width:100%;font-size:var(--fs);padding:10px 14px}.quick-switcher-list{max-height:320px;overflow-y:auto;display:flex;flex-direction:column;gap:2px;padding:4px 0}.quick-switcher-empty{color:var(--muted);font-size:var(--fs-sm);text-align:center;padding:24px;margin:0}.quick-switcher-item{display:flex;flex-direction:column;align-items:flex-start;width:100%;text-align:left;padding:8px 12px;background:transparent;border:0;border-radius:var(--r-sm);cursor:pointer;gap:2px}.quick-switcher-item:hover,.quick-switcher-item.is-selected{background:var(--hover)}.quick-switcher-item__title{font-size:var(--fs);font-weight:600;color:var(--ink)}.quick-switcher-item__path{font-size:var(--fs-xs);font-family:var(--mono);color:var(--muted)}.quick-switcher-item__tags{font-size:var(--fs-xs);color:var(--accent);margin-top:2px}.quick-switcher-hint{display:flex;align-items:center;gap:var(--s3);padding-top:var(--s2);border-top:1px solid var(--line);font-size:var(--fs-xs);color:var(--faint)}.quick-switcher-hint kbd{background:var(--raised);border:1px solid var(--line);border-radius:3px;padding:1px 4px;font-family:var(--mono);color:var(--muted);margin-right:4px}.brain-settings-modal{max-width:760px;width:95%;max-height:85vh;display:flex;flex-direction:column}.brain-settings-layout{display:grid;grid-template-columns:190px 1fr;gap:24px;min-height:400px;max-height:60vh}.brain-settings-nav{display:flex;flex-direction:column;gap:4px;border-right:1px solid var(--line);padding-right:14px}.brain-settings-nav-item{display:flex;align-items:center;gap:10px;padding:8px 12px;border-radius:4px;background:transparent;border:1px solid transparent;color:var(--muted);font-size:13px;text-align:left;cursor:pointer;transition:background var(--dur-fast),color var(--dur-fast)}.brain-settings-nav-item:hover{background:var(--hover);color:var(--ink)}.brain-settings-nav-item[data-active=true]{background:var(--panel);border-color:var(--line);color:var(--ink);font-weight:600}.brain-settings-content{overflow-y:auto;padding-right:8px}.brain-settings-section-title{margin:0 0 6px;font-size:15px;font-weight:600;color:var(--ink)}.brain-settings-section-desc{margin:0 0 18px;font-size:12px;color:var(--muted);line-height:1.5}.brain-theme-options{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin-bottom:24px}.brain-theme-card{padding:16px 12px;border:1px solid var(--line);border-radius:6px;background:var(--panel);color:var(--ink);cursor:pointer;text-align:center;font-size:13px;transition:border-color var(--dur-fast),background var(--dur-fast)}.brain-theme-card:hover{border-color:var(--line-strong);background:var(--hover)}.brain-theme-card[data-active=true]{border-color:var(--accent);background:var(--accent-soft);color:var(--accent);font-weight:600}.brain-settings-shortcuts-table{width:100%;border-collapse:collapse;font-size:13px}.brain-settings-shortcuts-table tr{border-bottom:1px solid var(--line-soft)}.brain-settings-shortcuts-table td{padding:8px 6px}.brain-settings-shortcuts-table kbd{background:var(--raised);border:1px solid var(--line);border-radius:3px;padding:2px 6px;font-family:var(--mono);font-size:12px;color:var(--ink);margin-right:4px}.brain-settings-vault-card{background:var(--panel);border:1px solid var(--line);border-radius:6px;padding:14px 16px;margin-bottom:16px}.brain-settings-vault-card dl{display:grid;grid-template-columns:120px 1fr;gap:6px 12px;font-size:12px;margin:0}.brain-settings-vault-card dt{color:var(--muted)}.brain-settings-vault-card dd{margin:0;color:var(--ink);font-family:var(--mono);word-break:break-all}@media (max-width: 640px){.brain-settings-layout{grid-template-columns:1fr;gap:16px}.brain-settings-nav{flex-direction:row;overflow-x:auto;border-right:none;border-bottom:1px solid var(--line);padding-right:0;padding-bottom:10px}.brain-theme-options{grid-template-columns:1fr}}.briefing-view{display:flex;flex-direction:column;height:100%;min-height:0;overflow:hidden;background:var(--bg)}.briefing-body{flex:1;overflow-y:auto;padding:20px;max-width:1080px;margin:0 auto;width:100%;box-sizing:border-box}.briefing-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:16px}.briefing-card{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-lg);padding:18px;display:flex;flex-direction:column;gap:12px}.briefing-card--summary{grid-column:1 / -1;background:var(--raised)}.briefing-card__head h3{margin:0;font-size:var(--fs-md);font-weight:600;color:var(--ink)}.briefing-summary-text{margin:0;font-size:var(--fs);line-height:1.6;color:var(--muted)}.briefing-stats-row{display:grid;grid-template-columns:repeat(4,1fr);gap:12px}.briefing-stat{display:flex;flex-direction:column;align-items:center;justify-content:center;background:var(--bg);padding:12px 8px;border-radius:var(--r);border:1px solid var(--line-soft)}.briefing-stat__num{font-family:var(--mono);font-size:20px;font-weight:700;color:var(--accent)}.briefing-stat__label{font-size:var(--fs-xs);text-transform:uppercase;letter-spacing:.05em;color:var(--muted);margin-top:4px}.briefing-languages{margin-top:8px;display:flex;flex-direction:column;gap:6px}.briefing-subhead{font-size:var(--fs-xs);color:var(--muted);text-transform:uppercase}.briefing-lang-chips{display:flex;flex-wrap:wrap;gap:6px}.briefing-lang-chip{font-family:var(--mono);font-size:var(--fs-xs);background:var(--raised);border:1px solid var(--line);padding:2px 8px;border-radius:999px;color:var(--ink)}.briefing-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}.briefing-list-item{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:6px 8px;border-radius:var(--r);background:var(--bg);border:1px solid var(--line-soft);flex-wrap:wrap}.briefing-link-btn{background:transparent;border:none;color:var(--ink);cursor:pointer;display:inline-flex;align-items:center;gap:6px;font-family:var(--mono);font-size:12px;text-align:left;padding:0;word-break:break-all}.briefing-link-btn:hover{color:var(--accent);text-decoration:underline}.briefing-desc{font-size:var(--fs-xs);color:var(--muted)}.briefing-degree-badge{font-family:var(--mono);font-size:11px;background:var(--raised);border:1px solid var(--line-strong);padding:2px 6px;border-radius:var(--r);color:var(--accent);font-weight:600}.briefing-change-meta{display:inline-flex;align-items:center;gap:8px;font-size:var(--fs-xs)}.briefing-kind-badge{font-family:var(--mono);font-size:10px;text-transform:uppercase;padding:1px 5px;border-radius:3px;background:var(--raised);border:1px solid var(--line);color:var(--muted)}.briefing-time{color:var(--faint)}.briefing-card--unavailable{text-align:center;padding:32px 20px;align-items:center}.briefing-unavailable-icon{width:36px;height:36px;color:var(--muted)}.briefing-card--unavailable h3{margin:0;font-size:var(--fs-md);color:var(--ink)}.briefing-card--unavailable p{margin:0;max-width:500px;color:var(--muted);font-size:var(--fs-sm);line-height:1.5}.briefing-empty{margin:0;color:var(--faint);font-size:var(--fs-sm);font-style:italic}@media (max-width: 640px){.briefing-body{padding:12px 8px}.briefing-grid{grid-template-columns:1fr}.briefing-stats-row{grid-template-columns:repeat(2,1fr)}}.ask-modal{width:680px;max-width:calc(100vw - 32px);max-height:85vh;display:flex;flex-direction:column;background:var(--panel);border:1px solid var(--line);border-radius:var(--r-lg);box-shadow:0 16px 48px #00000080;overflow:hidden;position:relative;margin:auto}.ask-modal__header{display:flex;align-items:center;gap:8px;padding:12px 16px;border-bottom:1px solid var(--line);background:var(--raised)}.ask-modal__input-wrap{display:flex;align-items:center;gap:10px;flex:1;min-width:0;position:relative}.ask-modal__search-icon{width:18px;height:18px;color:var(--muted);flex:none}.ask-modal__input{flex:1;border:none;background:transparent;font-size:15px;color:var(--ink);font-family:inherit;outline:none}.ask-modal__input::placeholder{color:var(--faint)}.ask-modal__clear-btn{background:transparent;border:none;color:var(--faint);font-size:14px;cursor:pointer;padding:4px 6px}.ask-modal__clear-btn:hover{color:var(--ink)}.ask-modal__body{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:14px}.ask-modal__state{text-align:center;padding:28px 16px;display:flex;flex-direction:column;align-items:center;gap:8px}.ask-modal__state-icon{width:32px;height:32px;color:var(--muted)}.ask-modal__state h4{margin:0;font-size:var(--fs-md);color:var(--ink)}.ask-modal__state p{margin:0;max-width:440px;color:var(--muted);font-size:var(--fs-sm);line-height:1.5}.ask-modal__empty{display:flex;flex-direction:column;gap:10px}.ask-modal__hint{margin:0;font-size:var(--fs-xs);color:var(--muted);text-transform:uppercase;letter-spacing:.05em}.ask-modal__suggestions{display:flex;flex-direction:column;gap:6px}.ask-modal__suggestion-chip{background:var(--raised);border:1px solid var(--line);border-radius:var(--r);padding:8px 12px;color:var(--ink);font-size:var(--fs-sm);cursor:pointer;text-align:left;transition:background var(--dur-fast),border-color var(--dur-fast)}.ask-modal__suggestion-chip:hover{background:var(--hover);border-color:var(--line-strong)}.ask-modal__response{display:flex;flex-direction:column;gap:14px}.ask-modal__answer-box{background:var(--raised);border:1px solid var(--line);border-radius:var(--r);padding:14px 16px;display:flex;flex-direction:column;gap:8px}.ask-modal__answer{font-size:14.5px;line-height:1.6;margin:0;color:var(--ink)}.ask-modal__meta{display:flex;align-items:center;gap:8px}.ask-modal__confidence{font-family:var(--mono);font-size:11px;padding:2px 6px;border-radius:3px;text-transform:uppercase}.ask-modal__confidence--high{color:var(--pos);background:var(--accent-soft)}.ask-modal__confidence--medium{color:var(--ink);background:var(--panel);border:1px solid var(--line)}.ask-modal__confidence--low{color:var(--muted);background:var(--panel);border:1px dashed var(--line-strong)}.ask-modal__intent-badge{font-family:var(--mono);font-size:11px;color:var(--muted);background:var(--panel);padding:2px 6px;border-radius:3px}.ask-modal__sources h5,.ask-modal__followups h5{margin:0 0 8px;font-size:var(--fs-xs);color:var(--muted);text-transform:uppercase;letter-spacing:.05em}.ask-modal__sources-list{display:flex;flex-direction:column;gap:8px}.ask-modal__source-card{background:var(--bg);border:1px solid var(--line);border-radius:var(--r);padding:10px 12px;cursor:pointer;text-align:left;display:flex;flex-direction:column;gap:4px;transition:border-color var(--dur-fast),background var(--dur-fast)}.ask-modal__source-card:hover{border-color:var(--accent);background:var(--hover)}.ask-modal__source-top{display:flex;align-items:center;gap:8px;font-size:var(--fs-sm)}.ask-modal__source-path{font-family:var(--mono);color:var(--ink)}.ask-modal__source-line{font-family:var(--mono);font-size:var(--fs-xs);color:var(--muted)}.ask-modal__source-symbol{font-size:var(--fs-xs);color:var(--muted)}.ask-modal__source-symbol code{color:var(--accent)}.ask-modal__source-why{font-size:var(--fs-xs);color:var(--faint)}.ask-modal__low-conf-alert{padding:10px 12px;background:var(--raised);border:1px dashed var(--line-strong);border-radius:var(--r);display:flex;align-items:center;justify-content:space-between;gap:8px;font-size:var(--fs-sm)}.ask-modal__followup-list{display:flex;flex-wrap:wrap;gap:6px}.ask-modal__followup-btn{background:var(--raised);border:1px solid var(--line);border-radius:999px;padding:5px 12px;font-size:12px;color:var(--ink);cursor:pointer;transition:background var(--dur-fast),border-color var(--dur-fast)}.ask-modal__followup-btn:hover{background:var(--hover);border-color:var(--line-strong)}.ask-modal__footer{padding:8px 16px;background:var(--raised);border-top:1px solid var(--line);display:flex;gap:16px;font-size:11px;color:var(--faint);flex-wrap:wrap}.ask-modal__footer kbd{font-family:var(--mono);background:var(--panel);border:1px solid var(--line);padding:1px 4px;border-radius:3px;color:var(--ink)}@media (max-width: 640px){.ask-modal{width:100%;max-width:calc(100vw - 16px);max-height:90vh}}.hygiene-view{padding:24px;max-width:1200px;margin:0 auto;display:flex;flex-direction:column;gap:20px;width:100%;box-sizing:border-box}.hygiene-subnav{display:flex;gap:8px;border-bottom:1px solid var(--line);padding-bottom:8px;overflow-x:auto}.hygiene-subnav-btn{background:transparent;border:1px solid transparent;border-radius:var(--r);padding:6px 14px;font-size:13px;font-weight:500;color:var(--muted);cursor:pointer;white-space:nowrap;transition:all var(--dur-fast)}.hygiene-subnav-btn:hover{background:var(--hover);color:var(--ink)}.hygiene-subnav-btn.active{background:var(--raised);border-color:var(--line-strong);color:var(--ink)}.hygiene-summary-banner{padding:16px 20px;border-radius:var(--r);display:flex;align-items:center;gap:16px;flex-wrap:wrap;background:var(--panel);border:1px solid var(--line)}.hygiene-summary-banner.hygiene-risk{border:1px solid var(--neg);background:#ef44440f}.hygiene-summary-banner.hygiene-attention{border:1px dashed var(--line-strong);background:var(--raised)}.hygiene-summary-banner.hygiene-ok{border-left:3px solid var(--pos);border-top:1px solid var(--line);border-right:1px solid var(--line);border-bottom:1px solid var(--line)}.hygiene-summary-text{font-size:14px;font-weight:500;color:var(--ink);flex:1;min-width:200px}.hygiene-summary-meta{font-size:12px;font-family:var(--mono);color:var(--faint)}.hygiene-badge{display:inline-flex;align-items:center;font-family:var(--mono);font-size:11px;text-transform:uppercase;letter-spacing:.04em;padding:3px 8px;border-radius:999px;white-space:nowrap}.hygiene-badge-risk{color:var(--neg);border:1px solid var(--neg);background:#ef44441f}.hygiene-badge-attention{color:var(--ink);border:1px dashed var(--line-strong);background:var(--raised)}.hygiene-badge-ok{color:var(--pos);border:1px solid var(--pos);background:var(--accent-soft)}.hygiene-section-container{display:flex;flex-direction:column;gap:12px}.hygiene-section-heading{font-size:15px;font-weight:600;color:var(--ink);margin:0}.hygiene-findings-list{display:flex;flex-direction:column;gap:10px}.hygiene-finding-row{display:flex;align-items:flex-start;gap:14px;padding:12px 16px;border-radius:var(--r);background:var(--panel);border:1px solid var(--line);flex-wrap:wrap}.hygiene-finding-row.hygiene-risk{border-color:var(--neg)}.hygiene-finding-row.hygiene-attention{border:1px dashed var(--line-strong)}.hygiene-finding-row.hygiene-ok{border-left:3px solid var(--pos)}.hygiene-finding-body{flex:1;min-width:240px;display:flex;flex-direction:column;gap:6px}.hygiene-finding-text{font-size:13.5px;font-weight:500;color:var(--ink)}.hygiene-finding-paths{font-family:var(--mono);font-size:12px;color:var(--muted);word-break:break-all}.hygiene-cmd-row{display:flex;align-items:center;gap:8px;background:var(--raised);border:1px solid var(--line);border-radius:var(--r);padding:6px 10px;margin-top:4px;max-width:100%;overflow-x:auto}.hygiene-cmd{font-family:var(--mono);font-size:12px;color:var(--ink);flex:1;white-space:nowrap;-webkit-user-select:all;user-select:all}.hygiene-copy-btn{background:var(--panel);border:1px solid var(--line);color:var(--ink);font-size:11px;font-family:var(--mono);padding:3px 8px;border-radius:4px;cursor:pointer;white-space:nowrap;transition:all var(--dur-fast)}.hygiene-copy-btn:hover{background:var(--hover);border-color:var(--line-strong)}.hygiene-checkouts-list{display:flex;flex-direction:column;gap:10px}.hygiene-checkout-card{border-radius:var(--r);background:var(--panel);border:1px solid var(--line);overflow:hidden;transition:border-color var(--dur-fast)}.hygiene-checkout-card.hygiene-risk{border-color:var(--neg)}.hygiene-checkout-card.hygiene-attention{border:1px dashed var(--line-strong)}.hygiene-checkout-card.hygiene-ok{border-left:3px solid var(--pos)}.hygiene-checkout-header{width:100%;display:flex;align-items:center;gap:12px;padding:12px 16px;background:transparent;border:none;cursor:pointer;text-align:left;color:inherit;flex-wrap:wrap}.hygiene-checkout-header:hover{background:var(--hover)}.hygiene-checkout-path{font-family:var(--mono);font-size:13px;font-weight:500;color:var(--ink);flex:1;min-width:180px;word-break:break-all}.hygiene-checkout-meta{font-size:12px;color:var(--muted);white-space:nowrap}.hygiene-toggle{font-size:11px;color:var(--faint);margin-left:4px}.hygiene-checkout-detail{padding:14px 16px;background:var(--raised);border-top:1px solid var(--line);display:flex;flex-direction:column;gap:14px}.hygiene-stat-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:10px}.hygiene-stat{display:flex;flex-direction:column;gap:2px;padding:8px 10px;background:var(--panel);border:1px solid var(--line);border-radius:var(--r)}.hygiene-stat-label{font-size:11px;color:var(--faint)}.hygiene-stat-value{font-size:14px;font-weight:600;font-family:var(--mono);color:var(--ink)}.hygiene-stat-warn{color:var(--neg)}.hygiene-stat-full{grid-column:1 / -1;background:transparent;border:none;padding:4px 0}.hygiene-unpushed-row,.hygiene-claim-row{display:flex;align-items:center;gap:10px;font-size:12px;padding:4px 0;border-bottom:1px solid var(--line-soft);flex-wrap:wrap}.hygiene-branch{font-family:var(--mono);font-weight:600;color:var(--ink)}.hygiene-ahead{color:var(--muted)}.hygiene-upstream{font-family:var(--mono);color:var(--faint)}.hygiene-no-upstream{font-family:var(--mono);color:var(--muted);border:1px dashed var(--line-strong);padding:1px 6px;border-radius:3px}.hygiene-agent{font-family:var(--mono);font-weight:600;color:var(--accent)}.hygiene-claim-paths{font-family:var(--mono);color:var(--muted)}.hygiene-conflicts{color:var(--neg);font-weight:500}.hygiene-status-box{padding:32px 20px;text-align:center;border-radius:var(--r);background:var(--panel);border:1px solid var(--line);display:flex;flex-direction:column;align-items:center;gap:10px}.hygiene-offline{border:1px dashed var(--line-strong)}.hygiene-status-title{font-size:15px;font-weight:600;color:var(--ink);margin:0}.hygiene-status-text{font-size:13px;color:var(--muted);max-width:500px;margin:0;line-height:1.5}.hygiene-spinner{width:24px;height:24px;border:2px solid var(--line);border-top-color:var(--accent);border-radius:50%;animation:spin .8s linear infinite}.hygiene-drives-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px}.hygiene-drive-card{padding:14px 16px;border-radius:var(--r);background:var(--panel);border:1px solid var(--line);display:flex;flex-direction:column;gap:8px}.hygiene-drive-card.hygiene-risk{border-color:var(--neg)}.hygiene-drive-card.hygiene-attention{border:1px dashed var(--line-strong)}.hygiene-drive-card.hygiene-ok{border-left:3px solid var(--pos)}.hygiene-drive-header{display:flex;align-items:center;justify-content:space-between}.hygiene-drive-mount{font-family:var(--mono);font-size:16px;font-weight:700;color:var(--ink)}.hygiene-drive-numbers{display:flex;justify-content:space-between;font-size:12px;font-family:var(--mono)}.hygiene-drive-free{color:var(--ink);font-weight:600}.hygiene-drive-total{color:var(--faint)}.hygiene-progress-bar{height:6px;background:var(--raised);border-radius:3px;overflow:hidden}.hygiene-progress-fill{height:100%;background:var(--accent);border-radius:3px;transition:width var(--dur)}.hygiene-forecast-list{display:flex;flex-direction:column;gap:8px}.hygiene-forecast-card{display:flex;align-items:center;gap:12px;padding:10px 14px;border-radius:var(--r);background:var(--panel);border:1px solid var(--line);font-size:13px}.hygiene-forecast-mount{font-family:var(--mono);font-weight:700;color:var(--ink)}.hygiene-forecast-text{color:var(--muted)}.hygiene-modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;background:#000000a6;display:flex;align-items:center;justify-content:center;z-index:999;padding:16px}.hygiene-modal-content{background:var(--panel);border:1px solid var(--line-strong);border-radius:var(--r);max-width:540px;width:100%;overflow:hidden;box-shadow:0 12px 36px #0006}.hygiene-modal-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--line)}.hygiene-modal-title{margin:0;font-size:16px;font-weight:600;color:var(--ink)}.hygiene-modal-close{background:transparent;border:none;color:var(--faint);font-size:16px;cursor:pointer;padding:4px}.hygiene-modal-body{padding:20px;display:flex;flex-direction:column;gap:14px;font-size:13.5px;line-height:1.55;color:var(--ink)}.hygiene-modal-note{padding:12px;background:var(--raised);border:1px dashed var(--line-strong);border-radius:var(--r);font-size:12px;color:var(--muted)}.hygiene-note-title{font-weight:600;color:var(--ink);margin:0 0 4px}.hygiene-modal-footer{padding:12px 20px;background:var(--raised);border-top:1px solid var(--line);display:flex;justify-content:flex-end}@media (max-width: 640px){.hygiene-view{padding:14px}.hygiene-checkout-header{flex-direction:column;align-items:flex-start}}.pb-city{--bg:#ddd3b4;--panel:#e9e1c8;--raised:#d3c9a8;--ink:#33301f;--muted:#6b6448;--faint:#9a9270;--line:#bfb493;--line-soft:#cfc5a5;--accent:#8a4b2a;--accent-ink:#f6f1e0;--accent-soft:#d8c9ae;--r:4px;--r-sm:3px;--k1:#8a4b2a;--k2:#3f6b52;--k3:#4a5f88;--k4:#7a5580;--k5:#8a7a2a;--k6:#7a6f5a;background:var(--bg);color:var(--ink);font-family:var(--mono);font-size:12px;height:100%;position:relative;overflow:hidden}.pb-city #app{display:grid;grid-template-columns:250px 1fr 268px;height:100%}.pb-city #app.closed{grid-template-columns:250px 1fr 0}@media (max-width:1120px){.pb-city #app{grid-template-columns:210px 1fr 240px}}.pb-city aside{border-right:1px solid var(--line);background:var(--panel);display:flex;flex-direction:column;min-height:0}.pb-city .hd{padding:14px 16px 12px;border-bottom:1px solid var(--line)}.pb-city .hd h1{font-size:13.5px;font-weight:600;letter-spacing:.01em}.pb-city .hd .repo{font-size:10px;color:var(--faint);margin-top:5px;letter-spacing:.05em}.pb-city .hd .kpis{display:flex;gap:16px;margin-top:11px}.pb-city .hd .kpis b{display:block;font-size:15px;font-weight:500;letter-spacing:-.02em;font-variant-numeric:tabular-nums}.pb-city .hd .kpis i{font-style:normal;font-size:8.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint)}.pb-city .q{padding:10px 14px;border-bottom:1px solid var(--line-soft)}.pb-city .q input{width:100%;font-family:var(--mono);font-size:11.5px;background:var(--bg);border-color:var(--line);padding:5px 8px}.pb-city .tree{flex:1;min-height:0;overflow:auto;padding:6px 0 12px}.pb-city .tr{display:flex;align-items:center;gap:7px;padding:3.5px 14px;cursor:pointer;border-left:2px solid transparent;white-space:nowrap;transition:background var(--fast)}.pb-city .tr:hover{background:var(--raised)}.pb-city .tr.on{background:var(--raised);border-left-color:var(--accent)}.pb-city .tr .sw{width:7px;height:7px;border-radius:1px;flex:none}.pb-city .tr .nm{flex:1;overflow:hidden;text-overflow:ellipsis;font-size:11.5px}.pb-city .tr .lc{font-size:9.5px;color:var(--faint);font-variant-numeric:tabular-nums}.pb-city .tr.dir .nm{font-weight:600;letter-spacing:.02em}.pb-city .tr.dir .lc{color:var(--muted)}.pb-city canvas{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;display:block;z-index:0;cursor:grab}.pb-city canvas.drag{cursor:grabbing}.pb-city #tip{position:absolute;z-index:3;pointer-events:none;display:none;transform:translate(14px,-50%);background:color-mix(in srgb,var(--panel) 94%,transparent);border:1px solid var(--ink);border-radius:2px;padding:5px 8px;font-size:10.5px;line-height:1.6;white-space:nowrap}.pb-city #tip b{color:var(--accent)}.pb-city #bar{position:absolute;left:50%;bottom:15px;transform:translate(-50%);z-index:4;display:flex;gap:4px;align-items:center;background:color-mix(in srgb,var(--panel) 92%,transparent);border:1px solid var(--line);border-radius:99px;padding:5px 7px;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);width:max-content;max-width:calc(100% - 28px);flex-wrap:wrap;justify-content:center}.pb-city #zlvl{font-family:var(--mono);min-width:44px;letter-spacing:0}.pb-city .tb{font:inherit;font-size:10.5px;color:var(--muted);background:none;border:0;border-radius:99px;padding:5px 10px;cursor:pointer;transition:all var(--fast);white-space:nowrap;letter-spacing:.04em}.pb-city .vsep{width:1px;height:15px;background:var(--line);margin:0 3px}.pb-city #legend{position:absolute;left:16px;bottom:15px;z-index:3;font-size:9.5px;color:var(--muted);line-height:1.9;letter-spacing:.03em;pointer-events:none}.pb-city #legend i{display:inline-block;width:7px;height:7px;border-radius:1px;margin-right:5px}.pb-city #crumb{position:absolute;left:16px;top:13px;z-index:3;font-size:10px;color:var(--faint);letter-spacing:.08em}.pb-city #crumb b{color:var(--ink);font-weight:600}.pb-city #side{border-left:1px solid var(--line);background:var(--panel);overflow:auto;min-width:0}.pb-city .dt{padding:16px 17px}.pb-city .dt .kind{font-size:9.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint)}.pb-city .dt h2{font-size:14px;font-weight:600;margin-top:7px;word-break:break-all;line-height:1.45}.pb-city .dt .note{font-size:11.5px;line-height:1.8;color:var(--muted);margin-top:10px}.pb-city .dt dl{display:grid;grid-template-columns:auto 1fr;gap:4px 12px;margin-top:13px;font-size:11px}.pb-city .dt dt{color:var(--faint)}.pb-city .dt dd{font-variant-numeric:tabular-nums;text-align:right}.pb-city .dt h3{font-size:9.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint);margin:16px 0 7px;padding-top:12px;border-top:1px solid var(--line-soft)}.pb-city .dep{display:flex;align-items:center;gap:7px;padding:4px 7px;margin:0 -7px;border-radius:2px;cursor:pointer;font-size:11px}.pb-city .dep:hover{background:var(--raised)}.pb-city .dep .sw{width:6px;height:6px;border-radius:1px;flex:none}.pb-city .dep span{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;direction:rtl;text-align:left}.pb-city .empty{padding:22px 17px;font-size:11.5px;color:var(--faint);line-height:1.9}.pb-city .empty code{background:var(--bg);border:1px solid var(--line);border-radius:2px;padding:2px 5px;font-size:10px}.pb-city .ws{padding:8px 14px 9px;cursor:pointer;border-left:2px solid transparent;transition:background var(--fast)}.pb-city .ws:hover{background:var(--raised)}.pb-city .ws.on{background:var(--raised);border-left-color:var(--accent)}.pb-city .ws.dying{opacity:.45}.pb-city .wsrow{display:flex;align-items:center;gap:7px;white-space:nowrap}.pb-city .wsrow .sw{width:8px;height:8px;border-radius:1px;flex:none}.pb-city .wsrow .nm{flex:1;overflow:hidden;text-overflow:ellipsis;font-size:11.5px;font-weight:600;letter-spacing:.02em}.pb-city .wsrow .tag{font-size:8px;letter-spacing:.08em;text-transform:uppercase;color:var(--faint);border:1px solid var(--line);border-radius:2px;padding:1px 4px}.pb-city .wsrow .lc{font-size:9.5px;color:var(--faint);font-variant-numeric:tabular-nums}.pb-city .loadbar{height:2px;background:var(--line-soft);border-radius:1px;margin:6px 0 0 15px;overflow:hidden}.pb-city .loadbar i{display:block;height:100%;border-radius:1px;transition:width .5s ease}.pb-city #feed{position:absolute;left:16px;bottom:52px;z-index:3;max-width:min(430px,calc(100% - 32px));font-size:10px;line-height:1.5;pointer-events:none;background:color-mix(in srgb,var(--panel) 88%,transparent);border:1px solid var(--line);border-radius:3px;padding:7px 10px;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.pb-city .fe{display:flex;gap:8px;white-space:nowrap;overflow:hidden;padding:.5px 0}.pb-city .fe .ft{color:var(--faint);font-variant-numeric:tabular-nums;flex:none}.pb-city .fe .fw{flex:none;max-width:110px;overflow:hidden;text-overflow:ellipsis;font-weight:600}.pb-city .fe .fm{color:var(--muted);overflow:hidden;text-overflow:ellipsis}.pb-city #gate{position:absolute;top:0;right:0;bottom:0;left:0;z-index:9;display:none;place-content:center;text-align:center;background:var(--bg);color:var(--muted);font-size:12px;line-height:2;padding:40px}.pb-city #app{display:grid;grid-template-columns:264px 1fr 0;height:100vh;transition:grid-template-columns .34s var(--ease)}.pb-city #app.open{grid-template-columns:264px 1fr 322px}@media (max-width:1180px){.pb-city #app{grid-template-columns:224px 1fr 0}.pb-city #app.open{grid-template-columns:224px 1fr 300px}}.pb-city .atlas-app{height:100%!important;margin-top:0}.pb-city .atlas-app>aside{display:grid;grid-template-rows:auto auto auto minmax(0,1fr) auto;overflow:hidden}.pb-city .atlas-app>aside .listwrap{min-height:0}.pb-city .atlas-app>aside .foot{flex:none}.pb-city aside{border-right:1px solid var(--line);display:flex;flex-direction:column;min-height:0;background:var(--panel)}.pb-city #stage{position:relative;overflow:hidden}.pb-city #drawer{border-left:1px solid var(--line);background:var(--panel);overflow:hidden;min-width:0}.pb-city .brand{padding:18px 18px 14px;border-bottom:1px solid var(--line-soft)}.pb-city .brand h1{font-size:15px;font-weight:560;letter-spacing:-.01em;display:flex;align-items:center;gap:8px}.pb-city .brand h1 .dot{width:7px;height:7px;border-radius:50%;background:var(--accent);box-shadow:0 0 10px var(--accent);flex:none}.pb-city .brand p{font-size:11.5px;color:var(--faint);margin-top:6px;line-height:1.6}.pb-city .searchbox{padding:12px 14px 10px;position:relative}.pb-city .searchbox input{width:100%;background:var(--raised);border-color:var(--line);padding:7px 9px 7px 27px;font-size:12.5px;border-radius:var(--r-sm)}.pb-city .searchbox input::placeholder{color:var(--faint)}.pb-city .searchbox svg{position:absolute;left:23px;top:50%;transform:translateY(-50%);width:12px;height:12px;color:var(--faint);pointer-events:none}.pb-city .legend{padding:2px 14px 12px;display:flex;flex-wrap:wrap;gap:4px}.pb-city .cl{display:flex;align-items:center;gap:5px;font-size:11px;color:var(--muted);background:none;border:1px solid transparent;border-radius:99px;padding:2.5px 8px 2.5px 6px;cursor:pointer;transition:all var(--fast);font-family:inherit}.pb-city .cl:hover{background:var(--raised)}.pb-city .cl i{width:7px;height:7px;border-radius:50%;flex:none;transition:opacity var(--fast)}.pb-city .cl.off{color:var(--faint)}.pb-city .cl.off i{opacity:.22}.pb-city .cl b{font-family:var(--mono);font-size:10px;font-weight:400;color:var(--faint)}.pb-city .listwrap{flex:1;min-height:0;overflow:auto;border-top:1px solid var(--line-soft);padding:6px 0 10px}.pb-city .lrow{display:flex;align-items:center;gap:8px;padding:5.5px 16px;cursor:pointer;border-left:2px solid transparent;transition:background var(--fast)}.pb-city .lrow:hover{background:var(--raised)}.pb-city .lrow.on{background:var(--raised);border-left-color:var(--accent)}.pb-city .lrow i{width:6px;height:6px;border-radius:50%;flex:none}.pb-city .lrow span{flex:1;font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pb-city .lrow b{font-family:var(--mono);font-size:10px;font-weight:400;color:var(--faint)}.pb-city .lrow mark{background:none;color:var(--accent)}.pb-city .foot{border-top:1px solid var(--line-soft);padding:10px 16px 12px;display:grid;grid-template-columns:1fr 1fr;gap:8px 10px}.pb-city .foot .k{font-family:var(--mono);font-size:14px;font-variant-numeric:tabular-nums;letter-spacing:-.02em}.pb-city .foot .l{font-size:9.5px;letter-spacing:.09em;text-transform:uppercase;color:var(--faint);margin-top:1px}.pb-city canvas{display:block;position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;z-index:0}.pb-city #labels,.pb-city #hud,.pb-city #tools,.pb-city #hint,.pb-city #pathbar{z-index:3}.pb-city #gate{z-index:6}.pb-city #labels{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none;overflow:hidden}.pb-city .lab{position:absolute;transform:translate(-50%,-50%);white-space:nowrap;font-size:11px;letter-spacing:.01em;color:var(--ink);text-shadow:0 0 4px var(--bg),0 0 9px var(--bg),0 1px 2px var(--halo);padding-top:0;will-change:transform,opacity}.pb-city .lab.sm{font-size:10px;color:var(--muted)}.pb-city #hud{position:absolute;right:14px;top:12px;text-align:right;pointer-events:none;font-family:var(--mono);font-size:10px;color:var(--faint);line-height:1.75;letter-spacing:.04em}.pb-city #hud b{color:var(--muted);font-weight:400}.pb-city #tools{position:absolute;left:50%;bottom:16px;transform:translate(-50%);display:flex;align-items:center;gap:6px;background:color-mix(in srgb,var(--panel) 88%,transparent);border:1px solid var(--line);border-radius:99px;padding:5px 7px;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);width:max-content;max-width:calc(100% - 28px);flex-wrap:wrap;justify-content:center}.pb-city #tools .sep{width:1px;height:16px;background:var(--line);margin:0 2px}.pb-city #zlvl{min-width:44px;letter-spacing:0}.pb-city .tb{font:500 11px var(--mono);letter-spacing:.05em;color:var(--muted);background:none;border:0;border-radius:99px;padding:5px 10px;cursor:pointer;transition:all var(--fast);white-space:nowrap}.pb-city .tb:hover{color:var(--ink);background:var(--raised)}.pb-city .tb.on{color:var(--accent-ink);background:var(--accent)}.pb-city #hint{position:absolute;left:16px;bottom:16px;font-family:var(--mono);font-size:10px;color:var(--faint);line-height:1.8;pointer-events:none;letter-spacing:.03em}.pb-city #hint kbd{color:var(--muted);font:inherit}.pb-city #pathbar{position:absolute;left:50%;top:14px;transform:translate(-50%);display:none;align-items:center;gap:8px;max-width:min(70%,760px);background:color-mix(in srgb,var(--panel) 90%,transparent);border:1px solid var(--accent);border-radius:99px;padding:6px 8px 6px 13px;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);font-size:11.5px;overflow:hidden}.pb-city #pathbar.on{display:flex}.pb-city #pathbar .chain{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--ink)}.pb-city #pathbar .chain em{font-style:normal;color:var(--faint);margin:0 5px}.pb-city #pathbar .x{font:inherit;color:var(--faint);background:none;border:0;cursor:pointer;padding:0 4px}.pb-city #pathbar .x:hover{color:var(--ink)}.pb-city .dr{width:100%;height:100%;display:flex;flex-direction:column;min-height:0}.pb-city .dr-head{padding:18px 18px 14px;border-bottom:1px solid var(--line-soft)}.pb-city .dr-head .kind{display:flex;align-items:center;gap:6px;font-family:var(--mono);font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint)}.pb-city .dr-head .kind i{width:7px;height:7px;border-radius:50%}.pb-city .dr-head h2{font-size:17px;font-weight:560;letter-spacing:-.02em;margin-top:9px}.pb-city .dr-head p{font-size:12.5px;line-height:1.75;color:var(--muted);margin-top:9px}.pb-city .prov{display:grid;grid-template-columns:auto 1fr;gap:4px 12px;margin-top:12px;padding-top:10px;border-top:1px solid var(--line-soft)}.pb-city .prov dt{font-family:var(--mono);font-size:9.5px;letter-spacing:.09em;text-transform:uppercase;color:var(--faint);padding-top:1px}.pb-city .prov dd{font-size:11.5px;color:var(--muted);line-height:1.5;word-break:break-all}.pb-city .prov dd.mono{font-family:var(--mono);font-size:10.5px}.pb-city .prov dd.bad{color:var(--neg);font-weight:500}.pb-city #hud-sys{margin-top:2px}.pb-city .dr-body{flex:1;min-height:0;overflow:auto;padding:4px 18px 18px}.pb-city .dr-sec{margin-top:18px}.pb-city .dr-sec h3{font-family:var(--mono);font-size:9.5px;letter-spacing:.11em;text-transform:uppercase;color:var(--faint);font-weight:400;margin-bottom:7px;display:flex;gap:6px;align-items:baseline}.pb-city .dr-sec h3 b{font-weight:400;color:var(--faint);opacity:.6}.pb-city .nb{display:flex;align-items:center;gap:7px;padding:5px 7px;margin:0 -7px;border-radius:var(--r-sm);cursor:pointer;transition:background var(--fast)}.pb-city .nb:hover{background:var(--raised)}.pb-city .nb i{width:6px;height:6px;border-radius:50%;flex:none}.pb-city .nb span{font-size:12px;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pb-city .nb u{font-family:var(--mono);font-size:9.5px;color:var(--faint);text-decoration:none;letter-spacing:.05em}.pb-city .dr-act{display:flex;gap:7px;padding:12px 18px;border-top:1px solid var(--line-soft)}.pb-city .dr-act .btn{flex:1;background:var(--raised);border-color:var(--line);font-size:11.5px;padding:6px 8px}.pb-city .dr-act .btn:hover{border-color:var(--faint);background:var(--raised)}.pb-city .dr-act .btn.primary{background:var(--accent);border-color:var(--accent);color:var(--accent-ink)}.pb-city #gate{position:absolute;top:0;right:0;bottom:0;left:0;display:none;place-content:center;text-align:center;background:var(--bg);color:var(--muted);font-size:13px;line-height:2;padding:40px}.pb-mesh{--bg:#0a0b0d;--panel:#101216;--raised:#181b21;--ink:#dfe3e8;--muted:#98a0aa;--faint:#5f6772;--line:#232830;--line-soft:#181c22;--accent:#cfd9e4;--accent-ink:#0b0e12;--accent-soft:#1a1f27;--ok:#5cc9a0;--warn:#e0a355;--bad:#e0685f;--r:5px;--r-sm:3px;background:var(--bg);color:var(--ink);font-size:12.5px;-webkit-font-smoothing:antialiased;-webkit-user-select:none;user-select:none;height:100%;position:relative;overflow:auto}.pb-mesh #glow,.pb-mesh #field{position:fixed;top:0;right:0;bottom:0;left:0;width:100%;height:100%;display:block}.pb-mesh #glow{z-index:0}.pb-mesh #field{z-index:1;cursor:crosshair}.pb-mesh .ov{position:fixed;z-index:5}.pb-mesh #hud{left:22px;top:18px;width:220px}.pb-mesh #hud h1{font-size:14px;font-weight:560;letter-spacing:-.01em;display:flex;align-items:center;gap:8px}.pb-mesh #hud h1 i{width:6px;height:6px;border-radius:50%;background:var(--accent);flex:none;font-style:normal}.pb-mesh #hud h1 em{font-family:var(--mono);font-size:9px;font-style:normal;letter-spacing:.16em;text-transform:uppercase;color:var(--faint);margin-left:auto}.pb-mesh #hud .tally{font-family:var(--mono);font-size:10px;letter-spacing:.05em;color:var(--faint);margin-top:7px;line-height:1.75}.pb-mesh #hud .tally b{color:var(--ink);font-weight:500;font-variant-numeric:tabular-nums}.pb-mesh #roster{left:22px;top:80px;width:220px;max-height:calc(100vh - 196px);overflow:auto;scrollbar-width:thin;scrollbar-color:var(--line) transparent}.pb-mesh .grp{margin-bottom:11px}.pb-mesh .grp>h2{font-family:var(--mono);font-size:9px;letter-spacing:.14em;text-transform:uppercase;color:var(--faint);font-weight:400;display:flex;align-items:center;gap:6px;padding:0 0 5px;border-bottom:1px solid var(--line-soft);margin-bottom:4px}.pb-mesh .grp>h2 i{width:5px;height:5px;border-radius:1px;flex:none;font-style:normal}.pb-mesh .grp>h2 .sp{flex:1}.pb-mesh .grp>h2 button{font:inherit;font-size:11px;line-height:1;color:var(--faint);background:none;border:1px solid var(--line);border-radius:2px;width:15px;height:15px;cursor:pointer;padding:0;transition:color var(--fast),border-color var(--fast)}.pb-mesh .grp>h2 button:hover:not(:disabled){color:var(--accent);border-color:var(--accent)}.pb-mesh .grp>h2 button:disabled{opacity:.3;cursor:default}.pb-mesh .ag{display:flex;align-items:center;gap:7px;padding:3px 5px 3px 3px;border-radius:var(--r-sm);cursor:pointer;transition:background var(--fast)}.pb-mesh .ag:hover{background:var(--raised)}.pb-mesh .ag.on{background:var(--accent-soft)}.pb-mesh .ag.down{opacity:.34}.pb-mesh .ag canvas{width:20px;height:20px;flex:none}.pb-mesh .ag .nm{font-family:var(--mono);font-size:10px;letter-spacing:.04em;color:var(--ink);flex:none;width:21px}.pb-mesh .ag .st{font-family:var(--mono);font-size:9.5px;letter-spacing:.06em;color:var(--muted);flex:1;white-space:nowrap;overflow:hidden}.pb-mesh .ag .q{font-family:var(--mono);font-size:9px;color:var(--accent);flex:none}.pb-mesh .ag .bar{width:26px;height:2px;background:var(--line);flex:none;border-radius:1px;overflow:hidden}.pb-mesh .ag .bar i{display:block;height:100%;background:var(--muted)}.pb-mesh #inspect{right:20px;top:18px;width:264px;background:color-mix(in srgb,var(--panel) 88%,transparent);border:1px solid var(--line);border-radius:var(--r);padding:13px 14px 14px;-webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px);display:none}.pb-mesh #inspect.on{display:block}.pb-mesh #inspect h3{font-size:13px;font-weight:560;display:flex;align-items:center;gap:7px}.pb-mesh #inspect h3 i{width:6px;height:6px;border-radius:50%;flex:none;font-style:normal}.pb-mesh #inspect h3 .x{margin-left:auto;font:inherit;font-size:12px;color:var(--faint);background:none;border:0;cursor:pointer;padding:0 2px}.pb-mesh #inspect h3 .x:hover{color:var(--ink)}.pb-mesh #inspect .kv{display:grid;grid-template-columns:auto 1fr;gap:3px 12px;margin-top:10px;font-family:var(--mono);font-size:10.5px}.pb-mesh #inspect .kv span{color:var(--faint);letter-spacing:.05em}.pb-mesh #inspect .kv b{color:var(--ink);font-weight:500;text-align:right;font-variant-numeric:tabular-nums}.pb-mesh .hist{display:flex;gap:1px;height:16px;margin-top:11px;align-items:flex-end}.pb-mesh .hist i{flex:1;border-radius:.5px;min-height:2px}.pb-mesh #inspect .note{font-size:10.5px;color:var(--faint);line-height:1.6;margin-top:9px;padding-top:9px;border-top:1px solid var(--line-soft)}.pb-mesh #inspect .act{margin-top:10px;display:flex;gap:6px}.pb-mesh .btn{font:inherit;font-size:11px;color:var(--muted);background:var(--raised);border:1px solid var(--line);border-radius:var(--r-sm);padding:4px 9px;cursor:pointer;transition:color var(--fast),border-color var(--fast);flex:1}.pb-mesh .btn:hover{border-color:var(--faint);color:var(--ink)}.pb-mesh .btn.on{background:var(--accent);border-color:var(--accent);color:var(--accent-ink)}.pb-mesh #ctl{left:0;right:0;bottom:0;border-top:1px solid var(--line);background:color-mix(in srgb,var(--panel) 90%,transparent);-webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px);display:flex;align-items:center;gap:18px;padding:9px 20px}.pb-mesh #ctl .fld{display:flex;align-items:center;gap:8px;font-family:var(--mono);font-size:10px;letter-spacing:.06em;color:var(--faint);white-space:nowrap}.pb-mesh #ctl .fld b{color:var(--ink);font-weight:500;min-width:42px;font-variant-numeric:tabular-nums}.pb-mesh #ctl input[type=range]{width:100px;accent-color:var(--accent);height:14px}.pb-mesh #ctl .sp{flex:1}.pb-mesh .seg{display:inline-flex;border:1px solid var(--line);border-radius:var(--r-sm);overflow:hidden}.pb-mesh .seg button{font:inherit;font-size:11px;color:var(--muted);background:none;border:0;padding:3px 9px;cursor:pointer;transition:background var(--fast),color var(--fast)}.pb-mesh .seg button.on{background:var(--accent);color:var(--accent-ink)}.pb-mesh #stats{display:flex;gap:15px;align-items:baseline}.pb-mesh #stats .m{font-family:var(--mono);font-variant-numeric:tabular-nums}.pb-mesh #stats .m u{display:block;font-size:9px;letter-spacing:.12em;text-transform:uppercase;color:var(--faint);text-decoration:none;white-space:nowrap}.pb-mesh #stats .m b{font-size:15px;font-weight:500;letter-spacing:-.01em;white-space:nowrap}.pb-mesh #stats .m b s{font-size:9.5px;color:var(--faint);text-decoration:none;margin-left:2px}.pb-mesh #verdict{font-size:11px;color:var(--muted);line-height:1.5;width:320px;flex:none}.pb-mesh #verdict b{color:var(--accent);font-weight:500}.pb-mesh #tip{position:fixed;left:22px;bottom:58px;z-index:5;font-family:var(--mono);font-size:9.5px;letter-spacing:.04em;color:var(--faint);pointer-events:none}@media (max-width:1080px){.pb-mesh #roster,.pb-mesh #tip{display:none}.pb-mesh #hud{width:auto}.pb-mesh #verdict{display:none}}.pb-mesh #mesh-root{font-family:var(--mono);z-index:6;position:fixed;top:0;right:0;bottom:0;left:0;pointer-events:none}.pb-mesh #mesh-root>*{pointer-events:auto}.pb-mesh #mesh-toggle{position:fixed;right:20px;bottom:58px;font:inherit;font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--faint);background:none;border:1px solid var(--line);border-radius:var(--r-sm);padding:4px 10px;cursor:pointer;transition:color var(--fast),border-color var(--fast)}.pb-mesh #mesh-toggle:hover{color:var(--ink);border-color:var(--faint)}.pb-mesh #mesh-toggle.on{color:var(--accent);border-color:var(--accent)}.pb-mesh #mesh-panel{position:fixed;right:20px;bottom:92px;width:300px;max-height:min(52vh,430px);display:flex;flex-direction:column;background:color-mix(in srgb,var(--panel) 90%,transparent);border:1px solid var(--line);border-radius:var(--r);-webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px);transition:opacity var(--slow),transform var(--slow)}.pb-mesh #mesh-panel[aria-hidden=true]{opacity:0;transform:translateY(8px);pointer-events:none}.pb-mesh .mp-head{display:flex;align-items:center;gap:8px;padding:10px 12px;border-bottom:1px solid var(--line-soft)}.pb-mesh .mp-head h3{font-size:11px;font-weight:500;letter-spacing:.08em;text-transform:uppercase}.pb-mesh .mp-count{font-size:9.5px;color:var(--faint);letter-spacing:.06em}.pb-mesh .mp-x{margin-left:auto;font:inherit;font-size:11px;color:var(--faint);background:none;border:0;cursor:pointer}.pb-mesh .mp-x:hover{color:var(--ink)}.pb-mesh .mp-list{overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--line) transparent;padding:4px 0}.pb-mesh .mp-row{display:flex;align-items:center;gap:8px;padding:4px 12px;font-size:10px;letter-spacing:.03em}.pb-mesh .mp-hue{width:7px;height:7px;border-radius:50%;flex:none}.pb-mesh .mp-name{color:var(--ink);flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pb-mesh .mp-name s{color:var(--faint);text-decoration:none;font-size:9px}.pb-mesh .mp-state{color:var(--muted);flex:none;font-size:9.5px}.pb-mesh .mp-state.off{color:var(--faint)}.pb-mesh .mp-task{color:var(--accent);flex:none;font-size:9.5px;min-width:22px;text-align:right}.pb-mesh .mp-bar{width:34px;height:2px;background:var(--line);border-radius:1px;overflow:hidden;flex:none}.pb-mesh .mp-bar i{display:block;height:100%}.pb-mesh .mp-foot{padding:7px 12px;border-top:1px solid var(--line-soft);font-size:8.5px;letter-spacing:.06em;color:var(--faint)}.pb-mesh #mesh-feed{position:fixed;left:22px;bottom:82px;width:340px;pointer-events:none;display:flex;flex-direction:column;gap:2px}.pb-mesh .mf-line{display:flex;align-items:center;gap:7px;font-size:9.5px;letter-spacing:.03em;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pb-mesh .mf-line s{color:var(--faint);text-decoration:none;font-size:8.5px;flex:none}.pb-mesh .mf-line i{width:5px;height:5px;border-radius:50%;flex:none}.pb-mesh .mf-info{background:var(--muted)}.pb-mesh .mf-ok{background:var(--ok)}.pb-mesh .mf-warn{background:var(--warn)}.pb-mesh .mf-bad{background:var(--bad)}@media (max-width:1080px){.pb-mesh #mesh-feed,.pb-mesh #mesh-panel,.pb-mesh #mesh-toggle{display:none}}.pb-mesh .mesh-trace{box-sizing:border-box;min-height:100%;max-width:1480px;margin:0 auto;padding:24px;color:var(--ink);font-family:var(--mono);line-height:1.5}.pb-mesh .mesh-trace--empty{display:grid;align-content:center;justify-items:start;gap:10px}.pb-mesh .mesh-trace--empty h2,.pb-mesh .mesh-trace__header h2{margin:0;font-size:18px;font-weight:600}.pb-mesh .mesh-trace--empty p,.pb-mesh .mesh-trace__header p{margin:0;color:var(--muted);max-width:760px}.pb-mesh .mesh-trace__muted{color:var(--faint)!important}.pb-mesh .mesh-trace__header{display:flex;justify-content:space-between;gap:24px;align-items:flex-start;margin-bottom:20px}.pb-mesh .mesh-trace__header h2 span{color:var(--faint);font-size:10px;font-weight:400;letter-spacing:.13em;text-transform:uppercase}.pb-mesh .mesh-trace__totals{display:flex;gap:18px;margin:0}.pb-mesh .mesh-trace__totals div{min-width:78px}.pb-mesh .mesh-trace__totals dt{color:var(--faint);font-size:9px;letter-spacing:.1em;text-transform:uppercase}.pb-mesh .mesh-trace__totals dd{margin:2px 0 0;color:var(--ink);font-size:20px;font-variant-numeric:tabular-nums}.pb-mesh .mesh-trace__notice{border:1px solid color-mix(in srgb,var(--warn) 42%,var(--line));background:color-mix(in srgb,var(--warn) 7%,transparent);padding:12px 14px;margin-bottom:16px;color:var(--muted)}.pb-mesh .mesh-trace__notice strong{color:var(--warn)}.pb-mesh .mesh-trace__notice ul{margin:7px 0 0;padding-left:18px}.pb-mesh .mesh-trace__grid{display:grid;grid-template-columns:minmax(320px,1fr) minmax(320px,1fr);gap:16px}.pb-mesh .mesh-trace__panel,.pb-mesh .mesh-trace__detail{border:1px solid var(--line);background:color-mix(in srgb,var(--panel) 92%,transparent)}.pb-mesh .mesh-trace__panel h3{margin:0;padding:10px 12px;border-bottom:1px solid var(--line);font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted)}.pb-mesh .mesh-trace__panel h3 span{color:var(--ink);float:right;font-variant-numeric:tabular-nums}.pb-mesh .mesh-trace__nodes,.pb-mesh .mesh-trace__edges{list-style:none;padding:0;margin:0}.pb-mesh .mesh-trace__nodes li+li,.pb-mesh .mesh-trace__edges li+li{border-top:1px solid var(--line-soft)}.pb-mesh .mesh-trace__node{width:100%;box-sizing:border-box;display:grid;grid-template-columns:76px minmax(100px,1fr) auto;gap:5px 10px;text-align:left;padding:10px 12px;border:0;color:inherit;background:transparent;cursor:pointer;font:inherit}.pb-mesh .mesh-trace__node:hover,.pb-mesh .mesh-trace__node.is-selected{background:var(--accent-soft)}.pb-mesh .mesh-trace__kind,.pb-mesh .mesh-trace__edge-kind{color:var(--accent);font-size:9px;letter-spacing:.1em;text-transform:uppercase}.pb-mesh .mesh-trace__label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pb-mesh .mesh-trace__events,.pb-mesh .mesh-trace__provenance{color:var(--faint);font-size:10px}.pb-mesh .mesh-trace__proof{grid-column:2 / -1;color:var(--muted);font-size:10px}.pb-mesh .mesh-trace__edges li{display:grid;grid-template-columns:84px minmax(0,1fr) auto minmax(0,1fr);gap:5px 8px;padding:10px 12px;color:var(--muted)}.pb-mesh .mesh-trace__edges li>span:not(.mesh-trace__edge-kind){overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pb-mesh .mesh-trace__edges small{grid-column:2 / -1;color:var(--faint);overflow-wrap:anywhere}.pb-mesh .mesh-trace__detail{margin-top:16px;padding:14px}.pb-mesh .mesh-trace__detail-head{display:flex;justify-content:space-between;gap:12px;align-items:start;border-bottom:1px solid var(--line);padding-bottom:10px}.pb-mesh .mesh-trace__detail-head h3{margin:3px 0 0;font-size:15px;font-family:inherit;overflow-wrap:anywhere}.pb-mesh .mesh-trace__detail-head button,.pb-mesh .mesh-trace__source{color:var(--ink);background:transparent;border:1px solid var(--line);cursor:pointer;font:inherit}.pb-mesh .mesh-trace__detail-head button{font-size:18px;width:30px;height:30px}.pb-mesh .mesh-trace__detail dl{display:grid;grid-template-columns:minmax(120px,180px) minmax(0,1fr);margin:14px 0;font-size:11px}.pb-mesh .mesh-trace__detail dl div{display:contents}.pb-mesh .mesh-trace__detail dt,.pb-mesh .mesh-trace__detail dd{border-bottom:1px solid var(--line-soft);padding:6px 0;overflow-wrap:anywhere}.pb-mesh .mesh-trace__detail dt{color:var(--faint)}.pb-mesh .mesh-trace__detail dd{margin:0;color:var(--muted)}.pb-mesh .mesh-trace__source{padding:7px 10px;border-radius:var(--r-sm)}.pb-mesh .mesh-trace__source:hover{border-color:var(--accent)}.pb-mesh .mesh-trace__timeline{margin-top:16px;border-top:1px solid var(--line);padding-top:12px}.pb-mesh .mesh-trace__timeline h4{margin:0 0 7px;color:var(--muted);font-size:10px;letter-spacing:.1em;text-transform:uppercase}.pb-mesh .mesh-trace__timeline p{color:var(--faint);margin:0}.pb-mesh .mesh-trace__timeline ol{list-style:none;padding:0;margin:8px 0 0}.pb-mesh .mesh-trace__timeline li{display:grid;grid-template-columns:auto auto minmax(0,1fr);gap:7px;padding:5px 0;color:var(--muted);font-size:10px}.pb-mesh .mesh-trace__timeline code{color:var(--accent)}.pb-mesh .mesh-trace__timeline time{color:var(--faint)}.pb-mesh .mesh-trace__timeline span{overflow-wrap:anywhere}@media (max-width: 800px){.pb-mesh .mesh-trace{padding:16px}.pb-mesh .mesh-trace__header,.pb-mesh .mesh-trace__totals{flex-direction:column}.pb-mesh .mesh-trace__grid{grid-template-columns:1fr}.pb-mesh .mesh-trace__timeline li{grid-template-columns:1fr;gap:1px}}.pb-graph{--graph-bg: var(--bg);--graph-edge: #868992;--graph-accent: var(--accent);--graph-label: #d4d6db;--graph-label-halo: var(--bg);--graph-other: #5d6068;--graph-g0: #8fb4f2;--graph-g1: #7ccfa9;--graph-g2: #dcb06a;--graph-g3: #cf9ad4;--graph-g4: #72c4cf;--graph-g5: #de8f88;--graph-g6: #b3bd72;--graph-g7: #a79fe2;--side-w: 288px;--inspector-w: 360px;display:grid;grid-template-columns:var(--side-w) minmax(0,1fr);height:100%;min-height:0;background:var(--bg);color:var(--ink)}.pb-graph[data-inspector=open]{grid-template-columns:var(--side-w) minmax(0,1fr) var(--inspector-w)}.pb-graph .pb-dot{width:8px;height:8px;border-radius:50%;flex:none;display:inline-block;background:var(--graph-other)}.pb-graph .pb-dot[data-slot="0"]{background:var(--graph-g0)}.pb-graph .pb-dot[data-slot="1"]{background:var(--graph-g1)}.pb-graph .pb-dot[data-slot="2"]{background:var(--graph-g2)}.pb-graph .pb-dot[data-slot="3"]{background:var(--graph-g3)}.pb-graph .pb-dot[data-slot="4"]{background:var(--graph-g4)}.pb-graph .pb-dot[data-slot="5"]{background:var(--graph-g5)}.pb-graph .pb-dot[data-slot="6"]{background:var(--graph-g6)}.pb-graph .pb-dot[data-slot="7"]{background:var(--graph-g7)}.pb-graph .pb-heading{font-size:var(--fs-xs);font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--faint);margin:var(--s5) 0 var(--s2);display:flex;align-items:center;gap:var(--s2)}.pb-graph .pb-quiet{color:var(--faint);font-size:var(--fs-sm);padding:var(--s2) var(--s2);line-height:1.5}.pb-graph__side{display:flex;flex-direction:column;min-height:0;padding:var(--s4) var(--s3) 0;border-right:1px solid var(--line);background:var(--panel)}.pb-search{display:flex;align-items:center;gap:var(--s2);height:36px;padding:0 var(--s3);border-radius:var(--r);background:var(--bg);border:1px solid var(--line);color:var(--faint);transition:border-color var(--dur-fast) var(--ease)}.pb-search:focus-within{border-color:var(--accent);color:var(--muted)}.pb-search input{flex:1;min-width:0;height:100%;border:0;outline:0;background:none;color:var(--ink);font:inherit;font-size:var(--fs)}.pb-search input::placeholder{color:var(--faint)}.pb-search input::-webkit-search-cancel-button{display:none}.pb-app .pb-search input:focus-visible{outline:none}.pb-search kbd{font-family:var(--mono);font-size:var(--fs-xs);color:var(--faint);border:1px solid var(--line);border-radius:var(--r-sm);padding:0 5px;line-height:16px}.pb-chips{display:flex;flex-wrap:wrap;gap:var(--s2);margin-top:var(--s3)}.pb-graph .pb-chip{display:inline-flex;align-items:center;gap:6px;height:26px;padding:0 10px;border-radius:999px;border:1px solid var(--line);font-size:var(--fs-sm);color:var(--faint);transition:color var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease),background-color var(--dur-fast) var(--ease)}.pb-graph .pb-chip[aria-pressed=true]{color:var(--ink);background:var(--raised);border-color:var(--line-strong)}.pb-graph .pb-chip:hover{border-color:var(--line-strong)}.pb-groups{list-style:none;display:grid;gap:1px}.pb-groups button{width:100%;display:flex;align-items:center;gap:var(--s2);height:28px;padding:0 var(--s2);border-radius:var(--r-sm);font-size:var(--fs-sm);text-align:left;transition:background-color var(--dur-fast) var(--ease),opacity var(--dur-fast) var(--ease)}.pb-groups button:hover{background:var(--hover)}.pb-groups button[aria-pressed=false]{opacity:.42}.pb-groups__name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pb-graph__list{list-style:none;flex:1;min-height:0;overflow-y:auto;margin:0 calc(var(--s3) * -1);padding:0 var(--s3) var(--s4);scrollbar-width:thin;scrollbar-color:var(--line) transparent}.pb-graph__list button{width:100%;display:flex;align-items:center;gap:var(--s2);min-height:36px;padding:var(--s1) var(--s2);border-radius:var(--r-sm);text-align:left;transition:background-color var(--dur-fast) var(--ease)}.pb-graph__list button:hover{background:var(--hover)}.pb-graph__list button[aria-current=true]{background:var(--accent-soft)}.pb-row__text{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}.pb-row__label{font-size:var(--fs);color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pb-row__path{font-family:var(--mono);font-size:var(--fs-xs);color:var(--faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;direction:rtl;text-align:left}.pb-graph__stage{position:relative;min-width:0;min-height:0;overflow:hidden;background:var(--graph-bg)}.pb-graph__canvas{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;display:block}.pb-graph__canvas:not([data-state=ready]){visibility:hidden}.pb-toolbar{position:absolute;top:var(--s3);left:var(--s3);z-index:var(--z-toolbar);display:flex;align-items:center;gap:2px;height:40px;padding:0 var(--s1);border:1px solid var(--line);border-radius:var(--r-lg);background:var(--panel);box-shadow:var(--shadow-sm);max-width:calc(100% - var(--s3) * 2);overflow-x:auto;scrollbar-width:none}.pb-toolbar__sep{width:1px;height:18px;background:var(--line);margin:0 var(--s1);flex:none}.pb-toolbar__zoom{min-width:52px;text-align:center;color:var(--muted)}.pb-segmented{display:inline-flex;padding:2px;gap:2px;border-radius:var(--r);background:var(--bg);flex:none}.pb-segmented button{height:28px;padding:0 10px;border-radius:calc(var(--r) - 2px);font-size:var(--fs-sm);color:var(--faint);transition:background-color var(--dur-fast) var(--ease),color var(--dur-fast) var(--ease)}.pb-segmented button:hover{color:var(--ink)}.pb-segmented button[aria-checked=true]{background:var(--raised);color:var(--ink)}.pb-graph__status{position:absolute;left:var(--s3);bottom:var(--s3);z-index:var(--z-toolbar);font-size:var(--fs-sm);color:var(--faint);padding:6px 10px;border-radius:var(--r);background:color-mix(in srgb,var(--bg) 82%,transparent);pointer-events:none}.pb-graph__stale{color:var(--warn)}.pb-graph-legend{position:absolute;right:var(--s3);bottom:var(--s3);z-index:var(--z-toolbar);width:260px;background:var(--panel);border:1px solid var(--line);border-radius:var(--r);padding:var(--s2) var(--s3);box-shadow:0 4px 16px #00000059;font-size:var(--fs-xs);display:flex;flex-direction:column;gap:var(--s2)}.pb-graph-legend__head{display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--line);padding-bottom:4px}.pb-graph-legend__head strong{font-size:var(--fs-xs);letter-spacing:.04em;text-transform:uppercase;color:var(--muted)}.pb-graph-legend__body{display:flex;flex-direction:column;gap:6px}.pb-graph-legend__row{display:flex;align-items:flex-start;gap:8px;color:var(--muted);line-height:1.4}.pb-graph-legend__row strong{color:var(--ink)}.pb-legend-dot-sample{width:10px;height:10px;border-radius:50%;background:var(--accent);flex:none;margin-top:2px}.pb-legend-line-sample{width:12px;height:2px;background:var(--muted);flex:none;margin-top:6px}.pb-legend-color-sample{width:10px;height:10px;border-radius:2px;background:var(--graph-g0);flex:none;margin-top:2px}.pb-state{position:absolute;top:0;right:0;bottom:0;left:0;display:grid;place-content:center;justify-items:center;gap:var(--s2);padding:var(--s6);text-align:center}.pb-state h2{font-size:var(--fs-lg);font-weight:600;color:var(--ink)}.pb-state p{font-size:var(--fs);color:var(--muted);max-width:44ch;line-height:1.55}.pb-state .pb-button{margin-top:var(--s3)}.pb-state__bar{width:160px;height:2px;border-radius:2px;background:var(--line);overflow:hidden;margin-bottom:var(--s3)}.pb-state__bar span{display:block;width:40%;height:100%;background:var(--accent);border-radius:2px;animation:pb-indeterminate 1.1s var(--ease) infinite}@keyframes pb-indeterminate{0%{transform:translate(-100%)}to{transform:translate(260%)}}.pb-inspector{display:flex;flex-direction:column;gap:var(--s3);min-height:0;overflow-y:auto;padding:var(--s4) var(--s4) var(--s6);border-left:1px solid var(--line);background:var(--panel);animation:pb-inspector-in var(--dur-slow) var(--ease);scrollbar-width:thin;scrollbar-color:var(--line) transparent}@keyframes pb-inspector-in{0%{opacity:0;transform:translate(8px)}to{opacity:1;transform:none}}.pb-inspector__head{display:flex;align-items:center;justify-content:space-between;gap:var(--s2)}.pb-inspector__kicker{display:flex;align-items:center;gap:6px;min-width:0;font-size:var(--fs-sm);color:var(--muted)}.pb-inspector__group{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pb-inspector__title{font-size:var(--fs-xl);font-weight:600;line-height:1.25;word-break:break-word}.pb-inspector__path{font-family:var(--mono);font-size:var(--fs-xs);color:var(--faint);line-height:1.6;word-break:break-all}.pb-facts{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s2)}.pb-facts div{padding:var(--s2) var(--s3);border-radius:var(--r);background:var(--raised)}.pb-facts dt{font-size:var(--fs-xs);color:var(--faint)}.pb-facts dd{font-size:var(--fs-md);color:var(--ink);margin-top:2px}.pb-graph .pb-facts .pb-num{color:var(--ink);font-size:var(--fs-md)}.pb-inspector__actions{display:flex;gap:var(--s2);flex-wrap:wrap}.pb-inspector__section ul{list-style:none;display:grid;gap:1px}.pb-inspector__section button{width:100%;display:flex;align-items:center;gap:var(--s2);min-height:30px;padding:0 var(--s2);border-radius:var(--r-sm);text-align:left;transition:background-color var(--dur-fast) var(--ease)}.pb-inspector__section button:hover{background:var(--hover)}@media (max-width: 1100px){.pb-graph{--side-w: 248px;--inspector-w: 320px }}@media (max-width: 860px){.pb-graph,.pb-graph[data-inspector=open]{grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr)}.pb-graph__side{border-right:0;border-bottom:1px solid var(--line);padding-bottom:var(--s3)}.pb-graph__side .pb-heading,.pb-graph__side .pb-groups,.pb-graph__side .pb-graph__list,.pb-graph__side>.pb-quiet{display:none}.pb-inspector{position:absolute;left:0;right:0;bottom:0;z-index:var(--z-panel);max-height:58%;border-left:0;border-top:1px solid var(--line);border-radius:var(--r-lg) var(--r-lg) 0 0;box-shadow:var(--shadow-lg);animation-name:pb-sheet-in}.pb-graph{position:relative}.pb-toolbar__zoom,.pb-timeline input[type=range]{display:none}}@keyframes pb-sheet-in{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}@media (prefers-reduced-motion: reduce){.pb-inspector,.pb-state__bar span{animation:none}.pb-graph *{transition:none!important}}.turns-view{min-height:0;height:100%;padding:16px 20px 24px;overflow:auto;color:var(--ink);background:var(--canvas)}.turns-toolbar{display:flex;align-items:center;justify-content:space-between;gap:16px;border-bottom:1px solid var(--line);padding-bottom:14px}.turns-toolbar h2{margin:0;font-size:20px;font-weight:620}.turns-toolbar p{margin:5px 0 0;color:var(--muted);font-size:13px}.turns-summary{display:flex;align-items:center;gap:5px;flex-wrap:wrap;margin:10px 0 0}.turns-summary button{border:0;border-radius:4px;padding:6px 8px;color:var(--ink);background:transparent;font:inherit;font-size:12px;cursor:pointer}.turns-summary button:hover,.turns-summary button[aria-pressed=true]{background:color-mix(in srgb,var(--accent) 16%,var(--panel))}.turns-summary button[aria-pressed=true]{box-shadow:inset 0 -2px var(--accent)}.turns-zoom,.turns-actions{display:flex;gap:6px;flex-wrap:wrap}.turns-zoom button,.turns-actions button,.turns-detail__head button,.turns-composer button{color:var(--ink);background:var(--panel);border:1px solid var(--line);border-radius:5px;padding:7px 10px;font:inherit;font-size:12px;cursor:pointer}.turns-zoom button[aria-pressed=true],.turns-actions button:hover,.turns-composer button:hover{border-color:var(--accent);background:color-mix(in srgb,var(--accent) 13%,var(--panel))}.turns-view button:focus-visible,.turns-view input:focus-visible,.turns-view textarea:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.turns-history-note{margin:10px 0;padding:9px 12px;border-left:3px solid #c49c52;background:color-mix(in srgb,#c49c52 10%,var(--panel));color:var(--ink);font-size:12px}.turns-key{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin:12px 0;color:var(--muted);font-size:11px}.turns-key span{display:inline-flex;gap:6px;align-items:center}.turns-key i{display:inline-block;width:13px;height:8px;border-radius:2px;background:#5eae88}.turns-key i[data-state=needs-task]{background:#709cd3}.turns-key i[data-state=awaiting-commit]{background:#c49c52}.turns-key i[data-state=blocked]{background:#c46b6b}.turns-key i[data-state=paused]{background:#8a82b7}.turns-key i[data-mark=claim]{height:3px;background:#84aab2}.turns-key i[data-mark=message]{width:8px;height:8px;background:#e2a453;transform:rotate(45deg)}.turns-key i[data-mark=approval]{width:8px;height:8px;border-radius:50%;background:#9acb7b}.turns-scrubber{display:grid;grid-template-columns:auto minmax(120px,1fr) auto;align-items:center;gap:10px;margin:0 0 13px;color:var(--muted);font-size:11px}.turns-scrubber input{width:100%;accent-color:var(--accent)}.turns-scrubber span{color:var(--ink);font-variant-numeric:tabular-nums}.turns-layout{display:grid;grid-template-columns:minmax(440px,1.8fr) minmax(290px,.8fr);gap:14px;align-items:start}.turns-scroll{min-width:0;overflow:auto;border:1px solid var(--line);border-radius:6px;background:var(--panel)}.turns-ruler,.turns-lane{display:grid;grid-template-columns:minmax(145px,24%) minmax(420px,1fr);align-items:center}.turns-ruler{position:sticky;z-index:3;top:0;color:var(--muted);background:var(--panel);border-bottom:1px solid var(--line);font-size:10px}.turns-ruler>span{padding:9px}.turns-ruler>div{display:flex;justify-content:space-between;padding:9px 3px;font-variant-numeric:tabular-nums}.turns-priority>h3{margin:0;padding:8px 10px 5px;color:var(--ink);background:color-mix(in srgb,var(--accent) 8%,var(--panel));border-bottom:1px solid var(--line);font-size:11px;font-weight:650}.turns-account h4{margin:0;padding:6px 10px 4px;color:var(--muted);background:color-mix(in srgb,var(--ink) 3%,var(--panel));border-bottom:1px solid var(--line);font-size:10px;font-weight:650;letter-spacing:.06em}.turns-lane{min-height:54px;border-bottom:1px solid var(--line)}.turns-worker{display:flex;min-width:0;flex-direction:column;gap:2px;padding:6px 9px}.turns-worker strong{overflow:hidden;font-size:12px;text-overflow:ellipsis;white-space:nowrap}.turns-worker span,.turns-worker small{overflow:hidden;color:var(--muted);font-size:10px;text-overflow:ellipsis;white-space:nowrap}.turns-track{position:relative;min-height:39px;height:100%;align-self:stretch;background:color-mix(in srgb,var(--canvas) 50%,transparent)}.turns-gridline{position:absolute;inset-block:0;width:1px;background:color-mix(in srgb,var(--line) 80%,transparent)}.turns-block{position:absolute;z-index:1;top:12px;min-width:5px;height:16px;padding:0;border:1px solid color-mix(in srgb,white 18%,transparent);border-radius:3px;background:#477f68;cursor:pointer}.turns-block[data-state=needs-task]{background:#557daf}.turns-block[data-state=awaiting-commit]{background:#9f7e3e}.turns-block[data-state=blocked]{background:#a75454}.turns-block[data-state=paused]{background:#746c9c}.turns-block[data-state=approved]{background:#668d49}.turns-block[data-still=true]{background:repeating-linear-gradient(135deg,#9b623d 0,#9b623d 5px,#b17b4f 5px,#b17b4f 8px)}.turns-block[data-selected=true]{outline:2px solid var(--ink);outline-offset:1px}.turns-still{position:absolute;left:4px;top:0;max-width:calc(100% - 8px);overflow:hidden;color:#fff;font-size:9px;font-weight:650;line-height:14px;text-overflow:clip;text-shadow:0 1px 2px #342115;white-space:nowrap;pointer-events:none}.turns-claim{position:absolute;z-index:0;top:33px;height:3px;min-width:3px;background:#84aab2}.turns-mark{position:absolute;z-index:2;top:4px;width:8px;height:8px;padding:0;border:1px solid var(--panel);cursor:pointer}.turns-mark--message{background:#e2a453;transform:rotate(45deg)}.turns-mark--approval{top:3px;width:10px;height:10px;border-radius:50%;background:#9acb7b}.turns-detail{position:sticky;top:0;max-height:calc(100vh - 150px);overflow:auto;border:1px solid var(--line);border-radius:6px;padding:13px;background:var(--panel)}.turns-detail__head{display:flex;justify-content:space-between;align-items:start;gap:8px;border-bottom:1px solid var(--line);padding-bottom:10px}.turns-detail__head span{color:var(--muted);font-size:9px;letter-spacing:.1em}.turns-detail__head h3,.turns-detail__empty h3{margin:3px 0 0;font-size:16px}.turns-detail dl{display:grid;grid-template-columns:92px 1fr;gap:8px;margin:12px 0;font-size:11px;line-height:1.45}.turns-detail dt{color:var(--muted)}.turns-detail dd{margin:0;overflow-wrap:anywhere}.turns-state{display:inline-block;border-left:3px solid #5eae88;padding-left:6px}.turns-state[data-state=blocked]{border-color:#c46b6b}.turns-state[data-state=awaiting-commit]{border-color:#c49c52}.turns-still-label{margin-left:7px;color:#e29a62}.turns-resting{border-top:1px solid var(--line)}.turns-resting__toggle{width:100%;padding:10px;border:0;text-align:left;color:var(--muted);background:color-mix(in srgb,var(--ink) 4%,var(--panel));font:inherit;font-size:12px;cursor:pointer}.turns-resting__toggle:hover{color:var(--ink)}.turns-resting__worker{display:grid;grid-template-columns:minmax(120px,24%) minmax(150px,1fr) 36px;align-items:center;gap:8px;min-height:38px;padding:5px 10px;border-top:1px solid var(--line);font-size:11px}.turns-resting__worker span{color:var(--muted)}.turns-stale-hatch{justify-self:end;width:28px;height:12px;background:repeating-linear-gradient(135deg,#9b623d 0 4px,transparent 4px 7px)}.turns-detail h4{margin:14px 0 5px;font-size:11px}.turns-detail ul{display:grid;gap:6px;margin:0;padding:0 0 0 15px;font-size:10px;overflow-wrap:anywhere}.turns-detail li span{display:block;color:var(--muted);margin-top:2px}.turns-actions{margin-top:14px}.turns-composer{display:grid;gap:8px;margin-top:10px;padding:10px;border:1px solid var(--line);border-radius:5px}.turns-composer h4{margin:0}.turns-composer label{display:grid;gap:4px;color:var(--muted);font-size:10px}.turns-composer input,.turns-composer textarea{min-width:0;border:1px solid var(--line);border-radius:4px;padding:7px;color:var(--ink);background:var(--canvas);font:inherit;font-size:11px}.turns-composer textarea{min-height:64px;resize:vertical}.turns-detail__empty{color:var(--muted);font-size:12px;line-height:1.5}.turns-detail__empty h3{color:var(--ink)}.turns-empty{padding:30px;color:var(--muted)}@media (max-width: 980px){.turns-layout{grid-template-columns:minmax(0,1fr)}.turns-detail{position:static;max-height:none}}@media (max-width: 620px){.turns-view{padding:10px}.turns-toolbar{align-items:start;flex-direction:column}.turns-scrubber{grid-template-columns:1fr auto}.turns-scrubber input{grid-column:1 / -1;grid-row:2}.turns-ruler,.turns-lane{grid-template-columns:120px minmax(420px,1fr)}}@media (prefers-reduced-motion: reduce){.turns-view *,.turns-view *:before,.turns-view *:after{scroll-behavior:auto!important;animation-duration:.01ms!important;transition-duration:.01ms!important}}.pb-app{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--bg);color:var(--ink);font-family:var(--sans);font-size:var(--fs)}.pb-app .pb-icon{width:16px;height:16px;flex:none}.pb-app .pb-num{font-family:var(--mono);font-variant-numeric:tabular-nums;color:var(--faint);font-size:var(--fs-xs)}.pb-sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}.pb-app :is(.pb-topbar,.pb-graph,.pb-banner,.pb-overlay-tools) button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}.pb-app :is(button,select,input):focus-visible{outline:2px solid var(--accent);outline-offset:2px}.pb-app :is(button):disabled{cursor:default;opacity:.45}.pb-app .pb-button{display:inline-flex;align-items:center;gap:var(--s2);height:32px;padding:0 var(--s3);border-radius:var(--r);border:1px solid var(--line);background:var(--raised);color:var(--ink);font-size:var(--fs-sm);font-weight:500;transition:background-color var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease)}.pb-app .pb-button:hover:not(:disabled){background:var(--hover);border-color:var(--line-strong)}.pb-app .pb-button--primary{background:var(--accent);border-color:var(--accent);color:var(--accent-ink)}.pb-app .pb-button--primary:hover:not(:disabled){background:var(--accent-hover);border-color:var(--accent-hover)}.pb-app .pb-tool{display:inline-flex;align-items:center;gap:6px;flex:none;height:32px;padding:0 var(--s2);border-radius:var(--r);color:var(--muted);font-size:var(--fs-sm);transition:background-color var(--dur-fast) var(--ease),color var(--dur-fast) var(--ease),transform var(--dur-fast) var(--ease)}.pb-app .pb-tool--icon{width:32px;padding:0;justify-content:center}.pb-app .pb-tool:hover:not(:disabled){background:var(--hover);color:var(--ink)}.pb-app .pb-tool:active:not(:disabled){transform:scale(.97)}.pb-timeline{display:inline-flex;align-items:center;gap:var(--s2);flex:none}.pb-timeline input[type=range]{width:140px;accent-color:var(--accent)}.pb-timeline__label{min-width:64px;color:var(--muted)}.pb-topbar{flex:none;display:flex;align-items:center;gap:var(--s4);height:52px;padding:0 var(--s3) 0 var(--s4);border-bottom:1px solid var(--line);background:var(--panel)}.pb-topbar__brand{display:flex;align-items:center;gap:var(--s2);flex:none;min-width:0}.pb-logo{width:10px;height:10px;border-radius:50%;flex:none;background:var(--accent)}.pb-topbar__product{font-size:var(--fs-md);font-weight:600;letter-spacing:-.01em}.pb-topbar__workspace{color:var(--muted);font-size:var(--fs)}.pb-select{position:relative;display:inline-flex;align-items:center;height:30px;border-radius:var(--r);color:var(--muted);transition:background-color var(--dur-fast) var(--ease),color var(--dur-fast) var(--ease)}.pb-select:hover{background:var(--hover);color:var(--ink)}.pb-select select{-webkit-appearance:none;-moz-appearance:none;appearance:none;height:100%;padding:0 28px 0 var(--s2);border:0;background:none;color:inherit;font:inherit;font-size:var(--fs);cursor:pointer;max-width:220px;text-overflow:ellipsis}.pb-select select option{background:var(--panel);color:var(--ink)}.pb-select .pb-icon{position:absolute;right:8px;pointer-events:none}.pb-tabs{display:flex;align-items:stretch;align-self:stretch;gap:2px;min-width:0;overflow-x:auto;scrollbar-width:none}.pb-tabs::-webkit-scrollbar{display:none}.pb-app .pb-tab{position:relative;flex:none;padding:0 var(--s3);color:var(--faint);font-size:var(--fs);font-weight:500;transition:color var(--dur-fast) var(--ease)}.pb-app .pb-tab:hover,.pb-app .pb-tab[aria-current=page]{color:var(--ink)}.pb-tab:after{content:"";position:absolute;left:var(--s3);right:var(--s3);bottom:-1px;height:2px;border-radius:2px 2px 0 0;background:var(--accent);transform:scaleX(0);transition:transform var(--dur) var(--ease)}.pb-tab[aria-current=page]:after{transform:scaleX(1)}.pb-topbar__actions{display:flex;align-items:center;gap:var(--s1);margin-left:auto;flex:none}.pb-global-search{display:flex;align-items:center;gap:7px;width:clamp(190px,25vw,360px);height:32px;padding:0 9px;border:1px solid var(--line);border-radius:7px;color:var(--faint);background:var(--bg)}.pb-global-search:focus-within{border-color:var(--accent);color:var(--ink)}.pb-global-search .pb-icon{width:15px;height:15px}.pb-global-search input{width:100%;min-width:0;border:0;outline:0;color:var(--ink);background:transparent;font:inherit;font-size:var(--fs-sm)}.pb-global-search input::placeholder{color:var(--faint)}.pb-global-search kbd{flex:none;padding:2px 4px;border:1px solid var(--line);border-radius:4px;color:var(--faint);font:10px var(--mono);white-space:nowrap}.pb-status{display:inline-flex;align-items:center;gap:6px;height:26px;padding:0 10px;margin-right:var(--s2);border-radius:999px;border:1px solid var(--line);font-size:var(--fs-sm);color:var(--muted);white-space:nowrap}.pb-status i{width:6px;height:6px;border-radius:50%;background:var(--faint)}.pb-status[data-tone=ok] i{background:var(--pos)}.pb-status[data-tone=warn] i{background:var(--warn)}.pb-status[data-tone=warn]{color:var(--warn)}.pb-status[data-tone=bad] i{background:var(--neg)}.pb-status[data-tone=bad]{color:var(--neg);border-color:color-mix(in srgb,var(--neg) 40%,var(--line))}@media (max-width: 1180px){.pb-topbar__secondary-action{display:none!important}.pb-global-search{width:clamp(150px,25vw,260px)}.pb-select select{max-width:150px}}.pb-banner{flex:none;display:flex;align-items:center;gap:var(--s3);flex-wrap:wrap;padding:var(--s2) var(--s4);border-bottom:1px solid color-mix(in srgb,var(--neg) 35%,var(--line));background:color-mix(in srgb,var(--neg) 9%,var(--panel));font-size:var(--fs);color:var(--ink)}.pb-banner strong{color:var(--neg);font-weight:600}.pb-banner code{font-family:var(--mono);font-size:var(--fs-sm)}.pb-banner .pb-button{margin-left:auto}.pb-drawer{flex:none;padding:var(--s3) var(--s4);border-bottom:1px solid var(--line);background:var(--panel);animation:pb-drawer-in var(--dur) var(--ease)}@keyframes pb-drawer-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}.pb-main{position:relative;flex:1;min-height:0;overflow:hidden}.pb-view{position:absolute;top:0;right:0;bottom:0;left:62px;min-height:0;display:flex;flex-direction:column}.pb-view--scroll{overflow:auto;display:block}.pb-main .notes-workbench{position:relative;flex:1;min-height:0;inset:auto;z-index:auto;height:100%}.pb-main .workbench-split{flex:1;min-height:0;height:100%}.pb-main .atlas-app{flex:1;min-height:0}.pb-main .pb-city,.pb-main .pb-mesh{flex:1;min-height:0;position:relative}.pb-main .brain-landing{position:absolute;top:0;right:0;bottom:0;left:0;overflow:auto}.pb-activity{position:absolute;z-index:12;inset:0 auto 0 0;width:62px;display:flex;flex-direction:column;align-items:stretch;gap:4px;padding:10px 6px;box-sizing:border-box;background:var(--panel);border-right:1px solid var(--line)}.pb-activity__item{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:5px;width:48px;min-height:50px;padding:5px 2px;border:0;border-radius:7px;color:var(--muted);background:transparent;font:inherit;font-size:9px;cursor:pointer;text-decoration:none;list-style:none}.pb-activity__item:hover,.pb-activity__item[aria-current=page]{color:var(--ink);background:var(--hover)}.pb-activity__item[aria-current=page]:before{content:"";position:absolute;inset:9px auto 9px -6px;width:2px;border-radius:2px;background:var(--accent)}.pb-activity__item .pb-icon{width:18px;height:18px}.pb-activity__item span{max-width:48px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pb-activity__divider{height:1px;margin:4px 2px;background:var(--line)}.pb-activity__views{position:relative}.pb-activity__views summary{cursor:pointer}.pb-activity__views summary::-webkit-details-marker{display:none}.pb-activity__grid{font-size:8px;line-height:6px;letter-spacing:1px;text-align:center}.pb-activity__menu{position:absolute;z-index:20;left:55px;top:0;width:190px;max-height:min(70vh,520px);overflow:auto;padding:5px;border:1px solid var(--line);border-radius:8px;background:var(--panel);box-shadow:0 12px 32px #0005}.pb-activity__menu button{display:block;width:100%;padding:8px 10px;border:0;border-radius:5px;text-align:left;color:var(--muted);background:transparent;font:inherit;font-size:var(--fs-sm);cursor:pointer}.pb-activity__menu button:hover,.pb-activity__menu button[aria-current=page]{color:var(--ink);background:var(--hover)}.pb-activity__settings{margin-top:auto}.pb-activity__glyph{width:18px;height:18px;display:grid;place-items:center;font-size:18px;line-height:1}.pb-activity__system{min-height:48px}.pb-statusbar{display:flex;align-items:center;gap:14px;min-height:25px;padding:0 12px 0 70px;border-top:1px solid var(--line);background:var(--panel);color:var(--muted);font:10px var(--mono);white-space:nowrap;overflow-x:auto}.pb-domain-view{position:absolute;top:0;right:0;bottom:0;left:62px;overflow:auto;padding:16px 20px 28px;box-sizing:border-box}.pb-domain-view .pb-view-header{position:sticky;top:-16px;z-index:2;margin:-16px -20px 14px;padding:12px 20px;background:var(--bg)}.pb-domain-view h2{margin:0;font-size:18px}.pb-domain-view h3{margin:0 0 8px;font-size:14px}.pb-domain-view h4{margin:16px 0 6px;font-size:12px}.pb-domain-card{margin:0 0 12px;padding:14px;border:1px solid var(--line);border-radius:8px;background:var(--panel)}.pb-domain-card p{margin:6px 0;color:var(--muted)}.pb-domain-error{padding:10px;border:1px solid var(--neg);border-radius:6px;color:var(--neg)}.pb-domain-note{color:var(--faint);font-size:var(--fs-sm)}.pb-domain-list{display:grid;gap:8px;padding-left:22px}.pb-domain-list li{padding-left:3px}.pb-domain-list li code,.pb-domain-list li span{display:block;overflow-wrap:anywhere}.pb-domain-list li span{margin-top:3px;color:var(--muted);font-size:var(--fs-sm)}.pb-drive-grid,.pb-metric-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:8px}.pb-drive-grid article,.pb-metric-grid article{display:grid;gap:5px;padding:10px;border:1px solid var(--line);border-radius:6px}.pb-drive-grid span,.pb-metric-grid span{color:var(--muted);font-size:var(--fs-sm)}.pb-table-wrap{overflow:auto}.pb-data-table{width:100%;border-collapse:collapse;text-align:left;font-size:var(--fs-sm)}.pb-data-table th,.pb-data-table td{padding:8px;border-bottom:1px solid var(--line);vertical-align:top}.pb-data-table th{color:var(--muted);font-weight:600}@media (max-width: 760px){.pb-view{left:52px}.pb-domain-view{left:52px;padding-inline:12px}.pb-domain-view .pb-view-header{margin-inline:-12px;padding-inline:12px}.pb-statusbar{padding-left:58px}.pb-activity{width:52px;padding-inline:2px}.pb-activity__item{width:46px}.pb-activity__menu{left:49px}.pb-topbar__workspace{display:none}.pb-global-search{width:clamp(110px,28vw,190px)}.pb-global-search kbd{display:none}}.pb-overlay-tools{position:absolute;left:50%;bottom:var(--s4);transform:translate(-50%);z-index:var(--z-toolbar);display:flex;align-items:center;height:40px;padding:0 var(--s1);border:1px solid var(--line);border-radius:var(--r-lg);background:var(--panel)}@media (max-width: 860px){.pb-topbar{gap:var(--s2);padding:0 var(--s2)}.pb-topbar__product,.pb-status span,.pb-topbar__actions .pb-tool:not(.pb-tool--icon) span{display:none}.pb-status{padding:0 8px;margin-right:0}.pb-select select{max-width:120px}}@media (max-width: 640px){html,body{overflow-x:hidden;max-width:100vw;width:100%}.pb-app{max-width:100vw;overflow-x:hidden}.pb-tabs--desktop{display:none!important}.pb-mobile-nav{display:block!important;position:relative}.pb-topbar{gap:4px!important;padding:0 8px!important;max-width:100vw!important;overflow:visible!important}.pb-topbar__brand{gap:4px!important;max-width:60%}.pb-topbar__workspace,.pb-status{display:none!important}.pb-topbar__actions{gap:4px!important}.pb-view-header{padding:8px 10px!important;gap:6px!important;flex-wrap:wrap!important}.pb-view-header p{display:none!important}.pb-view-header .pb-button{height:28px!important;padding:0 8px!important;font-size:11px!important}}@media (max-width: 760px){.pb-statusbar{gap:10px}}@media (prefers-reduced-motion: reduce){.pb-tab:after,.pb-drawer{transition:none;animation:none}} diff --git a/ui-dist/assets/index-BQBPTzQY.js b/ui-dist/assets/index-BQBPTzQY.js deleted file mode 100644 index 80c0508..0000000 --- a/ui-dist/assets/index-BQBPTzQY.js +++ /dev/null @@ -1,4302 +0,0 @@ -(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const a of document.querySelectorAll('link[rel="modulepreload"]'))i(a);new MutationObserver(a=>{for(const s of a)if(s.type==="childList")for(const r of s.addedNodes)r.tagName==="LINK"&&r.rel==="modulepreload"&&i(r)}).observe(document,{childList:!0,subtree:!0});function n(a){const s={};return a.integrity&&(s.integrity=a.integrity),a.referrerPolicy&&(s.referrerPolicy=a.referrerPolicy),a.crossOrigin==="use-credentials"?s.credentials="include":a.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function i(a){if(a.ep)return;a.ep=!0;const s=n(a);fetch(a.href,s)}})();function WS(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var Fv={exports:{}},xf={};/** - * @license React - * react-jsx-runtime.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var XS=Symbol.for("react.transitional.element"),qS=Symbol.for("react.fragment");function jv(t,e,n){var i=null;if(n!==void 0&&(i=""+n),e.key!==void 0&&(i=""+e.key),"key"in e){n={};for(var a in e)a!=="key"&&(n[a]=e[a])}else n=e;return e=n.ref,{$$typeof:XS,type:t,key:i,ref:e!==void 0?e:null,props:n}}xf.Fragment=qS;xf.jsx=jv;xf.jsxs=jv;Fv.exports=xf;var l=Fv.exports,Hv={exports:{}},at={};/** - * @license React - * react.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var rm=Symbol.for("react.transitional.element"),YS=Symbol.for("react.portal"),KS=Symbol.for("react.fragment"),ZS=Symbol.for("react.strict_mode"),$S=Symbol.for("react.profiler"),QS=Symbol.for("react.consumer"),JS=Symbol.for("react.context"),eM=Symbol.for("react.forward_ref"),tM=Symbol.for("react.suspense"),nM=Symbol.for("react.memo"),Gv=Symbol.for("react.lazy"),iM=Symbol.for("react.activity"),Rg=Symbol.iterator;function aM(t){return t===null||typeof t!="object"?null:(t=Rg&&t[Rg]||t["@@iterator"],typeof t=="function"?t:null)}var Vv={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},Wv=Object.assign,Xv={};function Ml(t,e,n){this.props=t,this.context=e,this.refs=Xv,this.updater=n||Vv}Ml.prototype.isReactComponent={};Ml.prototype.setState=function(t,e){if(typeof t!="object"&&typeof t!="function"&&t!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,t,e,"setState")};Ml.prototype.forceUpdate=function(t){this.updater.enqueueForceUpdate(this,t,"forceUpdate")};function qv(){}qv.prototype=Ml.prototype;function lm(t,e,n){this.props=t,this.context=e,this.refs=Xv,this.updater=n||Vv}var om=lm.prototype=new qv;om.constructor=lm;Wv(om,Ml.prototype);om.isPureReactComponent=!0;var Dg=Array.isArray;function uh(){}var Gt={H:null,A:null,T:null,S:null},Yv=Object.prototype.hasOwnProperty;function cm(t,e,n){var i=n.ref;return{$$typeof:rm,type:t,key:e,ref:i!==void 0?i:null,props:n}}function sM(t,e){return cm(t.type,e,t.props)}function um(t){return typeof t=="object"&&t!==null&&t.$$typeof===rm}function rM(t){var e={"=":"=0",":":"=2"};return"$"+t.replace(/[=:]/g,function(n){return e[n]})}var Lg=/\/+/g;function Hf(t,e){return typeof t=="object"&&t!==null&&t.key!=null?rM(""+t.key):e.toString(36)}function lM(t){switch(t.status){case"fulfilled":return t.value;case"rejected":throw t.reason;default:switch(typeof t.status=="string"?t.then(uh,uh):(t.status="pending",t.then(function(e){t.status==="pending"&&(t.status="fulfilled",t.value=e)},function(e){t.status==="pending"&&(t.status="rejected",t.reason=e)})),t.status){case"fulfilled":return t.value;case"rejected":throw t.reason}}throw t}function Lr(t,e,n,i,a){var s=typeof t;(s==="undefined"||s==="boolean")&&(t=null);var r=!1;if(t===null)r=!0;else switch(s){case"bigint":case"string":case"number":r=!0;break;case"object":switch(t.$$typeof){case rm:case YS:r=!0;break;case Gv:return r=t._init,Lr(r(t._payload),e,n,i,a)}}if(r)return a=a(t),r=i===""?"."+Hf(t,0):i,Dg(a)?(n="",r!=null&&(n=r.replace(Lg,"$&/")+"/"),Lr(a,e,n,"",function(u){return u})):a!=null&&(um(a)&&(a=sM(a,n+(a.key==null||t&&t.key===a.key?"":(""+a.key).replace(Lg,"$&/")+"/")+r)),e.push(a)),1;r=0;var o=i===""?".":i+":";if(Dg(t))for(var c=0;c<t.length;c++)i=t[c],s=o+Hf(i,c),r+=Lr(i,e,n,s,a);else if(c=aM(t),typeof c=="function")for(t=c.call(t),c=0;!(i=t.next()).done;)i=i.value,s=o+Hf(i,c++),r+=Lr(i,e,n,s,a);else if(s==="object"){if(typeof t.then=="function")return Lr(lM(t),e,n,i,a);throw e=String(t),Error("Objects are not valid as a React child (found: "+(e==="[object Object]"?"object with keys {"+Object.keys(t).join(", ")+"}":e)+"). If you meant to render a collection of children, use an array instead.")}return r}function rc(t,e,n){if(t==null)return t;var i=[],a=0;return Lr(t,i,"","",function(s){return e.call(n,s,a++)}),i}function oM(t){if(t._status===-1){var e=t._result;e=e(),e.then(function(n){(t._status===0||t._status===-1)&&(t._status=1,t._result=n)},function(n){(t._status===0||t._status===-1)&&(t._status=2,t._result=n)}),t._status===-1&&(t._status=0,t._result=e)}if(t._status===1)return t._result.default;throw t._result}var Ug=typeof reportError=="function"?reportError:function(t){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var e=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof t=="object"&&t!==null&&typeof t.message=="string"?String(t.message):String(t),error:t});if(!window.dispatchEvent(e))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",t);return}console.error(t)},cM={map:rc,forEach:function(t,e,n){rc(t,function(){e.apply(this,arguments)},n)},count:function(t){var e=0;return rc(t,function(){e++}),e},toArray:function(t){return rc(t,function(e){return e})||[]},only:function(t){if(!um(t))throw Error("React.Children.only expected to receive a single React element child.");return t}};at.Activity=iM;at.Children=cM;at.Component=Ml;at.Fragment=KS;at.Profiler=$S;at.PureComponent=lm;at.StrictMode=ZS;at.Suspense=tM;at.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=Gt;at.__COMPILER_RUNTIME={__proto__:null,c:function(t){return Gt.H.useMemoCache(t)}};at.cache=function(t){return function(){return t.apply(null,arguments)}};at.cacheSignal=function(){return null};at.cloneElement=function(t,e,n){if(t==null)throw Error("The argument must be a React element, but you passed "+t+".");var i=Wv({},t.props),a=t.key;if(e!=null)for(s in e.key!==void 0&&(a=""+e.key),e)!Yv.call(e,s)||s==="key"||s==="__self"||s==="__source"||s==="ref"&&e.ref===void 0||(i[s]=e[s]);var s=arguments.length-2;if(s===1)i.children=n;else if(1<s){for(var r=Array(s),o=0;o<s;o++)r[o]=arguments[o+2];i.children=r}return cm(t.type,a,i)};at.createContext=function(t){return t={$$typeof:JS,_currentValue:t,_currentValue2:t,_threadCount:0,Provider:null,Consumer:null},t.Provider=t,t.Consumer={$$typeof:QS,_context:t},t};at.createElement=function(t,e,n){var i,a={},s=null;if(e!=null)for(i in e.key!==void 0&&(s=""+e.key),e)Yv.call(e,i)&&i!=="key"&&i!=="__self"&&i!=="__source"&&(a[i]=e[i]);var r=arguments.length-2;if(r===1)a.children=n;else if(1<r){for(var o=Array(r),c=0;c<r;c++)o[c]=arguments[c+2];a.children=o}if(t&&t.defaultProps)for(i in r=t.defaultProps,r)a[i]===void 0&&(a[i]=r[i]);return cm(t,s,a)};at.createRef=function(){return{current:null}};at.forwardRef=function(t){return{$$typeof:eM,render:t}};at.isValidElement=um;at.lazy=function(t){return{$$typeof:Gv,_payload:{_status:-1,_result:t},_init:oM}};at.memo=function(t,e){return{$$typeof:nM,type:t,compare:e===void 0?null:e}};at.startTransition=function(t){var e=Gt.T,n={};Gt.T=n;try{var i=t(),a=Gt.S;a!==null&&a(n,i),typeof i=="object"&&i!==null&&typeof i.then=="function"&&i.then(uh,Ug)}catch(s){Ug(s)}finally{e!==null&&n.types!==null&&(e.types=n.types),Gt.T=e}};at.unstable_useCacheRefresh=function(){return Gt.H.useCacheRefresh()};at.use=function(t){return Gt.H.use(t)};at.useActionState=function(t,e,n){return Gt.H.useActionState(t,e,n)};at.useCallback=function(t,e){return Gt.H.useCallback(t,e)};at.useContext=function(t){return Gt.H.useContext(t)};at.useDebugValue=function(){};at.useDeferredValue=function(t,e){return Gt.H.useDeferredValue(t,e)};at.useEffect=function(t,e){return Gt.H.useEffect(t,e)};at.useEffectEvent=function(t){return Gt.H.useEffectEvent(t)};at.useId=function(){return Gt.H.useId()};at.useImperativeHandle=function(t,e,n){return Gt.H.useImperativeHandle(t,e,n)};at.useInsertionEffect=function(t,e){return Gt.H.useInsertionEffect(t,e)};at.useLayoutEffect=function(t,e){return Gt.H.useLayoutEffect(t,e)};at.useMemo=function(t,e){return Gt.H.useMemo(t,e)};at.useOptimistic=function(t,e){return Gt.H.useOptimistic(t,e)};at.useReducer=function(t,e,n){return Gt.H.useReducer(t,e,n)};at.useRef=function(t){return Gt.H.useRef(t)};at.useState=function(t){return Gt.H.useState(t)};at.useSyncExternalStore=function(t,e,n){return Gt.H.useSyncExternalStore(t,e,n)};at.useTransition=function(){return Gt.H.useTransition()};at.version="19.2.8";Hv.exports=at;var P=Hv.exports;const uM=WS(P);var Kv={exports:{}},vf={},Zv={exports:{}},$v={};/** - * @license React - * scheduler.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */(function(t){function e(B,k){var V=B.length;B.push(k);e:for(;0<V;){var X=V-1>>>1,ge=B[X];if(0<a(ge,k))B[X]=k,B[V]=ge,V=X;else break e}}function n(B){return B.length===0?null:B[0]}function i(B){if(B.length===0)return null;var k=B[0],V=B.pop();if(V!==k){B[0]=V;e:for(var X=0,ge=B.length,he=ge>>>1;X<he;){var Ae=2*(X+1)-1,q=B[Ae],fe=Ae+1,Ne=B[fe];if(0>a(q,V))fe<ge&&0>a(Ne,q)?(B[X]=Ne,B[fe]=V,X=fe):(B[X]=q,B[Ae]=V,X=Ae);else if(fe<ge&&0>a(Ne,V))B[X]=Ne,B[fe]=V,X=fe;else break e}}return k}function a(B,k){var V=B.sortIndex-k.sortIndex;return V!==0?V:B.id-k.id}if(t.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var s=performance;t.unstable_now=function(){return s.now()}}else{var r=Date,o=r.now();t.unstable_now=function(){return r.now()-o}}var c=[],u=[],d=1,m=null,f=3,p=!1,x=!1,_=!1,g=!1,h=typeof setTimeout=="function"?setTimeout:null,v=typeof clearTimeout=="function"?clearTimeout:null,b=typeof setImmediate<"u"?setImmediate:null;function y(B){for(var k=n(u);k!==null;){if(k.callback===null)i(u);else if(k.startTime<=B)i(u),k.sortIndex=k.expirationTime,e(c,k);else break;k=n(u)}}function A(B){if(_=!1,y(B),!x)if(n(c)!==null)x=!0,M||(M=!0,O());else{var k=n(u);k!==null&&D(A,k.startTime-B)}}var M=!1,T=-1,S=5,C=-1;function R(){return g?!0:!(t.unstable_now()-C<S)}function L(){if(g=!1,M){var B=t.unstable_now();C=B;var k=!0;try{e:{x=!1,_&&(_=!1,v(T),T=-1),p=!0;var V=f;try{t:{for(y(B),m=n(c);m!==null&&!(m.expirationTime>B&&R());){var X=m.callback;if(typeof X=="function"){m.callback=null,f=m.priorityLevel;var ge=X(m.expirationTime<=B);if(B=t.unstable_now(),typeof ge=="function"){m.callback=ge,y(B),k=!0;break t}m===n(c)&&i(c),y(B)}else i(c);m=n(c)}if(m!==null)k=!0;else{var he=n(u);he!==null&&D(A,he.startTime-B),k=!1}}break e}finally{m=null,f=V,p=!1}k=void 0}}finally{k?O():M=!1}}}var O;if(typeof b=="function")O=function(){b(L)};else if(typeof MessageChannel<"u"){var W=new MessageChannel,z=W.port2;W.port1.onmessage=L,O=function(){z.postMessage(null)}}else O=function(){h(L,0)};function D(B,k){T=h(function(){B(t.unstable_now())},k)}t.unstable_IdlePriority=5,t.unstable_ImmediatePriority=1,t.unstable_LowPriority=4,t.unstable_NormalPriority=3,t.unstable_Profiling=null,t.unstable_UserBlockingPriority=2,t.unstable_cancelCallback=function(B){B.callback=null},t.unstable_forceFrameRate=function(B){0>B||125<B?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):S=0<B?Math.floor(1e3/B):5},t.unstable_getCurrentPriorityLevel=function(){return f},t.unstable_next=function(B){switch(f){case 1:case 2:case 3:var k=3;break;default:k=f}var V=f;f=k;try{return B()}finally{f=V}},t.unstable_requestPaint=function(){g=!0},t.unstable_runWithPriority=function(B,k){switch(B){case 1:case 2:case 3:case 4:case 5:break;default:B=3}var V=f;f=B;try{return k()}finally{f=V}},t.unstable_scheduleCallback=function(B,k,V){var X=t.unstable_now();switch(typeof V=="object"&&V!==null?(V=V.delay,V=typeof V=="number"&&0<V?X+V:X):V=X,B){case 1:var ge=-1;break;case 2:ge=250;break;case 5:ge=1073741823;break;case 4:ge=1e4;break;default:ge=5e3}return ge=V+ge,B={id:d++,callback:k,priorityLevel:B,startTime:V,expirationTime:ge,sortIndex:-1},V>X?(B.sortIndex=V,e(u,B),n(c)===null&&B===n(u)&&(_?(v(T),T=-1):_=!0,D(A,V-X))):(B.sortIndex=ge,e(c,B),x||p||(x=!0,M||(M=!0,O()))),B},t.unstable_shouldYield=R,t.unstable_wrapCallback=function(B){var k=f;return function(){var V=f;f=k;try{return B.apply(this,arguments)}finally{f=V}}}})($v);Zv.exports=$v;var fM=Zv.exports,Qv={exports:{}},kn={};/** - * @license React - * react-dom.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var dM=P;function Jv(t){var e="https://react.dev/errors/"+t;if(1<arguments.length){e+="?args[]="+encodeURIComponent(arguments[1]);for(var n=2;n<arguments.length;n++)e+="&args[]="+encodeURIComponent(arguments[n])}return"Minified React error #"+t+"; visit "+e+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}function Ga(){}var On={d:{f:Ga,r:function(){throw Error(Jv(522))},D:Ga,C:Ga,L:Ga,m:Ga,X:Ga,S:Ga,M:Ga},p:0,findDOMNode:null},hM=Symbol.for("react.portal");function pM(t,e,n){var i=3<arguments.length&&arguments[3]!==void 0?arguments[3]:null;return{$$typeof:hM,key:i==null?null:""+i,children:t,containerInfo:e,implementation:n}}var co=dM.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;function _f(t,e){if(t==="font")return"";if(typeof e=="string")return e==="use-credentials"?e:""}kn.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=On;kn.createPortal=function(t,e){var n=2<arguments.length&&arguments[2]!==void 0?arguments[2]:null;if(!e||e.nodeType!==1&&e.nodeType!==9&&e.nodeType!==11)throw Error(Jv(299));return pM(t,e,null,n)};kn.flushSync=function(t){var e=co.T,n=On.p;try{if(co.T=null,On.p=2,t)return t()}finally{co.T=e,On.p=n,On.d.f()}};kn.preconnect=function(t,e){typeof t=="string"&&(e?(e=e.crossOrigin,e=typeof e=="string"?e==="use-credentials"?e:"":void 0):e=null,On.d.C(t,e))};kn.prefetchDNS=function(t){typeof t=="string"&&On.d.D(t)};kn.preinit=function(t,e){if(typeof t=="string"&&e&&typeof e.as=="string"){var n=e.as,i=_f(n,e.crossOrigin),a=typeof e.integrity=="string"?e.integrity:void 0,s=typeof e.fetchPriority=="string"?e.fetchPriority:void 0;n==="style"?On.d.S(t,typeof e.precedence=="string"?e.precedence:void 0,{crossOrigin:i,integrity:a,fetchPriority:s}):n==="script"&&On.d.X(t,{crossOrigin:i,integrity:a,fetchPriority:s,nonce:typeof e.nonce=="string"?e.nonce:void 0})}};kn.preinitModule=function(t,e){if(typeof t=="string")if(typeof e=="object"&&e!==null){if(e.as==null||e.as==="script"){var n=_f(e.as,e.crossOrigin);On.d.M(t,{crossOrigin:n,integrity:typeof e.integrity=="string"?e.integrity:void 0,nonce:typeof e.nonce=="string"?e.nonce:void 0})}}else e==null&&On.d.M(t)};kn.preload=function(t,e){if(typeof t=="string"&&typeof e=="object"&&e!==null&&typeof e.as=="string"){var n=e.as,i=_f(n,e.crossOrigin);On.d.L(t,n,{crossOrigin:i,integrity:typeof e.integrity=="string"?e.integrity:void 0,nonce:typeof e.nonce=="string"?e.nonce:void 0,type:typeof e.type=="string"?e.type:void 0,fetchPriority:typeof e.fetchPriority=="string"?e.fetchPriority:void 0,referrerPolicy:typeof e.referrerPolicy=="string"?e.referrerPolicy:void 0,imageSrcSet:typeof e.imageSrcSet=="string"?e.imageSrcSet:void 0,imageSizes:typeof e.imageSizes=="string"?e.imageSizes:void 0,media:typeof e.media=="string"?e.media:void 0})}};kn.preloadModule=function(t,e){if(typeof t=="string")if(e){var n=_f(e.as,e.crossOrigin);On.d.m(t,{as:typeof e.as=="string"&&e.as!=="script"?e.as:void 0,crossOrigin:n,integrity:typeof e.integrity=="string"?e.integrity:void 0})}else On.d.m(t)};kn.requestFormReset=function(t){On.d.r(t)};kn.unstable_batchedUpdates=function(t,e){return t(e)};kn.useFormState=function(t,e,n){return co.H.useFormState(t,e,n)};kn.useFormStatus=function(){return co.H.useHostTransitionStatus()};kn.version="19.2.8";function e_(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e_)}catch(t){console.error(t)}}e_(),Qv.exports=kn;var mM=Qv.exports;/** - * @license React - * react-dom-client.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var fn=fM,t_=P,gM=mM;function ue(t){var e="https://react.dev/errors/"+t;if(1<arguments.length){e+="?args[]="+encodeURIComponent(arguments[1]);for(var n=2;n<arguments.length;n++)e+="&args[]="+encodeURIComponent(arguments[n])}return"Minified React error #"+t+"; visit "+e+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}function n_(t){return!(!t||t.nodeType!==1&&t.nodeType!==9&&t.nodeType!==11)}function Xo(t){var e=t,n=t;if(t.alternate)for(;e.return;)e=e.return;else{t=e;do e=t,e.flags&4098&&(n=e.return),t=e.return;while(t)}return e.tag===3?n:null}function i_(t){if(t.tag===13){var e=t.memoizedState;if(e===null&&(t=t.alternate,t!==null&&(e=t.memoizedState)),e!==null)return e.dehydrated}return null}function a_(t){if(t.tag===31){var e=t.memoizedState;if(e===null&&(t=t.alternate,t!==null&&(e=t.memoizedState)),e!==null)return e.dehydrated}return null}function Og(t){if(Xo(t)!==t)throw Error(ue(188))}function xM(t){var e=t.alternate;if(!e){if(e=Xo(t),e===null)throw Error(ue(188));return e!==t?null:t}for(var n=t,i=e;;){var a=n.return;if(a===null)break;var s=a.alternate;if(s===null){if(i=a.return,i!==null){n=i;continue}break}if(a.child===s.child){for(s=a.child;s;){if(s===n)return Og(a),t;if(s===i)return Og(a),e;s=s.sibling}throw Error(ue(188))}if(n.return!==i.return)n=a,i=s;else{for(var r=!1,o=a.child;o;){if(o===n){r=!0,n=a,i=s;break}if(o===i){r=!0,i=a,n=s;break}o=o.sibling}if(!r){for(o=s.child;o;){if(o===n){r=!0,n=s,i=a;break}if(o===i){r=!0,i=s,n=a;break}o=o.sibling}if(!r)throw Error(ue(189))}}if(n.alternate!==i)throw Error(ue(190))}if(n.tag!==3)throw Error(ue(188));return n.stateNode.current===n?t:e}function s_(t){var e=t.tag;if(e===5||e===26||e===27||e===6)return t;for(t=t.child;t!==null;){if(e=s_(t),e!==null)return e;t=t.sibling}return null}var Vt=Object.assign,vM=Symbol.for("react.element"),lc=Symbol.for("react.transitional.element"),Jl=Symbol.for("react.portal"),Pr=Symbol.for("react.fragment"),r_=Symbol.for("react.strict_mode"),fh=Symbol.for("react.profiler"),l_=Symbol.for("react.consumer"),Sa=Symbol.for("react.context"),fm=Symbol.for("react.forward_ref"),dh=Symbol.for("react.suspense"),hh=Symbol.for("react.suspense_list"),dm=Symbol.for("react.memo"),$a=Symbol.for("react.lazy"),ph=Symbol.for("react.activity"),_M=Symbol.for("react.memo_cache_sentinel"),Pg=Symbol.iterator;function Ol(t){return t===null||typeof t!="object"?null:(t=Pg&&t[Pg]||t["@@iterator"],typeof t=="function"?t:null)}var yM=Symbol.for("react.client.reference");function mh(t){if(t==null)return null;if(typeof t=="function")return t.$$typeof===yM?null:t.displayName||t.name||null;if(typeof t=="string")return t;switch(t){case Pr:return"Fragment";case fh:return"Profiler";case r_:return"StrictMode";case dh:return"Suspense";case hh:return"SuspenseList";case ph:return"Activity"}if(typeof t=="object")switch(t.$$typeof){case Jl:return"Portal";case Sa:return t.displayName||"Context";case l_:return(t._context.displayName||"Context")+".Consumer";case fm:var e=t.render;return t=t.displayName,t||(t=e.displayName||e.name||"",t=t!==""?"ForwardRef("+t+")":"ForwardRef"),t;case dm:return e=t.displayName||null,e!==null?e:mh(t.type)||"Memo";case $a:e=t._payload,t=t._init;try{return mh(t(e))}catch{}}return null}var eo=Array.isArray,tt=t_.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,Tt=gM.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,Gs={pending:!1,data:null,method:null,action:null},gh=[],zr=-1;function ra(t){return{current:t}}function vn(t){0>zr||(t.current=gh[zr],gh[zr]=null,zr--)}function Bt(t,e){zr++,gh[zr]=t.current,t.current=e}var na=ra(null),wo=ra(null),ds=ra(null),Nu=ra(null);function Ru(t,e){switch(Bt(ds,e),Bt(wo,t),Bt(na,null),e.nodeType){case 9:case 11:t=(t=e.documentElement)&&(t=t.namespaceURI)?jx(t):0;break;default:if(t=e.tagName,e=e.namespaceURI)e=jx(e),t=wb(e,t);else switch(t){case"svg":t=1;break;case"math":t=2;break;default:t=0}}vn(na),Bt(na,t)}function rl(){vn(na),vn(wo),vn(ds)}function xh(t){t.memoizedState!==null&&Bt(Nu,t);var e=na.current,n=wb(e,t.type);e!==n&&(Bt(wo,t),Bt(na,n))}function Du(t){wo.current===t&&(vn(na),vn(wo)),Nu.current===t&&(vn(Nu),ko._currentValue=Gs)}var Gf,zg;function Os(t){if(Gf===void 0)try{throw Error()}catch(n){var e=n.stack.trim().match(/\n( *(at )?)/);Gf=e&&e[1]||"",zg=-1<n.stack.indexOf(` - at`)?" (<anonymous>)":-1<n.stack.indexOf("@")?"@unknown:0:0":""}return` -`+Gf+t+zg}var Vf=!1;function Wf(t,e){if(!t||Vf)return"";Vf=!0;var n=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var i={DetermineComponentFrameRoot:function(){try{if(e){var m=function(){throw Error()};if(Object.defineProperty(m.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(m,[])}catch(p){var f=p}Reflect.construct(t,[],m)}else{try{m.call()}catch(p){f=p}t.call(m.prototype)}}else{try{throw Error()}catch(p){f=p}(m=t())&&typeof m.catch=="function"&&m.catch(function(){})}}catch(p){if(p&&f&&typeof p.stack=="string")return[p.stack,f.stack]}return[null,null]}};i.DetermineComponentFrameRoot.displayName="DetermineComponentFrameRoot";var a=Object.getOwnPropertyDescriptor(i.DetermineComponentFrameRoot,"name");a&&a.configurable&&Object.defineProperty(i.DetermineComponentFrameRoot,"name",{value:"DetermineComponentFrameRoot"});var s=i.DetermineComponentFrameRoot(),r=s[0],o=s[1];if(r&&o){var c=r.split(` -`),u=o.split(` -`);for(a=i=0;i<c.length&&!c[i].includes("DetermineComponentFrameRoot");)i++;for(;a<u.length&&!u[a].includes("DetermineComponentFrameRoot");)a++;if(i===c.length||a===u.length)for(i=c.length-1,a=u.length-1;1<=i&&0<=a&&c[i]!==u[a];)a--;for(;1<=i&&0<=a;i--,a--)if(c[i]!==u[a]){if(i!==1||a!==1)do if(i--,a--,0>a||c[i]!==u[a]){var d=` -`+c[i].replace(" at new "," at ");return t.displayName&&d.includes("<anonymous>")&&(d=d.replace("<anonymous>",t.displayName)),d}while(1<=i&&0<=a);break}}}finally{Vf=!1,Error.prepareStackTrace=n}return(n=t?t.displayName||t.name:"")?Os(n):""}function bM(t,e){switch(t.tag){case 26:case 27:case 5:return Os(t.type);case 16:return Os("Lazy");case 13:return t.child!==e&&e!==null?Os("Suspense Fallback"):Os("Suspense");case 19:return Os("SuspenseList");case 0:case 15:return Wf(t.type,!1);case 11:return Wf(t.type.render,!1);case 1:return Wf(t.type,!0);case 31:return Os("Activity");default:return""}}function Ig(t){try{var e="",n=null;do e+=bM(t,n),n=t,t=t.return;while(t);return e}catch(i){return` -Error generating stack: `+i.message+` -`+i.stack}}var vh=Object.prototype.hasOwnProperty,hm=fn.unstable_scheduleCallback,Xf=fn.unstable_cancelCallback,SM=fn.unstable_shouldYield,MM=fn.unstable_requestPaint,li=fn.unstable_now,EM=fn.unstable_getCurrentPriorityLevel,o_=fn.unstable_ImmediatePriority,c_=fn.unstable_UserBlockingPriority,Lu=fn.unstable_NormalPriority,TM=fn.unstable_LowPriority,u_=fn.unstable_IdlePriority,AM=fn.log,wM=fn.unstable_setDisableYieldValue,qo=null,oi=null;function as(t){if(typeof AM=="function"&&wM(t),oi&&typeof oi.setStrictMode=="function")try{oi.setStrictMode(qo,t)}catch{}}var ci=Math.clz32?Math.clz32:RM,CM=Math.log,NM=Math.LN2;function RM(t){return t>>>=0,t===0?32:31-(CM(t)/NM|0)|0}var oc=256,cc=262144,uc=4194304;function Ps(t){var e=t&42;if(e!==0)return e;switch(t&-t){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return t&261888;case 262144:case 524288:case 1048576:case 2097152:return t&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return t&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return t}}function yf(t,e,n){var i=t.pendingLanes;if(i===0)return 0;var a=0,s=t.suspendedLanes,r=t.pingedLanes;t=t.warmLanes;var o=i&134217727;return o!==0?(i=o&~s,i!==0?a=Ps(i):(r&=o,r!==0?a=Ps(r):n||(n=o&~t,n!==0&&(a=Ps(n))))):(o=i&~s,o!==0?a=Ps(o):r!==0?a=Ps(r):n||(n=i&~t,n!==0&&(a=Ps(n)))),a===0?0:e!==0&&e!==a&&!(e&s)&&(s=a&-a,n=e&-e,s>=n||s===32&&(n&4194048)!==0)?e:a}function Yo(t,e){return(t.pendingLanes&~(t.suspendedLanes&~t.pingedLanes)&e)===0}function DM(t,e){switch(t){case 1:case 2:case 4:case 8:case 64:return e+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function f_(){var t=uc;return uc<<=1,!(uc&62914560)&&(uc=4194304),t}function qf(t){for(var e=[],n=0;31>n;n++)e.push(t);return e}function Ko(t,e){t.pendingLanes|=e,e!==268435456&&(t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0)}function LM(t,e,n,i,a,s){var r=t.pendingLanes;t.pendingLanes=n,t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0,t.expiredLanes&=n,t.entangledLanes&=n,t.errorRecoveryDisabledLanes&=n,t.shellSuspendCounter=0;var o=t.entanglements,c=t.expirationTimes,u=t.hiddenUpdates;for(n=r&~n;0<n;){var d=31-ci(n),m=1<<d;o[d]=0,c[d]=-1;var f=u[d];if(f!==null)for(u[d]=null,d=0;d<f.length;d++){var p=f[d];p!==null&&(p.lane&=-536870913)}n&=~m}i!==0&&d_(t,i,0),s!==0&&a===0&&t.tag!==0&&(t.suspendedLanes|=s&~(r&~e))}function d_(t,e,n){t.pendingLanes|=e,t.suspendedLanes&=~e;var i=31-ci(e);t.entangledLanes|=e,t.entanglements[i]=t.entanglements[i]|1073741824|n&261930}function h_(t,e){var n=t.entangledLanes|=e;for(t=t.entanglements;n;){var i=31-ci(n),a=1<<i;a&e|t[i]&e&&(t[i]|=e),n&=~a}}function p_(t,e){var n=e&-e;return n=n&42?1:pm(n),n&(t.suspendedLanes|e)?0:n}function pm(t){switch(t){case 2:t=1;break;case 8:t=4;break;case 32:t=16;break;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:case 4194304:case 8388608:case 16777216:case 33554432:t=128;break;case 268435456:t=134217728;break;default:t=0}return t}function mm(t){return t&=-t,2<t?8<t?t&134217727?32:268435456:8:2}function m_(){var t=Tt.p;return t!==0?t:(t=window.event,t===void 0?32:kb(t.type))}function kg(t,e){var n=Tt.p;try{return Tt.p=t,e()}finally{Tt.p=n}}var As=Math.random().toString(36).slice(2),bn="__reactFiber$"+As,Kn="__reactProps$"+As,El="__reactContainer$"+As,_h="__reactEvents$"+As,UM="__reactListeners$"+As,OM="__reactHandles$"+As,Bg="__reactResources$"+As,Zo="__reactMarker$"+As;function gm(t){delete t[bn],delete t[Kn],delete t[_h],delete t[UM],delete t[OM]}function Ir(t){var e=t[bn];if(e)return e;for(var n=t.parentNode;n;){if(e=n[El]||n[bn]){if(n=e.alternate,e.child!==null||n!==null&&n.child!==null)for(t=Xx(t);t!==null;){if(n=t[bn])return n;t=Xx(t)}return e}t=n,n=t.parentNode}return null}function Tl(t){if(t=t[bn]||t[El]){var e=t.tag;if(e===5||e===6||e===13||e===31||e===26||e===27||e===3)return t}return null}function to(t){var e=t.tag;if(e===5||e===26||e===27||e===6)return t.stateNode;throw Error(ue(33))}function Kr(t){var e=t[Bg];return e||(e=t[Bg]={hoistableStyles:new Map,hoistableScripts:new Map}),e}function gn(t){t[Zo]=!0}var g_=new Set,x_={};function ar(t,e){ll(t,e),ll(t+"Capture",e)}function ll(t,e){for(x_[t]=e,t=0;t<e.length;t++)g_.add(e[t])}var PM=RegExp("^[:A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD][:A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040]*$"),Fg={},jg={};function zM(t){return vh.call(jg,t)?!0:vh.call(Fg,t)?!1:PM.test(t)?jg[t]=!0:(Fg[t]=!0,!1)}function au(t,e,n){if(zM(e))if(n===null)t.removeAttribute(e);else{switch(typeof n){case"undefined":case"function":case"symbol":t.removeAttribute(e);return;case"boolean":var i=e.toLowerCase().slice(0,5);if(i!=="data-"&&i!=="aria-"){t.removeAttribute(e);return}}t.setAttribute(e,""+n)}}function fc(t,e,n){if(n===null)t.removeAttribute(e);else{switch(typeof n){case"undefined":case"function":case"symbol":case"boolean":t.removeAttribute(e);return}t.setAttribute(e,""+n)}}function oa(t,e,n,i){if(i===null)t.removeAttribute(n);else{switch(typeof i){case"undefined":case"function":case"symbol":case"boolean":t.removeAttribute(n);return}t.setAttributeNS(e,n,""+i)}}function vi(t){switch(typeof t){case"bigint":case"boolean":case"number":case"string":case"undefined":return t;case"object":return t;default:return""}}function v_(t){var e=t.type;return(t=t.nodeName)&&t.toLowerCase()==="input"&&(e==="checkbox"||e==="radio")}function IM(t,e,n){var i=Object.getOwnPropertyDescriptor(t.constructor.prototype,e);if(!t.hasOwnProperty(e)&&typeof i<"u"&&typeof i.get=="function"&&typeof i.set=="function"){var a=i.get,s=i.set;return Object.defineProperty(t,e,{configurable:!0,get:function(){return a.call(this)},set:function(r){n=""+r,s.call(this,r)}}),Object.defineProperty(t,e,{enumerable:i.enumerable}),{getValue:function(){return n},setValue:function(r){n=""+r},stopTracking:function(){t._valueTracker=null,delete t[e]}}}}function yh(t){if(!t._valueTracker){var e=v_(t)?"checked":"value";t._valueTracker=IM(t,e,""+t[e])}}function __(t){if(!t)return!1;var e=t._valueTracker;if(!e)return!0;var n=e.getValue(),i="";return t&&(i=v_(t)?t.checked?"true":"false":t.value),t=i,t!==n?(e.setValue(t),!0):!1}function Uu(t){if(t=t||(typeof document<"u"?document:void 0),typeof t>"u")return null;try{return t.activeElement||t.body}catch{return t.body}}var kM=/[\n"\\]/g;function bi(t){return t.replace(kM,function(e){return"\\"+e.charCodeAt(0).toString(16)+" "})}function bh(t,e,n,i,a,s,r,o){t.name="",r!=null&&typeof r!="function"&&typeof r!="symbol"&&typeof r!="boolean"?t.type=r:t.removeAttribute("type"),e!=null?r==="number"?(e===0&&t.value===""||t.value!=e)&&(t.value=""+vi(e)):t.value!==""+vi(e)&&(t.value=""+vi(e)):r!=="submit"&&r!=="reset"||t.removeAttribute("value"),e!=null?Sh(t,r,vi(e)):n!=null?Sh(t,r,vi(n)):i!=null&&t.removeAttribute("value"),a==null&&s!=null&&(t.defaultChecked=!!s),a!=null&&(t.checked=a&&typeof a!="function"&&typeof a!="symbol"),o!=null&&typeof o!="function"&&typeof o!="symbol"&&typeof o!="boolean"?t.name=""+vi(o):t.removeAttribute("name")}function y_(t,e,n,i,a,s,r,o){if(s!=null&&typeof s!="function"&&typeof s!="symbol"&&typeof s!="boolean"&&(t.type=s),e!=null||n!=null){if(!(s!=="submit"&&s!=="reset"||e!=null)){yh(t);return}n=n!=null?""+vi(n):"",e=e!=null?""+vi(e):n,o||e===t.value||(t.value=e),t.defaultValue=e}i=i??a,i=typeof i!="function"&&typeof i!="symbol"&&!!i,t.checked=o?t.checked:!!i,t.defaultChecked=!!i,r!=null&&typeof r!="function"&&typeof r!="symbol"&&typeof r!="boolean"&&(t.name=r),yh(t)}function Sh(t,e,n){e==="number"&&Uu(t.ownerDocument)===t||t.defaultValue===""+n||(t.defaultValue=""+n)}function Zr(t,e,n,i){if(t=t.options,e){e={};for(var a=0;a<n.length;a++)e["$"+n[a]]=!0;for(n=0;n<t.length;n++)a=e.hasOwnProperty("$"+t[n].value),t[n].selected!==a&&(t[n].selected=a),a&&i&&(t[n].defaultSelected=!0)}else{for(n=""+vi(n),e=null,a=0;a<t.length;a++){if(t[a].value===n){t[a].selected=!0,i&&(t[a].defaultSelected=!0);return}e!==null||t[a].disabled||(e=t[a])}e!==null&&(e.selected=!0)}}function b_(t,e,n){if(e!=null&&(e=""+vi(e),e!==t.value&&(t.value=e),n==null)){t.defaultValue!==e&&(t.defaultValue=e);return}t.defaultValue=n!=null?""+vi(n):""}function S_(t,e,n,i){if(e==null){if(i!=null){if(n!=null)throw Error(ue(92));if(eo(i)){if(1<i.length)throw Error(ue(93));i=i[0]}n=i}n==null&&(n=""),e=n}n=vi(e),t.defaultValue=n,i=t.textContent,i===n&&i!==""&&i!==null&&(t.value=i),yh(t)}function ol(t,e){if(e){var n=t.firstChild;if(n&&n===t.lastChild&&n.nodeType===3){n.nodeValue=e;return}}t.textContent=e}var BM=new Set("animationIterationCount aspectRatio borderImageOutset borderImageSlice borderImageWidth boxFlex boxFlexGroup boxOrdinalGroup columnCount columns flex flexGrow flexPositive flexShrink flexNegative flexOrder gridArea gridRow gridRowEnd gridRowSpan gridRowStart gridColumn gridColumnEnd gridColumnSpan gridColumnStart fontWeight lineClamp lineHeight opacity order orphans scale tabSize widows zIndex zoom fillOpacity floodOpacity stopOpacity strokeDasharray strokeDashoffset strokeMiterlimit strokeOpacity strokeWidth MozAnimationIterationCount MozBoxFlex MozBoxFlexGroup MozLineClamp msAnimationIterationCount msFlex msZoom msFlexGrow msFlexNegative msFlexOrder msFlexPositive msFlexShrink msGridColumn msGridColumnSpan msGridRow msGridRowSpan WebkitAnimationIterationCount WebkitBoxFlex WebKitBoxFlexGroup WebkitBoxOrdinalGroup WebkitColumnCount WebkitColumns WebkitFlex WebkitFlexGrow WebkitFlexPositive WebkitFlexShrink WebkitLineClamp".split(" "));function Hg(t,e,n){var i=e.indexOf("--")===0;n==null||typeof n=="boolean"||n===""?i?t.setProperty(e,""):e==="float"?t.cssFloat="":t[e]="":i?t.setProperty(e,n):typeof n!="number"||n===0||BM.has(e)?e==="float"?t.cssFloat=n:t[e]=(""+n).trim():t[e]=n+"px"}function M_(t,e,n){if(e!=null&&typeof e!="object")throw Error(ue(62));if(t=t.style,n!=null){for(var i in n)!n.hasOwnProperty(i)||e!=null&&e.hasOwnProperty(i)||(i.indexOf("--")===0?t.setProperty(i,""):i==="float"?t.cssFloat="":t[i]="");for(var a in e)i=e[a],e.hasOwnProperty(a)&&n[a]!==i&&Hg(t,a,i)}else for(var s in e)e.hasOwnProperty(s)&&Hg(t,s,e[s])}function xm(t){if(t.indexOf("-")===-1)return!1;switch(t){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var FM=new Map([["acceptCharset","accept-charset"],["htmlFor","for"],["httpEquiv","http-equiv"],["crossOrigin","crossorigin"],["accentHeight","accent-height"],["alignmentBaseline","alignment-baseline"],["arabicForm","arabic-form"],["baselineShift","baseline-shift"],["capHeight","cap-height"],["clipPath","clip-path"],["clipRule","clip-rule"],["colorInterpolation","color-interpolation"],["colorInterpolationFilters","color-interpolation-filters"],["colorProfile","color-profile"],["colorRendering","color-rendering"],["dominantBaseline","dominant-baseline"],["enableBackground","enable-background"],["fillOpacity","fill-opacity"],["fillRule","fill-rule"],["floodColor","flood-color"],["floodOpacity","flood-opacity"],["fontFamily","font-family"],["fontSize","font-size"],["fontSizeAdjust","font-size-adjust"],["fontStretch","font-stretch"],["fontStyle","font-style"],["fontVariant","font-variant"],["fontWeight","font-weight"],["glyphName","glyph-name"],["glyphOrientationHorizontal","glyph-orientation-horizontal"],["glyphOrientationVertical","glyph-orientation-vertical"],["horizAdvX","horiz-adv-x"],["horizOriginX","horiz-origin-x"],["imageRendering","image-rendering"],["letterSpacing","letter-spacing"],["lightingColor","lighting-color"],["markerEnd","marker-end"],["markerMid","marker-mid"],["markerStart","marker-start"],["overlinePosition","overline-position"],["overlineThickness","overline-thickness"],["paintOrder","paint-order"],["panose-1","panose-1"],["pointerEvents","pointer-events"],["renderingIntent","rendering-intent"],["shapeRendering","shape-rendering"],["stopColor","stop-color"],["stopOpacity","stop-opacity"],["strikethroughPosition","strikethrough-position"],["strikethroughThickness","strikethrough-thickness"],["strokeDasharray","stroke-dasharray"],["strokeDashoffset","stroke-dashoffset"],["strokeLinecap","stroke-linecap"],["strokeLinejoin","stroke-linejoin"],["strokeMiterlimit","stroke-miterlimit"],["strokeOpacity","stroke-opacity"],["strokeWidth","stroke-width"],["textAnchor","text-anchor"],["textDecoration","text-decoration"],["textRendering","text-rendering"],["transformOrigin","transform-origin"],["underlinePosition","underline-position"],["underlineThickness","underline-thickness"],["unicodeBidi","unicode-bidi"],["unicodeRange","unicode-range"],["unitsPerEm","units-per-em"],["vAlphabetic","v-alphabetic"],["vHanging","v-hanging"],["vIdeographic","v-ideographic"],["vMathematical","v-mathematical"],["vectorEffect","vector-effect"],["vertAdvY","vert-adv-y"],["vertOriginX","vert-origin-x"],["vertOriginY","vert-origin-y"],["wordSpacing","word-spacing"],["writingMode","writing-mode"],["xmlnsXlink","xmlns:xlink"],["xHeight","x-height"]]),jM=/^[\u0000-\u001F ]*j[\r\n\t]*a[\r\n\t]*v[\r\n\t]*a[\r\n\t]*s[\r\n\t]*c[\r\n\t]*r[\r\n\t]*i[\r\n\t]*p[\r\n\t]*t[\r\n\t]*:/i;function su(t){return jM.test(""+t)?"javascript:throw new Error('React has blocked a javascript: URL as a security precaution.')":t}function Ma(){}var Mh=null;function vm(t){return t=t.target||t.srcElement||window,t.correspondingUseElement&&(t=t.correspondingUseElement),t.nodeType===3?t.parentNode:t}var kr=null,$r=null;function Gg(t){var e=Tl(t);if(e&&(t=e.stateNode)){var n=t[Kn]||null;e:switch(t=e.stateNode,e.type){case"input":if(bh(t,n.value,n.defaultValue,n.defaultValue,n.checked,n.defaultChecked,n.type,n.name),e=n.name,n.type==="radio"&&e!=null){for(n=t;n.parentNode;)n=n.parentNode;for(n=n.querySelectorAll('input[name="'+bi(""+e)+'"][type="radio"]'),e=0;e<n.length;e++){var i=n[e];if(i!==t&&i.form===t.form){var a=i[Kn]||null;if(!a)throw Error(ue(90));bh(i,a.value,a.defaultValue,a.defaultValue,a.checked,a.defaultChecked,a.type,a.name)}}for(e=0;e<n.length;e++)i=n[e],i.form===t.form&&__(i)}break e;case"textarea":b_(t,n.value,n.defaultValue);break e;case"select":e=n.value,e!=null&&Zr(t,!!n.multiple,e,!1)}}}var Yf=!1;function E_(t,e,n){if(Yf)return t(e,n);Yf=!0;try{var i=t(e);return i}finally{if(Yf=!1,(kr!==null||$r!==null)&&(Lf(),kr&&(e=kr,t=$r,$r=kr=null,Gg(e),t)))for(e=0;e<t.length;e++)Gg(t[e])}}function Co(t,e){var n=t.stateNode;if(n===null)return null;var i=n[Kn]||null;if(i===null)return null;n=i[e];e:switch(e){case"onClick":case"onClickCapture":case"onDoubleClick":case"onDoubleClickCapture":case"onMouseDown":case"onMouseDownCapture":case"onMouseMove":case"onMouseMoveCapture":case"onMouseUp":case"onMouseUpCapture":case"onMouseEnter":(i=!i.disabled)||(t=t.type,i=!(t==="button"||t==="input"||t==="select"||t==="textarea")),t=!i;break e;default:t=!1}if(t)return null;if(n&&typeof n!="function")throw Error(ue(231,e,typeof n));return n}var Da=!(typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Eh=!1;if(Da)try{var Pl={};Object.defineProperty(Pl,"passive",{get:function(){Eh=!0}}),window.addEventListener("test",Pl,Pl),window.removeEventListener("test",Pl,Pl)}catch{Eh=!1}var ss=null,_m=null,ru=null;function T_(){if(ru)return ru;var t,e=_m,n=e.length,i,a="value"in ss?ss.value:ss.textContent,s=a.length;for(t=0;t<n&&e[t]===a[t];t++);var r=n-t;for(i=1;i<=r&&e[n-i]===a[s-i];i++);return ru=a.slice(t,1<i?1-i:void 0)}function lu(t){var e=t.keyCode;return"charCode"in t?(t=t.charCode,t===0&&e===13&&(t=13)):t=e,t===10&&(t=13),32<=t||t===13?t:0}function dc(){return!0}function Vg(){return!1}function $n(t){function e(n,i,a,s,r){this._reactName=n,this._targetInst=a,this.type=i,this.nativeEvent=s,this.target=r,this.currentTarget=null;for(var o in t)t.hasOwnProperty(o)&&(n=t[o],this[o]=n?n(s):s[o]);return this.isDefaultPrevented=(s.defaultPrevented!=null?s.defaultPrevented:s.returnValue===!1)?dc:Vg,this.isPropagationStopped=Vg,this}return Vt(e.prototype,{preventDefault:function(){this.defaultPrevented=!0;var n=this.nativeEvent;n&&(n.preventDefault?n.preventDefault():typeof n.returnValue!="unknown"&&(n.returnValue=!1),this.isDefaultPrevented=dc)},stopPropagation:function(){var n=this.nativeEvent;n&&(n.stopPropagation?n.stopPropagation():typeof n.cancelBubble!="unknown"&&(n.cancelBubble=!0),this.isPropagationStopped=dc)},persist:function(){},isPersistent:dc}),e}var sr={eventPhase:0,bubbles:0,cancelable:0,timeStamp:function(t){return t.timeStamp||Date.now()},defaultPrevented:0,isTrusted:0},bf=$n(sr),$o=Vt({},sr,{view:0,detail:0}),HM=$n($o),Kf,Zf,zl,Sf=Vt({},$o,{screenX:0,screenY:0,clientX:0,clientY:0,pageX:0,pageY:0,ctrlKey:0,shiftKey:0,altKey:0,metaKey:0,getModifierState:ym,button:0,buttons:0,relatedTarget:function(t){return t.relatedTarget===void 0?t.fromElement===t.srcElement?t.toElement:t.fromElement:t.relatedTarget},movementX:function(t){return"movementX"in t?t.movementX:(t!==zl&&(zl&&t.type==="mousemove"?(Kf=t.screenX-zl.screenX,Zf=t.screenY-zl.screenY):Zf=Kf=0,zl=t),Kf)},movementY:function(t){return"movementY"in t?t.movementY:Zf}}),Wg=$n(Sf),GM=Vt({},Sf,{dataTransfer:0}),VM=$n(GM),WM=Vt({},$o,{relatedTarget:0}),$f=$n(WM),XM=Vt({},sr,{animationName:0,elapsedTime:0,pseudoElement:0}),qM=$n(XM),YM=Vt({},sr,{clipboardData:function(t){return"clipboardData"in t?t.clipboardData:window.clipboardData}}),KM=$n(YM),ZM=Vt({},sr,{data:0}),Xg=$n(ZM),$M={Esc:"Escape",Spacebar:" ",Left:"ArrowLeft",Up:"ArrowUp",Right:"ArrowRight",Down:"ArrowDown",Del:"Delete",Win:"OS",Menu:"ContextMenu",Apps:"ContextMenu",Scroll:"ScrollLock",MozPrintableKey:"Unidentified"},QM={8:"Backspace",9:"Tab",12:"Clear",13:"Enter",16:"Shift",17:"Control",18:"Alt",19:"Pause",20:"CapsLock",27:"Escape",32:" ",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"ArrowLeft",38:"ArrowUp",39:"ArrowRight",40:"ArrowDown",45:"Insert",46:"Delete",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"NumLock",145:"ScrollLock",224:"Meta"},JM={Alt:"altKey",Control:"ctrlKey",Meta:"metaKey",Shift:"shiftKey"};function eE(t){var e=this.nativeEvent;return e.getModifierState?e.getModifierState(t):(t=JM[t])?!!e[t]:!1}function ym(){return eE}var tE=Vt({},$o,{key:function(t){if(t.key){var e=$M[t.key]||t.key;if(e!=="Unidentified")return e}return t.type==="keypress"?(t=lu(t),t===13?"Enter":String.fromCharCode(t)):t.type==="keydown"||t.type==="keyup"?QM[t.keyCode]||"Unidentified":""},code:0,location:0,ctrlKey:0,shiftKey:0,altKey:0,metaKey:0,repeat:0,locale:0,getModifierState:ym,charCode:function(t){return t.type==="keypress"?lu(t):0},keyCode:function(t){return t.type==="keydown"||t.type==="keyup"?t.keyCode:0},which:function(t){return t.type==="keypress"?lu(t):t.type==="keydown"||t.type==="keyup"?t.keyCode:0}}),nE=$n(tE),iE=Vt({},Sf,{pointerId:0,width:0,height:0,pressure:0,tangentialPressure:0,tiltX:0,tiltY:0,twist:0,pointerType:0,isPrimary:0}),qg=$n(iE),aE=Vt({},$o,{touches:0,targetTouches:0,changedTouches:0,altKey:0,metaKey:0,ctrlKey:0,shiftKey:0,getModifierState:ym}),sE=$n(aE),rE=Vt({},sr,{propertyName:0,elapsedTime:0,pseudoElement:0}),lE=$n(rE),oE=Vt({},Sf,{deltaX:function(t){return"deltaX"in t?t.deltaX:"wheelDeltaX"in t?-t.wheelDeltaX:0},deltaY:function(t){return"deltaY"in t?t.deltaY:"wheelDeltaY"in t?-t.wheelDeltaY:"wheelDelta"in t?-t.wheelDelta:0},deltaZ:0,deltaMode:0}),cE=$n(oE),uE=Vt({},sr,{newState:0,oldState:0}),fE=$n(uE),dE=[9,13,27,32],bm=Da&&"CompositionEvent"in window,uo=null;Da&&"documentMode"in document&&(uo=document.documentMode);var hE=Da&&"TextEvent"in window&&!uo,A_=Da&&(!bm||uo&&8<uo&&11>=uo),Yg=" ",Kg=!1;function w_(t,e){switch(t){case"keyup":return dE.indexOf(e.keyCode)!==-1;case"keydown":return e.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function C_(t){return t=t.detail,typeof t=="object"&&"data"in t?t.data:null}var Br=!1;function pE(t,e){switch(t){case"compositionend":return C_(e);case"keypress":return e.which!==32?null:(Kg=!0,Yg);case"textInput":return t=e.data,t===Yg&&Kg?null:t;default:return null}}function mE(t,e){if(Br)return t==="compositionend"||!bm&&w_(t,e)?(t=T_(),ru=_m=ss=null,Br=!1,t):null;switch(t){case"paste":return null;case"keypress":if(!(e.ctrlKey||e.altKey||e.metaKey)||e.ctrlKey&&e.altKey){if(e.char&&1<e.char.length)return e.char;if(e.which)return String.fromCharCode(e.which)}return null;case"compositionend":return A_&&e.locale!=="ko"?null:e.data;default:return null}}var gE={color:!0,date:!0,datetime:!0,"datetime-local":!0,email:!0,month:!0,number:!0,password:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0};function Zg(t){var e=t&&t.nodeName&&t.nodeName.toLowerCase();return e==="input"?!!gE[t.type]:e==="textarea"}function N_(t,e,n,i){kr?$r?$r.push(i):$r=[i]:kr=i,e=$u(e,"onChange"),0<e.length&&(n=new bf("onChange","change",null,n,i),t.push({event:n,listeners:e}))}var fo=null,No=null;function xE(t){Eb(t,0)}function Mf(t){var e=to(t);if(__(e))return t}function $g(t,e){if(t==="change")return e}var R_=!1;if(Da){var Qf;if(Da){var Jf="oninput"in document;if(!Jf){var Qg=document.createElement("div");Qg.setAttribute("oninput","return;"),Jf=typeof Qg.oninput=="function"}Qf=Jf}else Qf=!1;R_=Qf&&(!document.documentMode||9<document.documentMode)}function Jg(){fo&&(fo.detachEvent("onpropertychange",D_),No=fo=null)}function D_(t){if(t.propertyName==="value"&&Mf(No)){var e=[];N_(e,No,t,vm(t)),E_(xE,e)}}function vE(t,e,n){t==="focusin"?(Jg(),fo=e,No=n,fo.attachEvent("onpropertychange",D_)):t==="focusout"&&Jg()}function _E(t){if(t==="selectionchange"||t==="keyup"||t==="keydown")return Mf(No)}function yE(t,e){if(t==="click")return Mf(e)}function bE(t,e){if(t==="input"||t==="change")return Mf(e)}function SE(t,e){return t===e&&(t!==0||1/t===1/e)||t!==t&&e!==e}var fi=typeof Object.is=="function"?Object.is:SE;function Ro(t,e){if(fi(t,e))return!0;if(typeof t!="object"||t===null||typeof e!="object"||e===null)return!1;var n=Object.keys(t),i=Object.keys(e);if(n.length!==i.length)return!1;for(i=0;i<n.length;i++){var a=n[i];if(!vh.call(e,a)||!fi(t[a],e[a]))return!1}return!0}function ex(t){for(;t&&t.firstChild;)t=t.firstChild;return t}function tx(t,e){var n=ex(t);t=0;for(var i;n;){if(n.nodeType===3){if(i=t+n.textContent.length,t<=e&&i>=e)return{node:n,offset:e-t};t=i}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=ex(n)}}function L_(t,e){return t&&e?t===e?!0:t&&t.nodeType===3?!1:e&&e.nodeType===3?L_(t,e.parentNode):"contains"in t?t.contains(e):t.compareDocumentPosition?!!(t.compareDocumentPosition(e)&16):!1:!1}function U_(t){t=t!=null&&t.ownerDocument!=null&&t.ownerDocument.defaultView!=null?t.ownerDocument.defaultView:window;for(var e=Uu(t.document);e instanceof t.HTMLIFrameElement;){try{var n=typeof e.contentWindow.location.href=="string"}catch{n=!1}if(n)t=e.contentWindow;else break;e=Uu(t.document)}return e}function Sm(t){var e=t&&t.nodeName&&t.nodeName.toLowerCase();return e&&(e==="input"&&(t.type==="text"||t.type==="search"||t.type==="tel"||t.type==="url"||t.type==="password")||e==="textarea"||t.contentEditable==="true")}var ME=Da&&"documentMode"in document&&11>=document.documentMode,Fr=null,Th=null,ho=null,Ah=!1;function nx(t,e,n){var i=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;Ah||Fr==null||Fr!==Uu(i)||(i=Fr,"selectionStart"in i&&Sm(i)?i={start:i.selectionStart,end:i.selectionEnd}:(i=(i.ownerDocument&&i.ownerDocument.defaultView||window).getSelection(),i={anchorNode:i.anchorNode,anchorOffset:i.anchorOffset,focusNode:i.focusNode,focusOffset:i.focusOffset}),ho&&Ro(ho,i)||(ho=i,i=$u(Th,"onSelect"),0<i.length&&(e=new bf("onSelect","select",null,e,n),t.push({event:e,listeners:i}),e.target=Fr)))}function Ns(t,e){var n={};return n[t.toLowerCase()]=e.toLowerCase(),n["Webkit"+t]="webkit"+e,n["Moz"+t]="moz"+e,n}var jr={animationend:Ns("Animation","AnimationEnd"),animationiteration:Ns("Animation","AnimationIteration"),animationstart:Ns("Animation","AnimationStart"),transitionrun:Ns("Transition","TransitionRun"),transitionstart:Ns("Transition","TransitionStart"),transitioncancel:Ns("Transition","TransitionCancel"),transitionend:Ns("Transition","TransitionEnd")},ed={},O_={};Da&&(O_=document.createElement("div").style,"AnimationEvent"in window||(delete jr.animationend.animation,delete jr.animationiteration.animation,delete jr.animationstart.animation),"TransitionEvent"in window||delete jr.transitionend.transition);function rr(t){if(ed[t])return ed[t];if(!jr[t])return t;var e=jr[t],n;for(n in e)if(e.hasOwnProperty(n)&&n in O_)return ed[t]=e[n];return t}var P_=rr("animationend"),z_=rr("animationiteration"),I_=rr("animationstart"),EE=rr("transitionrun"),TE=rr("transitionstart"),AE=rr("transitioncancel"),k_=rr("transitionend"),B_=new Map,wh="abort auxClick beforeToggle cancel canPlay canPlayThrough click close contextMenu copy cut drag dragEnd dragEnter dragExit dragLeave dragOver dragStart drop durationChange emptied encrypted ended error gotPointerCapture input invalid keyDown keyPress keyUp load loadedData loadedMetadata loadStart lostPointerCapture mouseDown mouseMove mouseOut mouseOver mouseUp paste pause play playing pointerCancel pointerDown pointerMove pointerOut pointerOver pointerUp progress rateChange reset resize seeked seeking stalled submit suspend timeUpdate touchCancel touchEnd touchStart volumeChange scroll toggle touchMove waiting wheel".split(" ");wh.push("scrollEnd");function Bi(t,e){B_.set(t,e),ar(e,[t])}var Ou=typeof reportError=="function"?reportError:function(t){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var e=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof t=="object"&&t!==null&&typeof t.message=="string"?String(t.message):String(t),error:t});if(!window.dispatchEvent(e))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",t);return}console.error(t)},gi=[],Hr=0,Mm=0;function Ef(){for(var t=Hr,e=Mm=Hr=0;e<t;){var n=gi[e];gi[e++]=null;var i=gi[e];gi[e++]=null;var a=gi[e];gi[e++]=null;var s=gi[e];if(gi[e++]=null,i!==null&&a!==null){var r=i.pending;r===null?a.next=a:(a.next=r.next,r.next=a),i.pending=a}s!==0&&F_(n,a,s)}}function Tf(t,e,n,i){gi[Hr++]=t,gi[Hr++]=e,gi[Hr++]=n,gi[Hr++]=i,Mm|=i,t.lanes|=i,t=t.alternate,t!==null&&(t.lanes|=i)}function Em(t,e,n,i){return Tf(t,e,n,i),Pu(t)}function lr(t,e){return Tf(t,null,null,e),Pu(t)}function F_(t,e,n){t.lanes|=n;var i=t.alternate;i!==null&&(i.lanes|=n);for(var a=!1,s=t.return;s!==null;)s.childLanes|=n,i=s.alternate,i!==null&&(i.childLanes|=n),s.tag===22&&(t=s.stateNode,t===null||t._visibility&1||(a=!0)),t=s,s=s.return;return t.tag===3?(s=t.stateNode,a&&e!==null&&(a=31-ci(n),t=s.hiddenUpdates,i=t[a],i===null?t[a]=[e]:i.push(e),e.lane=n|536870912),s):null}function Pu(t){if(50<So)throw So=0,Yh=null,Error(ue(185));for(var e=t.return;e!==null;)t=e,e=t.return;return t.tag===3?t.stateNode:null}var Gr={};function wE(t,e,n,i){this.tag=t,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.refCleanup=this.ref=null,this.pendingProps=e,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=i,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function si(t,e,n,i){return new wE(t,e,n,i)}function Tm(t){return t=t.prototype,!(!t||!t.isReactComponent)}function Aa(t,e){var n=t.alternate;return n===null?(n=si(t.tag,e,t.key,t.mode),n.elementType=t.elementType,n.type=t.type,n.stateNode=t.stateNode,n.alternate=t,t.alternate=n):(n.pendingProps=e,n.type=t.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=t.flags&65011712,n.childLanes=t.childLanes,n.lanes=t.lanes,n.child=t.child,n.memoizedProps=t.memoizedProps,n.memoizedState=t.memoizedState,n.updateQueue=t.updateQueue,e=t.dependencies,n.dependencies=e===null?null:{lanes:e.lanes,firstContext:e.firstContext},n.sibling=t.sibling,n.index=t.index,n.ref=t.ref,n.refCleanup=t.refCleanup,n}function j_(t,e){t.flags&=65011714;var n=t.alternate;return n===null?(t.childLanes=0,t.lanes=e,t.child=null,t.subtreeFlags=0,t.memoizedProps=null,t.memoizedState=null,t.updateQueue=null,t.dependencies=null,t.stateNode=null):(t.childLanes=n.childLanes,t.lanes=n.lanes,t.child=n.child,t.subtreeFlags=0,t.deletions=null,t.memoizedProps=n.memoizedProps,t.memoizedState=n.memoizedState,t.updateQueue=n.updateQueue,t.type=n.type,e=n.dependencies,t.dependencies=e===null?null:{lanes:e.lanes,firstContext:e.firstContext}),t}function ou(t,e,n,i,a,s){var r=0;if(i=t,typeof t=="function")Tm(t)&&(r=1);else if(typeof t=="string")r=L1(t,n,na.current)?26:t==="html"||t==="head"||t==="body"?27:5;else e:switch(t){case ph:return t=si(31,n,e,a),t.elementType=ph,t.lanes=s,t;case Pr:return Vs(n.children,a,s,e);case r_:r=8,a|=24;break;case fh:return t=si(12,n,e,a|2),t.elementType=fh,t.lanes=s,t;case dh:return t=si(13,n,e,a),t.elementType=dh,t.lanes=s,t;case hh:return t=si(19,n,e,a),t.elementType=hh,t.lanes=s,t;default:if(typeof t=="object"&&t!==null)switch(t.$$typeof){case Sa:r=10;break e;case l_:r=9;break e;case fm:r=11;break e;case dm:r=14;break e;case $a:r=16,i=null;break e}r=29,n=Error(ue(130,t===null?"null":typeof t,"")),i=null}return e=si(r,n,e,a),e.elementType=t,e.type=i,e.lanes=s,e}function Vs(t,e,n,i){return t=si(7,t,i,e),t.lanes=n,t}function td(t,e,n){return t=si(6,t,null,e),t.lanes=n,t}function H_(t){var e=si(18,null,null,0);return e.stateNode=t,e}function nd(t,e,n){return e=si(4,t.children!==null?t.children:[],t.key,e),e.lanes=n,e.stateNode={containerInfo:t.containerInfo,pendingChildren:null,implementation:t.implementation},e}var ix=new WeakMap;function Si(t,e){if(typeof t=="object"&&t!==null){var n=ix.get(t);return n!==void 0?n:(e={value:t,source:e,stack:Ig(e)},ix.set(t,e),e)}return{value:t,source:e,stack:Ig(e)}}var Vr=[],Wr=0,zu=null,Do=0,_i=[],yi=0,bs=null,Qi=1,Ji="";function _a(t,e){Vr[Wr++]=Do,Vr[Wr++]=zu,zu=t,Do=e}function G_(t,e,n){_i[yi++]=Qi,_i[yi++]=Ji,_i[yi++]=bs,bs=t;var i=Qi;t=Ji;var a=32-ci(i)-1;i&=~(1<<a),n+=1;var s=32-ci(e)+a;if(30<s){var r=a-a%5;s=(i&(1<<r)-1).toString(32),i>>=r,a-=r,Qi=1<<32-ci(e)+a|n<<a|i,Ji=s+t}else Qi=1<<s|n<<a|i,Ji=t}function Am(t){t.return!==null&&(_a(t,1),G_(t,1,0))}function wm(t){for(;t===zu;)zu=Vr[--Wr],Vr[Wr]=null,Do=Vr[--Wr],Vr[Wr]=null;for(;t===bs;)bs=_i[--yi],_i[yi]=null,Ji=_i[--yi],_i[yi]=null,Qi=_i[--yi],_i[yi]=null}function V_(t,e){_i[yi++]=Qi,_i[yi++]=Ji,_i[yi++]=bs,Qi=e.id,Ji=e.overflow,bs=t}var Sn=null,Ht=null,mt=!1,hs=null,Mi=!1,Ch=Error(ue(519));function Ss(t){var e=Error(ue(418,1<arguments.length&&arguments[1]!==void 0&&arguments[1]?"text":"HTML",""));throw Lo(Si(e,t)),Ch}function ax(t){var e=t.stateNode,n=t.type,i=t.memoizedProps;switch(e[bn]=t,e[Kn]=i,n){case"dialog":ct("cancel",e),ct("close",e);break;case"iframe":case"object":case"embed":ct("load",e);break;case"video":case"audio":for(n=0;n<zo.length;n++)ct(zo[n],e);break;case"source":ct("error",e);break;case"img":case"image":case"link":ct("error",e),ct("load",e);break;case"details":ct("toggle",e);break;case"input":ct("invalid",e),y_(e,i.value,i.defaultValue,i.checked,i.defaultChecked,i.type,i.name,!0);break;case"select":ct("invalid",e);break;case"textarea":ct("invalid",e),S_(e,i.value,i.defaultValue,i.children)}n=i.children,typeof n!="string"&&typeof n!="number"&&typeof n!="bigint"||e.textContent===""+n||i.suppressHydrationWarning===!0||Ab(e.textContent,n)?(i.popover!=null&&(ct("beforetoggle",e),ct("toggle",e)),i.onScroll!=null&&ct("scroll",e),i.onScrollEnd!=null&&ct("scrollend",e),i.onClick!=null&&(e.onclick=Ma),e=!0):e=!1,e||Ss(t,!0)}function sx(t){for(Sn=t.return;Sn;)switch(Sn.tag){case 5:case 31:case 13:Mi=!1;return;case 27:case 3:Mi=!0;return;default:Sn=Sn.return}}function pr(t){if(t!==Sn)return!1;if(!mt)return sx(t),mt=!0,!1;var e=t.tag,n;if((n=e!==3&&e!==27)&&((n=e===5)&&(n=t.type,n=!(n!=="form"&&n!=="button")||Jh(t.type,t.memoizedProps)),n=!n),n&&Ht&&Ss(t),sx(t),e===13){if(t=t.memoizedState,t=t!==null?t.dehydrated:null,!t)throw Error(ue(317));Ht=Wx(t)}else if(e===31){if(t=t.memoizedState,t=t!==null?t.dehydrated:null,!t)throw Error(ue(317));Ht=Wx(t)}else e===27?(e=Ht,ws(t.type)?(t=ip,ip=null,Ht=t):Ht=e):Ht=Sn?wi(t.stateNode.nextSibling):null;return!0}function Ys(){Ht=Sn=null,mt=!1}function id(){var t=hs;return t!==null&&(Vn===null?Vn=t:Vn.push.apply(Vn,t),hs=null),t}function Lo(t){hs===null?hs=[t]:hs.push(t)}var Nh=ra(null),or=null,Ea=null;function Ja(t,e,n){Bt(Nh,e._currentValue),e._currentValue=n}function wa(t){t._currentValue=Nh.current,vn(Nh)}function Rh(t,e,n){for(;t!==null;){var i=t.alternate;if((t.childLanes&e)!==e?(t.childLanes|=e,i!==null&&(i.childLanes|=e)):i!==null&&(i.childLanes&e)!==e&&(i.childLanes|=e),t===n)break;t=t.return}}function Dh(t,e,n,i){var a=t.child;for(a!==null&&(a.return=t);a!==null;){var s=a.dependencies;if(s!==null){var r=a.child;s=s.firstContext;e:for(;s!==null;){var o=s;s=a;for(var c=0;c<e.length;c++)if(o.context===e[c]){s.lanes|=n,o=s.alternate,o!==null&&(o.lanes|=n),Rh(s.return,n,t),i||(r=null);break e}s=o.next}}else if(a.tag===18){if(r=a.return,r===null)throw Error(ue(341));r.lanes|=n,s=r.alternate,s!==null&&(s.lanes|=n),Rh(r,n,t),r=null}else r=a.child;if(r!==null)r.return=a;else for(r=a;r!==null;){if(r===t){r=null;break}if(a=r.sibling,a!==null){a.return=r.return,r=a;break}r=r.return}a=r}}function Al(t,e,n,i){t=null;for(var a=e,s=!1;a!==null;){if(!s){if(a.flags&524288)s=!0;else if(a.flags&262144)break}if(a.tag===10){var r=a.alternate;if(r===null)throw Error(ue(387));if(r=r.memoizedProps,r!==null){var o=a.type;fi(a.pendingProps.value,r.value)||(t!==null?t.push(o):t=[o])}}else if(a===Nu.current){if(r=a.alternate,r===null)throw Error(ue(387));r.memoizedState.memoizedState!==a.memoizedState.memoizedState&&(t!==null?t.push(ko):t=[ko])}a=a.return}t!==null&&Dh(e,t,n,i),e.flags|=262144}function Iu(t){for(t=t.firstContext;t!==null;){if(!fi(t.context._currentValue,t.memoizedValue))return!0;t=t.next}return!1}function Ks(t){or=t,Ea=null,t=t.dependencies,t!==null&&(t.firstContext=null)}function Mn(t){return W_(or,t)}function hc(t,e){return or===null&&Ks(t),W_(t,e)}function W_(t,e){var n=e._currentValue;if(e={context:e,memoizedValue:n,next:null},Ea===null){if(t===null)throw Error(ue(308));Ea=e,t.dependencies={lanes:0,firstContext:e},t.flags|=524288}else Ea=Ea.next=e;return n}var CE=typeof AbortController<"u"?AbortController:function(){var t=[],e=this.signal={aborted:!1,addEventListener:function(n,i){t.push(i)}};this.abort=function(){e.aborted=!0,t.forEach(function(n){return n()})}},NE=fn.unstable_scheduleCallback,RE=fn.unstable_NormalPriority,ln={$$typeof:Sa,Consumer:null,Provider:null,_currentValue:null,_currentValue2:null,_threadCount:0};function Cm(){return{controller:new CE,data:new Map,refCount:0}}function Qo(t){t.refCount--,t.refCount===0&&NE(RE,function(){t.controller.abort()})}var po=null,Lh=0,cl=0,Qr=null;function DE(t,e){if(po===null){var n=po=[];Lh=0,cl=Jm(),Qr={status:"pending",value:void 0,then:function(i){n.push(i)}}}return Lh++,e.then(rx,rx),e}function rx(){if(--Lh===0&&po!==null){Qr!==null&&(Qr.status="fulfilled");var t=po;po=null,cl=0,Qr=null;for(var e=0;e<t.length;e++)(0,t[e])()}}function LE(t,e){var n=[],i={status:"pending",value:null,reason:null,then:function(a){n.push(a)}};return t.then(function(){i.status="fulfilled",i.value=e;for(var a=0;a<n.length;a++)(0,n[a])(e)},function(a){for(i.status="rejected",i.reason=a,a=0;a<n.length;a++)(0,n[a])(void 0)}),i}var lx=tt.S;tt.S=function(t,e){sb=li(),typeof e=="object"&&e!==null&&typeof e.then=="function"&&DE(t,e),lx!==null&&lx(t,e)};var Ws=ra(null);function Nm(){var t=Ws.current;return t!==null?t:It.pooledCache}function cu(t,e){e===null?Bt(Ws,Ws.current):Bt(Ws,e.pool)}function X_(){var t=Nm();return t===null?null:{parent:ln._currentValue,pool:t}}var wl=Error(ue(460)),Rm=Error(ue(474)),Af=Error(ue(542)),ku={then:function(){}};function ox(t){return t=t.status,t==="fulfilled"||t==="rejected"}function q_(t,e,n){switch(n=t[n],n===void 0?t.push(e):n!==e&&(e.then(Ma,Ma),e=n),e.status){case"fulfilled":return e.value;case"rejected":throw t=e.reason,ux(t),t;default:if(typeof e.status=="string")e.then(Ma,Ma);else{if(t=It,t!==null&&100<t.shellSuspendCounter)throw Error(ue(482));t=e,t.status="pending",t.then(function(i){if(e.status==="pending"){var a=e;a.status="fulfilled",a.value=i}},function(i){if(e.status==="pending"){var a=e;a.status="rejected",a.reason=i}})}switch(e.status){case"fulfilled":return e.value;case"rejected":throw t=e.reason,ux(t),t}throw Xs=e,wl}}function zs(t){try{var e=t._init;return e(t._payload)}catch(n){throw n!==null&&typeof n=="object"&&typeof n.then=="function"?(Xs=n,wl):n}}var Xs=null;function cx(){if(Xs===null)throw Error(ue(459));var t=Xs;return Xs=null,t}function ux(t){if(t===wl||t===Af)throw Error(ue(483))}var Jr=null,Uo=0;function pc(t){var e=Uo;return Uo+=1,Jr===null&&(Jr=[]),q_(Jr,t,e)}function Il(t,e){e=e.props.ref,t.ref=e!==void 0?e:null}function mc(t,e){throw e.$$typeof===vM?Error(ue(525)):(t=Object.prototype.toString.call(e),Error(ue(31,t==="[object Object]"?"object with keys {"+Object.keys(e).join(", ")+"}":t)))}function Y_(t){function e(h,v){if(t){var b=h.deletions;b===null?(h.deletions=[v],h.flags|=16):b.push(v)}}function n(h,v){if(!t)return null;for(;v!==null;)e(h,v),v=v.sibling;return null}function i(h){for(var v=new Map;h!==null;)h.key!==null?v.set(h.key,h):v.set(h.index,h),h=h.sibling;return v}function a(h,v){return h=Aa(h,v),h.index=0,h.sibling=null,h}function s(h,v,b){return h.index=b,t?(b=h.alternate,b!==null?(b=b.index,b<v?(h.flags|=67108866,v):b):(h.flags|=67108866,v)):(h.flags|=1048576,v)}function r(h){return t&&h.alternate===null&&(h.flags|=67108866),h}function o(h,v,b,y){return v===null||v.tag!==6?(v=td(b,h.mode,y),v.return=h,v):(v=a(v,b),v.return=h,v)}function c(h,v,b,y){var A=b.type;return A===Pr?d(h,v,b.props.children,y,b.key):v!==null&&(v.elementType===A||typeof A=="object"&&A!==null&&A.$$typeof===$a&&zs(A)===v.type)?(v=a(v,b.props),Il(v,b),v.return=h,v):(v=ou(b.type,b.key,b.props,null,h.mode,y),Il(v,b),v.return=h,v)}function u(h,v,b,y){return v===null||v.tag!==4||v.stateNode.containerInfo!==b.containerInfo||v.stateNode.implementation!==b.implementation?(v=nd(b,h.mode,y),v.return=h,v):(v=a(v,b.children||[]),v.return=h,v)}function d(h,v,b,y,A){return v===null||v.tag!==7?(v=Vs(b,h.mode,y,A),v.return=h,v):(v=a(v,b),v.return=h,v)}function m(h,v,b){if(typeof v=="string"&&v!==""||typeof v=="number"||typeof v=="bigint")return v=td(""+v,h.mode,b),v.return=h,v;if(typeof v=="object"&&v!==null){switch(v.$$typeof){case lc:return b=ou(v.type,v.key,v.props,null,h.mode,b),Il(b,v),b.return=h,b;case Jl:return v=nd(v,h.mode,b),v.return=h,v;case $a:return v=zs(v),m(h,v,b)}if(eo(v)||Ol(v))return v=Vs(v,h.mode,b,null),v.return=h,v;if(typeof v.then=="function")return m(h,pc(v),b);if(v.$$typeof===Sa)return m(h,hc(h,v),b);mc(h,v)}return null}function f(h,v,b,y){var A=v!==null?v.key:null;if(typeof b=="string"&&b!==""||typeof b=="number"||typeof b=="bigint")return A!==null?null:o(h,v,""+b,y);if(typeof b=="object"&&b!==null){switch(b.$$typeof){case lc:return b.key===A?c(h,v,b,y):null;case Jl:return b.key===A?u(h,v,b,y):null;case $a:return b=zs(b),f(h,v,b,y)}if(eo(b)||Ol(b))return A!==null?null:d(h,v,b,y,null);if(typeof b.then=="function")return f(h,v,pc(b),y);if(b.$$typeof===Sa)return f(h,v,hc(h,b),y);mc(h,b)}return null}function p(h,v,b,y,A){if(typeof y=="string"&&y!==""||typeof y=="number"||typeof y=="bigint")return h=h.get(b)||null,o(v,h,""+y,A);if(typeof y=="object"&&y!==null){switch(y.$$typeof){case lc:return h=h.get(y.key===null?b:y.key)||null,c(v,h,y,A);case Jl:return h=h.get(y.key===null?b:y.key)||null,u(v,h,y,A);case $a:return y=zs(y),p(h,v,b,y,A)}if(eo(y)||Ol(y))return h=h.get(b)||null,d(v,h,y,A,null);if(typeof y.then=="function")return p(h,v,b,pc(y),A);if(y.$$typeof===Sa)return p(h,v,b,hc(v,y),A);mc(v,y)}return null}function x(h,v,b,y){for(var A=null,M=null,T=v,S=v=0,C=null;T!==null&&S<b.length;S++){T.index>S?(C=T,T=null):C=T.sibling;var R=f(h,T,b[S],y);if(R===null){T===null&&(T=C);break}t&&T&&R.alternate===null&&e(h,T),v=s(R,v,S),M===null?A=R:M.sibling=R,M=R,T=C}if(S===b.length)return n(h,T),mt&&_a(h,S),A;if(T===null){for(;S<b.length;S++)T=m(h,b[S],y),T!==null&&(v=s(T,v,S),M===null?A=T:M.sibling=T,M=T);return mt&&_a(h,S),A}for(T=i(T);S<b.length;S++)C=p(T,h,S,b[S],y),C!==null&&(t&&C.alternate!==null&&T.delete(C.key===null?S:C.key),v=s(C,v,S),M===null?A=C:M.sibling=C,M=C);return t&&T.forEach(function(L){return e(h,L)}),mt&&_a(h,S),A}function _(h,v,b,y){if(b==null)throw Error(ue(151));for(var A=null,M=null,T=v,S=v=0,C=null,R=b.next();T!==null&&!R.done;S++,R=b.next()){T.index>S?(C=T,T=null):C=T.sibling;var L=f(h,T,R.value,y);if(L===null){T===null&&(T=C);break}t&&T&&L.alternate===null&&e(h,T),v=s(L,v,S),M===null?A=L:M.sibling=L,M=L,T=C}if(R.done)return n(h,T),mt&&_a(h,S),A;if(T===null){for(;!R.done;S++,R=b.next())R=m(h,R.value,y),R!==null&&(v=s(R,v,S),M===null?A=R:M.sibling=R,M=R);return mt&&_a(h,S),A}for(T=i(T);!R.done;S++,R=b.next())R=p(T,h,S,R.value,y),R!==null&&(t&&R.alternate!==null&&T.delete(R.key===null?S:R.key),v=s(R,v,S),M===null?A=R:M.sibling=R,M=R);return t&&T.forEach(function(O){return e(h,O)}),mt&&_a(h,S),A}function g(h,v,b,y){if(typeof b=="object"&&b!==null&&b.type===Pr&&b.key===null&&(b=b.props.children),typeof b=="object"&&b!==null){switch(b.$$typeof){case lc:e:{for(var A=b.key;v!==null;){if(v.key===A){if(A=b.type,A===Pr){if(v.tag===7){n(h,v.sibling),y=a(v,b.props.children),y.return=h,h=y;break e}}else if(v.elementType===A||typeof A=="object"&&A!==null&&A.$$typeof===$a&&zs(A)===v.type){n(h,v.sibling),y=a(v,b.props),Il(y,b),y.return=h,h=y;break e}n(h,v);break}else e(h,v);v=v.sibling}b.type===Pr?(y=Vs(b.props.children,h.mode,y,b.key),y.return=h,h=y):(y=ou(b.type,b.key,b.props,null,h.mode,y),Il(y,b),y.return=h,h=y)}return r(h);case Jl:e:{for(A=b.key;v!==null;){if(v.key===A)if(v.tag===4&&v.stateNode.containerInfo===b.containerInfo&&v.stateNode.implementation===b.implementation){n(h,v.sibling),y=a(v,b.children||[]),y.return=h,h=y;break e}else{n(h,v);break}else e(h,v);v=v.sibling}y=nd(b,h.mode,y),y.return=h,h=y}return r(h);case $a:return b=zs(b),g(h,v,b,y)}if(eo(b))return x(h,v,b,y);if(Ol(b)){if(A=Ol(b),typeof A!="function")throw Error(ue(150));return b=A.call(b),_(h,v,b,y)}if(typeof b.then=="function")return g(h,v,pc(b),y);if(b.$$typeof===Sa)return g(h,v,hc(h,b),y);mc(h,b)}return typeof b=="string"&&b!==""||typeof b=="number"||typeof b=="bigint"?(b=""+b,v!==null&&v.tag===6?(n(h,v.sibling),y=a(v,b),y.return=h,h=y):(n(h,v),y=td(b,h.mode,y),y.return=h,h=y),r(h)):n(h,v)}return function(h,v,b,y){try{Uo=0;var A=g(h,v,b,y);return Jr=null,A}catch(T){if(T===wl||T===Af)throw T;var M=si(29,T,null,h.mode);return M.lanes=y,M.return=h,M}finally{}}}var Zs=Y_(!0),K_=Y_(!1),Qa=!1;function Dm(t){t.updateQueue={baseState:t.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function Uh(t,e){t=t.updateQueue,e.updateQueue===t&&(e.updateQueue={baseState:t.baseState,firstBaseUpdate:t.firstBaseUpdate,lastBaseUpdate:t.lastBaseUpdate,shared:t.shared,callbacks:null})}function ps(t){return{lane:t,tag:0,payload:null,callback:null,next:null}}function ms(t,e,n){var i=t.updateQueue;if(i===null)return null;if(i=i.shared,Et&2){var a=i.pending;return a===null?e.next=e:(e.next=a.next,a.next=e),i.pending=e,e=Pu(t),F_(t,null,n),e}return Tf(t,i,e,n),Pu(t)}function mo(t,e,n){if(e=e.updateQueue,e!==null&&(e=e.shared,(n&4194048)!==0)){var i=e.lanes;i&=t.pendingLanes,n|=i,e.lanes=n,h_(t,n)}}function ad(t,e){var n=t.updateQueue,i=t.alternate;if(i!==null&&(i=i.updateQueue,n===i)){var a=null,s=null;if(n=n.firstBaseUpdate,n!==null){do{var r={lane:n.lane,tag:n.tag,payload:n.payload,callback:null,next:null};s===null?a=s=r:s=s.next=r,n=n.next}while(n!==null);s===null?a=s=e:s=s.next=e}else a=s=e;n={baseState:i.baseState,firstBaseUpdate:a,lastBaseUpdate:s,shared:i.shared,callbacks:i.callbacks},t.updateQueue=n;return}t=n.lastBaseUpdate,t===null?n.firstBaseUpdate=e:t.next=e,n.lastBaseUpdate=e}var Oh=!1;function go(){if(Oh){var t=Qr;if(t!==null)throw t}}function xo(t,e,n,i){Oh=!1;var a=t.updateQueue;Qa=!1;var s=a.firstBaseUpdate,r=a.lastBaseUpdate,o=a.shared.pending;if(o!==null){a.shared.pending=null;var c=o,u=c.next;c.next=null,r===null?s=u:r.next=u,r=c;var d=t.alternate;d!==null&&(d=d.updateQueue,o=d.lastBaseUpdate,o!==r&&(o===null?d.firstBaseUpdate=u:o.next=u,d.lastBaseUpdate=c))}if(s!==null){var m=a.baseState;r=0,d=u=c=null,o=s;do{var f=o.lane&-536870913,p=f!==o.lane;if(p?(dt&f)===f:(i&f)===f){f!==0&&f===cl&&(Oh=!0),d!==null&&(d=d.next={lane:0,tag:o.tag,payload:o.payload,callback:null,next:null});e:{var x=t,_=o;f=e;var g=n;switch(_.tag){case 1:if(x=_.payload,typeof x=="function"){m=x.call(g,m,f);break e}m=x;break e;case 3:x.flags=x.flags&-65537|128;case 0:if(x=_.payload,f=typeof x=="function"?x.call(g,m,f):x,f==null)break e;m=Vt({},m,f);break e;case 2:Qa=!0}}f=o.callback,f!==null&&(t.flags|=64,p&&(t.flags|=8192),p=a.callbacks,p===null?a.callbacks=[f]:p.push(f))}else p={lane:f,tag:o.tag,payload:o.payload,callback:o.callback,next:null},d===null?(u=d=p,c=m):d=d.next=p,r|=f;if(o=o.next,o===null){if(o=a.shared.pending,o===null)break;p=o,o=p.next,p.next=null,a.lastBaseUpdate=p,a.shared.pending=null}}while(!0);d===null&&(c=m),a.baseState=c,a.firstBaseUpdate=u,a.lastBaseUpdate=d,s===null&&(a.shared.lanes=0),Es|=r,t.lanes=r,t.memoizedState=m}}function Z_(t,e){if(typeof t!="function")throw Error(ue(191,t));t.call(e)}function $_(t,e){var n=t.callbacks;if(n!==null)for(t.callbacks=null,t=0;t<n.length;t++)Z_(n[t],e)}var ul=ra(null),Bu=ra(0);function fx(t,e){t=Pa,Bt(Bu,t),Bt(ul,e),Pa=t|e.baseLanes}function Ph(){Bt(Bu,Pa),Bt(ul,ul.current)}function Lm(){Pa=Bu.current,vn(ul),vn(Bu)}var di=ra(null),Ai=null;function es(t){var e=t.alternate;Bt(tn,tn.current&1),Bt(di,t),Ai===null&&(e===null||ul.current!==null||e.memoizedState!==null)&&(Ai=t)}function zh(t){Bt(tn,tn.current),Bt(di,t),Ai===null&&(Ai=t)}function Q_(t){t.tag===22?(Bt(tn,tn.current),Bt(di,t),Ai===null&&(Ai=t)):ts()}function ts(){Bt(tn,tn.current),Bt(di,di.current)}function ai(t){vn(di),Ai===t&&(Ai=null),vn(tn)}var tn=ra(0);function Fu(t){for(var e=t;e!==null;){if(e.tag===13){var n=e.memoizedState;if(n!==null&&(n=n.dehydrated,n===null||tp(n)||np(n)))return e}else if(e.tag===19&&(e.memoizedProps.revealOrder==="forwards"||e.memoizedProps.revealOrder==="backwards"||e.memoizedProps.revealOrder==="unstable_legacy-backwards"||e.memoizedProps.revealOrder==="together")){if(e.flags&128)return e}else if(e.child!==null){e.child.return=e,e=e.child;continue}if(e===t)break;for(;e.sibling===null;){if(e.return===null||e.return===t)return null;e=e.return}e.sibling.return=e.return,e=e.sibling}return null}var La=0,rt=null,Ut=null,sn=null,ju=!1,el=!1,$s=!1,Hu=0,Oo=0,tl=null,UE=0;function Zt(){throw Error(ue(321))}function Um(t,e){if(e===null)return!1;for(var n=0;n<e.length&&n<t.length;n++)if(!fi(t[n],e[n]))return!1;return!0}function Om(t,e,n,i,a,s){return La=s,rt=e,e.memoizedState=null,e.updateQueue=null,e.lanes=0,tt.H=t===null||t.memoizedState===null?Ny:Wm,$s=!1,s=n(i,a),$s=!1,el&&(s=ey(e,n,i,a)),J_(t),s}function J_(t){tt.H=Po;var e=Ut!==null&&Ut.next!==null;if(La=0,sn=Ut=rt=null,ju=!1,Oo=0,tl=null,e)throw Error(ue(300));t===null||on||(t=t.dependencies,t!==null&&Iu(t)&&(on=!0))}function ey(t,e,n,i){rt=t;var a=0;do{if(el&&(tl=null),Oo=0,el=!1,25<=a)throw Error(ue(301));if(a+=1,sn=Ut=null,t.updateQueue!=null){var s=t.updateQueue;s.lastEffect=null,s.events=null,s.stores=null,s.memoCache!=null&&(s.memoCache.index=0)}tt.H=Ry,s=e(n,i)}while(el);return s}function OE(){var t=tt.H,e=t.useState()[0];return e=typeof e.then=="function"?Jo(e):e,t=t.useState()[0],(Ut!==null?Ut.memoizedState:null)!==t&&(rt.flags|=1024),e}function Pm(){var t=Hu!==0;return Hu=0,t}function zm(t,e,n){e.updateQueue=t.updateQueue,e.flags&=-2053,t.lanes&=~n}function Im(t){if(ju){for(t=t.memoizedState;t!==null;){var e=t.queue;e!==null&&(e.pending=null),t=t.next}ju=!1}La=0,sn=Ut=rt=null,el=!1,Oo=Hu=0,tl=null}function Ln(){var t={memoizedState:null,baseState:null,baseQueue:null,queue:null,next:null};return sn===null?rt.memoizedState=sn=t:sn=sn.next=t,sn}function nn(){if(Ut===null){var t=rt.alternate;t=t!==null?t.memoizedState:null}else t=Ut.next;var e=sn===null?rt.memoizedState:sn.next;if(e!==null)sn=e,Ut=t;else{if(t===null)throw rt.alternate===null?Error(ue(467)):Error(ue(310));Ut=t,t={memoizedState:Ut.memoizedState,baseState:Ut.baseState,baseQueue:Ut.baseQueue,queue:Ut.queue,next:null},sn===null?rt.memoizedState=sn=t:sn=sn.next=t}return sn}function wf(){return{lastEffect:null,events:null,stores:null,memoCache:null}}function Jo(t){var e=Oo;return Oo+=1,tl===null&&(tl=[]),t=q_(tl,t,e),e=rt,(sn===null?e.memoizedState:sn.next)===null&&(e=e.alternate,tt.H=e===null||e.memoizedState===null?Ny:Wm),t}function Cf(t){if(t!==null&&typeof t=="object"){if(typeof t.then=="function")return Jo(t);if(t.$$typeof===Sa)return Mn(t)}throw Error(ue(438,String(t)))}function km(t){var e=null,n=rt.updateQueue;if(n!==null&&(e=n.memoCache),e==null){var i=rt.alternate;i!==null&&(i=i.updateQueue,i!==null&&(i=i.memoCache,i!=null&&(e={data:i.data.map(function(a){return a.slice()}),index:0})))}if(e==null&&(e={data:[],index:0}),n===null&&(n=wf(),rt.updateQueue=n),n.memoCache=e,n=e.data[e.index],n===void 0)for(n=e.data[e.index]=Array(t),i=0;i<t;i++)n[i]=_M;return e.index++,n}function Ua(t,e){return typeof e=="function"?e(t):e}function uu(t){var e=nn();return Bm(e,Ut,t)}function Bm(t,e,n){var i=t.queue;if(i===null)throw Error(ue(311));i.lastRenderedReducer=n;var a=t.baseQueue,s=i.pending;if(s!==null){if(a!==null){var r=a.next;a.next=s.next,s.next=r}e.baseQueue=a=s,i.pending=null}if(s=t.baseState,a===null)t.memoizedState=s;else{e=a.next;var o=r=null,c=null,u=e,d=!1;do{var m=u.lane&-536870913;if(m!==u.lane?(dt&m)===m:(La&m)===m){var f=u.revertLane;if(f===0)c!==null&&(c=c.next={lane:0,revertLane:0,gesture:null,action:u.action,hasEagerState:u.hasEagerState,eagerState:u.eagerState,next:null}),m===cl&&(d=!0);else if((La&f)===f){u=u.next,f===cl&&(d=!0);continue}else m={lane:0,revertLane:u.revertLane,gesture:null,action:u.action,hasEagerState:u.hasEagerState,eagerState:u.eagerState,next:null},c===null?(o=c=m,r=s):c=c.next=m,rt.lanes|=f,Es|=f;m=u.action,$s&&n(s,m),s=u.hasEagerState?u.eagerState:n(s,m)}else f={lane:m,revertLane:u.revertLane,gesture:u.gesture,action:u.action,hasEagerState:u.hasEagerState,eagerState:u.eagerState,next:null},c===null?(o=c=f,r=s):c=c.next=f,rt.lanes|=m,Es|=m;u=u.next}while(u!==null&&u!==e);if(c===null?r=s:c.next=o,!fi(s,t.memoizedState)&&(on=!0,d&&(n=Qr,n!==null)))throw n;t.memoizedState=s,t.baseState=r,t.baseQueue=c,i.lastRenderedState=s}return a===null&&(i.lanes=0),[t.memoizedState,i.dispatch]}function sd(t){var e=nn(),n=e.queue;if(n===null)throw Error(ue(311));n.lastRenderedReducer=t;var i=n.dispatch,a=n.pending,s=e.memoizedState;if(a!==null){n.pending=null;var r=a=a.next;do s=t(s,r.action),r=r.next;while(r!==a);fi(s,e.memoizedState)||(on=!0),e.memoizedState=s,e.baseQueue===null&&(e.baseState=s),n.lastRenderedState=s}return[s,i]}function ty(t,e,n){var i=rt,a=nn(),s=mt;if(s){if(n===void 0)throw Error(ue(407));n=n()}else n=e();var r=!fi((Ut||a).memoizedState,n);if(r&&(a.memoizedState=n,on=!0),a=a.queue,Fm(ay.bind(null,i,a,t),[t]),a.getSnapshot!==e||r||sn!==null&&sn.memoizedState.tag&1){if(i.flags|=2048,fl(9,{destroy:void 0},iy.bind(null,i,a,n,e),null),It===null)throw Error(ue(349));s||La&127||ny(i,e,n)}return n}function ny(t,e,n){t.flags|=16384,t={getSnapshot:e,value:n},e=rt.updateQueue,e===null?(e=wf(),rt.updateQueue=e,e.stores=[t]):(n=e.stores,n===null?e.stores=[t]:n.push(t))}function iy(t,e,n,i){e.value=n,e.getSnapshot=i,sy(e)&&ry(t)}function ay(t,e,n){return n(function(){sy(e)&&ry(t)})}function sy(t){var e=t.getSnapshot;t=t.value;try{var n=e();return!fi(t,n)}catch{return!0}}function ry(t){var e=lr(t,2);e!==null&&qn(e,t,2)}function Ih(t){var e=Ln();if(typeof t=="function"){var n=t;if(t=n(),$s){as(!0);try{n()}finally{as(!1)}}}return e.memoizedState=e.baseState=t,e.queue={pending:null,lanes:0,dispatch:null,lastRenderedReducer:Ua,lastRenderedState:t},e}function ly(t,e,n,i){return t.baseState=n,Bm(t,Ut,typeof i=="function"?i:Ua)}function PE(t,e,n,i,a){if(Rf(t))throw Error(ue(485));if(t=e.action,t!==null){var s={payload:a,action:t,next:null,isTransition:!0,status:"pending",value:null,reason:null,listeners:[],then:function(r){s.listeners.push(r)}};tt.T!==null?n(!0):s.isTransition=!1,i(s),n=e.pending,n===null?(s.next=e.pending=s,oy(e,s)):(s.next=n.next,e.pending=n.next=s)}}function oy(t,e){var n=e.action,i=e.payload,a=t.state;if(e.isTransition){var s=tt.T,r={};tt.T=r;try{var o=n(a,i),c=tt.S;c!==null&&c(r,o),dx(t,e,o)}catch(u){kh(t,e,u)}finally{s!==null&&r.types!==null&&(s.types=r.types),tt.T=s}}else try{s=n(a,i),dx(t,e,s)}catch(u){kh(t,e,u)}}function dx(t,e,n){n!==null&&typeof n=="object"&&typeof n.then=="function"?n.then(function(i){hx(t,e,i)},function(i){return kh(t,e,i)}):hx(t,e,n)}function hx(t,e,n){e.status="fulfilled",e.value=n,cy(e),t.state=n,e=t.pending,e!==null&&(n=e.next,n===e?t.pending=null:(n=n.next,e.next=n,oy(t,n)))}function kh(t,e,n){var i=t.pending;if(t.pending=null,i!==null){i=i.next;do e.status="rejected",e.reason=n,cy(e),e=e.next;while(e!==i)}t.action=null}function cy(t){t=t.listeners;for(var e=0;e<t.length;e++)(0,t[e])()}function uy(t,e){return e}function px(t,e){if(mt){var n=It.formState;if(n!==null){e:{var i=rt;if(mt){if(Ht){t:{for(var a=Ht,s=Mi;a.nodeType!==8;){if(!s){a=null;break t}if(a=wi(a.nextSibling),a===null){a=null;break t}}s=a.data,a=s==="F!"||s==="F"?a:null}if(a){Ht=wi(a.nextSibling),i=a.data==="F!";break e}}Ss(i)}i=!1}i&&(e=n[0])}}return n=Ln(),n.memoizedState=n.baseState=e,i={pending:null,lanes:0,dispatch:null,lastRenderedReducer:uy,lastRenderedState:e},n.queue=i,n=Ay.bind(null,rt,i),i.dispatch=n,i=Ih(!1),s=Vm.bind(null,rt,!1,i.queue),i=Ln(),a={state:e,dispatch:null,action:t,pending:null},i.queue=a,n=PE.bind(null,rt,a,s,n),a.dispatch=n,i.memoizedState=t,[e,n,!1]}function mx(t){var e=nn();return fy(e,Ut,t)}function fy(t,e,n){if(e=Bm(t,e,uy)[0],t=uu(Ua)[0],typeof e=="object"&&e!==null&&typeof e.then=="function")try{var i=Jo(e)}catch(r){throw r===wl?Af:r}else i=e;e=nn();var a=e.queue,s=a.dispatch;return n!==e.memoizedState&&(rt.flags|=2048,fl(9,{destroy:void 0},zE.bind(null,a,n),null)),[i,s,t]}function zE(t,e){t.action=e}function gx(t){var e=nn(),n=Ut;if(n!==null)return fy(e,n,t);nn(),e=e.memoizedState,n=nn();var i=n.queue.dispatch;return n.memoizedState=t,[e,i,!1]}function fl(t,e,n,i){return t={tag:t,create:n,deps:i,inst:e,next:null},e=rt.updateQueue,e===null&&(e=wf(),rt.updateQueue=e),n=e.lastEffect,n===null?e.lastEffect=t.next=t:(i=n.next,n.next=t,t.next=i,e.lastEffect=t),t}function dy(){return nn().memoizedState}function fu(t,e,n,i){var a=Ln();rt.flags|=t,a.memoizedState=fl(1|e,{destroy:void 0},n,i===void 0?null:i)}function Nf(t,e,n,i){var a=nn();i=i===void 0?null:i;var s=a.memoizedState.inst;Ut!==null&&i!==null&&Um(i,Ut.memoizedState.deps)?a.memoizedState=fl(e,s,n,i):(rt.flags|=t,a.memoizedState=fl(1|e,s,n,i))}function xx(t,e){fu(8390656,8,t,e)}function Fm(t,e){Nf(2048,8,t,e)}function IE(t){rt.flags|=4;var e=rt.updateQueue;if(e===null)e=wf(),rt.updateQueue=e,e.events=[t];else{var n=e.events;n===null?e.events=[t]:n.push(t)}}function hy(t){var e=nn().memoizedState;return IE({ref:e,nextImpl:t}),function(){if(Et&2)throw Error(ue(440));return e.impl.apply(void 0,arguments)}}function py(t,e){return Nf(4,2,t,e)}function my(t,e){return Nf(4,4,t,e)}function gy(t,e){if(typeof e=="function"){t=t();var n=e(t);return function(){typeof n=="function"?n():e(null)}}if(e!=null)return t=t(),e.current=t,function(){e.current=null}}function xy(t,e,n){n=n!=null?n.concat([t]):null,Nf(4,4,gy.bind(null,e,t),n)}function jm(){}function vy(t,e){var n=nn();e=e===void 0?null:e;var i=n.memoizedState;return e!==null&&Um(e,i[1])?i[0]:(n.memoizedState=[t,e],t)}function _y(t,e){var n=nn();e=e===void 0?null:e;var i=n.memoizedState;if(e!==null&&Um(e,i[1]))return i[0];if(i=t(),$s){as(!0);try{t()}finally{as(!1)}}return n.memoizedState=[i,e],i}function Hm(t,e,n){return n===void 0||La&1073741824&&!(dt&261930)?t.memoizedState=e:(t.memoizedState=n,t=lb(),rt.lanes|=t,Es|=t,n)}function yy(t,e,n,i){return fi(n,e)?n:ul.current!==null?(t=Hm(t,n,i),fi(t,e)||(on=!0),t):!(La&42)||La&1073741824&&!(dt&261930)?(on=!0,t.memoizedState=n):(t=lb(),rt.lanes|=t,Es|=t,e)}function by(t,e,n,i,a){var s=Tt.p;Tt.p=s!==0&&8>s?s:8;var r=tt.T,o={};tt.T=o,Vm(t,!1,e,n);try{var c=a(),u=tt.S;if(u!==null&&u(o,c),c!==null&&typeof c=="object"&&typeof c.then=="function"){var d=LE(c,i);vo(t,e,d,ui(t))}else vo(t,e,i,ui(t))}catch(m){vo(t,e,{then:function(){},status:"rejected",reason:m},ui())}finally{Tt.p=s,r!==null&&o.types!==null&&(r.types=o.types),tt.T=r}}function kE(){}function Bh(t,e,n,i){if(t.tag!==5)throw Error(ue(476));var a=Sy(t).queue;by(t,a,e,Gs,n===null?kE:function(){return My(t),n(i)})}function Sy(t){var e=t.memoizedState;if(e!==null)return e;e={memoizedState:Gs,baseState:Gs,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Ua,lastRenderedState:Gs},next:null};var n={};return e.next={memoizedState:n,baseState:n,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Ua,lastRenderedState:n},next:null},t.memoizedState=e,t=t.alternate,t!==null&&(t.memoizedState=e),e}function My(t){var e=Sy(t);e.next===null&&(e=t.alternate.memoizedState),vo(t,e.next.queue,{},ui())}function Gm(){return Mn(ko)}function Ey(){return nn().memoizedState}function Ty(){return nn().memoizedState}function BE(t){for(var e=t.return;e!==null;){switch(e.tag){case 24:case 3:var n=ui();t=ps(n);var i=ms(e,t,n);i!==null&&(qn(i,e,n),mo(i,e,n)),e={cache:Cm()},t.payload=e;return}e=e.return}}function FE(t,e,n){var i=ui();n={lane:i,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null},Rf(t)?wy(e,n):(n=Em(t,e,n,i),n!==null&&(qn(n,t,i),Cy(n,e,i)))}function Ay(t,e,n){var i=ui();vo(t,e,n,i)}function vo(t,e,n,i){var a={lane:i,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null};if(Rf(t))wy(e,a);else{var s=t.alternate;if(t.lanes===0&&(s===null||s.lanes===0)&&(s=e.lastRenderedReducer,s!==null))try{var r=e.lastRenderedState,o=s(r,n);if(a.hasEagerState=!0,a.eagerState=o,fi(o,r))return Tf(t,e,a,0),It===null&&Ef(),!1}catch{}finally{}if(n=Em(t,e,a,i),n!==null)return qn(n,t,i),Cy(n,e,i),!0}return!1}function Vm(t,e,n,i){if(i={lane:2,revertLane:Jm(),gesture:null,action:i,hasEagerState:!1,eagerState:null,next:null},Rf(t)){if(e)throw Error(ue(479))}else e=Em(t,n,i,2),e!==null&&qn(e,t,2)}function Rf(t){var e=t.alternate;return t===rt||e!==null&&e===rt}function wy(t,e){el=ju=!0;var n=t.pending;n===null?e.next=e:(e.next=n.next,n.next=e),t.pending=e}function Cy(t,e,n){if(n&4194048){var i=e.lanes;i&=t.pendingLanes,n|=i,e.lanes=n,h_(t,n)}}var Po={readContext:Mn,use:Cf,useCallback:Zt,useContext:Zt,useEffect:Zt,useImperativeHandle:Zt,useLayoutEffect:Zt,useInsertionEffect:Zt,useMemo:Zt,useReducer:Zt,useRef:Zt,useState:Zt,useDebugValue:Zt,useDeferredValue:Zt,useTransition:Zt,useSyncExternalStore:Zt,useId:Zt,useHostTransitionStatus:Zt,useFormState:Zt,useActionState:Zt,useOptimistic:Zt,useMemoCache:Zt,useCacheRefresh:Zt};Po.useEffectEvent=Zt;var Ny={readContext:Mn,use:Cf,useCallback:function(t,e){return Ln().memoizedState=[t,e===void 0?null:e],t},useContext:Mn,useEffect:xx,useImperativeHandle:function(t,e,n){n=n!=null?n.concat([t]):null,fu(4194308,4,gy.bind(null,e,t),n)},useLayoutEffect:function(t,e){return fu(4194308,4,t,e)},useInsertionEffect:function(t,e){fu(4,2,t,e)},useMemo:function(t,e){var n=Ln();e=e===void 0?null:e;var i=t();if($s){as(!0);try{t()}finally{as(!1)}}return n.memoizedState=[i,e],i},useReducer:function(t,e,n){var i=Ln();if(n!==void 0){var a=n(e);if($s){as(!0);try{n(e)}finally{as(!1)}}}else a=e;return i.memoizedState=i.baseState=a,t={pending:null,lanes:0,dispatch:null,lastRenderedReducer:t,lastRenderedState:a},i.queue=t,t=t.dispatch=FE.bind(null,rt,t),[i.memoizedState,t]},useRef:function(t){var e=Ln();return t={current:t},e.memoizedState=t},useState:function(t){t=Ih(t);var e=t.queue,n=Ay.bind(null,rt,e);return e.dispatch=n,[t.memoizedState,n]},useDebugValue:jm,useDeferredValue:function(t,e){var n=Ln();return Hm(n,t,e)},useTransition:function(){var t=Ih(!1);return t=by.bind(null,rt,t.queue,!0,!1),Ln().memoizedState=t,[!1,t]},useSyncExternalStore:function(t,e,n){var i=rt,a=Ln();if(mt){if(n===void 0)throw Error(ue(407));n=n()}else{if(n=e(),It===null)throw Error(ue(349));dt&127||ny(i,e,n)}a.memoizedState=n;var s={value:n,getSnapshot:e};return a.queue=s,xx(ay.bind(null,i,s,t),[t]),i.flags|=2048,fl(9,{destroy:void 0},iy.bind(null,i,s,n,e),null),n},useId:function(){var t=Ln(),e=It.identifierPrefix;if(mt){var n=Ji,i=Qi;n=(i&~(1<<32-ci(i)-1)).toString(32)+n,e="_"+e+"R_"+n,n=Hu++,0<n&&(e+="H"+n.toString(32)),e+="_"}else n=UE++,e="_"+e+"r_"+n.toString(32)+"_";return t.memoizedState=e},useHostTransitionStatus:Gm,useFormState:px,useActionState:px,useOptimistic:function(t){var e=Ln();e.memoizedState=e.baseState=t;var n={pending:null,lanes:0,dispatch:null,lastRenderedReducer:null,lastRenderedState:null};return e.queue=n,e=Vm.bind(null,rt,!0,n),n.dispatch=e,[t,e]},useMemoCache:km,useCacheRefresh:function(){return Ln().memoizedState=BE.bind(null,rt)},useEffectEvent:function(t){var e=Ln(),n={impl:t};return e.memoizedState=n,function(){if(Et&2)throw Error(ue(440));return n.impl.apply(void 0,arguments)}}},Wm={readContext:Mn,use:Cf,useCallback:vy,useContext:Mn,useEffect:Fm,useImperativeHandle:xy,useInsertionEffect:py,useLayoutEffect:my,useMemo:_y,useReducer:uu,useRef:dy,useState:function(){return uu(Ua)},useDebugValue:jm,useDeferredValue:function(t,e){var n=nn();return yy(n,Ut.memoizedState,t,e)},useTransition:function(){var t=uu(Ua)[0],e=nn().memoizedState;return[typeof t=="boolean"?t:Jo(t),e]},useSyncExternalStore:ty,useId:Ey,useHostTransitionStatus:Gm,useFormState:mx,useActionState:mx,useOptimistic:function(t,e){var n=nn();return ly(n,Ut,t,e)},useMemoCache:km,useCacheRefresh:Ty};Wm.useEffectEvent=hy;var Ry={readContext:Mn,use:Cf,useCallback:vy,useContext:Mn,useEffect:Fm,useImperativeHandle:xy,useInsertionEffect:py,useLayoutEffect:my,useMemo:_y,useReducer:sd,useRef:dy,useState:function(){return sd(Ua)},useDebugValue:jm,useDeferredValue:function(t,e){var n=nn();return Ut===null?Hm(n,t,e):yy(n,Ut.memoizedState,t,e)},useTransition:function(){var t=sd(Ua)[0],e=nn().memoizedState;return[typeof t=="boolean"?t:Jo(t),e]},useSyncExternalStore:ty,useId:Ey,useHostTransitionStatus:Gm,useFormState:gx,useActionState:gx,useOptimistic:function(t,e){var n=nn();return Ut!==null?ly(n,Ut,t,e):(n.baseState=t,[t,n.queue.dispatch])},useMemoCache:km,useCacheRefresh:Ty};Ry.useEffectEvent=hy;function rd(t,e,n,i){e=t.memoizedState,n=n(i,e),n=n==null?e:Vt({},e,n),t.memoizedState=n,t.lanes===0&&(t.updateQueue.baseState=n)}var Fh={enqueueSetState:function(t,e,n){t=t._reactInternals;var i=ui(),a=ps(i);a.payload=e,n!=null&&(a.callback=n),e=ms(t,a,i),e!==null&&(qn(e,t,i),mo(e,t,i))},enqueueReplaceState:function(t,e,n){t=t._reactInternals;var i=ui(),a=ps(i);a.tag=1,a.payload=e,n!=null&&(a.callback=n),e=ms(t,a,i),e!==null&&(qn(e,t,i),mo(e,t,i))},enqueueForceUpdate:function(t,e){t=t._reactInternals;var n=ui(),i=ps(n);i.tag=2,e!=null&&(i.callback=e),e=ms(t,i,n),e!==null&&(qn(e,t,n),mo(e,t,n))}};function vx(t,e,n,i,a,s,r){return t=t.stateNode,typeof t.shouldComponentUpdate=="function"?t.shouldComponentUpdate(i,s,r):e.prototype&&e.prototype.isPureReactComponent?!Ro(n,i)||!Ro(a,s):!0}function _x(t,e,n,i){t=e.state,typeof e.componentWillReceiveProps=="function"&&e.componentWillReceiveProps(n,i),typeof e.UNSAFE_componentWillReceiveProps=="function"&&e.UNSAFE_componentWillReceiveProps(n,i),e.state!==t&&Fh.enqueueReplaceState(e,e.state,null)}function Qs(t,e){var n=e;if("ref"in e){n={};for(var i in e)i!=="ref"&&(n[i]=e[i])}if(t=t.defaultProps){n===e&&(n=Vt({},n));for(var a in t)n[a]===void 0&&(n[a]=t[a])}return n}function Dy(t){Ou(t)}function Ly(t){console.error(t)}function Uy(t){Ou(t)}function Gu(t,e){try{var n=t.onUncaughtError;n(e.value,{componentStack:e.stack})}catch(i){setTimeout(function(){throw i})}}function yx(t,e,n){try{var i=t.onCaughtError;i(n.value,{componentStack:n.stack,errorBoundary:e.tag===1?e.stateNode:null})}catch(a){setTimeout(function(){throw a})}}function jh(t,e,n){return n=ps(n),n.tag=3,n.payload={element:null},n.callback=function(){Gu(t,e)},n}function Oy(t){return t=ps(t),t.tag=3,t}function Py(t,e,n,i){var a=n.type.getDerivedStateFromError;if(typeof a=="function"){var s=i.value;t.payload=function(){return a(s)},t.callback=function(){yx(e,n,i)}}var r=n.stateNode;r!==null&&typeof r.componentDidCatch=="function"&&(t.callback=function(){yx(e,n,i),typeof a!="function"&&(gs===null?gs=new Set([this]):gs.add(this));var o=i.stack;this.componentDidCatch(i.value,{componentStack:o!==null?o:""})})}function jE(t,e,n,i,a){if(n.flags|=32768,i!==null&&typeof i=="object"&&typeof i.then=="function"){if(e=n.alternate,e!==null&&Al(e,n,a,!0),n=di.current,n!==null){switch(n.tag){case 31:case 13:return Ai===null?Yu():n.alternate===null&&$t===0&&($t=3),n.flags&=-257,n.flags|=65536,n.lanes=a,i===ku?n.flags|=16384:(e=n.updateQueue,e===null?n.updateQueue=new Set([i]):e.add(i),xd(t,i,a)),!1;case 22:return n.flags|=65536,i===ku?n.flags|=16384:(e=n.updateQueue,e===null?(e={transitions:null,markerInstances:null,retryQueue:new Set([i])},n.updateQueue=e):(n=e.retryQueue,n===null?e.retryQueue=new Set([i]):n.add(i)),xd(t,i,a)),!1}throw Error(ue(435,n.tag))}return xd(t,i,a),Yu(),!1}if(mt)return e=di.current,e!==null?(!(e.flags&65536)&&(e.flags|=256),e.flags|=65536,e.lanes=a,i!==Ch&&(t=Error(ue(422),{cause:i}),Lo(Si(t,n)))):(i!==Ch&&(e=Error(ue(423),{cause:i}),Lo(Si(e,n))),t=t.current.alternate,t.flags|=65536,a&=-a,t.lanes|=a,i=Si(i,n),a=jh(t.stateNode,i,a),ad(t,a),$t!==4&&($t=2)),!1;var s=Error(ue(520),{cause:i});if(s=Si(s,n),bo===null?bo=[s]:bo.push(s),$t!==4&&($t=2),e===null)return!0;i=Si(i,n),n=e;do{switch(n.tag){case 3:return n.flags|=65536,t=a&-a,n.lanes|=t,t=jh(n.stateNode,i,t),ad(n,t),!1;case 1:if(e=n.type,s=n.stateNode,(n.flags&128)===0&&(typeof e.getDerivedStateFromError=="function"||s!==null&&typeof s.componentDidCatch=="function"&&(gs===null||!gs.has(s))))return n.flags|=65536,a&=-a,n.lanes|=a,a=Oy(a),Py(a,t,n,i),ad(n,a),!1}n=n.return}while(n!==null);return!1}var Xm=Error(ue(461)),on=!1;function yn(t,e,n,i){e.child=t===null?K_(e,null,n,i):Zs(e,t.child,n,i)}function bx(t,e,n,i,a){n=n.render;var s=e.ref;if("ref"in i){var r={};for(var o in i)o!=="ref"&&(r[o]=i[o])}else r=i;return Ks(e),i=Om(t,e,n,r,s,a),o=Pm(),t!==null&&!on?(zm(t,e,a),Oa(t,e,a)):(mt&&o&&Am(e),e.flags|=1,yn(t,e,i,a),e.child)}function Sx(t,e,n,i,a){if(t===null){var s=n.type;return typeof s=="function"&&!Tm(s)&&s.defaultProps===void 0&&n.compare===null?(e.tag=15,e.type=s,zy(t,e,s,i,a)):(t=ou(n.type,null,i,e,e.mode,a),t.ref=e.ref,t.return=e,e.child=t)}if(s=t.child,!qm(t,a)){var r=s.memoizedProps;if(n=n.compare,n=n!==null?n:Ro,n(r,i)&&t.ref===e.ref)return Oa(t,e,a)}return e.flags|=1,t=Aa(s,i),t.ref=e.ref,t.return=e,e.child=t}function zy(t,e,n,i,a){if(t!==null){var s=t.memoizedProps;if(Ro(s,i)&&t.ref===e.ref)if(on=!1,e.pendingProps=i=s,qm(t,a))t.flags&131072&&(on=!0);else return e.lanes=t.lanes,Oa(t,e,a)}return Hh(t,e,n,i,a)}function Iy(t,e,n,i){var a=i.children,s=t!==null?t.memoizedState:null;if(t===null&&e.stateNode===null&&(e.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null}),i.mode==="hidden"){if(e.flags&128){if(s=s!==null?s.baseLanes|n:n,t!==null){for(i=e.child=t.child,a=0;i!==null;)a=a|i.lanes|i.childLanes,i=i.sibling;i=a&~s}else i=0,e.child=null;return Mx(t,e,s,n,i)}if(n&536870912)e.memoizedState={baseLanes:0,cachePool:null},t!==null&&cu(e,s!==null?s.cachePool:null),s!==null?fx(e,s):Ph(),Q_(e);else return i=e.lanes=536870912,Mx(t,e,s!==null?s.baseLanes|n:n,n,i)}else s!==null?(cu(e,s.cachePool),fx(e,s),ts(),e.memoizedState=null):(t!==null&&cu(e,null),Ph(),ts());return yn(t,e,a,n),e.child}function no(t,e){return t!==null&&t.tag===22||e.stateNode!==null||(e.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null}),e.sibling}function Mx(t,e,n,i,a){var s=Nm();return s=s===null?null:{parent:ln._currentValue,pool:s},e.memoizedState={baseLanes:n,cachePool:s},t!==null&&cu(e,null),Ph(),Q_(e),t!==null&&Al(t,e,i,!0),e.childLanes=a,null}function du(t,e){return e=Vu({mode:e.mode,children:e.children},t.mode),e.ref=t.ref,t.child=e,e.return=t,e}function Ex(t,e,n){return Zs(e,t.child,null,n),t=du(e,e.pendingProps),t.flags|=2,ai(e),e.memoizedState=null,t}function HE(t,e,n){var i=e.pendingProps,a=(e.flags&128)!==0;if(e.flags&=-129,t===null){if(mt){if(i.mode==="hidden")return t=du(e,i),e.lanes=536870912,no(null,t);if(zh(e),(t=Ht)?(t=Nb(t,Mi),t=t!==null&&t.data==="&"?t:null,t!==null&&(e.memoizedState={dehydrated:t,treeContext:bs!==null?{id:Qi,overflow:Ji}:null,retryLane:536870912,hydrationErrors:null},n=H_(t),n.return=e,e.child=n,Sn=e,Ht=null)):t=null,t===null)throw Ss(e);return e.lanes=536870912,null}return du(e,i)}var s=t.memoizedState;if(s!==null){var r=s.dehydrated;if(zh(e),a)if(e.flags&256)e.flags&=-257,e=Ex(t,e,n);else if(e.memoizedState!==null)e.child=t.child,e.flags|=128,e=null;else throw Error(ue(558));else if(on||Al(t,e,n,!1),a=(n&t.childLanes)!==0,on||a){if(i=It,i!==null&&(r=p_(i,n),r!==0&&r!==s.retryLane))throw s.retryLane=r,lr(t,r),qn(i,t,r),Xm;Yu(),e=Ex(t,e,n)}else t=s.treeContext,Ht=wi(r.nextSibling),Sn=e,mt=!0,hs=null,Mi=!1,t!==null&&V_(e,t),e=du(e,i),e.flags|=4096;return e}return t=Aa(t.child,{mode:i.mode,children:i.children}),t.ref=e.ref,e.child=t,t.return=e,t}function hu(t,e){var n=e.ref;if(n===null)t!==null&&t.ref!==null&&(e.flags|=4194816);else{if(typeof n!="function"&&typeof n!="object")throw Error(ue(284));(t===null||t.ref!==n)&&(e.flags|=4194816)}}function Hh(t,e,n,i,a){return Ks(e),n=Om(t,e,n,i,void 0,a),i=Pm(),t!==null&&!on?(zm(t,e,a),Oa(t,e,a)):(mt&&i&&Am(e),e.flags|=1,yn(t,e,n,a),e.child)}function Tx(t,e,n,i,a,s){return Ks(e),e.updateQueue=null,n=ey(e,i,n,a),J_(t),i=Pm(),t!==null&&!on?(zm(t,e,s),Oa(t,e,s)):(mt&&i&&Am(e),e.flags|=1,yn(t,e,n,s),e.child)}function Ax(t,e,n,i,a){if(Ks(e),e.stateNode===null){var s=Gr,r=n.contextType;typeof r=="object"&&r!==null&&(s=Mn(r)),s=new n(i,s),e.memoizedState=s.state!==null&&s.state!==void 0?s.state:null,s.updater=Fh,e.stateNode=s,s._reactInternals=e,s=e.stateNode,s.props=i,s.state=e.memoizedState,s.refs={},Dm(e),r=n.contextType,s.context=typeof r=="object"&&r!==null?Mn(r):Gr,s.state=e.memoizedState,r=n.getDerivedStateFromProps,typeof r=="function"&&(rd(e,n,r,i),s.state=e.memoizedState),typeof n.getDerivedStateFromProps=="function"||typeof s.getSnapshotBeforeUpdate=="function"||typeof s.UNSAFE_componentWillMount!="function"&&typeof s.componentWillMount!="function"||(r=s.state,typeof s.componentWillMount=="function"&&s.componentWillMount(),typeof s.UNSAFE_componentWillMount=="function"&&s.UNSAFE_componentWillMount(),r!==s.state&&Fh.enqueueReplaceState(s,s.state,null),xo(e,i,s,a),go(),s.state=e.memoizedState),typeof s.componentDidMount=="function"&&(e.flags|=4194308),i=!0}else if(t===null){s=e.stateNode;var o=e.memoizedProps,c=Qs(n,o);s.props=c;var u=s.context,d=n.contextType;r=Gr,typeof d=="object"&&d!==null&&(r=Mn(d));var m=n.getDerivedStateFromProps;d=typeof m=="function"||typeof s.getSnapshotBeforeUpdate=="function",o=e.pendingProps!==o,d||typeof s.UNSAFE_componentWillReceiveProps!="function"&&typeof s.componentWillReceiveProps!="function"||(o||u!==r)&&_x(e,s,i,r),Qa=!1;var f=e.memoizedState;s.state=f,xo(e,i,s,a),go(),u=e.memoizedState,o||f!==u||Qa?(typeof m=="function"&&(rd(e,n,m,i),u=e.memoizedState),(c=Qa||vx(e,n,c,i,f,u,r))?(d||typeof s.UNSAFE_componentWillMount!="function"&&typeof s.componentWillMount!="function"||(typeof s.componentWillMount=="function"&&s.componentWillMount(),typeof s.UNSAFE_componentWillMount=="function"&&s.UNSAFE_componentWillMount()),typeof s.componentDidMount=="function"&&(e.flags|=4194308)):(typeof s.componentDidMount=="function"&&(e.flags|=4194308),e.memoizedProps=i,e.memoizedState=u),s.props=i,s.state=u,s.context=r,i=c):(typeof s.componentDidMount=="function"&&(e.flags|=4194308),i=!1)}else{s=e.stateNode,Uh(t,e),r=e.memoizedProps,d=Qs(n,r),s.props=d,m=e.pendingProps,f=s.context,u=n.contextType,c=Gr,typeof u=="object"&&u!==null&&(c=Mn(u)),o=n.getDerivedStateFromProps,(u=typeof o=="function"||typeof s.getSnapshotBeforeUpdate=="function")||typeof s.UNSAFE_componentWillReceiveProps!="function"&&typeof s.componentWillReceiveProps!="function"||(r!==m||f!==c)&&_x(e,s,i,c),Qa=!1,f=e.memoizedState,s.state=f,xo(e,i,s,a),go();var p=e.memoizedState;r!==m||f!==p||Qa||t!==null&&t.dependencies!==null&&Iu(t.dependencies)?(typeof o=="function"&&(rd(e,n,o,i),p=e.memoizedState),(d=Qa||vx(e,n,d,i,f,p,c)||t!==null&&t.dependencies!==null&&Iu(t.dependencies))?(u||typeof s.UNSAFE_componentWillUpdate!="function"&&typeof s.componentWillUpdate!="function"||(typeof s.componentWillUpdate=="function"&&s.componentWillUpdate(i,p,c),typeof s.UNSAFE_componentWillUpdate=="function"&&s.UNSAFE_componentWillUpdate(i,p,c)),typeof s.componentDidUpdate=="function"&&(e.flags|=4),typeof s.getSnapshotBeforeUpdate=="function"&&(e.flags|=1024)):(typeof s.componentDidUpdate!="function"||r===t.memoizedProps&&f===t.memoizedState||(e.flags|=4),typeof s.getSnapshotBeforeUpdate!="function"||r===t.memoizedProps&&f===t.memoizedState||(e.flags|=1024),e.memoizedProps=i,e.memoizedState=p),s.props=i,s.state=p,s.context=c,i=d):(typeof s.componentDidUpdate!="function"||r===t.memoizedProps&&f===t.memoizedState||(e.flags|=4),typeof s.getSnapshotBeforeUpdate!="function"||r===t.memoizedProps&&f===t.memoizedState||(e.flags|=1024),i=!1)}return s=i,hu(t,e),i=(e.flags&128)!==0,s||i?(s=e.stateNode,n=i&&typeof n.getDerivedStateFromError!="function"?null:s.render(),e.flags|=1,t!==null&&i?(e.child=Zs(e,t.child,null,a),e.child=Zs(e,null,n,a)):yn(t,e,n,a),e.memoizedState=s.state,t=e.child):t=Oa(t,e,a),t}function wx(t,e,n,i){return Ys(),e.flags|=256,yn(t,e,n,i),e.child}var ld={dehydrated:null,treeContext:null,retryLane:0,hydrationErrors:null};function od(t){return{baseLanes:t,cachePool:X_()}}function cd(t,e,n){return t=t!==null?t.childLanes&~n:0,e&&(t|=ri),t}function ky(t,e,n){var i=e.pendingProps,a=!1,s=(e.flags&128)!==0,r;if((r=s)||(r=t!==null&&t.memoizedState===null?!1:(tn.current&2)!==0),r&&(a=!0,e.flags&=-129),r=(e.flags&32)!==0,e.flags&=-33,t===null){if(mt){if(a?es(e):ts(),(t=Ht)?(t=Nb(t,Mi),t=t!==null&&t.data!=="&"?t:null,t!==null&&(e.memoizedState={dehydrated:t,treeContext:bs!==null?{id:Qi,overflow:Ji}:null,retryLane:536870912,hydrationErrors:null},n=H_(t),n.return=e,e.child=n,Sn=e,Ht=null)):t=null,t===null)throw Ss(e);return np(t)?e.lanes=32:e.lanes=536870912,null}var o=i.children;return i=i.fallback,a?(ts(),a=e.mode,o=Vu({mode:"hidden",children:o},a),i=Vs(i,a,n,null),o.return=e,i.return=e,o.sibling=i,e.child=o,i=e.child,i.memoizedState=od(n),i.childLanes=cd(t,r,n),e.memoizedState=ld,no(null,i)):(es(e),Gh(e,o))}var c=t.memoizedState;if(c!==null&&(o=c.dehydrated,o!==null)){if(s)e.flags&256?(es(e),e.flags&=-257,e=ud(t,e,n)):e.memoizedState!==null?(ts(),e.child=t.child,e.flags|=128,e=null):(ts(),o=i.fallback,a=e.mode,i=Vu({mode:"visible",children:i.children},a),o=Vs(o,a,n,null),o.flags|=2,i.return=e,o.return=e,i.sibling=o,e.child=i,Zs(e,t.child,null,n),i=e.child,i.memoizedState=od(n),i.childLanes=cd(t,r,n),e.memoizedState=ld,e=no(null,i));else if(es(e),np(o)){if(r=o.nextSibling&&o.nextSibling.dataset,r)var u=r.dgst;r=u,i=Error(ue(419)),i.stack="",i.digest=r,Lo({value:i,source:null,stack:null}),e=ud(t,e,n)}else if(on||Al(t,e,n,!1),r=(n&t.childLanes)!==0,on||r){if(r=It,r!==null&&(i=p_(r,n),i!==0&&i!==c.retryLane))throw c.retryLane=i,lr(t,i),qn(r,t,i),Xm;tp(o)||Yu(),e=ud(t,e,n)}else tp(o)?(e.flags|=192,e.child=t.child,e=null):(t=c.treeContext,Ht=wi(o.nextSibling),Sn=e,mt=!0,hs=null,Mi=!1,t!==null&&V_(e,t),e=Gh(e,i.children),e.flags|=4096);return e}return a?(ts(),o=i.fallback,a=e.mode,c=t.child,u=c.sibling,i=Aa(c,{mode:"hidden",children:i.children}),i.subtreeFlags=c.subtreeFlags&65011712,u!==null?o=Aa(u,o):(o=Vs(o,a,n,null),o.flags|=2),o.return=e,i.return=e,i.sibling=o,e.child=i,no(null,i),i=e.child,o=t.child.memoizedState,o===null?o=od(n):(a=o.cachePool,a!==null?(c=ln._currentValue,a=a.parent!==c?{parent:c,pool:c}:a):a=X_(),o={baseLanes:o.baseLanes|n,cachePool:a}),i.memoizedState=o,i.childLanes=cd(t,r,n),e.memoizedState=ld,no(t.child,i)):(es(e),n=t.child,t=n.sibling,n=Aa(n,{mode:"visible",children:i.children}),n.return=e,n.sibling=null,t!==null&&(r=e.deletions,r===null?(e.deletions=[t],e.flags|=16):r.push(t)),e.child=n,e.memoizedState=null,n)}function Gh(t,e){return e=Vu({mode:"visible",children:e},t.mode),e.return=t,t.child=e}function Vu(t,e){return t=si(22,t,null,e),t.lanes=0,t}function ud(t,e,n){return Zs(e,t.child,null,n),t=Gh(e,e.pendingProps.children),t.flags|=2,e.memoizedState=null,t}function Cx(t,e,n){t.lanes|=e;var i=t.alternate;i!==null&&(i.lanes|=e),Rh(t.return,e,n)}function fd(t,e,n,i,a,s){var r=t.memoizedState;r===null?t.memoizedState={isBackwards:e,rendering:null,renderingStartTime:0,last:i,tail:n,tailMode:a,treeForkCount:s}:(r.isBackwards=e,r.rendering=null,r.renderingStartTime=0,r.last=i,r.tail=n,r.tailMode=a,r.treeForkCount=s)}function By(t,e,n){var i=e.pendingProps,a=i.revealOrder,s=i.tail;i=i.children;var r=tn.current,o=(r&2)!==0;if(o?(r=r&1|2,e.flags|=128):r&=1,Bt(tn,r),yn(t,e,i,n),i=mt?Do:0,!o&&t!==null&&t.flags&128)e:for(t=e.child;t!==null;){if(t.tag===13)t.memoizedState!==null&&Cx(t,n,e);else if(t.tag===19)Cx(t,n,e);else if(t.child!==null){t.child.return=t,t=t.child;continue}if(t===e)break e;for(;t.sibling===null;){if(t.return===null||t.return===e)break e;t=t.return}t.sibling.return=t.return,t=t.sibling}switch(a){case"forwards":for(n=e.child,a=null;n!==null;)t=n.alternate,t!==null&&Fu(t)===null&&(a=n),n=n.sibling;n=a,n===null?(a=e.child,e.child=null):(a=n.sibling,n.sibling=null),fd(e,!1,a,n,s,i);break;case"backwards":case"unstable_legacy-backwards":for(n=null,a=e.child,e.child=null;a!==null;){if(t=a.alternate,t!==null&&Fu(t)===null){e.child=a;break}t=a.sibling,a.sibling=n,n=a,a=t}fd(e,!0,n,null,s,i);break;case"together":fd(e,!1,null,null,void 0,i);break;default:e.memoizedState=null}return e.child}function Oa(t,e,n){if(t!==null&&(e.dependencies=t.dependencies),Es|=e.lanes,!(n&e.childLanes))if(t!==null){if(Al(t,e,n,!1),(n&e.childLanes)===0)return null}else return null;if(t!==null&&e.child!==t.child)throw Error(ue(153));if(e.child!==null){for(t=e.child,n=Aa(t,t.pendingProps),e.child=n,n.return=e;t.sibling!==null;)t=t.sibling,n=n.sibling=Aa(t,t.pendingProps),n.return=e;n.sibling=null}return e.child}function qm(t,e){return t.lanes&e?!0:(t=t.dependencies,!!(t!==null&&Iu(t)))}function GE(t,e,n){switch(e.tag){case 3:Ru(e,e.stateNode.containerInfo),Ja(e,ln,t.memoizedState.cache),Ys();break;case 27:case 5:xh(e);break;case 4:Ru(e,e.stateNode.containerInfo);break;case 10:Ja(e,e.type,e.memoizedProps.value);break;case 31:if(e.memoizedState!==null)return e.flags|=128,zh(e),null;break;case 13:var i=e.memoizedState;if(i!==null)return i.dehydrated!==null?(es(e),e.flags|=128,null):n&e.child.childLanes?ky(t,e,n):(es(e),t=Oa(t,e,n),t!==null?t.sibling:null);es(e);break;case 19:var a=(t.flags&128)!==0;if(i=(n&e.childLanes)!==0,i||(Al(t,e,n,!1),i=(n&e.childLanes)!==0),a){if(i)return By(t,e,n);e.flags|=128}if(a=e.memoizedState,a!==null&&(a.rendering=null,a.tail=null,a.lastEffect=null),Bt(tn,tn.current),i)break;return null;case 22:return e.lanes=0,Iy(t,e,n,e.pendingProps);case 24:Ja(e,ln,t.memoizedState.cache)}return Oa(t,e,n)}function Fy(t,e,n){if(t!==null)if(t.memoizedProps!==e.pendingProps)on=!0;else{if(!qm(t,n)&&!(e.flags&128))return on=!1,GE(t,e,n);on=!!(t.flags&131072)}else on=!1,mt&&e.flags&1048576&&G_(e,Do,e.index);switch(e.lanes=0,e.tag){case 16:e:{var i=e.pendingProps;if(t=zs(e.elementType),e.type=t,typeof t=="function")Tm(t)?(i=Qs(t,i),e.tag=1,e=Ax(null,e,t,i,n)):(e.tag=0,e=Hh(null,e,t,i,n));else{if(t!=null){var a=t.$$typeof;if(a===fm){e.tag=11,e=bx(null,e,t,i,n);break e}else if(a===dm){e.tag=14,e=Sx(null,e,t,i,n);break e}}throw e=mh(t)||t,Error(ue(306,e,""))}}return e;case 0:return Hh(t,e,e.type,e.pendingProps,n);case 1:return i=e.type,a=Qs(i,e.pendingProps),Ax(t,e,i,a,n);case 3:e:{if(Ru(e,e.stateNode.containerInfo),t===null)throw Error(ue(387));i=e.pendingProps;var s=e.memoizedState;a=s.element,Uh(t,e),xo(e,i,null,n);var r=e.memoizedState;if(i=r.cache,Ja(e,ln,i),i!==s.cache&&Dh(e,[ln],n,!0),go(),i=r.element,s.isDehydrated)if(s={element:i,isDehydrated:!1,cache:r.cache},e.updateQueue.baseState=s,e.memoizedState=s,e.flags&256){e=wx(t,e,i,n);break e}else if(i!==a){a=Si(Error(ue(424)),e),Lo(a),e=wx(t,e,i,n);break e}else{switch(t=e.stateNode.containerInfo,t.nodeType){case 9:t=t.body;break;default:t=t.nodeName==="HTML"?t.ownerDocument.body:t}for(Ht=wi(t.firstChild),Sn=e,mt=!0,hs=null,Mi=!0,n=K_(e,null,i,n),e.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling}else{if(Ys(),i===a){e=Oa(t,e,n);break e}yn(t,e,i,n)}e=e.child}return e;case 26:return hu(t,e),t===null?(n=Yx(e.type,null,e.pendingProps,null))?e.memoizedState=n:mt||(n=e.type,t=e.pendingProps,i=Qu(ds.current).createElement(n),i[bn]=e,i[Kn]=t,Tn(i,n,t),gn(i),e.stateNode=i):e.memoizedState=Yx(e.type,t.memoizedProps,e.pendingProps,t.memoizedState),null;case 27:return xh(e),t===null&&mt&&(i=e.stateNode=Rb(e.type,e.pendingProps,ds.current),Sn=e,Mi=!0,a=Ht,ws(e.type)?(ip=a,Ht=wi(i.firstChild)):Ht=a),yn(t,e,e.pendingProps.children,n),hu(t,e),t===null&&(e.flags|=4194304),e.child;case 5:return t===null&&mt&&((a=i=Ht)&&(i=_1(i,e.type,e.pendingProps,Mi),i!==null?(e.stateNode=i,Sn=e,Ht=wi(i.firstChild),Mi=!1,a=!0):a=!1),a||Ss(e)),xh(e),a=e.type,s=e.pendingProps,r=t!==null?t.memoizedProps:null,i=s.children,Jh(a,s)?i=null:r!==null&&Jh(a,r)&&(e.flags|=32),e.memoizedState!==null&&(a=Om(t,e,OE,null,null,n),ko._currentValue=a),hu(t,e),yn(t,e,i,n),e.child;case 6:return t===null&&mt&&((t=n=Ht)&&(n=y1(n,e.pendingProps,Mi),n!==null?(e.stateNode=n,Sn=e,Ht=null,t=!0):t=!1),t||Ss(e)),null;case 13:return ky(t,e,n);case 4:return Ru(e,e.stateNode.containerInfo),i=e.pendingProps,t===null?e.child=Zs(e,null,i,n):yn(t,e,i,n),e.child;case 11:return bx(t,e,e.type,e.pendingProps,n);case 7:return yn(t,e,e.pendingProps,n),e.child;case 8:return yn(t,e,e.pendingProps.children,n),e.child;case 12:return yn(t,e,e.pendingProps.children,n),e.child;case 10:return i=e.pendingProps,Ja(e,e.type,i.value),yn(t,e,i.children,n),e.child;case 9:return a=e.type._context,i=e.pendingProps.children,Ks(e),a=Mn(a),i=i(a),e.flags|=1,yn(t,e,i,n),e.child;case 14:return Sx(t,e,e.type,e.pendingProps,n);case 15:return zy(t,e,e.type,e.pendingProps,n);case 19:return By(t,e,n);case 31:return HE(t,e,n);case 22:return Iy(t,e,n,e.pendingProps);case 24:return Ks(e),i=Mn(ln),t===null?(a=Nm(),a===null&&(a=It,s=Cm(),a.pooledCache=s,s.refCount++,s!==null&&(a.pooledCacheLanes|=n),a=s),e.memoizedState={parent:i,cache:a},Dm(e),Ja(e,ln,a)):(t.lanes&n&&(Uh(t,e),xo(e,null,null,n),go()),a=t.memoizedState,s=e.memoizedState,a.parent!==i?(a={parent:i,cache:i},e.memoizedState=a,e.lanes===0&&(e.memoizedState=e.updateQueue.baseState=a),Ja(e,ln,i)):(i=s.cache,Ja(e,ln,i),i!==a.cache&&Dh(e,[ln],n,!0))),yn(t,e,e.pendingProps.children,n),e.child;case 29:throw e.pendingProps}throw Error(ue(156,e.tag))}function ca(t){t.flags|=4}function dd(t,e,n,i,a){if((e=(t.mode&32)!==0)&&(e=!1),e){if(t.flags|=16777216,(a&335544128)===a)if(t.stateNode.complete)t.flags|=8192;else if(ub())t.flags|=8192;else throw Xs=ku,Rm}else t.flags&=-16777217}function Nx(t,e){if(e.type!=="stylesheet"||e.state.loading&4)t.flags&=-16777217;else if(t.flags|=16777216,!Ub(e))if(ub())t.flags|=8192;else throw Xs=ku,Rm}function gc(t,e){e!==null&&(t.flags|=4),t.flags&16384&&(e=t.tag!==22?f_():536870912,t.lanes|=e,dl|=e)}function kl(t,e){if(!mt)switch(t.tailMode){case"hidden":e=t.tail;for(var n=null;e!==null;)e.alternate!==null&&(n=e),e=e.sibling;n===null?t.tail=null:n.sibling=null;break;case"collapsed":n=t.tail;for(var i=null;n!==null;)n.alternate!==null&&(i=n),n=n.sibling;i===null?e||t.tail===null?t.tail=null:t.tail.sibling=null:i.sibling=null}}function jt(t){var e=t.alternate!==null&&t.alternate.child===t.child,n=0,i=0;if(e)for(var a=t.child;a!==null;)n|=a.lanes|a.childLanes,i|=a.subtreeFlags&65011712,i|=a.flags&65011712,a.return=t,a=a.sibling;else for(a=t.child;a!==null;)n|=a.lanes|a.childLanes,i|=a.subtreeFlags,i|=a.flags,a.return=t,a=a.sibling;return t.subtreeFlags|=i,t.childLanes=n,e}function VE(t,e,n){var i=e.pendingProps;switch(wm(e),e.tag){case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return jt(e),null;case 1:return jt(e),null;case 3:return n=e.stateNode,i=null,t!==null&&(i=t.memoizedState.cache),e.memoizedState.cache!==i&&(e.flags|=2048),wa(ln),rl(),n.pendingContext&&(n.context=n.pendingContext,n.pendingContext=null),(t===null||t.child===null)&&(pr(e)?ca(e):t===null||t.memoizedState.isDehydrated&&!(e.flags&256)||(e.flags|=1024,id())),jt(e),null;case 26:var a=e.type,s=e.memoizedState;return t===null?(ca(e),s!==null?(jt(e),Nx(e,s)):(jt(e),dd(e,a,null,i,n))):s?s!==t.memoizedState?(ca(e),jt(e),Nx(e,s)):(jt(e),e.flags&=-16777217):(t=t.memoizedProps,t!==i&&ca(e),jt(e),dd(e,a,t,i,n)),null;case 27:if(Du(e),n=ds.current,a=e.type,t!==null&&e.stateNode!=null)t.memoizedProps!==i&&ca(e);else{if(!i){if(e.stateNode===null)throw Error(ue(166));return jt(e),null}t=na.current,pr(e)?ax(e):(t=Rb(a,i,n),e.stateNode=t,ca(e))}return jt(e),null;case 5:if(Du(e),a=e.type,t!==null&&e.stateNode!=null)t.memoizedProps!==i&&ca(e);else{if(!i){if(e.stateNode===null)throw Error(ue(166));return jt(e),null}if(s=na.current,pr(e))ax(e);else{var r=Qu(ds.current);switch(s){case 1:s=r.createElementNS("http://www.w3.org/2000/svg",a);break;case 2:s=r.createElementNS("http://www.w3.org/1998/Math/MathML",a);break;default:switch(a){case"svg":s=r.createElementNS("http://www.w3.org/2000/svg",a);break;case"math":s=r.createElementNS("http://www.w3.org/1998/Math/MathML",a);break;case"script":s=r.createElement("div"),s.innerHTML="<script><\/script>",s=s.removeChild(s.firstChild);break;case"select":s=typeof i.is=="string"?r.createElement("select",{is:i.is}):r.createElement("select"),i.multiple?s.multiple=!0:i.size&&(s.size=i.size);break;default:s=typeof i.is=="string"?r.createElement(a,{is:i.is}):r.createElement(a)}}s[bn]=e,s[Kn]=i;e:for(r=e.child;r!==null;){if(r.tag===5||r.tag===6)s.appendChild(r.stateNode);else if(r.tag!==4&&r.tag!==27&&r.child!==null){r.child.return=r,r=r.child;continue}if(r===e)break e;for(;r.sibling===null;){if(r.return===null||r.return===e)break e;r=r.return}r.sibling.return=r.return,r=r.sibling}e.stateNode=s;e:switch(Tn(s,a,i),a){case"button":case"input":case"select":case"textarea":i=!!i.autoFocus;break e;case"img":i=!0;break e;default:i=!1}i&&ca(e)}}return jt(e),dd(e,e.type,t===null?null:t.memoizedProps,e.pendingProps,n),null;case 6:if(t&&e.stateNode!=null)t.memoizedProps!==i&&ca(e);else{if(typeof i!="string"&&e.stateNode===null)throw Error(ue(166));if(t=ds.current,pr(e)){if(t=e.stateNode,n=e.memoizedProps,i=null,a=Sn,a!==null)switch(a.tag){case 27:case 5:i=a.memoizedProps}t[bn]=e,t=!!(t.nodeValue===n||i!==null&&i.suppressHydrationWarning===!0||Ab(t.nodeValue,n)),t||Ss(e,!0)}else t=Qu(t).createTextNode(i),t[bn]=e,e.stateNode=t}return jt(e),null;case 31:if(n=e.memoizedState,t===null||t.memoizedState!==null){if(i=pr(e),n!==null){if(t===null){if(!i)throw Error(ue(318));if(t=e.memoizedState,t=t!==null?t.dehydrated:null,!t)throw Error(ue(557));t[bn]=e}else Ys(),!(e.flags&128)&&(e.memoizedState=null),e.flags|=4;jt(e),t=!1}else n=id(),t!==null&&t.memoizedState!==null&&(t.memoizedState.hydrationErrors=n),t=!0;if(!t)return e.flags&256?(ai(e),e):(ai(e),null);if(e.flags&128)throw Error(ue(558))}return jt(e),null;case 13:if(i=e.memoizedState,t===null||t.memoizedState!==null&&t.memoizedState.dehydrated!==null){if(a=pr(e),i!==null&&i.dehydrated!==null){if(t===null){if(!a)throw Error(ue(318));if(a=e.memoizedState,a=a!==null?a.dehydrated:null,!a)throw Error(ue(317));a[bn]=e}else Ys(),!(e.flags&128)&&(e.memoizedState=null),e.flags|=4;jt(e),a=!1}else a=id(),t!==null&&t.memoizedState!==null&&(t.memoizedState.hydrationErrors=a),a=!0;if(!a)return e.flags&256?(ai(e),e):(ai(e),null)}return ai(e),e.flags&128?(e.lanes=n,e):(n=i!==null,t=t!==null&&t.memoizedState!==null,n&&(i=e.child,a=null,i.alternate!==null&&i.alternate.memoizedState!==null&&i.alternate.memoizedState.cachePool!==null&&(a=i.alternate.memoizedState.cachePool.pool),s=null,i.memoizedState!==null&&i.memoizedState.cachePool!==null&&(s=i.memoizedState.cachePool.pool),s!==a&&(i.flags|=2048)),n!==t&&n&&(e.child.flags|=8192),gc(e,e.updateQueue),jt(e),null);case 4:return rl(),t===null&&eg(e.stateNode.containerInfo),jt(e),null;case 10:return wa(e.type),jt(e),null;case 19:if(vn(tn),i=e.memoizedState,i===null)return jt(e),null;if(a=(e.flags&128)!==0,s=i.rendering,s===null)if(a)kl(i,!1);else{if($t!==0||t!==null&&t.flags&128)for(t=e.child;t!==null;){if(s=Fu(t),s!==null){for(e.flags|=128,kl(i,!1),t=s.updateQueue,e.updateQueue=t,gc(e,t),e.subtreeFlags=0,t=n,n=e.child;n!==null;)j_(n,t),n=n.sibling;return Bt(tn,tn.current&1|2),mt&&_a(e,i.treeForkCount),e.child}t=t.sibling}i.tail!==null&&li()>Xu&&(e.flags|=128,a=!0,kl(i,!1),e.lanes=4194304)}else{if(!a)if(t=Fu(s),t!==null){if(e.flags|=128,a=!0,t=t.updateQueue,e.updateQueue=t,gc(e,t),kl(i,!0),i.tail===null&&i.tailMode==="hidden"&&!s.alternate&&!mt)return jt(e),null}else 2*li()-i.renderingStartTime>Xu&&n!==536870912&&(e.flags|=128,a=!0,kl(i,!1),e.lanes=4194304);i.isBackwards?(s.sibling=e.child,e.child=s):(t=i.last,t!==null?t.sibling=s:e.child=s,i.last=s)}return i.tail!==null?(t=i.tail,i.rendering=t,i.tail=t.sibling,i.renderingStartTime=li(),t.sibling=null,n=tn.current,Bt(tn,a?n&1|2:n&1),mt&&_a(e,i.treeForkCount),t):(jt(e),null);case 22:case 23:return ai(e),Lm(),i=e.memoizedState!==null,t!==null?t.memoizedState!==null!==i&&(e.flags|=8192):i&&(e.flags|=8192),i?n&536870912&&!(e.flags&128)&&(jt(e),e.subtreeFlags&6&&(e.flags|=8192)):jt(e),n=e.updateQueue,n!==null&&gc(e,n.retryQueue),n=null,t!==null&&t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(n=t.memoizedState.cachePool.pool),i=null,e.memoizedState!==null&&e.memoizedState.cachePool!==null&&(i=e.memoizedState.cachePool.pool),i!==n&&(e.flags|=2048),t!==null&&vn(Ws),null;case 24:return n=null,t!==null&&(n=t.memoizedState.cache),e.memoizedState.cache!==n&&(e.flags|=2048),wa(ln),jt(e),null;case 25:return null;case 30:return null}throw Error(ue(156,e.tag))}function WE(t,e){switch(wm(e),e.tag){case 1:return t=e.flags,t&65536?(e.flags=t&-65537|128,e):null;case 3:return wa(ln),rl(),t=e.flags,t&65536&&!(t&128)?(e.flags=t&-65537|128,e):null;case 26:case 27:case 5:return Du(e),null;case 31:if(e.memoizedState!==null){if(ai(e),e.alternate===null)throw Error(ue(340));Ys()}return t=e.flags,t&65536?(e.flags=t&-65537|128,e):null;case 13:if(ai(e),t=e.memoizedState,t!==null&&t.dehydrated!==null){if(e.alternate===null)throw Error(ue(340));Ys()}return t=e.flags,t&65536?(e.flags=t&-65537|128,e):null;case 19:return vn(tn),null;case 4:return rl(),null;case 10:return wa(e.type),null;case 22:case 23:return ai(e),Lm(),t!==null&&vn(Ws),t=e.flags,t&65536?(e.flags=t&-65537|128,e):null;case 24:return wa(ln),null;case 25:return null;default:return null}}function jy(t,e){switch(wm(e),e.tag){case 3:wa(ln),rl();break;case 26:case 27:case 5:Du(e);break;case 4:rl();break;case 31:e.memoizedState!==null&&ai(e);break;case 13:ai(e);break;case 19:vn(tn);break;case 10:wa(e.type);break;case 22:case 23:ai(e),Lm(),t!==null&&vn(Ws);break;case 24:wa(ln)}}function ec(t,e){try{var n=e.updateQueue,i=n!==null?n.lastEffect:null;if(i!==null){var a=i.next;n=a;do{if((n.tag&t)===t){i=void 0;var s=n.create,r=n.inst;i=s(),r.destroy=i}n=n.next}while(n!==a)}}catch(o){Rt(e,e.return,o)}}function Ms(t,e,n){try{var i=e.updateQueue,a=i!==null?i.lastEffect:null;if(a!==null){var s=a.next;i=s;do{if((i.tag&t)===t){var r=i.inst,o=r.destroy;if(o!==void 0){r.destroy=void 0,a=e;var c=n,u=o;try{u()}catch(d){Rt(a,c,d)}}}i=i.next}while(i!==s)}}catch(d){Rt(e,e.return,d)}}function Hy(t){var e=t.updateQueue;if(e!==null){var n=t.stateNode;try{$_(e,n)}catch(i){Rt(t,t.return,i)}}}function Gy(t,e,n){n.props=Qs(t.type,t.memoizedProps),n.state=t.memoizedState;try{n.componentWillUnmount()}catch(i){Rt(t,e,i)}}function _o(t,e){try{var n=t.ref;if(n!==null){switch(t.tag){case 26:case 27:case 5:var i=t.stateNode;break;case 30:i=t.stateNode;break;default:i=t.stateNode}typeof n=="function"?t.refCleanup=n(i):n.current=i}}catch(a){Rt(t,e,a)}}function ea(t,e){var n=t.ref,i=t.refCleanup;if(n!==null)if(typeof i=="function")try{i()}catch(a){Rt(t,e,a)}finally{t.refCleanup=null,t=t.alternate,t!=null&&(t.refCleanup=null)}else if(typeof n=="function")try{n(null)}catch(a){Rt(t,e,a)}else n.current=null}function Vy(t){var e=t.type,n=t.memoizedProps,i=t.stateNode;try{e:switch(e){case"button":case"input":case"select":case"textarea":n.autoFocus&&i.focus();break e;case"img":n.src?i.src=n.src:n.srcSet&&(i.srcset=n.srcSet)}}catch(a){Rt(t,t.return,a)}}function hd(t,e,n){try{var i=t.stateNode;h1(i,t.type,n,e),i[Kn]=e}catch(a){Rt(t,t.return,a)}}function Wy(t){return t.tag===5||t.tag===3||t.tag===26||t.tag===27&&ws(t.type)||t.tag===4}function pd(t){e:for(;;){for(;t.sibling===null;){if(t.return===null||Wy(t.return))return null;t=t.return}for(t.sibling.return=t.return,t=t.sibling;t.tag!==5&&t.tag!==6&&t.tag!==18;){if(t.tag===27&&ws(t.type)||t.flags&2||t.child===null||t.tag===4)continue e;t.child.return=t,t=t.child}if(!(t.flags&2))return t.stateNode}}function Vh(t,e,n){var i=t.tag;if(i===5||i===6)t=t.stateNode,e?(n.nodeType===9?n.body:n.nodeName==="HTML"?n.ownerDocument.body:n).insertBefore(t,e):(e=n.nodeType===9?n.body:n.nodeName==="HTML"?n.ownerDocument.body:n,e.appendChild(t),n=n._reactRootContainer,n!=null||e.onclick!==null||(e.onclick=Ma));else if(i!==4&&(i===27&&ws(t.type)&&(n=t.stateNode,e=null),t=t.child,t!==null))for(Vh(t,e,n),t=t.sibling;t!==null;)Vh(t,e,n),t=t.sibling}function Wu(t,e,n){var i=t.tag;if(i===5||i===6)t=t.stateNode,e?n.insertBefore(t,e):n.appendChild(t);else if(i!==4&&(i===27&&ws(t.type)&&(n=t.stateNode),t=t.child,t!==null))for(Wu(t,e,n),t=t.sibling;t!==null;)Wu(t,e,n),t=t.sibling}function Xy(t){var e=t.stateNode,n=t.memoizedProps;try{for(var i=t.type,a=e.attributes;a.length;)e.removeAttributeNode(a[0]);Tn(e,i,n),e[bn]=t,e[Kn]=n}catch(s){Rt(t,t.return,s)}}var ya=!1,rn=!1,md=!1,Rx=typeof WeakSet=="function"?WeakSet:Set,mn=null;function XE(t,e){if(t=t.containerInfo,$h=nf,t=U_(t),Sm(t)){if("selectionStart"in t)var n={start:t.selectionStart,end:t.selectionEnd};else e:{n=(n=t.ownerDocument)&&n.defaultView||window;var i=n.getSelection&&n.getSelection();if(i&&i.rangeCount!==0){n=i.anchorNode;var a=i.anchorOffset,s=i.focusNode;i=i.focusOffset;try{n.nodeType,s.nodeType}catch{n=null;break e}var r=0,o=-1,c=-1,u=0,d=0,m=t,f=null;t:for(;;){for(var p;m!==n||a!==0&&m.nodeType!==3||(o=r+a),m!==s||i!==0&&m.nodeType!==3||(c=r+i),m.nodeType===3&&(r+=m.nodeValue.length),(p=m.firstChild)!==null;)f=m,m=p;for(;;){if(m===t)break t;if(f===n&&++u===a&&(o=r),f===s&&++d===i&&(c=r),(p=m.nextSibling)!==null)break;m=f,f=m.parentNode}m=p}n=o===-1||c===-1?null:{start:o,end:c}}else n=null}n=n||{start:0,end:0}}else n=null;for(Qh={focusedElem:t,selectionRange:n},nf=!1,mn=e;mn!==null;)if(e=mn,t=e.child,(e.subtreeFlags&1028)!==0&&t!==null)t.return=e,mn=t;else for(;mn!==null;){switch(e=mn,s=e.alternate,t=e.flags,e.tag){case 0:if(t&4&&(t=e.updateQueue,t=t!==null?t.events:null,t!==null))for(n=0;n<t.length;n++)a=t[n],a.ref.impl=a.nextImpl;break;case 11:case 15:break;case 1:if(t&1024&&s!==null){t=void 0,n=e,a=s.memoizedProps,s=s.memoizedState,i=n.stateNode;try{var x=Qs(n.type,a);t=i.getSnapshotBeforeUpdate(x,s),i.__reactInternalSnapshotBeforeUpdate=t}catch(_){Rt(n,n.return,_)}}break;case 3:if(t&1024){if(t=e.stateNode.containerInfo,n=t.nodeType,n===9)ep(t);else if(n===1)switch(t.nodeName){case"HEAD":case"HTML":case"BODY":ep(t);break;default:t.textContent=""}}break;case 5:case 26:case 27:case 6:case 4:case 17:break;default:if(t&1024)throw Error(ue(163))}if(t=e.sibling,t!==null){t.return=e.return,mn=t;break}mn=e.return}}function qy(t,e,n){var i=n.flags;switch(n.tag){case 0:case 11:case 15:fa(t,n),i&4&&ec(5,n);break;case 1:if(fa(t,n),i&4)if(t=n.stateNode,e===null)try{t.componentDidMount()}catch(r){Rt(n,n.return,r)}else{var a=Qs(n.type,e.memoizedProps);e=e.memoizedState;try{t.componentDidUpdate(a,e,t.__reactInternalSnapshotBeforeUpdate)}catch(r){Rt(n,n.return,r)}}i&64&&Hy(n),i&512&&_o(n,n.return);break;case 3:if(fa(t,n),i&64&&(t=n.updateQueue,t!==null)){if(e=null,n.child!==null)switch(n.child.tag){case 27:case 5:e=n.child.stateNode;break;case 1:e=n.child.stateNode}try{$_(t,e)}catch(r){Rt(n,n.return,r)}}break;case 27:e===null&&i&4&&Xy(n);case 26:case 5:fa(t,n),e===null&&i&4&&Vy(n),i&512&&_o(n,n.return);break;case 12:fa(t,n);break;case 31:fa(t,n),i&4&&Zy(t,n);break;case 13:fa(t,n),i&4&&$y(t,n),i&64&&(t=n.memoizedState,t!==null&&(t=t.dehydrated,t!==null&&(n=t1.bind(null,n),b1(t,n))));break;case 22:if(i=n.memoizedState!==null||ya,!i){e=e!==null&&e.memoizedState!==null||rn,a=ya;var s=rn;ya=i,(rn=e)&&!s?xa(t,n,(n.subtreeFlags&8772)!==0):fa(t,n),ya=a,rn=s}break;case 30:break;default:fa(t,n)}}function Yy(t){var e=t.alternate;e!==null&&(t.alternate=null,Yy(e)),t.child=null,t.deletions=null,t.sibling=null,t.tag===5&&(e=t.stateNode,e!==null&&gm(e)),t.stateNode=null,t.return=null,t.dependencies=null,t.memoizedProps=null,t.memoizedState=null,t.pendingProps=null,t.stateNode=null,t.updateQueue=null}var Xt=null,Gn=!1;function ua(t,e,n){for(n=n.child;n!==null;)Ky(t,e,n),n=n.sibling}function Ky(t,e,n){if(oi&&typeof oi.onCommitFiberUnmount=="function")try{oi.onCommitFiberUnmount(qo,n)}catch{}switch(n.tag){case 26:rn||ea(n,e),ua(t,e,n),n.memoizedState?n.memoizedState.count--:n.stateNode&&(n=n.stateNode,n.parentNode.removeChild(n));break;case 27:rn||ea(n,e);var i=Xt,a=Gn;ws(n.type)&&(Xt=n.stateNode,Gn=!1),ua(t,e,n),Mo(n.stateNode),Xt=i,Gn=a;break;case 5:rn||ea(n,e);case 6:if(i=Xt,a=Gn,Xt=null,ua(t,e,n),Xt=i,Gn=a,Xt!==null)if(Gn)try{(Xt.nodeType===9?Xt.body:Xt.nodeName==="HTML"?Xt.ownerDocument.body:Xt).removeChild(n.stateNode)}catch(s){Rt(n,e,s)}else try{Xt.removeChild(n.stateNode)}catch(s){Rt(n,e,s)}break;case 18:Xt!==null&&(Gn?(t=Xt,Gx(t.nodeType===9?t.body:t.nodeName==="HTML"?t.ownerDocument.body:t,n.stateNode),gl(t)):Gx(Xt,n.stateNode));break;case 4:i=Xt,a=Gn,Xt=n.stateNode.containerInfo,Gn=!0,ua(t,e,n),Xt=i,Gn=a;break;case 0:case 11:case 14:case 15:Ms(2,n,e),rn||Ms(4,n,e),ua(t,e,n);break;case 1:rn||(ea(n,e),i=n.stateNode,typeof i.componentWillUnmount=="function"&&Gy(n,e,i)),ua(t,e,n);break;case 21:ua(t,e,n);break;case 22:rn=(i=rn)||n.memoizedState!==null,ua(t,e,n),rn=i;break;default:ua(t,e,n)}}function Zy(t,e){if(e.memoizedState===null&&(t=e.alternate,t!==null&&(t=t.memoizedState,t!==null))){t=t.dehydrated;try{gl(t)}catch(n){Rt(e,e.return,n)}}}function $y(t,e){if(e.memoizedState===null&&(t=e.alternate,t!==null&&(t=t.memoizedState,t!==null&&(t=t.dehydrated,t!==null))))try{gl(t)}catch(n){Rt(e,e.return,n)}}function qE(t){switch(t.tag){case 31:case 13:case 19:var e=t.stateNode;return e===null&&(e=t.stateNode=new Rx),e;case 22:return t=t.stateNode,e=t._retryCache,e===null&&(e=t._retryCache=new Rx),e;default:throw Error(ue(435,t.tag))}}function xc(t,e){var n=qE(t);e.forEach(function(i){if(!n.has(i)){n.add(i);var a=n1.bind(null,t,i);i.then(a,a)}})}function Fn(t,e){var n=e.deletions;if(n!==null)for(var i=0;i<n.length;i++){var a=n[i],s=t,r=e,o=r;e:for(;o!==null;){switch(o.tag){case 27:if(ws(o.type)){Xt=o.stateNode,Gn=!1;break e}break;case 5:Xt=o.stateNode,Gn=!1;break e;case 3:case 4:Xt=o.stateNode.containerInfo,Gn=!0;break e}o=o.return}if(Xt===null)throw Error(ue(160));Ky(s,r,a),Xt=null,Gn=!1,s=a.alternate,s!==null&&(s.return=null),a.return=null}if(e.subtreeFlags&13886)for(e=e.child;e!==null;)Qy(e,t),e=e.sibling}var Pi=null;function Qy(t,e){var n=t.alternate,i=t.flags;switch(t.tag){case 0:case 11:case 14:case 15:Fn(e,t),jn(t),i&4&&(Ms(3,t,t.return),ec(3,t),Ms(5,t,t.return));break;case 1:Fn(e,t),jn(t),i&512&&(rn||n===null||ea(n,n.return)),i&64&&ya&&(t=t.updateQueue,t!==null&&(i=t.callbacks,i!==null&&(n=t.shared.hiddenCallbacks,t.shared.hiddenCallbacks=n===null?i:n.concat(i))));break;case 26:var a=Pi;if(Fn(e,t),jn(t),i&512&&(rn||n===null||ea(n,n.return)),i&4){var s=n!==null?n.memoizedState:null;if(i=t.memoizedState,n===null)if(i===null)if(t.stateNode===null){e:{i=t.type,n=t.memoizedProps,a=a.ownerDocument||a;t:switch(i){case"title":s=a.getElementsByTagName("title")[0],(!s||s[Zo]||s[bn]||s.namespaceURI==="http://www.w3.org/2000/svg"||s.hasAttribute("itemprop"))&&(s=a.createElement(i),a.head.insertBefore(s,a.querySelector("head > title"))),Tn(s,i,n),s[bn]=t,gn(s),i=s;break e;case"link":var r=Zx("link","href",a).get(i+(n.href||""));if(r){for(var o=0;o<r.length;o++)if(s=r[o],s.getAttribute("href")===(n.href==null||n.href===""?null:n.href)&&s.getAttribute("rel")===(n.rel==null?null:n.rel)&&s.getAttribute("title")===(n.title==null?null:n.title)&&s.getAttribute("crossorigin")===(n.crossOrigin==null?null:n.crossOrigin)){r.splice(o,1);break t}}s=a.createElement(i),Tn(s,i,n),a.head.appendChild(s);break;case"meta":if(r=Zx("meta","content",a).get(i+(n.content||""))){for(o=0;o<r.length;o++)if(s=r[o],s.getAttribute("content")===(n.content==null?null:""+n.content)&&s.getAttribute("name")===(n.name==null?null:n.name)&&s.getAttribute("property")===(n.property==null?null:n.property)&&s.getAttribute("http-equiv")===(n.httpEquiv==null?null:n.httpEquiv)&&s.getAttribute("charset")===(n.charSet==null?null:n.charSet)){r.splice(o,1);break t}}s=a.createElement(i),Tn(s,i,n),a.head.appendChild(s);break;default:throw Error(ue(468,i))}s[bn]=t,gn(s),i=s}t.stateNode=i}else $x(a,t.type,t.stateNode);else t.stateNode=Kx(a,i,t.memoizedProps);else s!==i?(s===null?n.stateNode!==null&&(n=n.stateNode,n.parentNode.removeChild(n)):s.count--,i===null?$x(a,t.type,t.stateNode):Kx(a,i,t.memoizedProps)):i===null&&t.stateNode!==null&&hd(t,t.memoizedProps,n.memoizedProps)}break;case 27:Fn(e,t),jn(t),i&512&&(rn||n===null||ea(n,n.return)),n!==null&&i&4&&hd(t,t.memoizedProps,n.memoizedProps);break;case 5:if(Fn(e,t),jn(t),i&512&&(rn||n===null||ea(n,n.return)),t.flags&32){a=t.stateNode;try{ol(a,"")}catch(x){Rt(t,t.return,x)}}i&4&&t.stateNode!=null&&(a=t.memoizedProps,hd(t,a,n!==null?n.memoizedProps:a)),i&1024&&(md=!0);break;case 6:if(Fn(e,t),jn(t),i&4){if(t.stateNode===null)throw Error(ue(162));i=t.memoizedProps,n=t.stateNode;try{n.nodeValue=i}catch(x){Rt(t,t.return,x)}}break;case 3:if(gu=null,a=Pi,Pi=Ju(e.containerInfo),Fn(e,t),Pi=a,jn(t),i&4&&n!==null&&n.memoizedState.isDehydrated)try{gl(e.containerInfo)}catch(x){Rt(t,t.return,x)}md&&(md=!1,Jy(t));break;case 4:i=Pi,Pi=Ju(t.stateNode.containerInfo),Fn(e,t),jn(t),Pi=i;break;case 12:Fn(e,t),jn(t);break;case 31:Fn(e,t),jn(t),i&4&&(i=t.updateQueue,i!==null&&(t.updateQueue=null,xc(t,i)));break;case 13:Fn(e,t),jn(t),t.child.flags&8192&&t.memoizedState!==null!=(n!==null&&n.memoizedState!==null)&&(Df=li()),i&4&&(i=t.updateQueue,i!==null&&(t.updateQueue=null,xc(t,i)));break;case 22:a=t.memoizedState!==null;var c=n!==null&&n.memoizedState!==null,u=ya,d=rn;if(ya=u||a,rn=d||c,Fn(e,t),rn=d,ya=u,jn(t),i&8192)e:for(e=t.stateNode,e._visibility=a?e._visibility&-2:e._visibility|1,a&&(n===null||c||ya||rn||Is(t)),n=null,e=t;;){if(e.tag===5||e.tag===26){if(n===null){c=n=e;try{if(s=c.stateNode,a)r=s.style,typeof r.setProperty=="function"?r.setProperty("display","none","important"):r.display="none";else{o=c.stateNode;var m=c.memoizedProps.style,f=m!=null&&m.hasOwnProperty("display")?m.display:null;o.style.display=f==null||typeof f=="boolean"?"":(""+f).trim()}}catch(x){Rt(c,c.return,x)}}}else if(e.tag===6){if(n===null){c=e;try{c.stateNode.nodeValue=a?"":c.memoizedProps}catch(x){Rt(c,c.return,x)}}}else if(e.tag===18){if(n===null){c=e;try{var p=c.stateNode;a?Vx(p,!0):Vx(c.stateNode,!1)}catch(x){Rt(c,c.return,x)}}}else if((e.tag!==22&&e.tag!==23||e.memoizedState===null||e===t)&&e.child!==null){e.child.return=e,e=e.child;continue}if(e===t)break e;for(;e.sibling===null;){if(e.return===null||e.return===t)break e;n===e&&(n=null),e=e.return}n===e&&(n=null),e.sibling.return=e.return,e=e.sibling}i&4&&(i=t.updateQueue,i!==null&&(n=i.retryQueue,n!==null&&(i.retryQueue=null,xc(t,n))));break;case 19:Fn(e,t),jn(t),i&4&&(i=t.updateQueue,i!==null&&(t.updateQueue=null,xc(t,i)));break;case 30:break;case 21:break;default:Fn(e,t),jn(t)}}function jn(t){var e=t.flags;if(e&2){try{for(var n,i=t.return;i!==null;){if(Wy(i)){n=i;break}i=i.return}if(n==null)throw Error(ue(160));switch(n.tag){case 27:var a=n.stateNode,s=pd(t);Wu(t,s,a);break;case 5:var r=n.stateNode;n.flags&32&&(ol(r,""),n.flags&=-33);var o=pd(t);Wu(t,o,r);break;case 3:case 4:var c=n.stateNode.containerInfo,u=pd(t);Vh(t,u,c);break;default:throw Error(ue(161))}}catch(d){Rt(t,t.return,d)}t.flags&=-3}e&4096&&(t.flags&=-4097)}function Jy(t){if(t.subtreeFlags&1024)for(t=t.child;t!==null;){var e=t;Jy(e),e.tag===5&&e.flags&1024&&e.stateNode.reset(),t=t.sibling}}function fa(t,e){if(e.subtreeFlags&8772)for(e=e.child;e!==null;)qy(t,e.alternate,e),e=e.sibling}function Is(t){for(t=t.child;t!==null;){var e=t;switch(e.tag){case 0:case 11:case 14:case 15:Ms(4,e,e.return),Is(e);break;case 1:ea(e,e.return);var n=e.stateNode;typeof n.componentWillUnmount=="function"&&Gy(e,e.return,n),Is(e);break;case 27:Mo(e.stateNode);case 26:case 5:ea(e,e.return),Is(e);break;case 22:e.memoizedState===null&&Is(e);break;case 30:Is(e);break;default:Is(e)}t=t.sibling}}function xa(t,e,n){for(n=n&&(e.subtreeFlags&8772)!==0,e=e.child;e!==null;){var i=e.alternate,a=t,s=e,r=s.flags;switch(s.tag){case 0:case 11:case 15:xa(a,s,n),ec(4,s);break;case 1:if(xa(a,s,n),i=s,a=i.stateNode,typeof a.componentDidMount=="function")try{a.componentDidMount()}catch(u){Rt(i,i.return,u)}if(i=s,a=i.updateQueue,a!==null){var o=i.stateNode;try{var c=a.shared.hiddenCallbacks;if(c!==null)for(a.shared.hiddenCallbacks=null,a=0;a<c.length;a++)Z_(c[a],o)}catch(u){Rt(i,i.return,u)}}n&&r&64&&Hy(s),_o(s,s.return);break;case 27:Xy(s);case 26:case 5:xa(a,s,n),n&&i===null&&r&4&&Vy(s),_o(s,s.return);break;case 12:xa(a,s,n);break;case 31:xa(a,s,n),n&&r&4&&Zy(a,s);break;case 13:xa(a,s,n),n&&r&4&&$y(a,s);break;case 22:s.memoizedState===null&&xa(a,s,n),_o(s,s.return);break;case 30:break;default:xa(a,s,n)}e=e.sibling}}function Ym(t,e){var n=null;t!==null&&t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(n=t.memoizedState.cachePool.pool),t=null,e.memoizedState!==null&&e.memoizedState.cachePool!==null&&(t=e.memoizedState.cachePool.pool),t!==n&&(t!=null&&t.refCount++,n!=null&&Qo(n))}function Km(t,e){t=null,e.alternate!==null&&(t=e.alternate.memoizedState.cache),e=e.memoizedState.cache,e!==t&&(e.refCount++,t!=null&&Qo(t))}function Di(t,e,n,i){if(e.subtreeFlags&10256)for(e=e.child;e!==null;)eb(t,e,n,i),e=e.sibling}function eb(t,e,n,i){var a=e.flags;switch(e.tag){case 0:case 11:case 15:Di(t,e,n,i),a&2048&&ec(9,e);break;case 1:Di(t,e,n,i);break;case 3:Di(t,e,n,i),a&2048&&(t=null,e.alternate!==null&&(t=e.alternate.memoizedState.cache),e=e.memoizedState.cache,e!==t&&(e.refCount++,t!=null&&Qo(t)));break;case 12:if(a&2048){Di(t,e,n,i),t=e.stateNode;try{var s=e.memoizedProps,r=s.id,o=s.onPostCommit;typeof o=="function"&&o(r,e.alternate===null?"mount":"update",t.passiveEffectDuration,-0)}catch(c){Rt(e,e.return,c)}}else Di(t,e,n,i);break;case 31:Di(t,e,n,i);break;case 13:Di(t,e,n,i);break;case 23:break;case 22:s=e.stateNode,r=e.alternate,e.memoizedState!==null?s._visibility&2?Di(t,e,n,i):yo(t,e):s._visibility&2?Di(t,e,n,i):(s._visibility|=2,Ur(t,e,n,i,(e.subtreeFlags&10256)!==0||!1)),a&2048&&Ym(r,e);break;case 24:Di(t,e,n,i),a&2048&&Km(e.alternate,e);break;default:Di(t,e,n,i)}}function Ur(t,e,n,i,a){for(a=a&&((e.subtreeFlags&10256)!==0||!1),e=e.child;e!==null;){var s=t,r=e,o=n,c=i,u=r.flags;switch(r.tag){case 0:case 11:case 15:Ur(s,r,o,c,a),ec(8,r);break;case 23:break;case 22:var d=r.stateNode;r.memoizedState!==null?d._visibility&2?Ur(s,r,o,c,a):yo(s,r):(d._visibility|=2,Ur(s,r,o,c,a)),a&&u&2048&&Ym(r.alternate,r);break;case 24:Ur(s,r,o,c,a),a&&u&2048&&Km(r.alternate,r);break;default:Ur(s,r,o,c,a)}e=e.sibling}}function yo(t,e){if(e.subtreeFlags&10256)for(e=e.child;e!==null;){var n=t,i=e,a=i.flags;switch(i.tag){case 22:yo(n,i),a&2048&&Ym(i.alternate,i);break;case 24:yo(n,i),a&2048&&Km(i.alternate,i);break;default:yo(n,i)}e=e.sibling}}var io=8192;function mr(t,e,n){if(t.subtreeFlags&io)for(t=t.child;t!==null;)tb(t,e,n),t=t.sibling}function tb(t,e,n){switch(t.tag){case 26:mr(t,e,n),t.flags&io&&t.memoizedState!==null&&U1(n,Pi,t.memoizedState,t.memoizedProps);break;case 5:mr(t,e,n);break;case 3:case 4:var i=Pi;Pi=Ju(t.stateNode.containerInfo),mr(t,e,n),Pi=i;break;case 22:t.memoizedState===null&&(i=t.alternate,i!==null&&i.memoizedState!==null?(i=io,io=16777216,mr(t,e,n),io=i):mr(t,e,n));break;default:mr(t,e,n)}}function nb(t){var e=t.alternate;if(e!==null&&(t=e.child,t!==null)){e.child=null;do e=t.sibling,t.sibling=null,t=e;while(t!==null)}}function Bl(t){var e=t.deletions;if(t.flags&16){if(e!==null)for(var n=0;n<e.length;n++){var i=e[n];mn=i,ab(i,t)}nb(t)}if(t.subtreeFlags&10256)for(t=t.child;t!==null;)ib(t),t=t.sibling}function ib(t){switch(t.tag){case 0:case 11:case 15:Bl(t),t.flags&2048&&Ms(9,t,t.return);break;case 3:Bl(t);break;case 12:Bl(t);break;case 22:var e=t.stateNode;t.memoizedState!==null&&e._visibility&2&&(t.return===null||t.return.tag!==13)?(e._visibility&=-3,pu(t)):Bl(t);break;default:Bl(t)}}function pu(t){var e=t.deletions;if(t.flags&16){if(e!==null)for(var n=0;n<e.length;n++){var i=e[n];mn=i,ab(i,t)}nb(t)}for(t=t.child;t!==null;){switch(e=t,e.tag){case 0:case 11:case 15:Ms(8,e,e.return),pu(e);break;case 22:n=e.stateNode,n._visibility&2&&(n._visibility&=-3,pu(e));break;default:pu(e)}t=t.sibling}}function ab(t,e){for(;mn!==null;){var n=mn;switch(n.tag){case 0:case 11:case 15:Ms(8,n,e);break;case 23:case 22:if(n.memoizedState!==null&&n.memoizedState.cachePool!==null){var i=n.memoizedState.cachePool.pool;i!=null&&i.refCount++}break;case 24:Qo(n.memoizedState.cache)}if(i=n.child,i!==null)i.return=n,mn=i;else e:for(n=t;mn!==null;){i=mn;var a=i.sibling,s=i.return;if(Yy(i),i===n){mn=null;break e}if(a!==null){a.return=s,mn=a;break e}mn=s}}}var YE={getCacheForType:function(t){var e=Mn(ln),n=e.data.get(t);return n===void 0&&(n=t(),e.data.set(t,n)),n},cacheSignal:function(){return Mn(ln).controller.signal}},KE=typeof WeakMap=="function"?WeakMap:Map,Et=0,It=null,ut=null,dt=0,Nt=0,ii=null,rs=!1,Cl=!1,Zm=!1,Pa=0,$t=0,Es=0,qs=0,$m=0,ri=0,dl=0,bo=null,Vn=null,Wh=!1,Df=0,sb=0,Xu=1/0,qu=null,gs=null,un=0,xs=null,hl=null,Ca=0,Xh=0,qh=null,rb=null,So=0,Yh=null;function ui(){return Et&2&&dt!==0?dt&-dt:tt.T!==null?Jm():m_()}function lb(){if(ri===0)if(!(dt&536870912)||mt){var t=cc;cc<<=1,!(cc&3932160)&&(cc=262144),ri=t}else ri=536870912;return t=di.current,t!==null&&(t.flags|=32),ri}function qn(t,e,n){(t===It&&(Nt===2||Nt===9)||t.cancelPendingCommit!==null)&&(pl(t,0),ls(t,dt,ri,!1)),Ko(t,n),(!(Et&2)||t!==It)&&(t===It&&(!(Et&2)&&(qs|=n),$t===4&&ls(t,dt,ri,!1)),la(t))}function ob(t,e,n){if(Et&6)throw Error(ue(327));var i=!n&&(e&127)===0&&(e&t.expiredLanes)===0||Yo(t,e),a=i?QE(t,e):gd(t,e,!0),s=i;do{if(a===0){Cl&&!i&&ls(t,e,0,!1);break}else{if(n=t.current.alternate,s&&!ZE(n)){a=gd(t,e,!1),s=!1;continue}if(a===2){if(s=e,t.errorRecoveryDisabledLanes&s)var r=0;else r=t.pendingLanes&-536870913,r=r!==0?r:r&536870912?536870912:0;if(r!==0){e=r;e:{var o=t;a=bo;var c=o.current.memoizedState.isDehydrated;if(c&&(pl(o,r).flags|=256),r=gd(o,r,!1),r!==2){if(Zm&&!c){o.errorRecoveryDisabledLanes|=s,qs|=s,a=4;break e}s=Vn,Vn=a,s!==null&&(Vn===null?Vn=s:Vn.push.apply(Vn,s))}a=r}if(s=!1,a!==2)continue}}if(a===1){pl(t,0),ls(t,e,0,!0);break}e:{switch(i=t,s=a,s){case 0:case 1:throw Error(ue(345));case 4:if((e&4194048)!==e)break;case 6:ls(i,e,ri,!rs);break e;case 2:Vn=null;break;case 3:case 5:break;default:throw Error(ue(329))}if((e&62914560)===e&&(a=Df+300-li(),10<a)){if(ls(i,e,ri,!rs),yf(i,0,!0)!==0)break e;Ca=e,i.timeoutHandle=Cb(Dx.bind(null,i,n,Vn,qu,Wh,e,ri,qs,dl,rs,s,"Throttled",-0,0),a);break e}Dx(i,n,Vn,qu,Wh,e,ri,qs,dl,rs,s,null,-0,0)}}break}while(!0);la(t)}function Dx(t,e,n,i,a,s,r,o,c,u,d,m,f,p){if(t.timeoutHandle=-1,m=e.subtreeFlags,m&8192||(m&16785408)===16785408){m={stylesheets:null,count:0,imgCount:0,imgBytes:0,suspenseyImages:[],waitingForImages:!0,waitingForViewTransition:!1,unsuspend:Ma},tb(e,s,m);var x=(s&62914560)===s?Df-li():(s&4194048)===s?sb-li():0;if(x=O1(m,x),x!==null){Ca=s,t.cancelPendingCommit=x(Ux.bind(null,t,e,s,n,i,a,r,o,c,d,m,null,f,p)),ls(t,s,r,!u);return}}Ux(t,e,s,n,i,a,r,o,c)}function ZE(t){for(var e=t;;){var n=e.tag;if((n===0||n===11||n===15)&&e.flags&16384&&(n=e.updateQueue,n!==null&&(n=n.stores,n!==null)))for(var i=0;i<n.length;i++){var a=n[i],s=a.getSnapshot;a=a.value;try{if(!fi(s(),a))return!1}catch{return!1}}if(n=e.child,e.subtreeFlags&16384&&n!==null)n.return=e,e=n;else{if(e===t)break;for(;e.sibling===null;){if(e.return===null||e.return===t)return!0;e=e.return}e.sibling.return=e.return,e=e.sibling}}return!0}function ls(t,e,n,i){e&=~$m,e&=~qs,t.suspendedLanes|=e,t.pingedLanes&=~e,i&&(t.warmLanes|=e),i=t.expirationTimes;for(var a=e;0<a;){var s=31-ci(a),r=1<<s;i[s]=-1,a&=~r}n!==0&&d_(t,n,e)}function Lf(){return Et&6?!0:(tc(0),!1)}function Qm(){if(ut!==null){if(Nt===0)var t=ut.return;else t=ut,Ea=or=null,Im(t),Jr=null,Uo=0,t=ut;for(;t!==null;)jy(t.alternate,t),t=t.return;ut=null}}function pl(t,e){var n=t.timeoutHandle;n!==-1&&(t.timeoutHandle=-1,g1(n)),n=t.cancelPendingCommit,n!==null&&(t.cancelPendingCommit=null,n()),Ca=0,Qm(),It=t,ut=n=Aa(t.current,null),dt=e,Nt=0,ii=null,rs=!1,Cl=Yo(t,e),Zm=!1,dl=ri=$m=qs=Es=$t=0,Vn=bo=null,Wh=!1,e&8&&(e|=e&32);var i=t.entangledLanes;if(i!==0)for(t=t.entanglements,i&=e;0<i;){var a=31-ci(i),s=1<<a;e|=t[a],i&=~s}return Pa=e,Ef(),n}function cb(t,e){rt=null,tt.H=Po,e===wl||e===Af?(e=cx(),Nt=3):e===Rm?(e=cx(),Nt=4):Nt=e===Xm?8:e!==null&&typeof e=="object"&&typeof e.then=="function"?6:1,ii=e,ut===null&&($t=1,Gu(t,Si(e,t.current)))}function ub(){var t=di.current;return t===null?!0:(dt&4194048)===dt?Ai===null:(dt&62914560)===dt||dt&536870912?t===Ai:!1}function fb(){var t=tt.H;return tt.H=Po,t===null?Po:t}function db(){var t=tt.A;return tt.A=YE,t}function Yu(){$t=4,rs||(dt&4194048)!==dt&&di.current!==null||(Cl=!0),!(Es&134217727)&&!(qs&134217727)||It===null||ls(It,dt,ri,!1)}function gd(t,e,n){var i=Et;Et|=2;var a=fb(),s=db();(It!==t||dt!==e)&&(qu=null,pl(t,e)),e=!1;var r=$t;e:do try{if(Nt!==0&&ut!==null){var o=ut,c=ii;switch(Nt){case 8:Qm(),r=6;break e;case 3:case 2:case 9:case 6:di.current===null&&(e=!0);var u=Nt;if(Nt=0,ii=null,Xr(t,o,c,u),n&&Cl){r=0;break e}break;default:u=Nt,Nt=0,ii=null,Xr(t,o,c,u)}}$E(),r=$t;break}catch(d){cb(t,d)}while(!0);return e&&t.shellSuspendCounter++,Ea=or=null,Et=i,tt.H=a,tt.A=s,ut===null&&(It=null,dt=0,Ef()),r}function $E(){for(;ut!==null;)hb(ut)}function QE(t,e){var n=Et;Et|=2;var i=fb(),a=db();It!==t||dt!==e?(qu=null,Xu=li()+500,pl(t,e)):Cl=Yo(t,e);e:do try{if(Nt!==0&&ut!==null){e=ut;var s=ii;t:switch(Nt){case 1:Nt=0,ii=null,Xr(t,e,s,1);break;case 2:case 9:if(ox(s)){Nt=0,ii=null,Lx(e);break}e=function(){Nt!==2&&Nt!==9||It!==t||(Nt=7),la(t)},s.then(e,e);break e;case 3:Nt=7;break e;case 4:Nt=5;break e;case 7:ox(s)?(Nt=0,ii=null,Lx(e)):(Nt=0,ii=null,Xr(t,e,s,7));break;case 5:var r=null;switch(ut.tag){case 26:r=ut.memoizedState;case 5:case 27:var o=ut;if(r?Ub(r):o.stateNode.complete){Nt=0,ii=null;var c=o.sibling;if(c!==null)ut=c;else{var u=o.return;u!==null?(ut=u,Uf(u)):ut=null}break t}}Nt=0,ii=null,Xr(t,e,s,5);break;case 6:Nt=0,ii=null,Xr(t,e,s,6);break;case 8:Qm(),$t=6;break e;default:throw Error(ue(462))}}JE();break}catch(d){cb(t,d)}while(!0);return Ea=or=null,tt.H=i,tt.A=a,Et=n,ut!==null?0:(It=null,dt=0,Ef(),$t)}function JE(){for(;ut!==null&&!SM();)hb(ut)}function hb(t){var e=Fy(t.alternate,t,Pa);t.memoizedProps=t.pendingProps,e===null?Uf(t):ut=e}function Lx(t){var e=t,n=e.alternate;switch(e.tag){case 15:case 0:e=Tx(n,e,e.pendingProps,e.type,void 0,dt);break;case 11:e=Tx(n,e,e.pendingProps,e.type.render,e.ref,dt);break;case 5:Im(e);default:jy(n,e),e=ut=j_(e,Pa),e=Fy(n,e,Pa)}t.memoizedProps=t.pendingProps,e===null?Uf(t):ut=e}function Xr(t,e,n,i){Ea=or=null,Im(e),Jr=null,Uo=0;var a=e.return;try{if(jE(t,a,e,n,dt)){$t=1,Gu(t,Si(n,t.current)),ut=null;return}}catch(s){if(a!==null)throw ut=a,s;$t=1,Gu(t,Si(n,t.current)),ut=null;return}e.flags&32768?(mt||i===1?t=!0:Cl||dt&536870912?t=!1:(rs=t=!0,(i===2||i===9||i===3||i===6)&&(i=di.current,i!==null&&i.tag===13&&(i.flags|=16384))),pb(e,t)):Uf(e)}function Uf(t){var e=t;do{if(e.flags&32768){pb(e,rs);return}t=e.return;var n=VE(e.alternate,e,Pa);if(n!==null){ut=n;return}if(e=e.sibling,e!==null){ut=e;return}ut=e=t}while(e!==null);$t===0&&($t=5)}function pb(t,e){do{var n=WE(t.alternate,t);if(n!==null){n.flags&=32767,ut=n;return}if(n=t.return,n!==null&&(n.flags|=32768,n.subtreeFlags=0,n.deletions=null),!e&&(t=t.sibling,t!==null)){ut=t;return}ut=t=n}while(t!==null);$t=6,ut=null}function Ux(t,e,n,i,a,s,r,o,c){t.cancelPendingCommit=null;do Of();while(un!==0);if(Et&6)throw Error(ue(327));if(e!==null){if(e===t.current)throw Error(ue(177));if(s=e.lanes|e.childLanes,s|=Mm,LM(t,n,s,r,o,c),t===It&&(ut=It=null,dt=0),hl=e,xs=t,Ca=n,Xh=s,qh=a,rb=i,e.subtreeFlags&10256||e.flags&10256?(t.callbackNode=null,t.callbackPriority=0,i1(Lu,function(){return _b(),null})):(t.callbackNode=null,t.callbackPriority=0),i=(e.flags&13878)!==0,e.subtreeFlags&13878||i){i=tt.T,tt.T=null,a=Tt.p,Tt.p=2,r=Et,Et|=4;try{XE(t,e,n)}finally{Et=r,Tt.p=a,tt.T=i}}un=1,mb(),gb(),xb()}}function mb(){if(un===1){un=0;var t=xs,e=hl,n=(e.flags&13878)!==0;if(e.subtreeFlags&13878||n){n=tt.T,tt.T=null;var i=Tt.p;Tt.p=2;var a=Et;Et|=4;try{Qy(e,t);var s=Qh,r=U_(t.containerInfo),o=s.focusedElem,c=s.selectionRange;if(r!==o&&o&&o.ownerDocument&&L_(o.ownerDocument.documentElement,o)){if(c!==null&&Sm(o)){var u=c.start,d=c.end;if(d===void 0&&(d=u),"selectionStart"in o)o.selectionStart=u,o.selectionEnd=Math.min(d,o.value.length);else{var m=o.ownerDocument||document,f=m&&m.defaultView||window;if(f.getSelection){var p=f.getSelection(),x=o.textContent.length,_=Math.min(c.start,x),g=c.end===void 0?_:Math.min(c.end,x);!p.extend&&_>g&&(r=g,g=_,_=r);var h=tx(o,_),v=tx(o,g);if(h&&v&&(p.rangeCount!==1||p.anchorNode!==h.node||p.anchorOffset!==h.offset||p.focusNode!==v.node||p.focusOffset!==v.offset)){var b=m.createRange();b.setStart(h.node,h.offset),p.removeAllRanges(),_>g?(p.addRange(b),p.extend(v.node,v.offset)):(b.setEnd(v.node,v.offset),p.addRange(b))}}}}for(m=[],p=o;p=p.parentNode;)p.nodeType===1&&m.push({element:p,left:p.scrollLeft,top:p.scrollTop});for(typeof o.focus=="function"&&o.focus(),o=0;o<m.length;o++){var y=m[o];y.element.scrollLeft=y.left,y.element.scrollTop=y.top}}nf=!!$h,Qh=$h=null}finally{Et=a,Tt.p=i,tt.T=n}}t.current=e,un=2}}function gb(){if(un===2){un=0;var t=xs,e=hl,n=(e.flags&8772)!==0;if(e.subtreeFlags&8772||n){n=tt.T,tt.T=null;var i=Tt.p;Tt.p=2;var a=Et;Et|=4;try{qy(t,e.alternate,e)}finally{Et=a,Tt.p=i,tt.T=n}}un=3}}function xb(){if(un===4||un===3){un=0,MM();var t=xs,e=hl,n=Ca,i=rb;e.subtreeFlags&10256||e.flags&10256?un=5:(un=0,hl=xs=null,vb(t,t.pendingLanes));var a=t.pendingLanes;if(a===0&&(gs=null),mm(n),e=e.stateNode,oi&&typeof oi.onCommitFiberRoot=="function")try{oi.onCommitFiberRoot(qo,e,void 0,(e.current.flags&128)===128)}catch{}if(i!==null){e=tt.T,a=Tt.p,Tt.p=2,tt.T=null;try{for(var s=t.onRecoverableError,r=0;r<i.length;r++){var o=i[r];s(o.value,{componentStack:o.stack})}}finally{tt.T=e,Tt.p=a}}Ca&3&&Of(),la(t),a=t.pendingLanes,n&261930&&a&42?t===Yh?So++:(So=0,Yh=t):So=0,tc(0)}}function vb(t,e){(t.pooledCacheLanes&=e)===0&&(e=t.pooledCache,e!=null&&(t.pooledCache=null,Qo(e)))}function Of(){return mb(),gb(),xb(),_b()}function _b(){if(un!==5)return!1;var t=xs,e=Xh;Xh=0;var n=mm(Ca),i=tt.T,a=Tt.p;try{Tt.p=32>n?32:n,tt.T=null,n=qh,qh=null;var s=xs,r=Ca;if(un=0,hl=xs=null,Ca=0,Et&6)throw Error(ue(331));var o=Et;if(Et|=4,ib(s.current),eb(s,s.current,r,n),Et=o,tc(0,!1),oi&&typeof oi.onPostCommitFiberRoot=="function")try{oi.onPostCommitFiberRoot(qo,s)}catch{}return!0}finally{Tt.p=a,tt.T=i,vb(t,e)}}function Ox(t,e,n){e=Si(n,e),e=jh(t.stateNode,e,2),t=ms(t,e,2),t!==null&&(Ko(t,2),la(t))}function Rt(t,e,n){if(t.tag===3)Ox(t,t,n);else for(;e!==null;){if(e.tag===3){Ox(e,t,n);break}else if(e.tag===1){var i=e.stateNode;if(typeof e.type.getDerivedStateFromError=="function"||typeof i.componentDidCatch=="function"&&(gs===null||!gs.has(i))){t=Si(n,t),n=Oy(2),i=ms(e,n,2),i!==null&&(Py(n,i,e,t),Ko(i,2),la(i));break}}e=e.return}}function xd(t,e,n){var i=t.pingCache;if(i===null){i=t.pingCache=new KE;var a=new Set;i.set(e,a)}else a=i.get(e),a===void 0&&(a=new Set,i.set(e,a));a.has(n)||(Zm=!0,a.add(n),t=e1.bind(null,t,e,n),e.then(t,t))}function e1(t,e,n){var i=t.pingCache;i!==null&&i.delete(e),t.pingedLanes|=t.suspendedLanes&n,t.warmLanes&=~n,It===t&&(dt&n)===n&&($t===4||$t===3&&(dt&62914560)===dt&&300>li()-Df?!(Et&2)&&pl(t,0):$m|=n,dl===dt&&(dl=0)),la(t)}function yb(t,e){e===0&&(e=f_()),t=lr(t,e),t!==null&&(Ko(t,e),la(t))}function t1(t){var e=t.memoizedState,n=0;e!==null&&(n=e.retryLane),yb(t,n)}function n1(t,e){var n=0;switch(t.tag){case 31:case 13:var i=t.stateNode,a=t.memoizedState;a!==null&&(n=a.retryLane);break;case 19:i=t.stateNode;break;case 22:i=t.stateNode._retryCache;break;default:throw Error(ue(314))}i!==null&&i.delete(e),yb(t,n)}function i1(t,e){return hm(t,e)}var Ku=null,Or=null,Kh=!1,Zu=!1,vd=!1,os=0;function la(t){t!==Or&&t.next===null&&(Or===null?Ku=Or=t:Or=Or.next=t),Zu=!0,Kh||(Kh=!0,s1())}function tc(t,e){if(!vd&&Zu){vd=!0;do for(var n=!1,i=Ku;i!==null;){if(t!==0){var a=i.pendingLanes;if(a===0)var s=0;else{var r=i.suspendedLanes,o=i.pingedLanes;s=(1<<31-ci(42|t)+1)-1,s&=a&~(r&~o),s=s&201326741?s&201326741|1:s?s|2:0}s!==0&&(n=!0,Px(i,s))}else s=dt,s=yf(i,i===It?s:0,i.cancelPendingCommit!==null||i.timeoutHandle!==-1),!(s&3)||Yo(i,s)||(n=!0,Px(i,s));i=i.next}while(n);vd=!1}}function a1(){bb()}function bb(){Zu=Kh=!1;var t=0;os!==0&&m1()&&(t=os);for(var e=li(),n=null,i=Ku;i!==null;){var a=i.next,s=Sb(i,e);s===0?(i.next=null,n===null?Ku=a:n.next=a,a===null&&(Or=n)):(n=i,(t!==0||s&3)&&(Zu=!0)),i=a}un!==0&&un!==5||tc(t),os!==0&&(os=0)}function Sb(t,e){for(var n=t.suspendedLanes,i=t.pingedLanes,a=t.expirationTimes,s=t.pendingLanes&-62914561;0<s;){var r=31-ci(s),o=1<<r,c=a[r];c===-1?(!(o&n)||o&i)&&(a[r]=DM(o,e)):c<=e&&(t.expiredLanes|=o),s&=~o}if(e=It,n=dt,n=yf(t,t===e?n:0,t.cancelPendingCommit!==null||t.timeoutHandle!==-1),i=t.callbackNode,n===0||t===e&&(Nt===2||Nt===9)||t.cancelPendingCommit!==null)return i!==null&&i!==null&&Xf(i),t.callbackNode=null,t.callbackPriority=0;if(!(n&3)||Yo(t,n)){if(e=n&-n,e===t.callbackPriority)return e;switch(i!==null&&Xf(i),mm(n)){case 2:case 8:n=c_;break;case 32:n=Lu;break;case 268435456:n=u_;break;default:n=Lu}return i=Mb.bind(null,t),n=hm(n,i),t.callbackPriority=e,t.callbackNode=n,e}return i!==null&&i!==null&&Xf(i),t.callbackPriority=2,t.callbackNode=null,2}function Mb(t,e){if(un!==0&&un!==5)return t.callbackNode=null,t.callbackPriority=0,null;var n=t.callbackNode;if(Of()&&t.callbackNode!==n)return null;var i=dt;return i=yf(t,t===It?i:0,t.cancelPendingCommit!==null||t.timeoutHandle!==-1),i===0?null:(ob(t,i,e),Sb(t,li()),t.callbackNode!=null&&t.callbackNode===n?Mb.bind(null,t):null)}function Px(t,e){if(Of())return null;ob(t,e,!0)}function s1(){x1(function(){Et&6?hm(o_,a1):bb()})}function Jm(){if(os===0){var t=cl;t===0&&(t=oc,oc<<=1,!(oc&261888)&&(oc=256)),os=t}return os}function zx(t){return t==null||typeof t=="symbol"||typeof t=="boolean"?null:typeof t=="function"?t:su(""+t)}function Ix(t,e){var n=e.ownerDocument.createElement("input");return n.name=e.name,n.value=e.value,t.id&&n.setAttribute("form",t.id),e.parentNode.insertBefore(n,e),t=new FormData(t),n.parentNode.removeChild(n),t}function r1(t,e,n,i,a){if(e==="submit"&&n&&n.stateNode===a){var s=zx((a[Kn]||null).action),r=i.submitter;r&&(e=(e=r[Kn]||null)?zx(e.formAction):r.getAttribute("formAction"),e!==null&&(s=e,r=null));var o=new bf("action","action",null,i,a);t.push({event:o,listeners:[{instance:null,listener:function(){if(i.defaultPrevented){if(os!==0){var c=r?Ix(a,r):new FormData(a);Bh(n,{pending:!0,data:c,method:a.method,action:s},null,c)}}else typeof s=="function"&&(o.preventDefault(),c=r?Ix(a,r):new FormData(a),Bh(n,{pending:!0,data:c,method:a.method,action:s},s,c))},currentTarget:a}]})}}for(var _d=0;_d<wh.length;_d++){var yd=wh[_d],l1=yd.toLowerCase(),o1=yd[0].toUpperCase()+yd.slice(1);Bi(l1,"on"+o1)}Bi(P_,"onAnimationEnd");Bi(z_,"onAnimationIteration");Bi(I_,"onAnimationStart");Bi("dblclick","onDoubleClick");Bi("focusin","onFocus");Bi("focusout","onBlur");Bi(EE,"onTransitionRun");Bi(TE,"onTransitionStart");Bi(AE,"onTransitionCancel");Bi(k_,"onTransitionEnd");ll("onMouseEnter",["mouseout","mouseover"]);ll("onMouseLeave",["mouseout","mouseover"]);ll("onPointerEnter",["pointerout","pointerover"]);ll("onPointerLeave",["pointerout","pointerover"]);ar("onChange","change click focusin focusout input keydown keyup selectionchange".split(" "));ar("onSelect","focusout contextmenu dragend focusin keydown keyup mousedown mouseup selectionchange".split(" "));ar("onBeforeInput",["compositionend","keypress","textInput","paste"]);ar("onCompositionEnd","compositionend focusout keydown keypress keyup mousedown".split(" "));ar("onCompositionStart","compositionstart focusout keydown keypress keyup mousedown".split(" "));ar("onCompositionUpdate","compositionupdate focusout keydown keypress keyup mousedown".split(" "));var zo="abort canplay canplaythrough durationchange emptied encrypted ended error loadeddata loadedmetadata loadstart pause play playing progress ratechange resize seeked seeking stalled suspend timeupdate volumechange waiting".split(" "),c1=new Set("beforetoggle cancel close invalid load scroll scrollend toggle".split(" ").concat(zo));function Eb(t,e){e=(e&4)!==0;for(var n=0;n<t.length;n++){var i=t[n],a=i.event;i=i.listeners;e:{var s=void 0;if(e)for(var r=i.length-1;0<=r;r--){var o=i[r],c=o.instance,u=o.currentTarget;if(o=o.listener,c!==s&&a.isPropagationStopped())break e;s=o,a.currentTarget=u;try{s(a)}catch(d){Ou(d)}a.currentTarget=null,s=c}else for(r=0;r<i.length;r++){if(o=i[r],c=o.instance,u=o.currentTarget,o=o.listener,c!==s&&a.isPropagationStopped())break e;s=o,a.currentTarget=u;try{s(a)}catch(d){Ou(d)}a.currentTarget=null,s=c}}}}function ct(t,e){var n=e[_h];n===void 0&&(n=e[_h]=new Set);var i=t+"__bubble";n.has(i)||(Tb(e,t,2,!1),n.add(i))}function bd(t,e,n){var i=0;e&&(i|=4),Tb(n,t,i,e)}var vc="_reactListening"+Math.random().toString(36).slice(2);function eg(t){if(!t[vc]){t[vc]=!0,g_.forEach(function(n){n!=="selectionchange"&&(c1.has(n)||bd(n,!1,t),bd(n,!0,t))});var e=t.nodeType===9?t:t.ownerDocument;e===null||e[vc]||(e[vc]=!0,bd("selectionchange",!1,e))}}function Tb(t,e,n,i){switch(kb(e)){case 2:var a=I1;break;case 8:a=k1;break;default:a=ag}n=a.bind(null,e,n,t),a=void 0,!Eh||e!=="touchstart"&&e!=="touchmove"&&e!=="wheel"||(a=!0),i?a!==void 0?t.addEventListener(e,n,{capture:!0,passive:a}):t.addEventListener(e,n,!0):a!==void 0?t.addEventListener(e,n,{passive:a}):t.addEventListener(e,n,!1)}function Sd(t,e,n,i,a){var s=i;if(!(e&1)&&!(e&2)&&i!==null)e:for(;;){if(i===null)return;var r=i.tag;if(r===3||r===4){var o=i.stateNode.containerInfo;if(o===a)break;if(r===4)for(r=i.return;r!==null;){var c=r.tag;if((c===3||c===4)&&r.stateNode.containerInfo===a)return;r=r.return}for(;o!==null;){if(r=Ir(o),r===null)return;if(c=r.tag,c===5||c===6||c===26||c===27){i=s=r;continue e}o=o.parentNode}}i=i.return}E_(function(){var u=s,d=vm(n),m=[];e:{var f=B_.get(t);if(f!==void 0){var p=bf,x=t;switch(t){case"keypress":if(lu(n)===0)break e;case"keydown":case"keyup":p=nE;break;case"focusin":x="focus",p=$f;break;case"focusout":x="blur",p=$f;break;case"beforeblur":case"afterblur":p=$f;break;case"click":if(n.button===2)break e;case"auxclick":case"dblclick":case"mousedown":case"mousemove":case"mouseup":case"mouseout":case"mouseover":case"contextmenu":p=Wg;break;case"drag":case"dragend":case"dragenter":case"dragexit":case"dragleave":case"dragover":case"dragstart":case"drop":p=VM;break;case"touchcancel":case"touchend":case"touchmove":case"touchstart":p=sE;break;case P_:case z_:case I_:p=qM;break;case k_:p=lE;break;case"scroll":case"scrollend":p=HM;break;case"wheel":p=cE;break;case"copy":case"cut":case"paste":p=KM;break;case"gotpointercapture":case"lostpointercapture":case"pointercancel":case"pointerdown":case"pointermove":case"pointerout":case"pointerover":case"pointerup":p=qg;break;case"toggle":case"beforetoggle":p=fE}var _=(e&4)!==0,g=!_&&(t==="scroll"||t==="scrollend"),h=_?f!==null?f+"Capture":null:f;_=[];for(var v=u,b;v!==null;){var y=v;if(b=y.stateNode,y=y.tag,y!==5&&y!==26&&y!==27||b===null||h===null||(y=Co(v,h),y!=null&&_.push(Io(v,y,b))),g)break;v=v.return}0<_.length&&(f=new p(f,x,null,n,d),m.push({event:f,listeners:_}))}}if(!(e&7)){e:{if(f=t==="mouseover"||t==="pointerover",p=t==="mouseout"||t==="pointerout",f&&n!==Mh&&(x=n.relatedTarget||n.fromElement)&&(Ir(x)||x[El]))break e;if((p||f)&&(f=d.window===d?d:(f=d.ownerDocument)?f.defaultView||f.parentWindow:window,p?(x=n.relatedTarget||n.toElement,p=u,x=x?Ir(x):null,x!==null&&(g=Xo(x),_=x.tag,x!==g||_!==5&&_!==27&&_!==6)&&(x=null)):(p=null,x=u),p!==x)){if(_=Wg,y="onMouseLeave",h="onMouseEnter",v="mouse",(t==="pointerout"||t==="pointerover")&&(_=qg,y="onPointerLeave",h="onPointerEnter",v="pointer"),g=p==null?f:to(p),b=x==null?f:to(x),f=new _(y,v+"leave",p,n,d),f.target=g,f.relatedTarget=b,y=null,Ir(d)===u&&(_=new _(h,v+"enter",x,n,d),_.target=b,_.relatedTarget=g,y=_),g=y,p&&x)t:{for(_=u1,h=p,v=x,b=0,y=h;y;y=_(y))b++;y=0;for(var A=v;A;A=_(A))y++;for(;0<b-y;)h=_(h),b--;for(;0<y-b;)v=_(v),y--;for(;b--;){if(h===v||v!==null&&h===v.alternate){_=h;break t}h=_(h),v=_(v)}_=null}else _=null;p!==null&&kx(m,f,p,_,!1),x!==null&&g!==null&&kx(m,g,x,_,!0)}}e:{if(f=u?to(u):window,p=f.nodeName&&f.nodeName.toLowerCase(),p==="select"||p==="input"&&f.type==="file")var M=$g;else if(Zg(f))if(R_)M=bE;else{M=_E;var T=vE}else p=f.nodeName,!p||p.toLowerCase()!=="input"||f.type!=="checkbox"&&f.type!=="radio"?u&&xm(u.elementType)&&(M=$g):M=yE;if(M&&(M=M(t,u))){N_(m,M,n,d);break e}T&&T(t,f,u),t==="focusout"&&u&&f.type==="number"&&u.memoizedProps.value!=null&&Sh(f,"number",f.value)}switch(T=u?to(u):window,t){case"focusin":(Zg(T)||T.contentEditable==="true")&&(Fr=T,Th=u,ho=null);break;case"focusout":ho=Th=Fr=null;break;case"mousedown":Ah=!0;break;case"contextmenu":case"mouseup":case"dragend":Ah=!1,nx(m,n,d);break;case"selectionchange":if(ME)break;case"keydown":case"keyup":nx(m,n,d)}var S;if(bm)e:{switch(t){case"compositionstart":var C="onCompositionStart";break e;case"compositionend":C="onCompositionEnd";break e;case"compositionupdate":C="onCompositionUpdate";break e}C=void 0}else Br?w_(t,n)&&(C="onCompositionEnd"):t==="keydown"&&n.keyCode===229&&(C="onCompositionStart");C&&(A_&&n.locale!=="ko"&&(Br||C!=="onCompositionStart"?C==="onCompositionEnd"&&Br&&(S=T_()):(ss=d,_m="value"in ss?ss.value:ss.textContent,Br=!0)),T=$u(u,C),0<T.length&&(C=new Xg(C,t,null,n,d),m.push({event:C,listeners:T}),S?C.data=S:(S=C_(n),S!==null&&(C.data=S)))),(S=hE?pE(t,n):mE(t,n))&&(C=$u(u,"onBeforeInput"),0<C.length&&(T=new Xg("onBeforeInput","beforeinput",null,n,d),m.push({event:T,listeners:C}),T.data=S)),r1(m,t,u,n,d)}Eb(m,e)})}function Io(t,e,n){return{instance:t,listener:e,currentTarget:n}}function $u(t,e){for(var n=e+"Capture",i=[];t!==null;){var a=t,s=a.stateNode;if(a=a.tag,a!==5&&a!==26&&a!==27||s===null||(a=Co(t,n),a!=null&&i.unshift(Io(t,a,s)),a=Co(t,e),a!=null&&i.push(Io(t,a,s))),t.tag===3)return i;t=t.return}return[]}function u1(t){if(t===null)return null;do t=t.return;while(t&&t.tag!==5&&t.tag!==27);return t||null}function kx(t,e,n,i,a){for(var s=e._reactName,r=[];n!==null&&n!==i;){var o=n,c=o.alternate,u=o.stateNode;if(o=o.tag,c!==null&&c===i)break;o!==5&&o!==26&&o!==27||u===null||(c=u,a?(u=Co(n,s),u!=null&&r.unshift(Io(n,u,c))):a||(u=Co(n,s),u!=null&&r.push(Io(n,u,c)))),n=n.return}r.length!==0&&t.push({event:e,listeners:r})}var f1=/\r\n?/g,d1=/\u0000|\uFFFD/g;function Bx(t){return(typeof t=="string"?t:""+t).replace(f1,` -`).replace(d1,"")}function Ab(t,e){return e=Bx(e),Bx(t)===e}function Lt(t,e,n,i,a,s){switch(n){case"children":typeof i=="string"?e==="body"||e==="textarea"&&i===""||ol(t,i):(typeof i=="number"||typeof i=="bigint")&&e!=="body"&&ol(t,""+i);break;case"className":fc(t,"class",i);break;case"tabIndex":fc(t,"tabindex",i);break;case"dir":case"role":case"viewBox":case"width":case"height":fc(t,n,i);break;case"style":M_(t,i,s);break;case"data":if(e!=="object"){fc(t,"data",i);break}case"src":case"href":if(i===""&&(e!=="a"||n!=="href")){t.removeAttribute(n);break}if(i==null||typeof i=="function"||typeof i=="symbol"||typeof i=="boolean"){t.removeAttribute(n);break}i=su(""+i),t.setAttribute(n,i);break;case"action":case"formAction":if(typeof i=="function"){t.setAttribute(n,"javascript:throw new Error('A React form was unexpectedly submitted. If you called form.submit() manually, consider using form.requestSubmit() instead. If you\\'re trying to use event.stopPropagation() in a submit event handler, consider also calling event.preventDefault().')");break}else typeof s=="function"&&(n==="formAction"?(e!=="input"&&Lt(t,e,"name",a.name,a,null),Lt(t,e,"formEncType",a.formEncType,a,null),Lt(t,e,"formMethod",a.formMethod,a,null),Lt(t,e,"formTarget",a.formTarget,a,null)):(Lt(t,e,"encType",a.encType,a,null),Lt(t,e,"method",a.method,a,null),Lt(t,e,"target",a.target,a,null)));if(i==null||typeof i=="symbol"||typeof i=="boolean"){t.removeAttribute(n);break}i=su(""+i),t.setAttribute(n,i);break;case"onClick":i!=null&&(t.onclick=Ma);break;case"onScroll":i!=null&&ct("scroll",t);break;case"onScrollEnd":i!=null&&ct("scrollend",t);break;case"dangerouslySetInnerHTML":if(i!=null){if(typeof i!="object"||!("__html"in i))throw Error(ue(61));if(n=i.__html,n!=null){if(a.children!=null)throw Error(ue(60));t.innerHTML=n}}break;case"multiple":t.multiple=i&&typeof i!="function"&&typeof i!="symbol";break;case"muted":t.muted=i&&typeof i!="function"&&typeof i!="symbol";break;case"suppressContentEditableWarning":case"suppressHydrationWarning":case"defaultValue":case"defaultChecked":case"innerHTML":case"ref":break;case"autoFocus":break;case"xlinkHref":if(i==null||typeof i=="function"||typeof i=="boolean"||typeof i=="symbol"){t.removeAttribute("xlink:href");break}n=su(""+i),t.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",n);break;case"contentEditable":case"spellCheck":case"draggable":case"value":case"autoReverse":case"externalResourcesRequired":case"focusable":case"preserveAlpha":i!=null&&typeof i!="function"&&typeof i!="symbol"?t.setAttribute(n,""+i):t.removeAttribute(n);break;case"inert":case"allowFullScreen":case"async":case"autoPlay":case"controls":case"default":case"defer":case"disabled":case"disablePictureInPicture":case"disableRemotePlayback":case"formNoValidate":case"hidden":case"loop":case"noModule":case"noValidate":case"open":case"playsInline":case"readOnly":case"required":case"reversed":case"scoped":case"seamless":case"itemScope":i&&typeof i!="function"&&typeof i!="symbol"?t.setAttribute(n,""):t.removeAttribute(n);break;case"capture":case"download":i===!0?t.setAttribute(n,""):i!==!1&&i!=null&&typeof i!="function"&&typeof i!="symbol"?t.setAttribute(n,i):t.removeAttribute(n);break;case"cols":case"rows":case"size":case"span":i!=null&&typeof i!="function"&&typeof i!="symbol"&&!isNaN(i)&&1<=i?t.setAttribute(n,i):t.removeAttribute(n);break;case"rowSpan":case"start":i==null||typeof i=="function"||typeof i=="symbol"||isNaN(i)?t.removeAttribute(n):t.setAttribute(n,i);break;case"popover":ct("beforetoggle",t),ct("toggle",t),au(t,"popover",i);break;case"xlinkActuate":oa(t,"http://www.w3.org/1999/xlink","xlink:actuate",i);break;case"xlinkArcrole":oa(t,"http://www.w3.org/1999/xlink","xlink:arcrole",i);break;case"xlinkRole":oa(t,"http://www.w3.org/1999/xlink","xlink:role",i);break;case"xlinkShow":oa(t,"http://www.w3.org/1999/xlink","xlink:show",i);break;case"xlinkTitle":oa(t,"http://www.w3.org/1999/xlink","xlink:title",i);break;case"xlinkType":oa(t,"http://www.w3.org/1999/xlink","xlink:type",i);break;case"xmlBase":oa(t,"http://www.w3.org/XML/1998/namespace","xml:base",i);break;case"xmlLang":oa(t,"http://www.w3.org/XML/1998/namespace","xml:lang",i);break;case"xmlSpace":oa(t,"http://www.w3.org/XML/1998/namespace","xml:space",i);break;case"is":au(t,"is",i);break;case"innerText":case"textContent":break;default:(!(2<n.length)||n[0]!=="o"&&n[0]!=="O"||n[1]!=="n"&&n[1]!=="N")&&(n=FM.get(n)||n,au(t,n,i))}}function Zh(t,e,n,i,a,s){switch(n){case"style":M_(t,i,s);break;case"dangerouslySetInnerHTML":if(i!=null){if(typeof i!="object"||!("__html"in i))throw Error(ue(61));if(n=i.__html,n!=null){if(a.children!=null)throw Error(ue(60));t.innerHTML=n}}break;case"children":typeof i=="string"?ol(t,i):(typeof i=="number"||typeof i=="bigint")&&ol(t,""+i);break;case"onScroll":i!=null&&ct("scroll",t);break;case"onScrollEnd":i!=null&&ct("scrollend",t);break;case"onClick":i!=null&&(t.onclick=Ma);break;case"suppressContentEditableWarning":case"suppressHydrationWarning":case"innerHTML":case"ref":break;case"innerText":case"textContent":break;default:if(!x_.hasOwnProperty(n))e:{if(n[0]==="o"&&n[1]==="n"&&(a=n.endsWith("Capture"),e=n.slice(2,a?n.length-7:void 0),s=t[Kn]||null,s=s!=null?s[n]:null,typeof s=="function"&&t.removeEventListener(e,s,a),typeof i=="function")){typeof s!="function"&&s!==null&&(n in t?t[n]=null:t.hasAttribute(n)&&t.removeAttribute(n)),t.addEventListener(e,i,a);break e}n in t?t[n]=i:i===!0?t.setAttribute(n,""):au(t,n,i)}}}function Tn(t,e,n){switch(e){case"div":case"span":case"svg":case"path":case"a":case"g":case"p":case"li":break;case"img":ct("error",t),ct("load",t);var i=!1,a=!1,s;for(s in n)if(n.hasOwnProperty(s)){var r=n[s];if(r!=null)switch(s){case"src":i=!0;break;case"srcSet":a=!0;break;case"children":case"dangerouslySetInnerHTML":throw Error(ue(137,e));default:Lt(t,e,s,r,n,null)}}a&&Lt(t,e,"srcSet",n.srcSet,n,null),i&&Lt(t,e,"src",n.src,n,null);return;case"input":ct("invalid",t);var o=s=r=a=null,c=null,u=null;for(i in n)if(n.hasOwnProperty(i)){var d=n[i];if(d!=null)switch(i){case"name":a=d;break;case"type":r=d;break;case"checked":c=d;break;case"defaultChecked":u=d;break;case"value":s=d;break;case"defaultValue":o=d;break;case"children":case"dangerouslySetInnerHTML":if(d!=null)throw Error(ue(137,e));break;default:Lt(t,e,i,d,n,null)}}y_(t,s,o,c,u,r,a,!1);return;case"select":ct("invalid",t),i=r=s=null;for(a in n)if(n.hasOwnProperty(a)&&(o=n[a],o!=null))switch(a){case"value":s=o;break;case"defaultValue":r=o;break;case"multiple":i=o;default:Lt(t,e,a,o,n,null)}e=s,n=r,t.multiple=!!i,e!=null?Zr(t,!!i,e,!1):n!=null&&Zr(t,!!i,n,!0);return;case"textarea":ct("invalid",t),s=a=i=null;for(r in n)if(n.hasOwnProperty(r)&&(o=n[r],o!=null))switch(r){case"value":i=o;break;case"defaultValue":a=o;break;case"children":s=o;break;case"dangerouslySetInnerHTML":if(o!=null)throw Error(ue(91));break;default:Lt(t,e,r,o,n,null)}S_(t,i,a,s);return;case"option":for(c in n)if(n.hasOwnProperty(c)&&(i=n[c],i!=null))switch(c){case"selected":t.selected=i&&typeof i!="function"&&typeof i!="symbol";break;default:Lt(t,e,c,i,n,null)}return;case"dialog":ct("beforetoggle",t),ct("toggle",t),ct("cancel",t),ct("close",t);break;case"iframe":case"object":ct("load",t);break;case"video":case"audio":for(i=0;i<zo.length;i++)ct(zo[i],t);break;case"image":ct("error",t),ct("load",t);break;case"details":ct("toggle",t);break;case"embed":case"source":case"link":ct("error",t),ct("load",t);case"area":case"base":case"br":case"col":case"hr":case"keygen":case"meta":case"param":case"track":case"wbr":case"menuitem":for(u in n)if(n.hasOwnProperty(u)&&(i=n[u],i!=null))switch(u){case"children":case"dangerouslySetInnerHTML":throw Error(ue(137,e));default:Lt(t,e,u,i,n,null)}return;default:if(xm(e)){for(d in n)n.hasOwnProperty(d)&&(i=n[d],i!==void 0&&Zh(t,e,d,i,n,void 0));return}}for(o in n)n.hasOwnProperty(o)&&(i=n[o],i!=null&&Lt(t,e,o,i,n,null))}function h1(t,e,n,i){switch(e){case"div":case"span":case"svg":case"path":case"a":case"g":case"p":case"li":break;case"input":var a=null,s=null,r=null,o=null,c=null,u=null,d=null;for(p in n){var m=n[p];if(n.hasOwnProperty(p)&&m!=null)switch(p){case"checked":break;case"value":break;case"defaultValue":c=m;default:i.hasOwnProperty(p)||Lt(t,e,p,null,i,m)}}for(var f in i){var p=i[f];if(m=n[f],i.hasOwnProperty(f)&&(p!=null||m!=null))switch(f){case"type":s=p;break;case"name":a=p;break;case"checked":u=p;break;case"defaultChecked":d=p;break;case"value":r=p;break;case"defaultValue":o=p;break;case"children":case"dangerouslySetInnerHTML":if(p!=null)throw Error(ue(137,e));break;default:p!==m&&Lt(t,e,f,p,i,m)}}bh(t,r,o,c,u,d,s,a);return;case"select":p=r=o=f=null;for(s in n)if(c=n[s],n.hasOwnProperty(s)&&c!=null)switch(s){case"value":break;case"multiple":p=c;default:i.hasOwnProperty(s)||Lt(t,e,s,null,i,c)}for(a in i)if(s=i[a],c=n[a],i.hasOwnProperty(a)&&(s!=null||c!=null))switch(a){case"value":f=s;break;case"defaultValue":o=s;break;case"multiple":r=s;default:s!==c&&Lt(t,e,a,s,i,c)}e=o,n=r,i=p,f!=null?Zr(t,!!n,f,!1):!!i!=!!n&&(e!=null?Zr(t,!!n,e,!0):Zr(t,!!n,n?[]:"",!1));return;case"textarea":p=f=null;for(o in n)if(a=n[o],n.hasOwnProperty(o)&&a!=null&&!i.hasOwnProperty(o))switch(o){case"value":break;case"children":break;default:Lt(t,e,o,null,i,a)}for(r in i)if(a=i[r],s=n[r],i.hasOwnProperty(r)&&(a!=null||s!=null))switch(r){case"value":f=a;break;case"defaultValue":p=a;break;case"children":break;case"dangerouslySetInnerHTML":if(a!=null)throw Error(ue(91));break;default:a!==s&&Lt(t,e,r,a,i,s)}b_(t,f,p);return;case"option":for(var x in n)if(f=n[x],n.hasOwnProperty(x)&&f!=null&&!i.hasOwnProperty(x))switch(x){case"selected":t.selected=!1;break;default:Lt(t,e,x,null,i,f)}for(c in i)if(f=i[c],p=n[c],i.hasOwnProperty(c)&&f!==p&&(f!=null||p!=null))switch(c){case"selected":t.selected=f&&typeof f!="function"&&typeof f!="symbol";break;default:Lt(t,e,c,f,i,p)}return;case"img":case"link":case"area":case"base":case"br":case"col":case"embed":case"hr":case"keygen":case"meta":case"param":case"source":case"track":case"wbr":case"menuitem":for(var _ in n)f=n[_],n.hasOwnProperty(_)&&f!=null&&!i.hasOwnProperty(_)&&Lt(t,e,_,null,i,f);for(u in i)if(f=i[u],p=n[u],i.hasOwnProperty(u)&&f!==p&&(f!=null||p!=null))switch(u){case"children":case"dangerouslySetInnerHTML":if(f!=null)throw Error(ue(137,e));break;default:Lt(t,e,u,f,i,p)}return;default:if(xm(e)){for(var g in n)f=n[g],n.hasOwnProperty(g)&&f!==void 0&&!i.hasOwnProperty(g)&&Zh(t,e,g,void 0,i,f);for(d in i)f=i[d],p=n[d],!i.hasOwnProperty(d)||f===p||f===void 0&&p===void 0||Zh(t,e,d,f,i,p);return}}for(var h in n)f=n[h],n.hasOwnProperty(h)&&f!=null&&!i.hasOwnProperty(h)&&Lt(t,e,h,null,i,f);for(m in i)f=i[m],p=n[m],!i.hasOwnProperty(m)||f===p||f==null&&p==null||Lt(t,e,m,f,i,p)}function Fx(t){switch(t){case"css":case"script":case"font":case"img":case"image":case"input":case"link":return!0;default:return!1}}function p1(){if(typeof performance.getEntriesByType=="function"){for(var t=0,e=0,n=performance.getEntriesByType("resource"),i=0;i<n.length;i++){var a=n[i],s=a.transferSize,r=a.initiatorType,o=a.duration;if(s&&o&&Fx(r)){for(r=0,o=a.responseEnd,i+=1;i<n.length;i++){var c=n[i],u=c.startTime;if(u>o)break;var d=c.transferSize,m=c.initiatorType;d&&Fx(m)&&(c=c.responseEnd,r+=d*(c<o?1:(o-u)/(c-u)))}if(--i,e+=8*(s+r)/(a.duration/1e3),t++,10<t)break}}if(0<t)return e/t/1e6}return navigator.connection&&(t=navigator.connection.downlink,typeof t=="number")?t:5}var $h=null,Qh=null;function Qu(t){return t.nodeType===9?t:t.ownerDocument}function jx(t){switch(t){case"http://www.w3.org/2000/svg":return 1;case"http://www.w3.org/1998/Math/MathML":return 2;default:return 0}}function wb(t,e){if(t===0)switch(e){case"svg":return 1;case"math":return 2;default:return 0}return t===1&&e==="foreignObject"?0:t}function Jh(t,e){return t==="textarea"||t==="noscript"||typeof e.children=="string"||typeof e.children=="number"||typeof e.children=="bigint"||typeof e.dangerouslySetInnerHTML=="object"&&e.dangerouslySetInnerHTML!==null&&e.dangerouslySetInnerHTML.__html!=null}var Md=null;function m1(){var t=window.event;return t&&t.type==="popstate"?t===Md?!1:(Md=t,!0):(Md=null,!1)}var Cb=typeof setTimeout=="function"?setTimeout:void 0,g1=typeof clearTimeout=="function"?clearTimeout:void 0,Hx=typeof Promise=="function"?Promise:void 0,x1=typeof queueMicrotask=="function"?queueMicrotask:typeof Hx<"u"?function(t){return Hx.resolve(null).then(t).catch(v1)}:Cb;function v1(t){setTimeout(function(){throw t})}function ws(t){return t==="head"}function Gx(t,e){var n=e,i=0;do{var a=n.nextSibling;if(t.removeChild(n),a&&a.nodeType===8)if(n=a.data,n==="/$"||n==="/&"){if(i===0){t.removeChild(a),gl(e);return}i--}else if(n==="$"||n==="$?"||n==="$~"||n==="$!"||n==="&")i++;else if(n==="html")Mo(t.ownerDocument.documentElement);else if(n==="head"){n=t.ownerDocument.head,Mo(n);for(var s=n.firstChild;s;){var r=s.nextSibling,o=s.nodeName;s[Zo]||o==="SCRIPT"||o==="STYLE"||o==="LINK"&&s.rel.toLowerCase()==="stylesheet"||n.removeChild(s),s=r}}else n==="body"&&Mo(t.ownerDocument.body);n=a}while(n);gl(e)}function Vx(t,e){var n=t;t=0;do{var i=n.nextSibling;if(n.nodeType===1?e?(n._stashedDisplay=n.style.display,n.style.display="none"):(n.style.display=n._stashedDisplay||"",n.getAttribute("style")===""&&n.removeAttribute("style")):n.nodeType===3&&(e?(n._stashedText=n.nodeValue,n.nodeValue=""):n.nodeValue=n._stashedText||""),i&&i.nodeType===8)if(n=i.data,n==="/$"){if(t===0)break;t--}else n!=="$"&&n!=="$?"&&n!=="$~"&&n!=="$!"||t++;n=i}while(n)}function ep(t){var e=t.firstChild;for(e&&e.nodeType===10&&(e=e.nextSibling);e;){var n=e;switch(e=e.nextSibling,n.nodeName){case"HTML":case"HEAD":case"BODY":ep(n),gm(n);continue;case"SCRIPT":case"STYLE":continue;case"LINK":if(n.rel.toLowerCase()==="stylesheet")continue}t.removeChild(n)}}function _1(t,e,n,i){for(;t.nodeType===1;){var a=n;if(t.nodeName.toLowerCase()!==e.toLowerCase()){if(!i&&(t.nodeName!=="INPUT"||t.type!=="hidden"))break}else if(i){if(!t[Zo])switch(e){case"meta":if(!t.hasAttribute("itemprop"))break;return t;case"link":if(s=t.getAttribute("rel"),s==="stylesheet"&&t.hasAttribute("data-precedence"))break;if(s!==a.rel||t.getAttribute("href")!==(a.href==null||a.href===""?null:a.href)||t.getAttribute("crossorigin")!==(a.crossOrigin==null?null:a.crossOrigin)||t.getAttribute("title")!==(a.title==null?null:a.title))break;return t;case"style":if(t.hasAttribute("data-precedence"))break;return t;case"script":if(s=t.getAttribute("src"),(s!==(a.src==null?null:a.src)||t.getAttribute("type")!==(a.type==null?null:a.type)||t.getAttribute("crossorigin")!==(a.crossOrigin==null?null:a.crossOrigin))&&s&&t.hasAttribute("async")&&!t.hasAttribute("itemprop"))break;return t;default:return t}}else if(e==="input"&&t.type==="hidden"){var s=a.name==null?null:""+a.name;if(a.type==="hidden"&&t.getAttribute("name")===s)return t}else return t;if(t=wi(t.nextSibling),t===null)break}return null}function y1(t,e,n){if(e==="")return null;for(;t.nodeType!==3;)if((t.nodeType!==1||t.nodeName!=="INPUT"||t.type!=="hidden")&&!n||(t=wi(t.nextSibling),t===null))return null;return t}function Nb(t,e){for(;t.nodeType!==8;)if((t.nodeType!==1||t.nodeName!=="INPUT"||t.type!=="hidden")&&!e||(t=wi(t.nextSibling),t===null))return null;return t}function tp(t){return t.data==="$?"||t.data==="$~"}function np(t){return t.data==="$!"||t.data==="$?"&&t.ownerDocument.readyState!=="loading"}function b1(t,e){var n=t.ownerDocument;if(t.data==="$~")t._reactRetry=e;else if(t.data!=="$?"||n.readyState!=="loading")e();else{var i=function(){e(),n.removeEventListener("DOMContentLoaded",i)};n.addEventListener("DOMContentLoaded",i),t._reactRetry=i}}function wi(t){for(;t!=null;t=t.nextSibling){var e=t.nodeType;if(e===1||e===3)break;if(e===8){if(e=t.data,e==="$"||e==="$!"||e==="$?"||e==="$~"||e==="&"||e==="F!"||e==="F")break;if(e==="/$"||e==="/&")return null}}return t}var ip=null;function Wx(t){t=t.nextSibling;for(var e=0;t;){if(t.nodeType===8){var n=t.data;if(n==="/$"||n==="/&"){if(e===0)return wi(t.nextSibling);e--}else n!=="$"&&n!=="$!"&&n!=="$?"&&n!=="$~"&&n!=="&"||e++}t=t.nextSibling}return null}function Xx(t){t=t.previousSibling;for(var e=0;t;){if(t.nodeType===8){var n=t.data;if(n==="$"||n==="$!"||n==="$?"||n==="$~"||n==="&"){if(e===0)return t;e--}else n!=="/$"&&n!=="/&"||e++}t=t.previousSibling}return null}function Rb(t,e,n){switch(e=Qu(n),t){case"html":if(t=e.documentElement,!t)throw Error(ue(452));return t;case"head":if(t=e.head,!t)throw Error(ue(453));return t;case"body":if(t=e.body,!t)throw Error(ue(454));return t;default:throw Error(ue(451))}}function Mo(t){for(var e=t.attributes;e.length;)t.removeAttributeNode(e[0]);gm(t)}var Ni=new Map,qx=new Set;function Ju(t){return typeof t.getRootNode=="function"?t.getRootNode():t.nodeType===9?t:t.ownerDocument}var ka=Tt.d;Tt.d={f:S1,r:M1,D:E1,C:T1,L:A1,m:w1,X:N1,S:C1,M:R1};function S1(){var t=ka.f(),e=Lf();return t||e}function M1(t){var e=Tl(t);e!==null&&e.tag===5&&e.type==="form"?My(e):ka.r(t)}var Nl=typeof document>"u"?null:document;function Db(t,e,n){var i=Nl;if(i&&typeof e=="string"&&e){var a=bi(e);a='link[rel="'+t+'"][href="'+a+'"]',typeof n=="string"&&(a+='[crossorigin="'+n+'"]'),qx.has(a)||(qx.add(a),t={rel:t,crossOrigin:n,href:e},i.querySelector(a)===null&&(e=i.createElement("link"),Tn(e,"link",t),gn(e),i.head.appendChild(e)))}}function E1(t){ka.D(t),Db("dns-prefetch",t,null)}function T1(t,e){ka.C(t,e),Db("preconnect",t,e)}function A1(t,e,n){ka.L(t,e,n);var i=Nl;if(i&&t&&e){var a='link[rel="preload"][as="'+bi(e)+'"]';e==="image"&&n&&n.imageSrcSet?(a+='[imagesrcset="'+bi(n.imageSrcSet)+'"]',typeof n.imageSizes=="string"&&(a+='[imagesizes="'+bi(n.imageSizes)+'"]')):a+='[href="'+bi(t)+'"]';var s=a;switch(e){case"style":s=ml(t);break;case"script":s=Rl(t)}Ni.has(s)||(t=Vt({rel:"preload",href:e==="image"&&n&&n.imageSrcSet?void 0:t,as:e},n),Ni.set(s,t),i.querySelector(a)!==null||e==="style"&&i.querySelector(nc(s))||e==="script"&&i.querySelector(ic(s))||(e=i.createElement("link"),Tn(e,"link",t),gn(e),i.head.appendChild(e)))}}function w1(t,e){ka.m(t,e);var n=Nl;if(n&&t){var i=e&&typeof e.as=="string"?e.as:"script",a='link[rel="modulepreload"][as="'+bi(i)+'"][href="'+bi(t)+'"]',s=a;switch(i){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":s=Rl(t)}if(!Ni.has(s)&&(t=Vt({rel:"modulepreload",href:t},e),Ni.set(s,t),n.querySelector(a)===null)){switch(i){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(n.querySelector(ic(s)))return}i=n.createElement("link"),Tn(i,"link",t),gn(i),n.head.appendChild(i)}}}function C1(t,e,n){ka.S(t,e,n);var i=Nl;if(i&&t){var a=Kr(i).hoistableStyles,s=ml(t);e=e||"default";var r=a.get(s);if(!r){var o={loading:0,preload:null};if(r=i.querySelector(nc(s)))o.loading=5;else{t=Vt({rel:"stylesheet",href:t,"data-precedence":e},n),(n=Ni.get(s))&&tg(t,n);var c=r=i.createElement("link");gn(c),Tn(c,"link",t),c._p=new Promise(function(u,d){c.onload=u,c.onerror=d}),c.addEventListener("load",function(){o.loading|=1}),c.addEventListener("error",function(){o.loading|=2}),o.loading|=4,mu(r,e,i)}r={type:"stylesheet",instance:r,count:1,state:o},a.set(s,r)}}}function N1(t,e){ka.X(t,e);var n=Nl;if(n&&t){var i=Kr(n).hoistableScripts,a=Rl(t),s=i.get(a);s||(s=n.querySelector(ic(a)),s||(t=Vt({src:t,async:!0},e),(e=Ni.get(a))&&ng(t,e),s=n.createElement("script"),gn(s),Tn(s,"link",t),n.head.appendChild(s)),s={type:"script",instance:s,count:1,state:null},i.set(a,s))}}function R1(t,e){ka.M(t,e);var n=Nl;if(n&&t){var i=Kr(n).hoistableScripts,a=Rl(t),s=i.get(a);s||(s=n.querySelector(ic(a)),s||(t=Vt({src:t,async:!0,type:"module"},e),(e=Ni.get(a))&&ng(t,e),s=n.createElement("script"),gn(s),Tn(s,"link",t),n.head.appendChild(s)),s={type:"script",instance:s,count:1,state:null},i.set(a,s))}}function Yx(t,e,n,i){var a=(a=ds.current)?Ju(a):null;if(!a)throw Error(ue(446));switch(t){case"meta":case"title":return null;case"style":return typeof n.precedence=="string"&&typeof n.href=="string"?(e=ml(n.href),n=Kr(a).hoistableStyles,i=n.get(e),i||(i={type:"style",instance:null,count:0,state:null},n.set(e,i)),i):{type:"void",instance:null,count:0,state:null};case"link":if(n.rel==="stylesheet"&&typeof n.href=="string"&&typeof n.precedence=="string"){t=ml(n.href);var s=Kr(a).hoistableStyles,r=s.get(t);if(r||(a=a.ownerDocument||a,r={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},s.set(t,r),(s=a.querySelector(nc(t)))&&!s._p&&(r.instance=s,r.state.loading=5),Ni.has(t)||(n={rel:"preload",as:"style",href:n.href,crossOrigin:n.crossOrigin,integrity:n.integrity,media:n.media,hrefLang:n.hrefLang,referrerPolicy:n.referrerPolicy},Ni.set(t,n),s||D1(a,t,n,r.state))),e&&i===null)throw Error(ue(528,""));return r}if(e&&i!==null)throw Error(ue(529,""));return null;case"script":return e=n.async,n=n.src,typeof n=="string"&&e&&typeof e!="function"&&typeof e!="symbol"?(e=Rl(n),n=Kr(a).hoistableScripts,i=n.get(e),i||(i={type:"script",instance:null,count:0,state:null},n.set(e,i)),i):{type:"void",instance:null,count:0,state:null};default:throw Error(ue(444,t))}}function ml(t){return'href="'+bi(t)+'"'}function nc(t){return'link[rel="stylesheet"]['+t+"]"}function Lb(t){return Vt({},t,{"data-precedence":t.precedence,precedence:null})}function D1(t,e,n,i){t.querySelector('link[rel="preload"][as="style"]['+e+"]")?i.loading=1:(e=t.createElement("link"),i.preload=e,e.addEventListener("load",function(){return i.loading|=1}),e.addEventListener("error",function(){return i.loading|=2}),Tn(e,"link",n),gn(e),t.head.appendChild(e))}function Rl(t){return'[src="'+bi(t)+'"]'}function ic(t){return"script[async]"+t}function Kx(t,e,n){if(e.count++,e.instance===null)switch(e.type){case"style":var i=t.querySelector('style[data-href~="'+bi(n.href)+'"]');if(i)return e.instance=i,gn(i),i;var a=Vt({},n,{"data-href":n.href,"data-precedence":n.precedence,href:null,precedence:null});return i=(t.ownerDocument||t).createElement("style"),gn(i),Tn(i,"style",a),mu(i,n.precedence,t),e.instance=i;case"stylesheet":a=ml(n.href);var s=t.querySelector(nc(a));if(s)return e.state.loading|=4,e.instance=s,gn(s),s;i=Lb(n),(a=Ni.get(a))&&tg(i,a),s=(t.ownerDocument||t).createElement("link"),gn(s);var r=s;return r._p=new Promise(function(o,c){r.onload=o,r.onerror=c}),Tn(s,"link",i),e.state.loading|=4,mu(s,n.precedence,t),e.instance=s;case"script":return s=Rl(n.src),(a=t.querySelector(ic(s)))?(e.instance=a,gn(a),a):(i=n,(a=Ni.get(s))&&(i=Vt({},n),ng(i,a)),t=t.ownerDocument||t,a=t.createElement("script"),gn(a),Tn(a,"link",i),t.head.appendChild(a),e.instance=a);case"void":return null;default:throw Error(ue(443,e.type))}else e.type==="stylesheet"&&!(e.state.loading&4)&&(i=e.instance,e.state.loading|=4,mu(i,n.precedence,t));return e.instance}function mu(t,e,n){for(var i=n.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),a=i.length?i[i.length-1]:null,s=a,r=0;r<i.length;r++){var o=i[r];if(o.dataset.precedence===e)s=o;else if(s!==a)break}s?s.parentNode.insertBefore(t,s.nextSibling):(e=n.nodeType===9?n.head:n,e.insertBefore(t,e.firstChild))}function tg(t,e){t.crossOrigin==null&&(t.crossOrigin=e.crossOrigin),t.referrerPolicy==null&&(t.referrerPolicy=e.referrerPolicy),t.title==null&&(t.title=e.title)}function ng(t,e){t.crossOrigin==null&&(t.crossOrigin=e.crossOrigin),t.referrerPolicy==null&&(t.referrerPolicy=e.referrerPolicy),t.integrity==null&&(t.integrity=e.integrity)}var gu=null;function Zx(t,e,n){if(gu===null){var i=new Map,a=gu=new Map;a.set(n,i)}else a=gu,i=a.get(n),i||(i=new Map,a.set(n,i));if(i.has(t))return i;for(i.set(t,null),n=n.getElementsByTagName(t),a=0;a<n.length;a++){var s=n[a];if(!(s[Zo]||s[bn]||t==="link"&&s.getAttribute("rel")==="stylesheet")&&s.namespaceURI!=="http://www.w3.org/2000/svg"){var r=s.getAttribute(e)||"";r=t+r;var o=i.get(r);o?o.push(s):i.set(r,[s])}}return i}function $x(t,e,n){t=t.ownerDocument||t,t.head.insertBefore(n,e==="title"?t.querySelector("head > title"):null)}function L1(t,e,n){if(n===1||e.itemProp!=null)return!1;switch(t){case"meta":case"title":return!0;case"style":if(typeof e.precedence!="string"||typeof e.href!="string"||e.href==="")break;return!0;case"link":if(typeof e.rel!="string"||typeof e.href!="string"||e.href===""||e.onLoad||e.onError)break;switch(e.rel){case"stylesheet":return t=e.disabled,typeof e.precedence=="string"&&t==null;default:return!0}case"script":if(e.async&&typeof e.async!="function"&&typeof e.async!="symbol"&&!e.onLoad&&!e.onError&&e.src&&typeof e.src=="string")return!0}return!1}function Ub(t){return!(t.type==="stylesheet"&&!(t.state.loading&3))}function U1(t,e,n,i){if(n.type==="stylesheet"&&(typeof i.media!="string"||matchMedia(i.media).matches!==!1)&&!(n.state.loading&4)){if(n.instance===null){var a=ml(i.href),s=e.querySelector(nc(a));if(s){e=s._p,e!==null&&typeof e=="object"&&typeof e.then=="function"&&(t.count++,t=ef.bind(t),e.then(t,t)),n.state.loading|=4,n.instance=s,gn(s);return}s=e.ownerDocument||e,i=Lb(i),(a=Ni.get(a))&&tg(i,a),s=s.createElement("link"),gn(s);var r=s;r._p=new Promise(function(o,c){r.onload=o,r.onerror=c}),Tn(s,"link",i),n.instance=s}t.stylesheets===null&&(t.stylesheets=new Map),t.stylesheets.set(n,e),(e=n.state.preload)&&!(n.state.loading&3)&&(t.count++,n=ef.bind(t),e.addEventListener("load",n),e.addEventListener("error",n))}}var Ed=0;function O1(t,e){return t.stylesheets&&t.count===0&&xu(t,t.stylesheets),0<t.count||0<t.imgCount?function(n){var i=setTimeout(function(){if(t.stylesheets&&xu(t,t.stylesheets),t.unsuspend){var s=t.unsuspend;t.unsuspend=null,s()}},6e4+e);0<t.imgBytes&&Ed===0&&(Ed=62500*p1());var a=setTimeout(function(){if(t.waitingForImages=!1,t.count===0&&(t.stylesheets&&xu(t,t.stylesheets),t.unsuspend)){var s=t.unsuspend;t.unsuspend=null,s()}},(t.imgBytes>Ed?50:800)+e);return t.unsuspend=n,function(){t.unsuspend=null,clearTimeout(i),clearTimeout(a)}}:null}function ef(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)xu(this,this.stylesheets);else if(this.unsuspend){var t=this.unsuspend;this.unsuspend=null,t()}}}var tf=null;function xu(t,e){t.stylesheets=null,t.unsuspend!==null&&(t.count++,tf=new Map,e.forEach(P1,t),tf=null,ef.call(t))}function P1(t,e){if(!(e.state.loading&4)){var n=tf.get(t);if(n)var i=n.get(null);else{n=new Map,tf.set(t,n);for(var a=t.querySelectorAll("link[data-precedence],style[data-precedence]"),s=0;s<a.length;s++){var r=a[s];(r.nodeName==="LINK"||r.getAttribute("media")!=="not all")&&(n.set(r.dataset.precedence,r),i=r)}i&&n.set(null,i)}a=e.instance,r=a.getAttribute("data-precedence"),s=n.get(r)||i,s===i&&n.set(null,a),n.set(r,a),this.count++,i=ef.bind(this),a.addEventListener("load",i),a.addEventListener("error",i),s?s.parentNode.insertBefore(a,s.nextSibling):(t=t.nodeType===9?t.head:t,t.insertBefore(a,t.firstChild)),e.state.loading|=4}}var ko={$$typeof:Sa,Provider:null,Consumer:null,_currentValue:Gs,_currentValue2:Gs,_threadCount:0};function z1(t,e,n,i,a,s,r,o,c){this.tag=1,this.containerInfo=t,this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.next=this.pendingContext=this.context=this.cancelPendingCommit=null,this.callbackPriority=0,this.expirationTimes=qf(-1),this.entangledLanes=this.shellSuspendCounter=this.errorRecoveryDisabledLanes=this.expiredLanes=this.warmLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=qf(0),this.hiddenUpdates=qf(null),this.identifierPrefix=i,this.onUncaughtError=a,this.onCaughtError=s,this.onRecoverableError=r,this.pooledCache=null,this.pooledCacheLanes=0,this.formState=c,this.incompleteTransitions=new Map}function Ob(t,e,n,i,a,s,r,o,c,u,d,m){return t=new z1(t,e,n,r,c,u,d,m,o),e=1,s===!0&&(e|=24),s=si(3,null,null,e),t.current=s,s.stateNode=t,e=Cm(),e.refCount++,t.pooledCache=e,e.refCount++,s.memoizedState={element:i,isDehydrated:n,cache:e},Dm(s),t}function Pb(t){return t?(t=Gr,t):Gr}function zb(t,e,n,i,a,s){a=Pb(a),i.context===null?i.context=a:i.pendingContext=a,i=ps(e),i.payload={element:n},s=s===void 0?null:s,s!==null&&(i.callback=s),n=ms(t,i,e),n!==null&&(qn(n,t,e),mo(n,t,e))}function Qx(t,e){if(t=t.memoizedState,t!==null&&t.dehydrated!==null){var n=t.retryLane;t.retryLane=n!==0&&n<e?n:e}}function ig(t,e){Qx(t,e),(t=t.alternate)&&Qx(t,e)}function Ib(t){if(t.tag===13||t.tag===31){var e=lr(t,67108864);e!==null&&qn(e,t,67108864),ig(t,67108864)}}function Jx(t){if(t.tag===13||t.tag===31){var e=ui();e=pm(e);var n=lr(t,e);n!==null&&qn(n,t,e),ig(t,e)}}var nf=!0;function I1(t,e,n,i){var a=tt.T;tt.T=null;var s=Tt.p;try{Tt.p=2,ag(t,e,n,i)}finally{Tt.p=s,tt.T=a}}function k1(t,e,n,i){var a=tt.T;tt.T=null;var s=Tt.p;try{Tt.p=8,ag(t,e,n,i)}finally{Tt.p=s,tt.T=a}}function ag(t,e,n,i){if(nf){var a=ap(i);if(a===null)Sd(t,e,i,af,n),e0(t,i);else if(F1(a,t,e,n,i))i.stopPropagation();else if(e0(t,i),e&4&&-1<B1.indexOf(t)){for(;a!==null;){var s=Tl(a);if(s!==null)switch(s.tag){case 3:if(s=s.stateNode,s.current.memoizedState.isDehydrated){var r=Ps(s.pendingLanes);if(r!==0){var o=s;for(o.pendingLanes|=2,o.entangledLanes|=2;r;){var c=1<<31-ci(r);o.entanglements[1]|=c,r&=~c}la(s),!(Et&6)&&(Xu=li()+500,tc(0))}}break;case 31:case 13:o=lr(s,2),o!==null&&qn(o,s,2),Lf(),ig(s,2)}if(s=ap(i),s===null&&Sd(t,e,i,af,n),s===a)break;a=s}a!==null&&i.stopPropagation()}else Sd(t,e,i,null,n)}}function ap(t){return t=vm(t),sg(t)}var af=null;function sg(t){if(af=null,t=Ir(t),t!==null){var e=Xo(t);if(e===null)t=null;else{var n=e.tag;if(n===13){if(t=i_(e),t!==null)return t;t=null}else if(n===31){if(t=a_(e),t!==null)return t;t=null}else if(n===3){if(e.stateNode.current.memoizedState.isDehydrated)return e.tag===3?e.stateNode.containerInfo:null;t=null}else e!==t&&(t=null)}}return af=t,null}function kb(t){switch(t){case"beforetoggle":case"cancel":case"click":case"close":case"contextmenu":case"copy":case"cut":case"auxclick":case"dblclick":case"dragend":case"dragstart":case"drop":case"focusin":case"focusout":case"input":case"invalid":case"keydown":case"keypress":case"keyup":case"mousedown":case"mouseup":case"paste":case"pause":case"play":case"pointercancel":case"pointerdown":case"pointerup":case"ratechange":case"reset":case"resize":case"seeked":case"submit":case"toggle":case"touchcancel":case"touchend":case"touchstart":case"volumechange":case"change":case"selectionchange":case"textInput":case"compositionstart":case"compositionend":case"compositionupdate":case"beforeblur":case"afterblur":case"beforeinput":case"blur":case"fullscreenchange":case"focus":case"hashchange":case"popstate":case"select":case"selectstart":return 2;case"drag":case"dragenter":case"dragexit":case"dragleave":case"dragover":case"mousemove":case"mouseout":case"mouseover":case"pointermove":case"pointerout":case"pointerover":case"scroll":case"touchmove":case"wheel":case"mouseenter":case"mouseleave":case"pointerenter":case"pointerleave":return 8;case"message":switch(EM()){case o_:return 2;case c_:return 8;case Lu:case TM:return 32;case u_:return 268435456;default:return 32}default:return 32}}var sp=!1,vs=null,_s=null,ys=null,Bo=new Map,Fo=new Map,ns=[],B1="mousedown mouseup touchcancel touchend touchstart auxclick dblclick pointercancel pointerdown pointerup dragend dragstart drop compositionend compositionstart keydown keypress keyup input textInput copy cut paste click change contextmenu reset".split(" ");function e0(t,e){switch(t){case"focusin":case"focusout":vs=null;break;case"dragenter":case"dragleave":_s=null;break;case"mouseover":case"mouseout":ys=null;break;case"pointerover":case"pointerout":Bo.delete(e.pointerId);break;case"gotpointercapture":case"lostpointercapture":Fo.delete(e.pointerId)}}function Fl(t,e,n,i,a,s){return t===null||t.nativeEvent!==s?(t={blockedOn:e,domEventName:n,eventSystemFlags:i,nativeEvent:s,targetContainers:[a]},e!==null&&(e=Tl(e),e!==null&&Ib(e)),t):(t.eventSystemFlags|=i,e=t.targetContainers,a!==null&&e.indexOf(a)===-1&&e.push(a),t)}function F1(t,e,n,i,a){switch(e){case"focusin":return vs=Fl(vs,t,e,n,i,a),!0;case"dragenter":return _s=Fl(_s,t,e,n,i,a),!0;case"mouseover":return ys=Fl(ys,t,e,n,i,a),!0;case"pointerover":var s=a.pointerId;return Bo.set(s,Fl(Bo.get(s)||null,t,e,n,i,a)),!0;case"gotpointercapture":return s=a.pointerId,Fo.set(s,Fl(Fo.get(s)||null,t,e,n,i,a)),!0}return!1}function Bb(t){var e=Ir(t.target);if(e!==null){var n=Xo(e);if(n!==null){if(e=n.tag,e===13){if(e=i_(n),e!==null){t.blockedOn=e,kg(t.priority,function(){Jx(n)});return}}else if(e===31){if(e=a_(n),e!==null){t.blockedOn=e,kg(t.priority,function(){Jx(n)});return}}else if(e===3&&n.stateNode.current.memoizedState.isDehydrated){t.blockedOn=n.tag===3?n.stateNode.containerInfo:null;return}}}t.blockedOn=null}function vu(t){if(t.blockedOn!==null)return!1;for(var e=t.targetContainers;0<e.length;){var n=ap(t.nativeEvent);if(n===null){n=t.nativeEvent;var i=new n.constructor(n.type,n);Mh=i,n.target.dispatchEvent(i),Mh=null}else return e=Tl(n),e!==null&&Ib(e),t.blockedOn=n,!1;e.shift()}return!0}function t0(t,e,n){vu(t)&&n.delete(e)}function j1(){sp=!1,vs!==null&&vu(vs)&&(vs=null),_s!==null&&vu(_s)&&(_s=null),ys!==null&&vu(ys)&&(ys=null),Bo.forEach(t0),Fo.forEach(t0)}function _c(t,e){t.blockedOn===e&&(t.blockedOn=null,sp||(sp=!0,fn.unstable_scheduleCallback(fn.unstable_NormalPriority,j1)))}var yc=null;function n0(t){yc!==t&&(yc=t,fn.unstable_scheduleCallback(fn.unstable_NormalPriority,function(){yc===t&&(yc=null);for(var e=0;e<t.length;e+=3){var n=t[e],i=t[e+1],a=t[e+2];if(typeof i!="function"){if(sg(i||n)===null)continue;break}var s=Tl(n);s!==null&&(t.splice(e,3),e-=3,Bh(s,{pending:!0,data:a,method:n.method,action:i},i,a))}}))}function gl(t){function e(c){return _c(c,t)}vs!==null&&_c(vs,t),_s!==null&&_c(_s,t),ys!==null&&_c(ys,t),Bo.forEach(e),Fo.forEach(e);for(var n=0;n<ns.length;n++){var i=ns[n];i.blockedOn===t&&(i.blockedOn=null)}for(;0<ns.length&&(n=ns[0],n.blockedOn===null);)Bb(n),n.blockedOn===null&&ns.shift();if(n=(t.ownerDocument||t).$$reactFormReplay,n!=null)for(i=0;i<n.length;i+=3){var a=n[i],s=n[i+1],r=a[Kn]||null;if(typeof s=="function")r||n0(n);else if(r){var o=null;if(s&&s.hasAttribute("formAction")){if(a=s,r=s[Kn]||null)o=r.formAction;else if(sg(a)!==null)continue}else o=r.action;typeof o=="function"?n[i+1]=o:(n.splice(i,3),i-=3),n0(n)}}}function Fb(){function t(s){s.canIntercept&&s.info==="react-transition"&&s.intercept({handler:function(){return new Promise(function(r){return a=r})},focusReset:"manual",scroll:"manual"})}function e(){a!==null&&(a(),a=null),i||setTimeout(n,20)}function n(){if(!i&&!navigation.transition){var s=navigation.currentEntry;s&&s.url!=null&&navigation.navigate(s.url,{state:s.getState(),info:"react-transition",history:"replace"})}}if(typeof navigation=="object"){var i=!1,a=null;return navigation.addEventListener("navigate",t),navigation.addEventListener("navigatesuccess",e),navigation.addEventListener("navigateerror",e),setTimeout(n,100),function(){i=!0,navigation.removeEventListener("navigate",t),navigation.removeEventListener("navigatesuccess",e),navigation.removeEventListener("navigateerror",e),a!==null&&(a(),a=null)}}}function rg(t){this._internalRoot=t}Pf.prototype.render=rg.prototype.render=function(t){var e=this._internalRoot;if(e===null)throw Error(ue(409));var n=e.current,i=ui();zb(n,i,t,e,null,null)};Pf.prototype.unmount=rg.prototype.unmount=function(){var t=this._internalRoot;if(t!==null){this._internalRoot=null;var e=t.containerInfo;zb(t.current,2,null,t,null,null),Lf(),e[El]=null}};function Pf(t){this._internalRoot=t}Pf.prototype.unstable_scheduleHydration=function(t){if(t){var e=m_();t={blockedOn:null,target:t,priority:e};for(var n=0;n<ns.length&&e!==0&&e<ns[n].priority;n++);ns.splice(n,0,t),n===0&&Bb(t)}};var i0=t_.version;if(i0!=="19.2.8")throw Error(ue(527,i0,"19.2.8"));Tt.findDOMNode=function(t){var e=t._reactInternals;if(e===void 0)throw typeof t.render=="function"?Error(ue(188)):(t=Object.keys(t).join(","),Error(ue(268,t)));return t=xM(e),t=t!==null?s_(t):null,t=t===null?null:t.stateNode,t};var H1={bundleType:0,version:"19.2.8",rendererPackageName:"react-dom",currentDispatcherRef:tt,reconcilerVersion:"19.2.8"};if(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__<"u"){var bc=__REACT_DEVTOOLS_GLOBAL_HOOK__;if(!bc.isDisabled&&bc.supportsFiber)try{qo=bc.inject(H1),oi=bc}catch{}}vf.createRoot=function(t,e){if(!n_(t))throw Error(ue(299));var n=!1,i="",a=Dy,s=Ly,r=Uy;return e!=null&&(e.unstable_strictMode===!0&&(n=!0),e.identifierPrefix!==void 0&&(i=e.identifierPrefix),e.onUncaughtError!==void 0&&(a=e.onUncaughtError),e.onCaughtError!==void 0&&(s=e.onCaughtError),e.onRecoverableError!==void 0&&(r=e.onRecoverableError)),e=Ob(t,1,!1,null,null,n,i,null,a,s,r,Fb),t[El]=e.current,eg(t),new rg(e)};vf.hydrateRoot=function(t,e,n){if(!n_(t))throw Error(ue(299));var i=!1,a="",s=Dy,r=Ly,o=Uy,c=null;return n!=null&&(n.unstable_strictMode===!0&&(i=!0),n.identifierPrefix!==void 0&&(a=n.identifierPrefix),n.onUncaughtError!==void 0&&(s=n.onUncaughtError),n.onCaughtError!==void 0&&(r=n.onCaughtError),n.onRecoverableError!==void 0&&(o=n.onRecoverableError),n.formState!==void 0&&(c=n.formState)),e=Ob(t,1,!0,e,n??null,i,a,c,s,r,o,Fb),e.context=Pb(null),n=e.current,i=ui(),i=pm(i),a=ps(i),a.callback=null,ms(n,a,i),n=i,e.current.lanes=n,Ko(e,n),la(e),t[El]=e.current,eg(t),new Pf(e)};vf.version="19.2.8";function jb(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(jb)}catch(t){console.error(t)}}jb(),Kv.exports=vf;var G1=Kv.exports;function Hb(t){if(typeof t!="string"||t==="")return t;const e=t.split(/[\\/]/).filter(Boolean);return e.length>0?e[e.length-1]:t}const Gb="plugbrain.workspace",V1="plugbrain.auth_token";function Vb(){var e,n,i,a;let t="";try{t=((e=new URLSearchParams(window.location.search).get("token"))==null?void 0:e.trim())||((i=(n=window.__PLUGBRAIN__)==null?void 0:n.token)==null?void 0:i.trim())||((a=localStorage.getItem(V1))==null?void 0:a.trim())||""}catch{}return{"Content-Type":"application/json",...t?{Authorization:`Bearer ${t}`,"x-plug-auth-token":t}:{}}}function W1(){try{return localStorage.getItem(Gb)||""}catch{return""}}function X1(t){try{localStorage.setItem(Gb,t)}catch{}}async function a0(){const t=await fetch("/api/galaxy");if(!t.ok)throw new Error(`Galaxie: HTTP ${t.status}`);const e=await t.json();if(!(e!=null&&e.ok)||!Array.isArray(e.planets))throw new Error("Die Galaxie antwortet unvollständig.");return e.planets}function s0(t){if(typeof t!="string")return"";const e=t.trim();if(e==="")return"";if(/^[a-zA-Z]:[\\/]/.test(e)||/^[\\/]{2}/.test(e)){const i=e.replace(/\\/g,"/"),a=i.startsWith("//")?`//${i.slice(2).replace(/\/{2,}/g,"/")}`:i.replace(/\/{2,}/g,"/");return(a==="//"||/^[a-zA-Z]:\/$/.test(a)?a:a.replace(/\/+$/,"")).toLowerCase()}return e==="/"?e:e.replace(/\/+$/,"")}function q1(t,e){var i;const n=s0(e);return!n||!Array.isArray(t)?"":((i=t.find(a=>typeof(a==null?void 0:a.id)=="string"&&s0(a.root)===n))==null?void 0:i.id)??""}async function Y1(t,e,n){var s;const i=await fetch("/api/workspaces",{method:"POST",headers:Vb(),body:JSON.stringify({root:t,name:e})});if(!i.ok){const r=await i.json().catch(()=>null);throw new Error((r==null?void 0:r.error)??`Registrieren: HTTP ${i.status}`)}const a=await i.json();if(!(a!=null&&a.ok)||!((s=a.workspace)!=null&&s.id))throw new Error("Registrieren: unvollständige Antwort.");return await Wb(a.workspace.id,n),a.workspace.id}function rp(t){var r,o,c;const e=t==null?void 0:t.run;if(!e)return"Kein Indexlauf bekannt.";if(t.stale)return t.ownerAlive&&!t.recoverable?"Indexlauf ohne neuen Fortschritt; der Owner-Prozess läuft noch. Die Sperre bleibt geschützt.":"Indexlauf ohne neuen Fortschritt; der frühere Owner ist nicht mehr aktiv. Er kann erneut gestartet werden.";const n=Math.max(0,Math.round((Date.now()-Date.parse(e.startedAt))/1e3)),i={starting:"startet",scan:"sammelt Dateien",classify:"vergleicht",write:"schreibt",resolve:"verknüpft",publish:"veröffentlicht",done:"fertig",failed:"fehlgeschlagen"}[e.phase]??e.phase;if(e.finishedAt)return e.ok?`Fertig: ${((r=e.result)==null?void 0:r.files)??e.scanned} Dateien, ${((o=e.result)==null?void 0:o.symbols)??0} Symbole, ${((c=e.result)==null?void 0:c.edges)??0} Kanten in ${n} s.`:`Indexlauf fehlgeschlagen: ${e.error??"unbekannter Grund"}`;const a=e.total>0?`/${e.total}`:"",s=e.total>0?` (${Math.round(e.processed/e.total*100)} %)`:"";return`Indexiert: ${i} ${e.processed}${a}${s} — ${n} s`}function K1(t,e){return t!=null&&t.running||t!=null&&t.stale?rp(t):e.startsWith("Indexiert:")||e.startsWith("Indexlauf ohne neuen Fortschritt;")?"":e}async function Z1(t){const e=await fetch(`/api/index/progress?workspace=${encodeURIComponent(t)}`);return e.ok?e.json():null}const $1=t=>new Promise(e=>setTimeout(e,t));async function Q1(t,e){for(;;){await $1(900);const n=await Z1(t);if(n===null)throw new Error("Der Fortschritt ist nicht abrufbar.");if(e==null||e(n),n.running)continue;if(n.stale)throw n.ownerAlive&&!n.recoverable?new Error("Der Indexlauf meldet keinen neuen Fortschritt, aber der Owner-Prozess läuft noch. Die Sperre bleibt geschützt; nach Ende oder Neustart des Owners erneut indizieren."):new Error("Der Indexlauf ist verstummt und sein Owner ist nicht mehr aktiv. Er kann erneut gestartet werden.");const i=n.run;if(!i)throw new Error("Kein Indexlauf bekannt.");if(i.ok)return i.result;throw new Error(i.error??"Indexlauf fehlgeschlagen.")}}async function Wb(t,e){const n=await fetch("/api/reindex",{method:"POST",headers:Vb(),body:JSON.stringify({workspace:t})}),i=await n.json().catch(()=>null);if(n.status===409&&(i!=null&&i.busy))throw i.ownerAlive&&!i.recoverable?new Error("Ein stiller Indexlauf gehört noch einem lebenden Owner-Prozess. Die Sperre bleibt geschützt; nach Ende oder Neustart des Owners erneut indizieren."):i.recoverable?new Error("Der vorherige Index-Owner ist nicht mehr aktiv. Der Lauf kann erneut gestartet werden."):new Error(i.error??"Ein Indexlauf ist bereits unterwegs.");if(!n.ok)throw new Error(`Indizieren: HTTP ${n.status}`);if(!(i!=null&&i.ok))throw new Error("Indizieren: unvollständige Antwort.");return i.result!==void 0&&i.result!==null?i.result:Q1(t,e)}const Xb="plugbrain.auth_token",lp="plugbrain.agent_id";function J1(){var t,e;try{return((e=(t=window.__PLUGBRAIN__)==null?void 0:t.token)==null?void 0:e.trim())??""}catch{return""}}function qb(){var n,i;let t="";try{t=((n=new URLSearchParams(window.location.search).get("token"))==null?void 0:n.trim())??""}catch{}if(t)return Yb(t),t;const e=J1();if(e)return e;try{return((i=localStorage.getItem(Xb))==null?void 0:i.trim())??""}catch{return""}}function Yb(t){try{localStorage.setItem(Xb,t)}catch{}}function Fi(){try{const t=new URLSearchParams(window.location.search).get("agent");return t?(localStorage.setItem(lp,t),t):localStorage.getItem(lp)||"agy"}catch{return"agy"}}function eT(t){try{localStorage.setItem(lp,t)}catch{}}function Qn(){const t=qb(),e={"Content-Type":"application/json"};return t&&(e.Authorization=`Bearer ${t}`,e["x-plug-auth-token"]=t),e}async function tT(t){const e=await fetch(`/api/planet?workspace=${encodeURIComponent(t)}`);if(!e.ok){const i=await e.json().catch(()=>null);throw new Error((i==null?void 0:i.error)??`Planet inventory HTTP ${e.status}`)}const n=await e.json();if(!(n!=null&&n.ok)||!n.planet||!Array.isArray(n.planet.checkouts))throw new Error("Planet-Inventar unvollständig");return n.planet}async function nT(t,e){const n=await fetch("/api/planet/selection",{method:"POST",headers:Qn(),body:JSON.stringify({workspace:t,checkoutIds:e})}),i=await n.json().catch(()=>null);if(!n.ok||!(i!=null&&i.ok)||!i.planet)throw new Error((i==null?void 0:i.error)??`Code-Auswahl HTTP ${n.status}`);return i.planet}async function Kb(t){const e=await fetch(`/api/git?workspace=${encodeURIComponent(t)}`);if(!e.ok)throw new Error(`Git-Status HTTP ${e.status}`);return e.json()}async function iT(t){const e=await fetch(`/api/mesh?workspace=${encodeURIComponent(t)}`);if(!e.ok)throw new Error(`Mesh HTTP ${e.status}`);const n=await e.json(),i=n==null?void 0:n.mesh;if(!(n!=null&&n.ok)||!i||i.workspaceId!==t||!Array.isArray(i.nodes)||!Array.isArray(i.edges))throw new Error("Mesh-Projektion unvollständig oder für einen anderen Workspace");return i}async function aT(t,e={}){const n=new URLSearchParams({workspace:t});e.agentId&&n.set("agentId",e.agentId),e.taskId&&n.set("taskId",e.taskId),e.workerId&&n.set("workerId",e.workerId),e.limit!==void 0&&n.set("limit",String(e.limit));const i=await fetch(`/api/mesh/timeline?${n}`);if(!i.ok)throw new Error(`Mesh-Zeitleiste HTTP ${i.status}`);const a=await i.json();if(!(a!=null&&a.ok)||!Array.isArray(a.timeline))throw new Error("Mesh-Zeitleiste unvollständig");return a.timeline}async function sT(t,e){const n=await fetch(`/api/provenance?workspace=${encodeURIComponent(t)}&path=${encodeURIComponent(e)}`);if(!n.ok)throw new Error(`Provenance HTTP ${n.status}`);return n.json()}const rT=6e4;async function lT(t,e=Fi(),n="AGY",{maxWaitMs:i=rT}={}){const a=Date.now()+i;for(;;){const s=await fetch("/api/agent/attach",{method:"POST",headers:Qn(),body:JSON.stringify({workspace:t,agentId:e,name:n})});if(s.ok)return s.json();const r=await s.json().catch(()=>null),o=a-Date.now();if(s.status===503&&(r==null?void 0:r.busy)===!0&&o>0){const c=Math.min(Math.max(Number(r.retryAfterMs)||1e3,5),5e3,o);await new Promise(u=>setTimeout(u,c));continue}throw new Error((r==null?void 0:r.error)??(r==null?void 0:r.reason)??`Agent Attach HTTP ${s.status}`)}}const _u=new Map;function ac(t,e=Fi()){const n=`${t}\0${e}`,i=_u.get(n);if(i)return i;const a=lT(t,e).then(()=>{}).catch(()=>{_u.delete(n)});return _u.set(n,a),a}function oT(){_u.clear()}async function op(t,e,n=Fi()){await ac(t,n);const i=await fetch("/api/agent/search",{method:"POST",headers:Qn(),body:JSON.stringify({workspace:t,agentId:n,query:e})});if(!i.ok){const s=await i.json().catch(()=>null);throw new Error((s==null?void 0:s.error)??`Search HTTP ${i.status}`)}const a=await i.json();return Array.isArray(a==null?void 0:a.hits)?a.hits:[]}async function cT(t,e,n=Fi()){await ac(t,n);const i=await fetch("/api/agent/read",{method:"POST",headers:Qn(),body:JSON.stringify({workspace:t,agentId:n,path:e})});if(!i.ok){const s=await i.json().catch(()=>null),r=(s==null?void 0:s.error)??`HTTP ${i.status}`;return{ok:!1,path:e,content:"",bytes:0,lang:null,error:r}}const a=await i.json();return{ok:!0,path:a.path??e,content:a.content??"",bytes:a.bytes??0,lang:a.lang??null}}async function r0(t,e,n=Fi()){const i=await fetch("/api/context/pack",{method:"POST",headers:Qn(),body:JSON.stringify({workspaceId:t,goal:e,agentId:n})});if(!i.ok){const a=await i.json().catch(()=>null);throw new Error((a==null?void 0:a.error)??`Context Pack HTTP ${i.status}`)}return i.json()}async function uT(t){const e=await fetch(`/api/context/pack/${encodeURIComponent(t)}/staleness`);if(!e.ok){const n=await e.json().catch(()=>null);throw new Error((n==null?void 0:n.error)??`Staleness HTTP ${e.status}`)}return e.json()}async function fT(t,e){const n=await fetch(`/api/notes/query?workspace=${encodeURIComponent(t)}&q=${encodeURIComponent(e)}`,{headers:Qn()});if(!n.ok){const i=await n.json().catch(()=>null);throw new Error((i==null?void 0:i.error)??`Notes Query HTTP ${n.status}`)}return n.json()}async function cp(t,e,n=30){const i=await fetch(`/api/notes/search?workspace=${encodeURIComponent(t)}&q=${encodeURIComponent(e)}&limit=${n}&lines=1`,{headers:Qn()});if(!i.ok){const a=await i.json().catch(()=>null);throw new Error((a==null?void 0:a.error)??`Notizsuche HTTP ${i.status}`)}return i.json()}async function dT(t,e){const n=await fetch(`/api/notes/backlinks?workspace=${encodeURIComponent(t)}&path=${encodeURIComponent(e)}`);if(!n.ok)return[];const i=await n.json();return Array.isArray(i==null?void 0:i.backlinks)?i.backlinks:[]}async function hT(t){const e=await fetch(`/api/notes?workspace=${encodeURIComponent(t)}&limit=5000`),n=await e.json().catch(()=>null);if(!e.ok||!(n!=null&&n.ok))throw new Error((n==null?void 0:n.error)??`Notizen HTTP ${e.status}`);return Array.isArray(n.notes)?n.notes:[]}async function Sc(t,e,n=Fi()){await ac(t,n);const i=await fetch(`/api/notes/read?workspace=${encodeURIComponent(t)}&path=${encodeURIComponent(e)}&agentId=${encodeURIComponent(n)}`),a=await i.json().catch(()=>null);if(!i.ok||!(a!=null&&a.ok)||!a.note)throw new Error((a==null?void 0:a.error)??`Notiz lesen HTTP ${i.status}`);return a.note}async function jl(t,e,n,i,a=Fi(),s=!1){await ac(t,a);const r=await fetch("/api/notes/write",{method:"POST",headers:Qn(),body:JSON.stringify({workspace:t,agentId:a,path:e,content:n,...i?{expectedHash:i}:{},...s?{createOnly:!0}:{}})}),o=await r.json().catch(()=>null);if(!r.ok||!(o!=null&&o.ok)){const c=new Error((o==null?void 0:o.error)??`Notiz speichern HTTP ${r.status}`);throw Object.assign(c,{conflict:o==null?void 0:o.conflict,status:r.status}),c}return o}async function pT(t,e={}){const n=new URLSearchParams({workspace:t});e.focus&&n.set("focus",e.focus),e.depth!==void 0&&n.set("depth",String(e.depth)),e.filter&&n.set("filter",e.filter),e.limit!==void 0&&n.set("limit",String(e.limit));const i=await fetch(`/api/notes/graph?${n}`),a=await i.json().catch(()=>null);if(!i.ok||!(a!=null&&a.ok)||!a.graph)throw new Error((a==null?void 0:a.error)??`Wissensgraph HTTP ${i.status}`);return a.graph}async function l0(t,e){const n=await fetch(`/api/notes/attachments?workspace=${encodeURIComponent(t)}¬e=${encodeURIComponent(e)}`),i=await n.json().catch(()=>null);if(!n.ok||!(i!=null&&i.ok))throw new Error((i==null?void 0:i.error)??`Anhänge HTTP ${n.status}`);return Array.isArray(i.attachments)?i.attachments:[]}async function mT(t,e,n,i=Fi()){await ac(t,i);const a=new Uint8Array(await n.arrayBuffer());let s="";for(let u=0;u<a.length;u+=32768)s+=String.fromCharCode(...a.subarray(u,u+32768));const r=btoa(s),o=await fetch("/api/notes/attachment",{method:"POST",headers:Qn(),body:JSON.stringify({workspace:t,agentId:i,note:e,name:n.name,base64:r})}),c=await o.json().catch(()=>null);if(!o.ok||!(c!=null&&c.ok)||!c.attachment)throw new Error((c==null?void 0:c.error)??`Anhang speichern HTTP ${o.status}`);return c.attachment}function gT(t,e,n,i=Fi()){return`/api/notes/attachment?workspace=${encodeURIComponent(t)}¬e=${encodeURIComponent(e)}&name=${encodeURIComponent(n)}&agentId=${encodeURIComponent(i)}`}function o0(t,e,n=Fi()){const i=new URLSearchParams({workspace:t,agentId:n});return e&&i.set("note",e),`/api/notes/export?${i}`}async function xT(t){try{const e=await fetch(`/api/briefing?workspace=${encodeURIComponent(t)}`,{headers:Qn()});if(e.status===404||e.status===501)return{routeMissing:!0};if(!e.ok){const n=await e.json().catch(()=>null);return{serverError:!0,status:e.status,error:(n==null?void 0:n.error)??`HTTP ${e.status}`}}return await e.json()}catch(e){return{unreachable:!0,error:e instanceof Error?e.message:String(e)}}}async function vT(t,e,n=5){try{const i=await fetch("/api/ask",{method:"POST",headers:Qn(),body:JSON.stringify({workspace:t,question:e,limit:n})});if(i.status===404||i.status===501)return{routeMissing:!0};if(!i.ok){const a=await i.json().catch(()=>null);return{serverError:!0,status:i.status,error:(a==null?void 0:a.error)??`HTTP ${i.status}`}}return await i.json()}catch(i){return{unreachable:!0,error:i instanceof Error?i.message:String(i)}}}async function _T(t){try{const e=await fetch(`/api/hygiene?workspace=${encodeURIComponent(t)}`,{headers:Qn()});if(e.status===404||e.status===501)return{routeMissing:!0};if(!e.ok){const n=await e.json().catch(()=>null);return{serverError:!0,status:e.status,error:(n==null?void 0:n.error)??`HTTP ${e.status}`}}return await e.json()}catch(e){return{unreachable:!0,error:e instanceof Error?e.message:String(e)}}}async function yT(){try{const t=await fetch("/api/machine",{headers:Qn()});if(t.status===404||t.status===501)return{routeMissing:!0};if(!t.ok){const e=await t.json().catch(()=>null);return{serverError:!0,status:t.status,error:(e==null?void 0:e.error)??`HTTP ${t.status}`}}return await t.json()}catch(t){return{unreachable:!0,error:t instanceof Error?t.message:String(t)}}}async function bT(t=!1){try{const n=await fetch(`/api/repos${t?"?dirty=1&page=1":"?page=1"}`,{headers:Qn()});if(n.status===404||n.status===501)return{routeMissing:!0};if(!n.ok){const i=await n.json().catch(()=>null);return{serverError:!0,status:n.status,error:(i==null?void 0:i.error)??`HTTP ${n.status}`}}return await n.json()}catch(e){return{unreachable:!0,error:e instanceof Error?e.message:String(e)}}}const qt=[],cs=[],va=[],jo=[],Eo={},xn=[],yu=[],Zi=7.2,qr=6,Ho=["--k1","--k2","--k3","--k4","--k5","--k6"],lg=t=>getComputedStyle(document.documentElement).getPropertyValue(t).trim(),ST=t=>t.agentColor||lg(Ho[(t.ki??0)%Ho.length]),c0=t=>lg(Ho[t.ki%Ho.length]),Zb=new Map;let $b="loc";function MT(t){$b=t}const ET=t=>{const e=Math.max(1,...qt.map(i=>i.loc)),n=Math.max(1,...qt.map(i=>i.usedBy.length));return t.dying?0:$b==="loc"?1.5+t.loc/e*26:1.5+t.usedBy.length/n*26},up=new Set,TT=t=>(up.add(t),()=>up.delete(t)),xl=()=>up.forEach(t=>t());function fp(t,e,n="ok"){yu.unshift({t:new Date,ws:t,msg:e,kind:n,id:Math.random().toString(36).slice(2)}),yu.length>60&&yu.pop()}function zf(){var o;let e=0,n=0,i=0;const a=jo.filter(c=>xn.find(u=>u.id===c)),s=new Set;for(const c of a){const u=qt.filter(x=>x.dir===c&&!x.dying);if(!u.length&&((o=xn.find(x=>x.id===c))!=null&&o.dying))continue;const d=Math.max(1,Math.ceil(Math.sqrt(Math.max(1,u.length)))),m=d*Zi+qr,f=Math.max(1,Math.ceil(Math.max(1,u.length)/d))*Zi+qr;n+m>74&&n>0&&(e+=i,n=0,i=0);let p=va.find(x=>x.dir===c);p||(p={dir:c,x:n+m/2,z:e+f/2,w:.01,h:.01},va.push(p)),Object.assign(p,{tx:n,tz:e,tw:m,th:f,cols:d}),s.add(c),n+=m,i=Math.max(i,f)}const r=va.filter(c=>s.has(c.dir));if(r.length){const c=Math.max(...r.map(d=>d.tx+d.tw))/2,u=Math.max(...r.map(d=>d.tz+d.th))/2;for(const d of r)d.tx-=c,d.tz-=u;for(const d of r)qt.filter(f=>f.dir===d.dir).forEach((f,p)=>{f.tx=d.tx+qr/2+p%d.cols*Zi+Zi/2,f.tz=d.tz+qr/2+Math.floor(p/d.cols)*Zi+Zi/2,f.x===void 0&&(f.x=f.tx,f.z=f.tz)})}for(let c=va.length-1;c>=0;c--)!s.has(va[c].dir)&&!qt.some(u=>u.dir===va[c].dir)&&va.splice(c,1);for(const c of a)Zb.set(c,.5)}function AT(t,e,n=!1){let i=xn.find(a=>a.id===t);return i||(i={id:t,name:e||t,ki:xn.length,load:0,events:0,createdAt:new Date,dying:!1,sim:n},xn.push(i),jo.includes(t)||jo.push(t),fp(e||t,"workspace registered","reg"),zf(),xl(),i)}function wT(t,{path:e,loc:n=40,deps:i=[],note:a="",agentColor:s=null,agentName:r=null,access:o=null}){const c=e.split("/").pop(),u=e.includes("/")&&e.startsWith(t.id+"/")?e:`${t.id}/${e}`;let d=Eo[u];if(d)return d.loc+=Math.max(2,Math.round(n*.25)),d.pulse=1,s&&(d.agentColor=s,d.agentName=r,d.access=o),d;d={path:u,name:c,dir:t.id,top:t.id,ki:t.ki,loc:n,deps:[],usedBy:[],note:a,agentColor:s,agentName:r,access:o,x:void 0,z:void 0,h:0,pulse:1,dying:!1};for(let m of i){m.includes("/")||(m=`${t.id}/${m}`);const f=Eo[m];f&&(d.deps.push(m),cs.push({from:d,to:f}),f.usedBy.push(u))}return qt.push(d),Eo[u]=d,zf(),xl(),d}function CT(){let t=!1;for(let e=qt.length-1;e>=0;e--){const n=qt[e];if(n.dying&&n.h<.25){qt.splice(e,1),delete Eo[n.path],t=!0;for(let i=cs.length-1;i>=0;i--)(cs[i].from===n||cs[i].to===n)&&cs.splice(i,1);for(const i of qt){const a=i.deps.indexOf(n.path);a>=0&&i.deps.splice(a,1);const s=i.usedBy.indexOf(n.path);s>=0&&i.usedBy.splice(s,1)}}}for(let e=xn.length-1;e>=0;e--){const n=xn[e];if(n.dying&&!qt.some(i=>i.dir===n.id)){xn.splice(e,1);const i=jo.indexOf(n.id);i>=0&&jo.splice(i,1),t=!0}}t&&(zf(),xl())}setInterval(()=>{let t=!1;for(const e of xn)e.load>.01&&(e.load*=.82,t=!0);t&&xl()},600);const ao={register({id:t,name:e}={}){return t?AT(String(t),e&&String(e),!1):console.warn("[PlugBrainCity] register() needs an id")},grow(t,{path:e,loc:n=40,deps:i=[],note:a="",agentColor:s=null,agentName:r=null,access:o=null}={}){const c=xn.find(u=>u.id===t);return!c||!e?console.warn("[PlugBrainCity] grow() needs a registered workspace id and a path"):(c.load=Math.min(1,c.load+.3),c.events++,wT(c,{path:e,loc:n,deps:i,note:a,agentColor:s,agentName:r,access:o}))},event(t,e){const n=xn.find(a=>a.id===t);if(!n)return;const i=qt.filter(a=>a.dir===t&&!a.dying);i.length&&(i[Math.floor(Math.random()*i.length)].pulse=1),n.load=Math.min(1,n.load+.25),n.events++,fp(n.name,String(e||"event")),xl()},unregister(t){const e=xn.find(n=>n.id===t);e&&(e.dying=!0,qt.filter(n=>n.dir===t).forEach(n=>{n.dying=!0}),fp(e.name,"workspace unregistered","sys"),zf(),xl())},list:()=>xn.map(t=>({id:t.id,name:t.name,buildings:qt.filter(e=>e.dir===t.id).length})),simulated:()=>!1};window.PlugBrainCity=ao;/** - * @license - * Copyright 2010-2026 Three.js Authors - * SPDX-License-Identifier: MIT - */const og="185",NT=0,u0=1,RT=2,bu=1,DT=2,so=3,Ts=0,Yn=1,ba=2,Na=0,nl=1,f0=2,d0=3,h0=4,LT=5,Bs=100,UT=101,OT=102,PT=103,zT=104,IT=200,kT=201,BT=202,FT=203,dp=204,hp=205,jT=206,HT=207,GT=208,VT=209,WT=210,XT=211,qT=212,YT=213,KT=214,pp=0,mp=1,gp=2,vl=3,xp=4,vp=5,_p=6,yp=7,Qb=0,ZT=1,$T=2,ia=0,Jb=1,eS=2,tS=3,nS=4,iS=5,aS=6,sS=7,rS=300,Js=301,_l=302,Td=303,Ad=304,If=306,bp=1e3,Ta=1001,Sp=1002,En=1003,QT=1004,Mc=1005,Rn=1006,wd=1007,js=1008,Ei=1009,lS=1010,oS=1011,Go=1012,cg=1013,sa=1014,Ii=1015,za=1016,ug=1017,fg=1018,Vo=1020,cS=35902,uS=35899,fS=1021,dS=1022,ki=1023,Ia=1026,Hs=1027,dg=1028,hg=1029,er=1030,pg=1031,mg=1033,Su=33776,Mu=33777,Eu=33778,Tu=33779,Mp=35840,Ep=35841,Tp=35842,Ap=35843,wp=36196,Cp=37492,Np=37496,Rp=37488,Dp=37489,sf=37490,Lp=37491,Up=37808,Op=37809,Pp=37810,zp=37811,Ip=37812,kp=37813,Bp=37814,Fp=37815,jp=37816,Hp=37817,Gp=37818,Vp=37819,Wp=37820,Xp=37821,qp=36492,Yp=36494,Kp=36495,Zp=36283,$p=36284,rf=36285,Qp=36286,JT=3200,p0=0,eA=1,is="",xi="srgb",lf="srgb-linear",of="linear",Ct="srgb",gr=7680,m0=519,tA=512,nA=513,iA=514,gg=515,aA=516,sA=517,xg=518,rA=519,g0=35044,x0="300 es",ta=2e3,cf=2001;function lA(t){for(let e=t.length-1;e>=0;--e)if(t[e]>=65535)return!0;return!1}function uf(t){return document.createElementNS("http://www.w3.org/1999/xhtml",t)}function oA(){const t=uf("canvas");return t.style.display="block",t}const v0={};function _0(...t){const e="THREE."+t.shift();console.log(e,...t)}function hS(t){const e=t[0];if(typeof e=="string"&&e.startsWith("TSL:")){const n=t[1];n&&n.isStackTrace?t[0]+=" "+n.getLocation():t[1]='Stack trace not available. Enable "THREE.Node.captureStackTrace" to capture stack traces.'}return t}function Je(...t){t=hS(t);const e="THREE."+t.shift();{const n=t[0];n&&n.isStackTrace?console.warn(n.getError(e)):console.warn(e,...t)}}function St(...t){t=hS(t);const e="THREE."+t.shift();{const n=t[0];n&&n.isStackTrace?console.error(n.getError(e)):console.error(e,...t)}}function il(...t){const e=t.join(" ");e in v0||(v0[e]=!0,Je(...t))}function cA(t,e,n){return new Promise(function(i,a){function s(){switch(t.clientWaitSync(e,t.SYNC_FLUSH_COMMANDS_BIT,0)){case t.WAIT_FAILED:a();break;case t.TIMEOUT_EXPIRED:setTimeout(s,n);break;default:i()}}setTimeout(s,n)})}const uA={[pp]:mp,[gp]:_p,[xp]:yp,[vl]:vp,[mp]:pp,[_p]:gp,[yp]:xp,[vp]:vl};class cr{addEventListener(e,n){this._listeners===void 0&&(this._listeners={});const i=this._listeners;i[e]===void 0&&(i[e]=[]),i[e].indexOf(n)===-1&&i[e].push(n)}hasEventListener(e,n){const i=this._listeners;return i===void 0?!1:i[e]!==void 0&&i[e].indexOf(n)!==-1}removeEventListener(e,n){const i=this._listeners;if(i===void 0)return;const a=i[e];if(a!==void 0){const s=a.indexOf(n);s!==-1&&a.splice(s,1)}}dispatchEvent(e){const n=this._listeners;if(n===void 0)return;const i=n[e.type];if(i!==void 0){e.target=this;const a=i.slice(0);for(let s=0,r=a.length;s<r;s++)a[s].call(this,e);e.target=null}}}const Cn=["00","01","02","03","04","05","06","07","08","09","0a","0b","0c","0d","0e","0f","10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f","20","21","22","23","24","25","26","27","28","29","2a","2b","2c","2d","2e","2f","30","31","32","33","34","35","36","37","38","39","3a","3b","3c","3d","3e","3f","40","41","42","43","44","45","46","47","48","49","4a","4b","4c","4d","4e","4f","50","51","52","53","54","55","56","57","58","59","5a","5b","5c","5d","5e","5f","60","61","62","63","64","65","66","67","68","69","6a","6b","6c","6d","6e","6f","70","71","72","73","74","75","76","77","78","79","7a","7b","7c","7d","7e","7f","80","81","82","83","84","85","86","87","88","89","8a","8b","8c","8d","8e","8f","90","91","92","93","94","95","96","97","98","99","9a","9b","9c","9d","9e","9f","a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","aa","ab","ac","ad","ae","af","b0","b1","b2","b3","b4","b5","b6","b7","b8","b9","ba","bb","bc","bd","be","bf","c0","c1","c2","c3","c4","c5","c6","c7","c8","c9","ca","cb","cc","cd","ce","cf","d0","d1","d2","d3","d4","d5","d6","d7","d8","d9","da","db","dc","dd","de","df","e0","e1","e2","e3","e4","e5","e6","e7","e8","e9","ea","eb","ec","ed","ee","ef","f0","f1","f2","f3","f4","f5","f6","f7","f8","f9","fa","fb","fc","fd","fe","ff"],Au=Math.PI/180,Jp=180/Math.PI;function sc(){const t=Math.random()*4294967295|0,e=Math.random()*4294967295|0,n=Math.random()*4294967295|0,i=Math.random()*4294967295|0;return(Cn[t&255]+Cn[t>>8&255]+Cn[t>>16&255]+Cn[t>>24&255]+"-"+Cn[e&255]+Cn[e>>8&255]+"-"+Cn[e>>16&15|64]+Cn[e>>24&255]+"-"+Cn[n&63|128]+Cn[n>>8&255]+"-"+Cn[n>>16&255]+Cn[n>>24&255]+Cn[i&255]+Cn[i>>8&255]+Cn[i>>16&255]+Cn[i>>24&255]).toLowerCase()}function pt(t,e,n){return Math.max(e,Math.min(n,t))}function fA(t,e){return(t%e+e)%e}function Cd(t,e,n){return(1-n)*t+n*e}function Hl(t,e){switch(e.constructor){case Float32Array:return t;case Uint32Array:return t/4294967295;case Uint16Array:return t/65535;case Uint8Array:return t/255;case Int32Array:return Math.max(t/2147483647,-1);case Int16Array:return Math.max(t/32767,-1);case Int8Array:return Math.max(t/127,-1);default:throw new Error("THREE.MathUtils: Invalid component type.")}}function Hn(t,e){switch(e.constructor){case Float32Array:return t;case Uint32Array:return Math.round(t*4294967295);case Uint16Array:return Math.round(t*65535);case Uint8Array:return Math.round(t*255);case Int32Array:return Math.round(t*2147483647);case Int16Array:return Math.round(t*32767);case Int8Array:return Math.round(t*127);default:throw new Error("THREE.MathUtils: Invalid component type.")}}const Eg=class Eg{constructor(e=0,n=0){this.x=e,this.y=n}get width(){return this.x}set width(e){this.x=e}get height(){return this.y}set height(e){this.y=e}set(e,n){return this.x=e,this.y=n,this}setScalar(e){return this.x=e,this.y=e,this}setX(e){return this.x=e,this}setY(e){return this.y=e,this}setComponent(e,n){switch(e){case 0:this.x=n;break;case 1:this.y=n;break;default:throw new Error("THREE.Vector2: index is out of range: "+e)}return this}getComponent(e){switch(e){case 0:return this.x;case 1:return this.y;default:throw new Error("THREE.Vector2: index is out of range: "+e)}}clone(){return new this.constructor(this.x,this.y)}copy(e){return this.x=e.x,this.y=e.y,this}add(e){return this.x+=e.x,this.y+=e.y,this}addScalar(e){return this.x+=e,this.y+=e,this}addVectors(e,n){return this.x=e.x+n.x,this.y=e.y+n.y,this}addScaledVector(e,n){return this.x+=e.x*n,this.y+=e.y*n,this}sub(e){return this.x-=e.x,this.y-=e.y,this}subScalar(e){return this.x-=e,this.y-=e,this}subVectors(e,n){return this.x=e.x-n.x,this.y=e.y-n.y,this}multiply(e){return this.x*=e.x,this.y*=e.y,this}multiplyScalar(e){return this.x*=e,this.y*=e,this}divide(e){return this.x/=e.x,this.y/=e.y,this}divideScalar(e){return this.multiplyScalar(1/e)}applyMatrix3(e){const n=this.x,i=this.y,a=e.elements;return this.x=a[0]*n+a[3]*i+a[6],this.y=a[1]*n+a[4]*i+a[7],this}min(e){return this.x=Math.min(this.x,e.x),this.y=Math.min(this.y,e.y),this}max(e){return this.x=Math.max(this.x,e.x),this.y=Math.max(this.y,e.y),this}clamp(e,n){return this.x=pt(this.x,e.x,n.x),this.y=pt(this.y,e.y,n.y),this}clampScalar(e,n){return this.x=pt(this.x,e,n),this.y=pt(this.y,e,n),this}clampLength(e,n){const i=this.length();return this.divideScalar(i||1).multiplyScalar(pt(i,e,n))}floor(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this}ceil(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this}round(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this}roundToZero(){return this.x=Math.trunc(this.x),this.y=Math.trunc(this.y),this}negate(){return this.x=-this.x,this.y=-this.y,this}dot(e){return this.x*e.x+this.y*e.y}cross(e){return this.x*e.y-this.y*e.x}lengthSq(){return this.x*this.x+this.y*this.y}length(){return Math.sqrt(this.x*this.x+this.y*this.y)}manhattanLength(){return Math.abs(this.x)+Math.abs(this.y)}normalize(){return this.divideScalar(this.length()||1)}angle(){return Math.atan2(-this.y,-this.x)+Math.PI}angleTo(e){const n=Math.sqrt(this.lengthSq()*e.lengthSq());if(n===0)return Math.PI/2;const i=this.dot(e)/n;return Math.acos(pt(i,-1,1))}distanceTo(e){return Math.sqrt(this.distanceToSquared(e))}distanceToSquared(e){const n=this.x-e.x,i=this.y-e.y;return n*n+i*i}manhattanDistanceTo(e){return Math.abs(this.x-e.x)+Math.abs(this.y-e.y)}setLength(e){return this.normalize().multiplyScalar(e)}lerp(e,n){return this.x+=(e.x-this.x)*n,this.y+=(e.y-this.y)*n,this}lerpVectors(e,n,i){return this.x=e.x+(n.x-e.x)*i,this.y=e.y+(n.y-e.y)*i,this}equals(e){return e.x===this.x&&e.y===this.y}fromArray(e,n=0){return this.x=e[n],this.y=e[n+1],this}toArray(e=[],n=0){return e[n]=this.x,e[n+1]=this.y,e}fromBufferAttribute(e,n){return this.x=e.getX(n),this.y=e.getY(n),this}rotateAround(e,n){const i=Math.cos(n),a=Math.sin(n),s=this.x-e.x,r=this.y-e.y;return this.x=s*i-r*a+e.x,this.y=s*a+r*i+e.y,this}random(){return this.x=Math.random(),this.y=Math.random(),this}*[Symbol.iterator](){yield this.x,yield this.y}};Eg.prototype.isVector2=!0;let At=Eg;class Dl{constructor(e=0,n=0,i=0,a=1){this.isQuaternion=!0,this._x=e,this._y=n,this._z=i,this._w=a}static slerpFlat(e,n,i,a,s,r,o){let c=i[a+0],u=i[a+1],d=i[a+2],m=i[a+3],f=s[r+0],p=s[r+1],x=s[r+2],_=s[r+3];if(m!==_||c!==f||u!==p||d!==x){let g=c*f+u*p+d*x+m*_;g<0&&(f=-f,p=-p,x=-x,_=-_,g=-g);let h=1-o;if(g<.9995){const v=Math.acos(g),b=Math.sin(v);h=Math.sin(h*v)/b,o=Math.sin(o*v)/b,c=c*h+f*o,u=u*h+p*o,d=d*h+x*o,m=m*h+_*o}else{c=c*h+f*o,u=u*h+p*o,d=d*h+x*o,m=m*h+_*o;const v=1/Math.sqrt(c*c+u*u+d*d+m*m);c*=v,u*=v,d*=v,m*=v}}e[n]=c,e[n+1]=u,e[n+2]=d,e[n+3]=m}static multiplyQuaternionsFlat(e,n,i,a,s,r){const o=i[a],c=i[a+1],u=i[a+2],d=i[a+3],m=s[r],f=s[r+1],p=s[r+2],x=s[r+3];return e[n]=o*x+d*m+c*p-u*f,e[n+1]=c*x+d*f+u*m-o*p,e[n+2]=u*x+d*p+o*f-c*m,e[n+3]=d*x-o*m-c*f-u*p,e}get x(){return this._x}set x(e){this._x=e,this._onChangeCallback()}get y(){return this._y}set y(e){this._y=e,this._onChangeCallback()}get z(){return this._z}set z(e){this._z=e,this._onChangeCallback()}get w(){return this._w}set w(e){this._w=e,this._onChangeCallback()}set(e,n,i,a){return this._x=e,this._y=n,this._z=i,this._w=a,this._onChangeCallback(),this}clone(){return new this.constructor(this._x,this._y,this._z,this._w)}copy(e){return this._x=e.x,this._y=e.y,this._z=e.z,this._w=e.w,this._onChangeCallback(),this}setFromEuler(e,n=!0){const i=e._x,a=e._y,s=e._z,r=e._order,o=Math.cos,c=Math.sin,u=o(i/2),d=o(a/2),m=o(s/2),f=c(i/2),p=c(a/2),x=c(s/2);switch(r){case"XYZ":this._x=f*d*m+u*p*x,this._y=u*p*m-f*d*x,this._z=u*d*x+f*p*m,this._w=u*d*m-f*p*x;break;case"YXZ":this._x=f*d*m+u*p*x,this._y=u*p*m-f*d*x,this._z=u*d*x-f*p*m,this._w=u*d*m+f*p*x;break;case"ZXY":this._x=f*d*m-u*p*x,this._y=u*p*m+f*d*x,this._z=u*d*x+f*p*m,this._w=u*d*m-f*p*x;break;case"ZYX":this._x=f*d*m-u*p*x,this._y=u*p*m+f*d*x,this._z=u*d*x-f*p*m,this._w=u*d*m+f*p*x;break;case"YZX":this._x=f*d*m+u*p*x,this._y=u*p*m+f*d*x,this._z=u*d*x-f*p*m,this._w=u*d*m-f*p*x;break;case"XZY":this._x=f*d*m-u*p*x,this._y=u*p*m-f*d*x,this._z=u*d*x+f*p*m,this._w=u*d*m+f*p*x;break;default:Je("Quaternion: .setFromEuler() encountered an unknown order: "+r)}return n===!0&&this._onChangeCallback(),this}setFromAxisAngle(e,n){const i=n/2,a=Math.sin(i);return this._x=e.x*a,this._y=e.y*a,this._z=e.z*a,this._w=Math.cos(i),this._onChangeCallback(),this}setFromRotationMatrix(e){const n=e.elements,i=n[0],a=n[4],s=n[8],r=n[1],o=n[5],c=n[9],u=n[2],d=n[6],m=n[10],f=i+o+m;if(f>0){const p=.5/Math.sqrt(f+1);this._w=.25/p,this._x=(d-c)*p,this._y=(s-u)*p,this._z=(r-a)*p}else if(i>o&&i>m){const p=2*Math.sqrt(1+i-o-m);this._w=(d-c)/p,this._x=.25*p,this._y=(a+r)/p,this._z=(s+u)/p}else if(o>m){const p=2*Math.sqrt(1+o-i-m);this._w=(s-u)/p,this._x=(a+r)/p,this._y=.25*p,this._z=(c+d)/p}else{const p=2*Math.sqrt(1+m-i-o);this._w=(r-a)/p,this._x=(s+u)/p,this._y=(c+d)/p,this._z=.25*p}return this._onChangeCallback(),this}setFromUnitVectors(e,n){let i=e.dot(n)+1;return i<1e-8?(i=0,Math.abs(e.x)>Math.abs(e.z)?(this._x=-e.y,this._y=e.x,this._z=0,this._w=i):(this._x=0,this._y=-e.z,this._z=e.y,this._w=i)):(this._x=e.y*n.z-e.z*n.y,this._y=e.z*n.x-e.x*n.z,this._z=e.x*n.y-e.y*n.x,this._w=i),this.normalize()}angleTo(e){return 2*Math.acos(Math.abs(pt(this.dot(e),-1,1)))}rotateTowards(e,n){const i=this.angleTo(e);if(i===0)return this;const a=Math.min(1,n/i);return this.slerp(e,a),this}identity(){return this.set(0,0,0,1)}invert(){return this.conjugate()}conjugate(){return this._x*=-1,this._y*=-1,this._z*=-1,this._onChangeCallback(),this}dot(e){return this._x*e._x+this._y*e._y+this._z*e._z+this._w*e._w}lengthSq(){return this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w}length(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)}normalize(){let e=this.length();return e===0?(this._x=0,this._y=0,this._z=0,this._w=1):(e=1/e,this._x=this._x*e,this._y=this._y*e,this._z=this._z*e,this._w=this._w*e),this._onChangeCallback(),this}multiply(e){return this.multiplyQuaternions(this,e)}premultiply(e){return this.multiplyQuaternions(e,this)}multiplyQuaternions(e,n){const i=e._x,a=e._y,s=e._z,r=e._w,o=n._x,c=n._y,u=n._z,d=n._w;return this._x=i*d+r*o+a*u-s*c,this._y=a*d+r*c+s*o-i*u,this._z=s*d+r*u+i*c-a*o,this._w=r*d-i*o-a*c-s*u,this._onChangeCallback(),this}slerp(e,n){let i=e._x,a=e._y,s=e._z,r=e._w,o=this.dot(e);o<0&&(i=-i,a=-a,s=-s,r=-r,o=-o);let c=1-n;if(o<.9995){const u=Math.acos(o),d=Math.sin(u);c=Math.sin(c*u)/d,n=Math.sin(n*u)/d,this._x=this._x*c+i*n,this._y=this._y*c+a*n,this._z=this._z*c+s*n,this._w=this._w*c+r*n,this._onChangeCallback()}else this._x=this._x*c+i*n,this._y=this._y*c+a*n,this._z=this._z*c+s*n,this._w=this._w*c+r*n,this.normalize();return this}slerpQuaternions(e,n,i){return this.copy(e).slerp(n,i)}random(){const e=2*Math.PI*Math.random(),n=2*Math.PI*Math.random(),i=Math.random(),a=Math.sqrt(1-i),s=Math.sqrt(i);return this.set(a*Math.sin(e),a*Math.cos(e),s*Math.sin(n),s*Math.cos(n))}equals(e){return e._x===this._x&&e._y===this._y&&e._z===this._z&&e._w===this._w}fromArray(e,n=0){return this._x=e[n],this._y=e[n+1],this._z=e[n+2],this._w=e[n+3],this._onChangeCallback(),this}toArray(e=[],n=0){return e[n]=this._x,e[n+1]=this._y,e[n+2]=this._z,e[n+3]=this._w,e}fromBufferAttribute(e,n){return this._x=e.getX(n),this._y=e.getY(n),this._z=e.getZ(n),this._w=e.getW(n),this._onChangeCallback(),this}toJSON(){return this.toArray()}_onChange(e){return this._onChangeCallback=e,this}_onChangeCallback(){}*[Symbol.iterator](){yield this._x,yield this._y,yield this._z,yield this._w}}const Tg=class Tg{constructor(e=0,n=0,i=0){this.x=e,this.y=n,this.z=i}set(e,n,i){return i===void 0&&(i=this.z),this.x=e,this.y=n,this.z=i,this}setScalar(e){return this.x=e,this.y=e,this.z=e,this}setX(e){return this.x=e,this}setY(e){return this.y=e,this}setZ(e){return this.z=e,this}setComponent(e,n){switch(e){case 0:this.x=n;break;case 1:this.y=n;break;case 2:this.z=n;break;default:throw new Error("THREE.Vector3: index is out of range: "+e)}return this}getComponent(e){switch(e){case 0:return this.x;case 1:return this.y;case 2:return this.z;default:throw new Error("THREE.Vector3: index is out of range: "+e)}}clone(){return new this.constructor(this.x,this.y,this.z)}copy(e){return this.x=e.x,this.y=e.y,this.z=e.z,this}add(e){return this.x+=e.x,this.y+=e.y,this.z+=e.z,this}addScalar(e){return this.x+=e,this.y+=e,this.z+=e,this}addVectors(e,n){return this.x=e.x+n.x,this.y=e.y+n.y,this.z=e.z+n.z,this}addScaledVector(e,n){return this.x+=e.x*n,this.y+=e.y*n,this.z+=e.z*n,this}sub(e){return this.x-=e.x,this.y-=e.y,this.z-=e.z,this}subScalar(e){return this.x-=e,this.y-=e,this.z-=e,this}subVectors(e,n){return this.x=e.x-n.x,this.y=e.y-n.y,this.z=e.z-n.z,this}multiply(e){return this.x*=e.x,this.y*=e.y,this.z*=e.z,this}multiplyScalar(e){return this.x*=e,this.y*=e,this.z*=e,this}multiplyVectors(e,n){return this.x=e.x*n.x,this.y=e.y*n.y,this.z=e.z*n.z,this}applyEuler(e){return this.applyQuaternion(y0.setFromEuler(e))}applyAxisAngle(e,n){return this.applyQuaternion(y0.setFromAxisAngle(e,n))}applyMatrix3(e){const n=this.x,i=this.y,a=this.z,s=e.elements;return this.x=s[0]*n+s[3]*i+s[6]*a,this.y=s[1]*n+s[4]*i+s[7]*a,this.z=s[2]*n+s[5]*i+s[8]*a,this}applyNormalMatrix(e){return this.applyMatrix3(e).normalize()}applyMatrix4(e){const n=this.x,i=this.y,a=this.z,s=e.elements,r=1/(s[3]*n+s[7]*i+s[11]*a+s[15]);return this.x=(s[0]*n+s[4]*i+s[8]*a+s[12])*r,this.y=(s[1]*n+s[5]*i+s[9]*a+s[13])*r,this.z=(s[2]*n+s[6]*i+s[10]*a+s[14])*r,this}applyQuaternion(e){const n=this.x,i=this.y,a=this.z,s=e.x,r=e.y,o=e.z,c=e.w,u=2*(r*a-o*i),d=2*(o*n-s*a),m=2*(s*i-r*n);return this.x=n+c*u+r*m-o*d,this.y=i+c*d+o*u-s*m,this.z=a+c*m+s*d-r*u,this}project(e){return this.applyMatrix4(e.matrixWorldInverse).applyMatrix4(e.projectionMatrix)}unproject(e){return this.applyMatrix4(e.projectionMatrixInverse).applyMatrix4(e.matrixWorld)}transformDirection(e){const n=this.x,i=this.y,a=this.z,s=e.elements;return this.x=s[0]*n+s[4]*i+s[8]*a,this.y=s[1]*n+s[5]*i+s[9]*a,this.z=s[2]*n+s[6]*i+s[10]*a,this.normalize()}divide(e){return this.x/=e.x,this.y/=e.y,this.z/=e.z,this}divideScalar(e){return this.multiplyScalar(1/e)}min(e){return this.x=Math.min(this.x,e.x),this.y=Math.min(this.y,e.y),this.z=Math.min(this.z,e.z),this}max(e){return this.x=Math.max(this.x,e.x),this.y=Math.max(this.y,e.y),this.z=Math.max(this.z,e.z),this}clamp(e,n){return this.x=pt(this.x,e.x,n.x),this.y=pt(this.y,e.y,n.y),this.z=pt(this.z,e.z,n.z),this}clampScalar(e,n){return this.x=pt(this.x,e,n),this.y=pt(this.y,e,n),this.z=pt(this.z,e,n),this}clampLength(e,n){const i=this.length();return this.divideScalar(i||1).multiplyScalar(pt(i,e,n))}floor(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this}ceil(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this}round(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this}roundToZero(){return this.x=Math.trunc(this.x),this.y=Math.trunc(this.y),this.z=Math.trunc(this.z),this}negate(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this}dot(e){return this.x*e.x+this.y*e.y+this.z*e.z}lengthSq(){return this.x*this.x+this.y*this.y+this.z*this.z}length(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)}manhattanLength(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)}normalize(){return this.divideScalar(this.length()||1)}setLength(e){return this.normalize().multiplyScalar(e)}lerp(e,n){return this.x+=(e.x-this.x)*n,this.y+=(e.y-this.y)*n,this.z+=(e.z-this.z)*n,this}lerpVectors(e,n,i){return this.x=e.x+(n.x-e.x)*i,this.y=e.y+(n.y-e.y)*i,this.z=e.z+(n.z-e.z)*i,this}cross(e){return this.crossVectors(this,e)}crossVectors(e,n){const i=e.x,a=e.y,s=e.z,r=n.x,o=n.y,c=n.z;return this.x=a*c-s*o,this.y=s*r-i*c,this.z=i*o-a*r,this}projectOnVector(e){const n=e.lengthSq();if(n===0)return this.set(0,0,0);const i=e.dot(this)/n;return this.copy(e).multiplyScalar(i)}projectOnPlane(e){return Nd.copy(this).projectOnVector(e),this.sub(Nd)}reflect(e){return this.sub(Nd.copy(e).multiplyScalar(2*this.dot(e)))}angleTo(e){const n=Math.sqrt(this.lengthSq()*e.lengthSq());if(n===0)return Math.PI/2;const i=this.dot(e)/n;return Math.acos(pt(i,-1,1))}distanceTo(e){return Math.sqrt(this.distanceToSquared(e))}distanceToSquared(e){const n=this.x-e.x,i=this.y-e.y,a=this.z-e.z;return n*n+i*i+a*a}manhattanDistanceTo(e){return Math.abs(this.x-e.x)+Math.abs(this.y-e.y)+Math.abs(this.z-e.z)}setFromSpherical(e){return this.setFromSphericalCoords(e.radius,e.phi,e.theta)}setFromSphericalCoords(e,n,i){const a=Math.sin(n)*e;return this.x=a*Math.sin(i),this.y=Math.cos(n)*e,this.z=a*Math.cos(i),this}setFromCylindrical(e){return this.setFromCylindricalCoords(e.radius,e.theta,e.y)}setFromCylindricalCoords(e,n,i){return this.x=e*Math.sin(n),this.y=i,this.z=e*Math.cos(n),this}setFromMatrixPosition(e){const n=e.elements;return this.x=n[12],this.y=n[13],this.z=n[14],this}setFromMatrixScale(e){const n=this.setFromMatrixColumn(e,0).length(),i=this.setFromMatrixColumn(e,1).length(),a=this.setFromMatrixColumn(e,2).length();return this.x=n,this.y=i,this.z=a,this}setFromMatrixColumn(e,n){return this.fromArray(e.elements,n*4)}setFromMatrix3Column(e,n){return this.fromArray(e.elements,n*3)}setFromEuler(e){return this.x=e._x,this.y=e._y,this.z=e._z,this}setFromColor(e){return this.x=e.r,this.y=e.g,this.z=e.b,this}equals(e){return e.x===this.x&&e.y===this.y&&e.z===this.z}fromArray(e,n=0){return this.x=e[n],this.y=e[n+1],this.z=e[n+2],this}toArray(e=[],n=0){return e[n]=this.x,e[n+1]=this.y,e[n+2]=this.z,e}fromBufferAttribute(e,n){return this.x=e.getX(n),this.y=e.getY(n),this.z=e.getZ(n),this}random(){return this.x=Math.random(),this.y=Math.random(),this.z=Math.random(),this}randomDirection(){const e=Math.random()*Math.PI*2,n=Math.random()*2-1,i=Math.sqrt(1-n*n);return this.x=i*Math.cos(e),this.y=n,this.z=i*Math.sin(e),this}*[Symbol.iterator](){yield this.x,yield this.y,yield this.z}};Tg.prototype.isVector3=!0;let Y=Tg;const Nd=new Y,y0=new Dl,Ag=class Ag{constructor(e,n,i,a,s,r,o,c,u){this.elements=[1,0,0,0,1,0,0,0,1],e!==void 0&&this.set(e,n,i,a,s,r,o,c,u)}set(e,n,i,a,s,r,o,c,u){const d=this.elements;return d[0]=e,d[1]=a,d[2]=o,d[3]=n,d[4]=s,d[5]=c,d[6]=i,d[7]=r,d[8]=u,this}identity(){return this.set(1,0,0,0,1,0,0,0,1),this}copy(e){const n=this.elements,i=e.elements;return n[0]=i[0],n[1]=i[1],n[2]=i[2],n[3]=i[3],n[4]=i[4],n[5]=i[5],n[6]=i[6],n[7]=i[7],n[8]=i[8],this}extractBasis(e,n,i){return e.setFromMatrix3Column(this,0),n.setFromMatrix3Column(this,1),i.setFromMatrix3Column(this,2),this}setFromMatrix4(e){const n=e.elements;return this.set(n[0],n[4],n[8],n[1],n[5],n[9],n[2],n[6],n[10]),this}multiply(e){return this.multiplyMatrices(this,e)}premultiply(e){return this.multiplyMatrices(e,this)}multiplyMatrices(e,n){const i=e.elements,a=n.elements,s=this.elements,r=i[0],o=i[3],c=i[6],u=i[1],d=i[4],m=i[7],f=i[2],p=i[5],x=i[8],_=a[0],g=a[3],h=a[6],v=a[1],b=a[4],y=a[7],A=a[2],M=a[5],T=a[8];return s[0]=r*_+o*v+c*A,s[3]=r*g+o*b+c*M,s[6]=r*h+o*y+c*T,s[1]=u*_+d*v+m*A,s[4]=u*g+d*b+m*M,s[7]=u*h+d*y+m*T,s[2]=f*_+p*v+x*A,s[5]=f*g+p*b+x*M,s[8]=f*h+p*y+x*T,this}multiplyScalar(e){const n=this.elements;return n[0]*=e,n[3]*=e,n[6]*=e,n[1]*=e,n[4]*=e,n[7]*=e,n[2]*=e,n[5]*=e,n[8]*=e,this}determinant(){const e=this.elements,n=e[0],i=e[1],a=e[2],s=e[3],r=e[4],o=e[5],c=e[6],u=e[7],d=e[8];return n*r*d-n*o*u-i*s*d+i*o*c+a*s*u-a*r*c}invert(){const e=this.elements,n=e[0],i=e[1],a=e[2],s=e[3],r=e[4],o=e[5],c=e[6],u=e[7],d=e[8],m=d*r-o*u,f=o*c-d*s,p=u*s-r*c,x=n*m+i*f+a*p;if(x===0)return this.set(0,0,0,0,0,0,0,0,0);const _=1/x;return e[0]=m*_,e[1]=(a*u-d*i)*_,e[2]=(o*i-a*r)*_,e[3]=f*_,e[4]=(d*n-a*c)*_,e[5]=(a*s-o*n)*_,e[6]=p*_,e[7]=(i*c-u*n)*_,e[8]=(r*n-i*s)*_,this}transpose(){let e;const n=this.elements;return e=n[1],n[1]=n[3],n[3]=e,e=n[2],n[2]=n[6],n[6]=e,e=n[5],n[5]=n[7],n[7]=e,this}getNormalMatrix(e){return this.setFromMatrix4(e).invert().transpose()}transposeIntoArray(e){const n=this.elements;return e[0]=n[0],e[1]=n[3],e[2]=n[6],e[3]=n[1],e[4]=n[4],e[5]=n[7],e[6]=n[2],e[7]=n[5],e[8]=n[8],this}setUvTransform(e,n,i,a,s,r,o){const c=Math.cos(s),u=Math.sin(s);return this.set(i*c,i*u,-i*(c*r+u*o)+r+e,-a*u,a*c,-a*(-u*r+c*o)+o+n,0,0,1),this}scale(e,n){return il("Matrix3: .scale() is deprecated. Use .makeScale() instead."),this.premultiply(Rd.makeScale(e,n)),this}rotate(e){return il("Matrix3: .rotate() is deprecated. Use .makeRotation() instead."),this.premultiply(Rd.makeRotation(-e)),this}translate(e,n){return il("Matrix3: .translate() is deprecated. Use .makeTranslation() instead."),this.premultiply(Rd.makeTranslation(e,n)),this}makeTranslation(e,n){return e.isVector2?this.set(1,0,e.x,0,1,e.y,0,0,1):this.set(1,0,e,0,1,n,0,0,1),this}makeRotation(e){const n=Math.cos(e),i=Math.sin(e);return this.set(n,-i,0,i,n,0,0,0,1),this}makeScale(e,n){return this.set(e,0,0,0,n,0,0,0,1),this}equals(e){const n=this.elements,i=e.elements;for(let a=0;a<9;a++)if(n[a]!==i[a])return!1;return!0}fromArray(e,n=0){for(let i=0;i<9;i++)this.elements[i]=e[i+n];return this}toArray(e=[],n=0){const i=this.elements;return e[n]=i[0],e[n+1]=i[1],e[n+2]=i[2],e[n+3]=i[3],e[n+4]=i[4],e[n+5]=i[5],e[n+6]=i[6],e[n+7]=i[7],e[n+8]=i[8],e}clone(){return new this.constructor().fromArray(this.elements)}};Ag.prototype.isMatrix3=!0;let it=Ag;const Rd=new it,b0=new it().set(.4123908,.3575843,.1804808,.212639,.7151687,.0721923,.0193308,.1191948,.9505322),S0=new it().set(3.2409699,-1.5373832,-.4986108,-.9692436,1.8759675,.0415551,.0556301,-.203977,1.0569715);function dA(){const t={enabled:!0,workingColorSpace:lf,spaces:{},convert:function(a,s,r){return this.enabled===!1||s===r||!s||!r||(this.spaces[s].transfer===Ct&&(a.r=Ra(a.r),a.g=Ra(a.g),a.b=Ra(a.b)),this.spaces[s].primaries!==this.spaces[r].primaries&&(a.applyMatrix3(this.spaces[s].toXYZ),a.applyMatrix3(this.spaces[r].fromXYZ)),this.spaces[r].transfer===Ct&&(a.r=al(a.r),a.g=al(a.g),a.b=al(a.b))),a},workingToColorSpace:function(a,s){return this.convert(a,this.workingColorSpace,s)},colorSpaceToWorking:function(a,s){return this.convert(a,s,this.workingColorSpace)},getPrimaries:function(a){return this.spaces[a].primaries},getTransfer:function(a){return a===is?of:this.spaces[a].transfer},getToneMappingMode:function(a){return this.spaces[a].outputColorSpaceConfig.toneMappingMode||"standard"},getLuminanceCoefficients:function(a,s=this.workingColorSpace){return a.fromArray(this.spaces[s].luminanceCoefficients)},define:function(a){Object.assign(this.spaces,a)},_getMatrix:function(a,s,r){return a.copy(this.spaces[s].toXYZ).multiply(this.spaces[r].fromXYZ)},_getDrawingBufferColorSpace:function(a){return this.spaces[a].outputColorSpaceConfig.drawingBufferColorSpace},_getUnpackColorSpace:function(a=this.workingColorSpace){return this.spaces[a].workingColorSpaceConfig.unpackColorSpace},fromWorkingColorSpace:function(a,s){return il("ColorManagement: .fromWorkingColorSpace() has been renamed to .workingToColorSpace()."),t.workingToColorSpace(a,s)},toWorkingColorSpace:function(a,s){return il("ColorManagement: .toWorkingColorSpace() has been renamed to .colorSpaceToWorking()."),t.colorSpaceToWorking(a,s)}},e=[.64,.33,.3,.6,.15,.06],n=[.2126,.7152,.0722],i=[.3127,.329];return t.define({[lf]:{primaries:e,whitePoint:i,transfer:of,toXYZ:b0,fromXYZ:S0,luminanceCoefficients:n,workingColorSpaceConfig:{unpackColorSpace:xi},outputColorSpaceConfig:{drawingBufferColorSpace:xi}},[xi]:{primaries:e,whitePoint:i,transfer:Ct,toXYZ:b0,fromXYZ:S0,luminanceCoefficients:n,outputColorSpaceConfig:{drawingBufferColorSpace:xi}}}),t}const ht=dA();function Ra(t){return t<.04045?t*.0773993808:Math.pow(t*.9478672986+.0521327014,2.4)}function al(t){return t<.0031308?t*12.92:1.055*Math.pow(t,.41666)-.055}let xr;class hA{static getDataURL(e,n="image/png"){if(/^data:/i.test(e.src)||typeof HTMLCanvasElement>"u")return e.src;let i;if(e instanceof HTMLCanvasElement)i=e;else{xr===void 0&&(xr=uf("canvas")),xr.width=e.width,xr.height=e.height;const a=xr.getContext("2d");e instanceof ImageData?a.putImageData(e,0,0):a.drawImage(e,0,0,e.width,e.height),i=xr}return i.toDataURL(n)}static sRGBToLinear(e){if(typeof HTMLImageElement<"u"&&e instanceof HTMLImageElement||typeof HTMLCanvasElement<"u"&&e instanceof HTMLCanvasElement||typeof ImageBitmap<"u"&&e instanceof ImageBitmap){const n=uf("canvas");n.width=e.width,n.height=e.height;const i=n.getContext("2d");i.drawImage(e,0,0,e.width,e.height);const a=i.getImageData(0,0,e.width,e.height),s=a.data;for(let r=0;r<s.length;r++)s[r]=Ra(s[r]/255)*255;return i.putImageData(a,0,0),n}else if(e.data){const n=e.data.slice(0);for(let i=0;i<n.length;i++)n instanceof Uint8Array||n instanceof Uint8ClampedArray?n[i]=Math.floor(Ra(n[i]/255)*255):n[i]=Ra(n[i]);return{data:n,width:e.width,height:e.height}}else return Je("ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied."),e}}let pA=0;class vg{constructor(e=null){this.isSource=!0,Object.defineProperty(this,"id",{value:pA++}),this.uuid=sc(),this.data=e,this.dataReady=!0,this.version=0}getSize(e){const n=this.data;return typeof HTMLVideoElement<"u"&&n instanceof HTMLVideoElement?e.set(n.videoWidth,n.videoHeight,0):typeof VideoFrame<"u"&&n instanceof VideoFrame?e.set(n.displayWidth,n.displayHeight,0):n!==null?e.set(n.width,n.height,n.depth||0):e.set(0,0,0),e}set needsUpdate(e){e===!0&&this.version++}toJSON(e){const n=e===void 0||typeof e=="string";if(!n&&e.images[this.uuid]!==void 0)return e.images[this.uuid];const i={uuid:this.uuid,url:""},a=this.data;if(a!==null){let s;if(Array.isArray(a)){s=[];for(let r=0,o=a.length;r<o;r++)a[r].isDataTexture?s.push(Dd(a[r].image)):s.push(Dd(a[r]))}else s=Dd(a);i.url=s}return n||(e.images[this.uuid]=i),i}}function Dd(t){return typeof HTMLImageElement<"u"&&t instanceof HTMLImageElement||typeof HTMLCanvasElement<"u"&&t instanceof HTMLCanvasElement||typeof ImageBitmap<"u"&&t instanceof ImageBitmap?hA.getDataURL(t):t.data?{data:Array.from(t.data),width:t.width,height:t.height,type:t.data.constructor.name}:(Je("Texture: Unable to serialize Texture."),{})}let mA=0;const Ld=new Y;class zn extends cr{constructor(e=zn.DEFAULT_IMAGE,n=zn.DEFAULT_MAPPING,i=Ta,a=Ta,s=Rn,r=js,o=ki,c=Ei,u=zn.DEFAULT_ANISOTROPY,d=is){super(),this.isTexture=!0,Object.defineProperty(this,"id",{value:mA++}),this.uuid=sc(),this.name="",this.source=new vg(e),this.mipmaps=[],this.mapping=n,this.channel=0,this.wrapS=i,this.wrapT=a,this.magFilter=s,this.minFilter=r,this.anisotropy=u,this.format=o,this.internalFormat=null,this.type=c,this.offset=new At(0,0),this.repeat=new At(1,1),this.center=new At(0,0),this.rotation=0,this.matrixAutoUpdate=!0,this.matrix=new it,this.generateMipmaps=!0,this.premultiplyAlpha=!1,this.flipY=!0,this.unpackAlignment=4,this.colorSpace=d,this.userData={},this.updateRanges=[],this.version=0,this.onUpdate=null,this.renderTarget=null,this.isRenderTargetTexture=!1,this.isArrayTexture=!!(e&&e.depth&&e.depth>1),this.pmremVersion=0,this.normalized=!1}get width(){return this.source.getSize(Ld).x}get height(){return this.source.getSize(Ld).y}get depth(){return this.source.getSize(Ld).z}get image(){return this.source.data}set image(e){this.source.data=e}updateMatrix(){this.matrix.setUvTransform(this.offset.x,this.offset.y,this.repeat.x,this.repeat.y,this.rotation,this.center.x,this.center.y)}addUpdateRange(e,n){this.updateRanges.push({start:e,count:n})}clearUpdateRanges(){this.updateRanges.length=0}clone(){return new this.constructor().copy(this)}copy(e){return this.name=e.name,this.source=e.source,this.mipmaps=e.mipmaps.slice(0),this.mapping=e.mapping,this.channel=e.channel,this.wrapS=e.wrapS,this.wrapT=e.wrapT,this.magFilter=e.magFilter,this.minFilter=e.minFilter,this.anisotropy=e.anisotropy,this.format=e.format,this.internalFormat=e.internalFormat,this.type=e.type,this.normalized=e.normalized,this.offset.copy(e.offset),this.repeat.copy(e.repeat),this.center.copy(e.center),this.rotation=e.rotation,this.matrixAutoUpdate=e.matrixAutoUpdate,this.matrix.copy(e.matrix),this.generateMipmaps=e.generateMipmaps,this.premultiplyAlpha=e.premultiplyAlpha,this.flipY=e.flipY,this.unpackAlignment=e.unpackAlignment,this.colorSpace=e.colorSpace,this.renderTarget=e.renderTarget,this.isRenderTargetTexture=e.isRenderTargetTexture,this.isArrayTexture=e.isArrayTexture,this.userData=JSON.parse(JSON.stringify(e.userData)),this.needsUpdate=!0,this}setValues(e){for(const n in e){const i=e[n];if(i===void 0){Je(`Texture.setValues(): parameter '${n}' has value of undefined.`);continue}const a=this[n];if(a===void 0){Je(`Texture.setValues(): property '${n}' does not exist.`);continue}a&&i&&a.isVector2&&i.isVector2||a&&i&&a.isVector3&&i.isVector3||a&&i&&a.isMatrix3&&i.isMatrix3?a.copy(i):this[n]=i}}toJSON(e){const n=e===void 0||typeof e=="string";if(!n&&e.textures[this.uuid]!==void 0)return e.textures[this.uuid];const i={metadata:{version:4.7,type:"Texture",generator:"Texture.toJSON"},uuid:this.uuid,name:this.name,image:this.source.toJSON(e).uuid,mapping:this.mapping,channel:this.channel,repeat:[this.repeat.x,this.repeat.y],offset:[this.offset.x,this.offset.y],center:[this.center.x,this.center.y],rotation:this.rotation,wrap:[this.wrapS,this.wrapT],format:this.format,internalFormat:this.internalFormat,type:this.type,normalized:this.normalized,colorSpace:this.colorSpace,minFilter:this.minFilter,magFilter:this.magFilter,anisotropy:this.anisotropy,flipY:this.flipY,generateMipmaps:this.generateMipmaps,premultiplyAlpha:this.premultiplyAlpha,unpackAlignment:this.unpackAlignment};return Object.keys(this.userData).length>0&&(i.userData=this.userData),n||(e.textures[this.uuid]=i),i}dispose(){this.dispatchEvent({type:"dispose"})}transformUv(e){if(this.mapping!==rS)return e;if(e.applyMatrix3(this.matrix),e.x<0||e.x>1)switch(this.wrapS){case bp:e.x=e.x-Math.floor(e.x);break;case Ta:e.x=e.x<0?0:1;break;case Sp:Math.abs(Math.floor(e.x)%2)===1?e.x=Math.ceil(e.x)-e.x:e.x=e.x-Math.floor(e.x);break}if(e.y<0||e.y>1)switch(this.wrapT){case bp:e.y=e.y-Math.floor(e.y);break;case Ta:e.y=e.y<0?0:1;break;case Sp:Math.abs(Math.floor(e.y)%2)===1?e.y=Math.ceil(e.y)-e.y:e.y=e.y-Math.floor(e.y);break}return this.flipY&&(e.y=1-e.y),e}set needsUpdate(e){e===!0&&(this.version++,this.source.needsUpdate=!0)}set needsPMREMUpdate(e){e===!0&&this.pmremVersion++}}zn.DEFAULT_IMAGE=null;zn.DEFAULT_MAPPING=rS;zn.DEFAULT_ANISOTROPY=1;const wg=class wg{constructor(e=0,n=0,i=0,a=1){this.x=e,this.y=n,this.z=i,this.w=a}get width(){return this.z}set width(e){this.z=e}get height(){return this.w}set height(e){this.w=e}set(e,n,i,a){return this.x=e,this.y=n,this.z=i,this.w=a,this}setScalar(e){return this.x=e,this.y=e,this.z=e,this.w=e,this}setX(e){return this.x=e,this}setY(e){return this.y=e,this}setZ(e){return this.z=e,this}setW(e){return this.w=e,this}setComponent(e,n){switch(e){case 0:this.x=n;break;case 1:this.y=n;break;case 2:this.z=n;break;case 3:this.w=n;break;default:throw new Error("THREE.Vector4: index is out of range: "+e)}return this}getComponent(e){switch(e){case 0:return this.x;case 1:return this.y;case 2:return this.z;case 3:return this.w;default:throw new Error("THREE.Vector4: index is out of range: "+e)}}clone(){return new this.constructor(this.x,this.y,this.z,this.w)}copy(e){return this.x=e.x,this.y=e.y,this.z=e.z,this.w=e.w!==void 0?e.w:1,this}add(e){return this.x+=e.x,this.y+=e.y,this.z+=e.z,this.w+=e.w,this}addScalar(e){return this.x+=e,this.y+=e,this.z+=e,this.w+=e,this}addVectors(e,n){return this.x=e.x+n.x,this.y=e.y+n.y,this.z=e.z+n.z,this.w=e.w+n.w,this}addScaledVector(e,n){return this.x+=e.x*n,this.y+=e.y*n,this.z+=e.z*n,this.w+=e.w*n,this}sub(e){return this.x-=e.x,this.y-=e.y,this.z-=e.z,this.w-=e.w,this}subScalar(e){return this.x-=e,this.y-=e,this.z-=e,this.w-=e,this}subVectors(e,n){return this.x=e.x-n.x,this.y=e.y-n.y,this.z=e.z-n.z,this.w=e.w-n.w,this}multiply(e){return this.x*=e.x,this.y*=e.y,this.z*=e.z,this.w*=e.w,this}multiplyScalar(e){return this.x*=e,this.y*=e,this.z*=e,this.w*=e,this}applyMatrix4(e){const n=this.x,i=this.y,a=this.z,s=this.w,r=e.elements;return this.x=r[0]*n+r[4]*i+r[8]*a+r[12]*s,this.y=r[1]*n+r[5]*i+r[9]*a+r[13]*s,this.z=r[2]*n+r[6]*i+r[10]*a+r[14]*s,this.w=r[3]*n+r[7]*i+r[11]*a+r[15]*s,this}divide(e){return this.x/=e.x,this.y/=e.y,this.z/=e.z,this.w/=e.w,this}divideScalar(e){return this.multiplyScalar(1/e)}setAxisAngleFromQuaternion(e){this.w=2*Math.acos(e.w);const n=Math.sqrt(1-e.w*e.w);return n<1e-4?(this.x=1,this.y=0,this.z=0):(this.x=e.x/n,this.y=e.y/n,this.z=e.z/n),this}setAxisAngleFromRotationMatrix(e){let n,i,a,s;const c=e.elements,u=c[0],d=c[4],m=c[8],f=c[1],p=c[5],x=c[9],_=c[2],g=c[6],h=c[10];if(Math.abs(d-f)<.01&&Math.abs(m-_)<.01&&Math.abs(x-g)<.01){if(Math.abs(d+f)<.1&&Math.abs(m+_)<.1&&Math.abs(x+g)<.1&&Math.abs(u+p+h-3)<.1)return this.set(1,0,0,0),this;n=Math.PI;const b=(u+1)/2,y=(p+1)/2,A=(h+1)/2,M=(d+f)/4,T=(m+_)/4,S=(x+g)/4;return b>y&&b>A?b<.01?(i=0,a=.707106781,s=.707106781):(i=Math.sqrt(b),a=M/i,s=T/i):y>A?y<.01?(i=.707106781,a=0,s=.707106781):(a=Math.sqrt(y),i=M/a,s=S/a):A<.01?(i=.707106781,a=.707106781,s=0):(s=Math.sqrt(A),i=T/s,a=S/s),this.set(i,a,s,n),this}let v=Math.sqrt((g-x)*(g-x)+(m-_)*(m-_)+(f-d)*(f-d));return Math.abs(v)<.001&&(v=1),this.x=(g-x)/v,this.y=(m-_)/v,this.z=(f-d)/v,this.w=Math.acos((u+p+h-1)/2),this}setFromMatrixPosition(e){const n=e.elements;return this.x=n[12],this.y=n[13],this.z=n[14],this.w=n[15],this}min(e){return this.x=Math.min(this.x,e.x),this.y=Math.min(this.y,e.y),this.z=Math.min(this.z,e.z),this.w=Math.min(this.w,e.w),this}max(e){return this.x=Math.max(this.x,e.x),this.y=Math.max(this.y,e.y),this.z=Math.max(this.z,e.z),this.w=Math.max(this.w,e.w),this}clamp(e,n){return this.x=pt(this.x,e.x,n.x),this.y=pt(this.y,e.y,n.y),this.z=pt(this.z,e.z,n.z),this.w=pt(this.w,e.w,n.w),this}clampScalar(e,n){return this.x=pt(this.x,e,n),this.y=pt(this.y,e,n),this.z=pt(this.z,e,n),this.w=pt(this.w,e,n),this}clampLength(e,n){const i=this.length();return this.divideScalar(i||1).multiplyScalar(pt(i,e,n))}floor(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this.w=Math.floor(this.w),this}ceil(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this.w=Math.ceil(this.w),this}round(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this.w=Math.round(this.w),this}roundToZero(){return this.x=Math.trunc(this.x),this.y=Math.trunc(this.y),this.z=Math.trunc(this.z),this.w=Math.trunc(this.w),this}negate(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this.w=-this.w,this}dot(e){return this.x*e.x+this.y*e.y+this.z*e.z+this.w*e.w}lengthSq(){return this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w}length(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w)}manhattanLength(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)+Math.abs(this.w)}normalize(){return this.divideScalar(this.length()||1)}setLength(e){return this.normalize().multiplyScalar(e)}lerp(e,n){return this.x+=(e.x-this.x)*n,this.y+=(e.y-this.y)*n,this.z+=(e.z-this.z)*n,this.w+=(e.w-this.w)*n,this}lerpVectors(e,n,i){return this.x=e.x+(n.x-e.x)*i,this.y=e.y+(n.y-e.y)*i,this.z=e.z+(n.z-e.z)*i,this.w=e.w+(n.w-e.w)*i,this}equals(e){return e.x===this.x&&e.y===this.y&&e.z===this.z&&e.w===this.w}fromArray(e,n=0){return this.x=e[n],this.y=e[n+1],this.z=e[n+2],this.w=e[n+3],this}toArray(e=[],n=0){return e[n]=this.x,e[n+1]=this.y,e[n+2]=this.z,e[n+3]=this.w,e}fromBufferAttribute(e,n){return this.x=e.getX(n),this.y=e.getY(n),this.z=e.getZ(n),this.w=e.getW(n),this}random(){return this.x=Math.random(),this.y=Math.random(),this.z=Math.random(),this.w=Math.random(),this}*[Symbol.iterator](){yield this.x,yield this.y,yield this.z,yield this.w}};wg.prototype.isVector4=!0;let Qt=wg;class gA extends cr{constructor(e=1,n=1,i={}){super(),i=Object.assign({generateMipmaps:!1,internalFormat:null,minFilter:Rn,depthBuffer:!0,stencilBuffer:!1,resolveDepthBuffer:!0,resolveStencilBuffer:!0,depthTexture:null,samples:0,count:1,depth:1,multiview:!1,useArrayDepthTexture:!1},i),this.isRenderTarget=!0,this.width=e,this.height=n,this.depth=i.depth,this.scissor=new Qt(0,0,e,n),this.scissorTest=!1,this.viewport=new Qt(0,0,e,n),this.textures=[];const a={width:e,height:n,depth:i.depth},s=new zn(a),r=i.count;for(let o=0;o<r;o++)this.textures[o]=s.clone(),this.textures[o].isRenderTargetTexture=!0,this.textures[o].renderTarget=this;this._setTextureOptions(i),this.depthBuffer=i.depthBuffer,this.stencilBuffer=i.stencilBuffer,this.resolveDepthBuffer=i.resolveDepthBuffer,this.resolveStencilBuffer=i.resolveStencilBuffer,this._depthTexture=null,this.depthTexture=i.depthTexture,this.samples=i.samples,this.multiview=i.multiview,this.useArrayDepthTexture=i.useArrayDepthTexture}_setTextureOptions(e={}){const n={minFilter:Rn,generateMipmaps:!1,flipY:!1,internalFormat:null};e.mapping!==void 0&&(n.mapping=e.mapping),e.wrapS!==void 0&&(n.wrapS=e.wrapS),e.wrapT!==void 0&&(n.wrapT=e.wrapT),e.wrapR!==void 0&&(n.wrapR=e.wrapR),e.magFilter!==void 0&&(n.magFilter=e.magFilter),e.minFilter!==void 0&&(n.minFilter=e.minFilter),e.format!==void 0&&(n.format=e.format),e.type!==void 0&&(n.type=e.type),e.anisotropy!==void 0&&(n.anisotropy=e.anisotropy),e.colorSpace!==void 0&&(n.colorSpace=e.colorSpace),e.flipY!==void 0&&(n.flipY=e.flipY),e.generateMipmaps!==void 0&&(n.generateMipmaps=e.generateMipmaps),e.internalFormat!==void 0&&(n.internalFormat=e.internalFormat);for(let i=0;i<this.textures.length;i++)this.textures[i].setValues(n)}get texture(){return this.textures[0]}set texture(e){this.textures[0]=e}set depthTexture(e){this._depthTexture!==null&&(this._depthTexture.renderTarget=null),e!==null&&(e.renderTarget=this),this._depthTexture=e}get depthTexture(){return this._depthTexture}setSize(e,n,i=1){if(this.width!==e||this.height!==n||this.depth!==i){this.width=e,this.height=n,this.depth=i;for(let a=0,s=this.textures.length;a<s;a++)this.textures[a].image.width=e,this.textures[a].image.height=n,this.textures[a].image.depth=i,this.textures[a].isData3DTexture!==!0&&(this.textures[a].isArrayTexture=this.textures[a].image.depth>1);this.dispose()}this.viewport.set(0,0,e,n),this.scissor.set(0,0,e,n)}clone(){return new this.constructor().copy(this)}copy(e){this.width=e.width,this.height=e.height,this.depth=e.depth,this.scissor.copy(e.scissor),this.scissorTest=e.scissorTest,this.viewport.copy(e.viewport),this.textures.length=0;for(let n=0,i=e.textures.length;n<i;n++){this.textures[n]=e.textures[n].clone(),this.textures[n].isRenderTargetTexture=!0,this.textures[n].renderTarget=this;const a=Object.assign({},e.textures[n].image);this.textures[n].source=new vg(a)}return this.depthBuffer=e.depthBuffer,this.stencilBuffer=e.stencilBuffer,this.resolveDepthBuffer=e.resolveDepthBuffer,this.resolveStencilBuffer=e.resolveStencilBuffer,e.depthTexture!==null&&(this.depthTexture=e.depthTexture.clone()),this.samples=e.samples,this.multiview=e.multiview,this.useArrayDepthTexture=e.useArrayDepthTexture,this}dispose(){this.dispatchEvent({type:"dispose"})}}class aa extends gA{constructor(e=1,n=1,i={}){super(e,n,i),this.isWebGLRenderTarget=!0}}class pS extends zn{constructor(e=null,n=1,i=1,a=1){super(null),this.isDataArrayTexture=!0,this.image={data:e,width:n,height:i,depth:a},this.magFilter=En,this.minFilter=En,this.wrapR=Ta,this.generateMipmaps=!1,this.flipY=!1,this.unpackAlignment=1,this.layerUpdates=new Set}addLayerUpdate(e){this.layerUpdates.add(e)}clearLayerUpdates(){this.layerUpdates.clear()}}class xA extends zn{constructor(e=null,n=1,i=1,a=1){super(null),this.isData3DTexture=!0,this.image={data:e,width:n,height:i,depth:a},this.magFilter=En,this.minFilter=En,this.wrapR=Ta,this.generateMipmaps=!1,this.flipY=!1,this.unpackAlignment=1}}const gf=class gf{constructor(e,n,i,a,s,r,o,c,u,d,m,f,p,x,_,g){this.elements=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],e!==void 0&&this.set(e,n,i,a,s,r,o,c,u,d,m,f,p,x,_,g)}set(e,n,i,a,s,r,o,c,u,d,m,f,p,x,_,g){const h=this.elements;return h[0]=e,h[4]=n,h[8]=i,h[12]=a,h[1]=s,h[5]=r,h[9]=o,h[13]=c,h[2]=u,h[6]=d,h[10]=m,h[14]=f,h[3]=p,h[7]=x,h[11]=_,h[15]=g,this}identity(){return this.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1),this}clone(){return new gf().fromArray(this.elements)}copy(e){const n=this.elements,i=e.elements;return n[0]=i[0],n[1]=i[1],n[2]=i[2],n[3]=i[3],n[4]=i[4],n[5]=i[5],n[6]=i[6],n[7]=i[7],n[8]=i[8],n[9]=i[9],n[10]=i[10],n[11]=i[11],n[12]=i[12],n[13]=i[13],n[14]=i[14],n[15]=i[15],this}copyPosition(e){const n=this.elements,i=e.elements;return n[12]=i[12],n[13]=i[13],n[14]=i[14],this}setFromMatrix3(e){const n=e.elements;return this.set(n[0],n[3],n[6],0,n[1],n[4],n[7],0,n[2],n[5],n[8],0,0,0,0,1),this}extractBasis(e,n,i){return this.determinantAffine()===0?(e.set(1,0,0),n.set(0,1,0),i.set(0,0,1),this):(e.setFromMatrixColumn(this,0),n.setFromMatrixColumn(this,1),i.setFromMatrixColumn(this,2),this)}makeBasis(e,n,i){return this.set(e.x,n.x,i.x,0,e.y,n.y,i.y,0,e.z,n.z,i.z,0,0,0,0,1),this}extractRotation(e){if(e.determinantAffine()===0)return this.identity();const n=this.elements,i=e.elements,a=1/vr.setFromMatrixColumn(e,0).length(),s=1/vr.setFromMatrixColumn(e,1).length(),r=1/vr.setFromMatrixColumn(e,2).length();return n[0]=i[0]*a,n[1]=i[1]*a,n[2]=i[2]*a,n[3]=0,n[4]=i[4]*s,n[5]=i[5]*s,n[6]=i[6]*s,n[7]=0,n[8]=i[8]*r,n[9]=i[9]*r,n[10]=i[10]*r,n[11]=0,n[12]=0,n[13]=0,n[14]=0,n[15]=1,this}makeRotationFromEuler(e){const n=this.elements,i=e.x,a=e.y,s=e.z,r=Math.cos(i),o=Math.sin(i),c=Math.cos(a),u=Math.sin(a),d=Math.cos(s),m=Math.sin(s);if(e.order==="XYZ"){const f=r*d,p=r*m,x=o*d,_=o*m;n[0]=c*d,n[4]=-c*m,n[8]=u,n[1]=p+x*u,n[5]=f-_*u,n[9]=-o*c,n[2]=_-f*u,n[6]=x+p*u,n[10]=r*c}else if(e.order==="YXZ"){const f=c*d,p=c*m,x=u*d,_=u*m;n[0]=f+_*o,n[4]=x*o-p,n[8]=r*u,n[1]=r*m,n[5]=r*d,n[9]=-o,n[2]=p*o-x,n[6]=_+f*o,n[10]=r*c}else if(e.order==="ZXY"){const f=c*d,p=c*m,x=u*d,_=u*m;n[0]=f-_*o,n[4]=-r*m,n[8]=x+p*o,n[1]=p+x*o,n[5]=r*d,n[9]=_-f*o,n[2]=-r*u,n[6]=o,n[10]=r*c}else if(e.order==="ZYX"){const f=r*d,p=r*m,x=o*d,_=o*m;n[0]=c*d,n[4]=x*u-p,n[8]=f*u+_,n[1]=c*m,n[5]=_*u+f,n[9]=p*u-x,n[2]=-u,n[6]=o*c,n[10]=r*c}else if(e.order==="YZX"){const f=r*c,p=r*u,x=o*c,_=o*u;n[0]=c*d,n[4]=_-f*m,n[8]=x*m+p,n[1]=m,n[5]=r*d,n[9]=-o*d,n[2]=-u*d,n[6]=p*m+x,n[10]=f-_*m}else if(e.order==="XZY"){const f=r*c,p=r*u,x=o*c,_=o*u;n[0]=c*d,n[4]=-m,n[8]=u*d,n[1]=f*m+_,n[5]=r*d,n[9]=p*m-x,n[2]=x*m-p,n[6]=o*d,n[10]=_*m+f}return n[3]=0,n[7]=0,n[11]=0,n[12]=0,n[13]=0,n[14]=0,n[15]=1,this}makeRotationFromQuaternion(e){return this.compose(vA,e,_A)}lookAt(e,n,i){const a=this.elements;return ti.subVectors(e,n),ti.lengthSq()===0&&(ti.z=1),ti.normalize(),Va.crossVectors(i,ti),Va.lengthSq()===0&&(Math.abs(i.z)===1?ti.x+=1e-4:ti.z+=1e-4,ti.normalize(),Va.crossVectors(i,ti)),Va.normalize(),Ec.crossVectors(ti,Va),a[0]=Va.x,a[4]=Ec.x,a[8]=ti.x,a[1]=Va.y,a[5]=Ec.y,a[9]=ti.y,a[2]=Va.z,a[6]=Ec.z,a[10]=ti.z,this}multiply(e){return this.multiplyMatrices(this,e)}premultiply(e){return this.multiplyMatrices(e,this)}multiplyMatrices(e,n){const i=e.elements,a=n.elements,s=this.elements,r=i[0],o=i[4],c=i[8],u=i[12],d=i[1],m=i[5],f=i[9],p=i[13],x=i[2],_=i[6],g=i[10],h=i[14],v=i[3],b=i[7],y=i[11],A=i[15],M=a[0],T=a[4],S=a[8],C=a[12],R=a[1],L=a[5],O=a[9],W=a[13],z=a[2],D=a[6],B=a[10],k=a[14],V=a[3],X=a[7],ge=a[11],he=a[15];return s[0]=r*M+o*R+c*z+u*V,s[4]=r*T+o*L+c*D+u*X,s[8]=r*S+o*O+c*B+u*ge,s[12]=r*C+o*W+c*k+u*he,s[1]=d*M+m*R+f*z+p*V,s[5]=d*T+m*L+f*D+p*X,s[9]=d*S+m*O+f*B+p*ge,s[13]=d*C+m*W+f*k+p*he,s[2]=x*M+_*R+g*z+h*V,s[6]=x*T+_*L+g*D+h*X,s[10]=x*S+_*O+g*B+h*ge,s[14]=x*C+_*W+g*k+h*he,s[3]=v*M+b*R+y*z+A*V,s[7]=v*T+b*L+y*D+A*X,s[11]=v*S+b*O+y*B+A*ge,s[15]=v*C+b*W+y*k+A*he,this}multiplyScalar(e){const n=this.elements;return n[0]*=e,n[4]*=e,n[8]*=e,n[12]*=e,n[1]*=e,n[5]*=e,n[9]*=e,n[13]*=e,n[2]*=e,n[6]*=e,n[10]*=e,n[14]*=e,n[3]*=e,n[7]*=e,n[11]*=e,n[15]*=e,this}determinant(){const e=this.elements,n=e[0],i=e[4],a=e[8],s=e[12],r=e[1],o=e[5],c=e[9],u=e[13],d=e[2],m=e[6],f=e[10],p=e[14],x=e[3],_=e[7],g=e[11],h=e[15],v=c*p-u*f,b=o*p-u*m,y=o*f-c*m,A=r*p-u*d,M=r*f-c*d,T=r*m-o*d;return n*(_*v-g*b+h*y)-i*(x*v-g*A+h*M)+a*(x*b-_*A+h*T)-s*(x*y-_*M+g*T)}determinantAffine(){const e=this.elements,n=e[0],i=e[4],a=e[8],s=e[1],r=e[5],o=e[9],c=e[2],u=e[6],d=e[10];return n*(r*d-o*u)-i*(s*d-o*c)+a*(s*u-r*c)}transpose(){const e=this.elements;let n;return n=e[1],e[1]=e[4],e[4]=n,n=e[2],e[2]=e[8],e[8]=n,n=e[6],e[6]=e[9],e[9]=n,n=e[3],e[3]=e[12],e[12]=n,n=e[7],e[7]=e[13],e[13]=n,n=e[11],e[11]=e[14],e[14]=n,this}setPosition(e,n,i){const a=this.elements;return e.isVector3?(a[12]=e.x,a[13]=e.y,a[14]=e.z):(a[12]=e,a[13]=n,a[14]=i),this}invert(){const e=this.elements,n=e[0],i=e[1],a=e[2],s=e[3],r=e[4],o=e[5],c=e[6],u=e[7],d=e[8],m=e[9],f=e[10],p=e[11],x=e[12],_=e[13],g=e[14],h=e[15],v=n*o-i*r,b=n*c-a*r,y=n*u-s*r,A=i*c-a*o,M=i*u-s*o,T=a*u-s*c,S=d*_-m*x,C=d*g-f*x,R=d*h-p*x,L=m*g-f*_,O=m*h-p*_,W=f*h-p*g,z=v*W-b*O+y*L+A*R-M*C+T*S;if(z===0)return this.set(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);const D=1/z;return e[0]=(o*W-c*O+u*L)*D,e[1]=(a*O-i*W-s*L)*D,e[2]=(_*T-g*M+h*A)*D,e[3]=(f*M-m*T-p*A)*D,e[4]=(c*R-r*W-u*C)*D,e[5]=(n*W-a*R+s*C)*D,e[6]=(g*y-x*T-h*b)*D,e[7]=(d*T-f*y+p*b)*D,e[8]=(r*O-o*R+u*S)*D,e[9]=(i*R-n*O-s*S)*D,e[10]=(x*M-_*y+h*v)*D,e[11]=(m*y-d*M-p*v)*D,e[12]=(o*C-r*L-c*S)*D,e[13]=(n*L-i*C+a*S)*D,e[14]=(_*b-x*A-g*v)*D,e[15]=(d*A-m*b+f*v)*D,this}scale(e){const n=this.elements,i=e.x,a=e.y,s=e.z;return n[0]*=i,n[4]*=a,n[8]*=s,n[1]*=i,n[5]*=a,n[9]*=s,n[2]*=i,n[6]*=a,n[10]*=s,n[3]*=i,n[7]*=a,n[11]*=s,this}getMaxScaleOnAxis(){const e=this.elements,n=e[0]*e[0]+e[1]*e[1]+e[2]*e[2],i=e[4]*e[4]+e[5]*e[5]+e[6]*e[6],a=e[8]*e[8]+e[9]*e[9]+e[10]*e[10];return Math.sqrt(Math.max(n,i,a))}makeTranslation(e,n,i){return e.isVector3?this.set(1,0,0,e.x,0,1,0,e.y,0,0,1,e.z,0,0,0,1):this.set(1,0,0,e,0,1,0,n,0,0,1,i,0,0,0,1),this}makeRotationX(e){const n=Math.cos(e),i=Math.sin(e);return this.set(1,0,0,0,0,n,-i,0,0,i,n,0,0,0,0,1),this}makeRotationY(e){const n=Math.cos(e),i=Math.sin(e);return this.set(n,0,i,0,0,1,0,0,-i,0,n,0,0,0,0,1),this}makeRotationZ(e){const n=Math.cos(e),i=Math.sin(e);return this.set(n,-i,0,0,i,n,0,0,0,0,1,0,0,0,0,1),this}makeRotationAxis(e,n){const i=Math.cos(n),a=Math.sin(n),s=1-i,r=e.x,o=e.y,c=e.z,u=s*r,d=s*o;return this.set(u*r+i,u*o-a*c,u*c+a*o,0,u*o+a*c,d*o+i,d*c-a*r,0,u*c-a*o,d*c+a*r,s*c*c+i,0,0,0,0,1),this}makeScale(e,n,i){return this.set(e,0,0,0,0,n,0,0,0,0,i,0,0,0,0,1),this}makeShear(e,n,i,a,s,r){return this.set(1,i,s,0,e,1,r,0,n,a,1,0,0,0,0,1),this}compose(e,n,i){const a=this.elements,s=n._x,r=n._y,o=n._z,c=n._w,u=s+s,d=r+r,m=o+o,f=s*u,p=s*d,x=s*m,_=r*d,g=r*m,h=o*m,v=c*u,b=c*d,y=c*m,A=i.x,M=i.y,T=i.z;return a[0]=(1-(_+h))*A,a[1]=(p+y)*A,a[2]=(x-b)*A,a[3]=0,a[4]=(p-y)*M,a[5]=(1-(f+h))*M,a[6]=(g+v)*M,a[7]=0,a[8]=(x+b)*T,a[9]=(g-v)*T,a[10]=(1-(f+_))*T,a[11]=0,a[12]=e.x,a[13]=e.y,a[14]=e.z,a[15]=1,this}decompose(e,n,i){const a=this.elements;e.x=a[12],e.y=a[13],e.z=a[14];const s=this.determinantAffine();if(s===0)return i.set(1,1,1),n.identity(),this;let r=vr.set(a[0],a[1],a[2]).length();const o=vr.set(a[4],a[5],a[6]).length(),c=vr.set(a[8],a[9],a[10]).length();s<0&&(r=-r),Li.copy(this);const u=1/r,d=1/o,m=1/c;return Li.elements[0]*=u,Li.elements[1]*=u,Li.elements[2]*=u,Li.elements[4]*=d,Li.elements[5]*=d,Li.elements[6]*=d,Li.elements[8]*=m,Li.elements[9]*=m,Li.elements[10]*=m,n.setFromRotationMatrix(Li),i.x=r,i.y=o,i.z=c,this}makePerspective(e,n,i,a,s,r,o=ta,c=!1){const u=this.elements,d=2*s/(n-e),m=2*s/(i-a),f=(n+e)/(n-e),p=(i+a)/(i-a);let x,_;if(c)x=s/(r-s),_=r*s/(r-s);else if(o===ta)x=-(r+s)/(r-s),_=-2*r*s/(r-s);else if(o===cf)x=-r/(r-s),_=-r*s/(r-s);else throw new Error("THREE.Matrix4.makePerspective(): Invalid coordinate system: "+o);return u[0]=d,u[4]=0,u[8]=f,u[12]=0,u[1]=0,u[5]=m,u[9]=p,u[13]=0,u[2]=0,u[6]=0,u[10]=x,u[14]=_,u[3]=0,u[7]=0,u[11]=-1,u[15]=0,this}makeOrthographic(e,n,i,a,s,r,o=ta,c=!1){const u=this.elements,d=2/(n-e),m=2/(i-a),f=-(n+e)/(n-e),p=-(i+a)/(i-a);let x,_;if(c)x=1/(r-s),_=r/(r-s);else if(o===ta)x=-2/(r-s),_=-(r+s)/(r-s);else if(o===cf)x=-1/(r-s),_=-s/(r-s);else throw new Error("THREE.Matrix4.makeOrthographic(): Invalid coordinate system: "+o);return u[0]=d,u[4]=0,u[8]=0,u[12]=f,u[1]=0,u[5]=m,u[9]=0,u[13]=p,u[2]=0,u[6]=0,u[10]=x,u[14]=_,u[3]=0,u[7]=0,u[11]=0,u[15]=1,this}equals(e){const n=this.elements,i=e.elements;for(let a=0;a<16;a++)if(n[a]!==i[a])return!1;return!0}fromArray(e,n=0){for(let i=0;i<16;i++)this.elements[i]=e[i+n];return this}toArray(e=[],n=0){const i=this.elements;return e[n]=i[0],e[n+1]=i[1],e[n+2]=i[2],e[n+3]=i[3],e[n+4]=i[4],e[n+5]=i[5],e[n+6]=i[6],e[n+7]=i[7],e[n+8]=i[8],e[n+9]=i[9],e[n+10]=i[10],e[n+11]=i[11],e[n+12]=i[12],e[n+13]=i[13],e[n+14]=i[14],e[n+15]=i[15],e}};gf.prototype.isMatrix4=!0;let Ft=gf;const vr=new Y,Li=new Ft,vA=new Y(0,0,0),_A=new Y(1,1,1),Va=new Y,Ec=new Y,ti=new Y,M0=new Ft,E0=new Dl;class tr{constructor(e=0,n=0,i=0,a=tr.DEFAULT_ORDER){this.isEuler=!0,this._x=e,this._y=n,this._z=i,this._order=a}get x(){return this._x}set x(e){this._x=e,this._onChangeCallback()}get y(){return this._y}set y(e){this._y=e,this._onChangeCallback()}get z(){return this._z}set z(e){this._z=e,this._onChangeCallback()}get order(){return this._order}set order(e){this._order=e,this._onChangeCallback()}set(e,n,i,a=this._order){return this._x=e,this._y=n,this._z=i,this._order=a,this._onChangeCallback(),this}clone(){return new this.constructor(this._x,this._y,this._z,this._order)}copy(e){return this._x=e._x,this._y=e._y,this._z=e._z,this._order=e._order,this._onChangeCallback(),this}setFromRotationMatrix(e,n=this._order,i=!0){const a=e.elements,s=a[0],r=a[4],o=a[8],c=a[1],u=a[5],d=a[9],m=a[2],f=a[6],p=a[10];switch(n){case"XYZ":this._y=Math.asin(pt(o,-1,1)),Math.abs(o)<.9999999?(this._x=Math.atan2(-d,p),this._z=Math.atan2(-r,s)):(this._x=Math.atan2(f,u),this._z=0);break;case"YXZ":this._x=Math.asin(-pt(d,-1,1)),Math.abs(d)<.9999999?(this._y=Math.atan2(o,p),this._z=Math.atan2(c,u)):(this._y=Math.atan2(-m,s),this._z=0);break;case"ZXY":this._x=Math.asin(pt(f,-1,1)),Math.abs(f)<.9999999?(this._y=Math.atan2(-m,p),this._z=Math.atan2(-r,u)):(this._y=0,this._z=Math.atan2(c,s));break;case"ZYX":this._y=Math.asin(-pt(m,-1,1)),Math.abs(m)<.9999999?(this._x=Math.atan2(f,p),this._z=Math.atan2(c,s)):(this._x=0,this._z=Math.atan2(-r,u));break;case"YZX":this._z=Math.asin(pt(c,-1,1)),Math.abs(c)<.9999999?(this._x=Math.atan2(-d,u),this._y=Math.atan2(-m,s)):(this._x=0,this._y=Math.atan2(o,p));break;case"XZY":this._z=Math.asin(-pt(r,-1,1)),Math.abs(r)<.9999999?(this._x=Math.atan2(f,u),this._y=Math.atan2(o,s)):(this._x=Math.atan2(-d,p),this._y=0);break;default:Je("Euler: .setFromRotationMatrix() encountered an unknown order: "+n)}return this._order=n,i===!0&&this._onChangeCallback(),this}setFromQuaternion(e,n,i){return M0.makeRotationFromQuaternion(e),this.setFromRotationMatrix(M0,n,i)}setFromVector3(e,n=this._order){return this.set(e.x,e.y,e.z,n)}reorder(e){return E0.setFromEuler(this),this.setFromQuaternion(E0,e)}equals(e){return e._x===this._x&&e._y===this._y&&e._z===this._z&&e._order===this._order}fromArray(e){return this._x=e[0],this._y=e[1],this._z=e[2],e[3]!==void 0&&(this._order=e[3]),this._onChangeCallback(),this}toArray(e=[],n=0){return e[n]=this._x,e[n+1]=this._y,e[n+2]=this._z,e[n+3]=this._order,e}_onChange(e){return this._onChangeCallback=e,this}_onChangeCallback(){}*[Symbol.iterator](){yield this._x,yield this._y,yield this._z,yield this._order}}tr.DEFAULT_ORDER="XYZ";class mS{constructor(){this.mask=1}set(e){this.mask=(1<<e|0)>>>0}enable(e){this.mask|=1<<e|0}enableAll(){this.mask=-1}toggle(e){this.mask^=1<<e|0}disable(e){this.mask&=~(1<<e|0)}disableAll(){this.mask=0}test(e){return(this.mask&e.mask)!==0}isEnabled(e){return(this.mask&(1<<e|0))!==0}}let yA=0;const T0=new Y,_r=new Dl,da=new Ft,Tc=new Y,Gl=new Y,bA=new Y,SA=new Dl,A0=new Y(1,0,0),w0=new Y(0,1,0),C0=new Y(0,0,1),N0={type:"added"},MA={type:"removed"},yr={type:"childadded",child:null},Ud={type:"childremoved",child:null};class In extends cr{constructor(){super(),this.isObject3D=!0,Object.defineProperty(this,"id",{value:yA++}),this.uuid=sc(),this.name="",this.type="Object3D",this.parent=null,this.children=[],this.up=In.DEFAULT_UP.clone();const e=new Y,n=new tr,i=new Dl,a=new Y(1,1,1);function s(){i.setFromEuler(n,!1)}function r(){n.setFromQuaternion(i,void 0,!1)}n._onChange(s),i._onChange(r),Object.defineProperties(this,{position:{configurable:!0,enumerable:!0,value:e},rotation:{configurable:!0,enumerable:!0,value:n},quaternion:{configurable:!0,enumerable:!0,value:i},scale:{configurable:!0,enumerable:!0,value:a},modelViewMatrix:{value:new Ft},normalMatrix:{value:new it}}),this.matrix=new Ft,this.matrixWorld=new Ft,this.matrixAutoUpdate=In.DEFAULT_MATRIX_AUTO_UPDATE,this.matrixWorldAutoUpdate=In.DEFAULT_MATRIX_WORLD_AUTO_UPDATE,this.matrixWorldNeedsUpdate=!1,this.layers=new mS,this.visible=!0,this.castShadow=!1,this.receiveShadow=!1,this.frustumCulled=!0,this.renderOrder=0,this.animations=[],this.customDepthMaterial=void 0,this.customDistanceMaterial=void 0,this.static=!1,this.userData={},this.pivot=null}onBeforeShadow(){}onAfterShadow(){}onBeforeRender(){}onAfterRender(){}applyMatrix4(e){this.matrixAutoUpdate&&this.updateMatrix(),this.matrix.premultiply(e),this.matrix.decompose(this.position,this.quaternion,this.scale)}applyQuaternion(e){return this.quaternion.premultiply(e),this}setRotationFromAxisAngle(e,n){this.quaternion.setFromAxisAngle(e,n)}setRotationFromEuler(e){this.quaternion.setFromEuler(e,!0)}setRotationFromMatrix(e){this.quaternion.setFromRotationMatrix(e)}setRotationFromQuaternion(e){this.quaternion.copy(e)}rotateOnAxis(e,n){return _r.setFromAxisAngle(e,n),this.quaternion.multiply(_r),this}rotateOnWorldAxis(e,n){return _r.setFromAxisAngle(e,n),this.quaternion.premultiply(_r),this}rotateX(e){return this.rotateOnAxis(A0,e)}rotateY(e){return this.rotateOnAxis(w0,e)}rotateZ(e){return this.rotateOnAxis(C0,e)}translateOnAxis(e,n){return T0.copy(e).applyQuaternion(this.quaternion),this.position.add(T0.multiplyScalar(n)),this}translateX(e){return this.translateOnAxis(A0,e)}translateY(e){return this.translateOnAxis(w0,e)}translateZ(e){return this.translateOnAxis(C0,e)}localToWorld(e){return this.updateWorldMatrix(!0,!1),e.applyMatrix4(this.matrixWorld)}worldToLocal(e){return this.updateWorldMatrix(!0,!1),e.applyMatrix4(da.copy(this.matrixWorld).invert())}lookAt(e,n,i){e.isVector3?Tc.copy(e):Tc.set(e,n,i);const a=this.parent;this.updateWorldMatrix(!0,!1),Gl.setFromMatrixPosition(this.matrixWorld),this.isCamera||this.isLight?da.lookAt(Gl,Tc,this.up):da.lookAt(Tc,Gl,this.up),this.quaternion.setFromRotationMatrix(da),a&&(da.extractRotation(a.matrixWorld),_r.setFromRotationMatrix(da),this.quaternion.premultiply(_r.invert()))}add(e){if(arguments.length>1){for(let n=0;n<arguments.length;n++)this.add(arguments[n]);return this}return e===this?(St("Object3D.add: object can't be added as a child of itself.",e),this):(e&&e.isObject3D?(e.removeFromParent(),e.parent=this,this.children.push(e),e.dispatchEvent(N0),yr.child=e,this.dispatchEvent(yr),yr.child=null):St("Object3D.add: object not an instance of THREE.Object3D.",e),this)}remove(e){if(arguments.length>1){for(let i=0;i<arguments.length;i++)this.remove(arguments[i]);return this}const n=this.children.indexOf(e);return n!==-1&&(e.parent=null,this.children.splice(n,1),e.dispatchEvent(MA),Ud.child=e,this.dispatchEvent(Ud),Ud.child=null),this}removeFromParent(){const e=this.parent;return e!==null&&e.remove(this),this}clear(){return this.remove(...this.children)}attach(e){return this.updateWorldMatrix(!0,!1),da.copy(this.matrixWorld).invert(),e.parent!==null&&(e.parent.updateWorldMatrix(!0,!1),da.multiply(e.parent.matrixWorld)),e.applyMatrix4(da),e.removeFromParent(),e.parent=this,this.children.push(e),e.updateWorldMatrix(!1,!0),e.dispatchEvent(N0),yr.child=e,this.dispatchEvent(yr),yr.child=null,this}getObjectById(e){return this.getObjectByProperty("id",e)}getObjectByName(e){return this.getObjectByProperty("name",e)}getObjectByProperty(e,n){if(this[e]===n)return this;for(let i=0,a=this.children.length;i<a;i++){const r=this.children[i].getObjectByProperty(e,n);if(r!==void 0)return r}}getObjectsByProperty(e,n,i=[]){this[e]===n&&i.push(this);const a=this.children;for(let s=0,r=a.length;s<r;s++)a[s].getObjectsByProperty(e,n,i);return i}getWorldPosition(e){return this.updateWorldMatrix(!0,!1),e.setFromMatrixPosition(this.matrixWorld)}getWorldQuaternion(e){return this.updateWorldMatrix(!0,!1),this.matrixWorld.decompose(Gl,e,bA),e}getWorldScale(e){return this.updateWorldMatrix(!0,!1),this.matrixWorld.decompose(Gl,SA,e),e}getWorldDirection(e){this.updateWorldMatrix(!0,!1);const n=this.matrixWorld.elements;return e.set(n[8],n[9],n[10]).normalize()}raycast(){}traverse(e){e(this);const n=this.children;for(let i=0,a=n.length;i<a;i++)n[i].traverse(e)}traverseVisible(e){if(this.visible===!1)return;e(this);const n=this.children;for(let i=0,a=n.length;i<a;i++)n[i].traverseVisible(e)}traverseAncestors(e){const n=this.parent;n!==null&&(e(n),n.traverseAncestors(e))}updateMatrix(){this.matrix.compose(this.position,this.quaternion,this.scale);const e=this.pivot;if(e!==null){const n=e.x,i=e.y,a=e.z,s=this.matrix.elements;s[12]+=n-s[0]*n-s[4]*i-s[8]*a,s[13]+=i-s[1]*n-s[5]*i-s[9]*a,s[14]+=a-s[2]*n-s[6]*i-s[10]*a}this.matrixWorldNeedsUpdate=!0}updateMatrixWorld(e){this.matrixAutoUpdate&&this.updateMatrix(),(this.matrixWorldNeedsUpdate||e)&&(this.matrixWorldAutoUpdate===!0&&(this.parent===null?this.matrixWorld.copy(this.matrix):this.matrixWorld.multiplyMatrices(this.parent.matrixWorld,this.matrix)),this.matrixWorldNeedsUpdate=!1,e=!0);const n=this.children;for(let i=0,a=n.length;i<a;i++)n[i].updateMatrixWorld(e)}updateWorldMatrix(e,n,i=!1){const a=this.parent;if(e===!0&&a!==null&&a.updateWorldMatrix(!0,!1),this.matrixAutoUpdate&&this.updateMatrix(),(this.matrixWorldNeedsUpdate||i)&&(this.matrixWorldAutoUpdate===!0&&(this.parent===null?this.matrixWorld.copy(this.matrix):this.matrixWorld.multiplyMatrices(this.parent.matrixWorld,this.matrix)),this.matrixWorldNeedsUpdate=!1,i=!0),n===!0){const s=this.children;for(let r=0,o=s.length;r<o;r++)s[r].updateWorldMatrix(!1,!0,i)}}toJSON(e){const n=e===void 0||typeof e=="string",i={};n&&(e={geometries:{},materials:{},textures:{},images:{},shapes:{},skeletons:{},animations:{},nodes:{}},i.metadata={version:4.7,type:"Object",generator:"Object3D.toJSON"});const a={};a.uuid=this.uuid,a.type=this.type,this.name!==""&&(a.name=this.name),this.castShadow===!0&&(a.castShadow=!0),this.receiveShadow===!0&&(a.receiveShadow=!0),this.visible===!1&&(a.visible=!1),this.frustumCulled===!1&&(a.frustumCulled=!1),this.renderOrder!==0&&(a.renderOrder=this.renderOrder),this.static!==!1&&(a.static=this.static),Object.keys(this.userData).length>0&&(a.userData=this.userData),a.layers=this.layers.mask,a.matrix=this.matrix.toArray(),a.up=this.up.toArray(),this.pivot!==null&&(a.pivot=this.pivot.toArray()),this.matrixAutoUpdate===!1&&(a.matrixAutoUpdate=!1),this.morphTargetDictionary!==void 0&&(a.morphTargetDictionary=Object.assign({},this.morphTargetDictionary)),this.morphTargetInfluences!==void 0&&(a.morphTargetInfluences=this.morphTargetInfluences.slice()),this.isInstancedMesh&&(a.type="InstancedMesh",a.count=this.count,a.instanceMatrix=this.instanceMatrix.toJSON(),this.instanceColor!==null&&(a.instanceColor=this.instanceColor.toJSON())),this.isBatchedMesh&&(a.type="BatchedMesh",a.perObjectFrustumCulled=this.perObjectFrustumCulled,a.sortObjects=this.sortObjects,a.drawRanges=this._drawRanges,a.reservedRanges=this._reservedRanges,a.geometryInfo=this._geometryInfo.map(o=>({...o,boundingBox:o.boundingBox?o.boundingBox.toJSON():void 0,boundingSphere:o.boundingSphere?o.boundingSphere.toJSON():void 0})),a.instanceInfo=this._instanceInfo.map(o=>({...o})),a.availableInstanceIds=this._availableInstanceIds.slice(),a.availableGeometryIds=this._availableGeometryIds.slice(),a.nextIndexStart=this._nextIndexStart,a.nextVertexStart=this._nextVertexStart,a.geometryCount=this._geometryCount,a.maxInstanceCount=this._maxInstanceCount,a.maxVertexCount=this._maxVertexCount,a.maxIndexCount=this._maxIndexCount,a.geometryInitialized=this._geometryInitialized,a.matricesTexture=this._matricesTexture.toJSON(e),a.indirectTexture=this._indirectTexture.toJSON(e),this._colorsTexture!==null&&(a.colorsTexture=this._colorsTexture.toJSON(e)),this.boundingSphere!==null&&(a.boundingSphere=this.boundingSphere.toJSON()),this.boundingBox!==null&&(a.boundingBox=this.boundingBox.toJSON()));function s(o,c){return o[c.uuid]===void 0&&(o[c.uuid]=c.toJSON(e)),c.uuid}if(this.isScene)this.background&&(this.background.isColor?a.background=this.background.toJSON():this.background.isTexture&&(a.background=this.background.toJSON(e).uuid)),this.environment&&this.environment.isTexture&&this.environment.isRenderTargetTexture!==!0&&(a.environment=this.environment.toJSON(e).uuid);else if(this.isMesh||this.isLine||this.isPoints){a.geometry=s(e.geometries,this.geometry);const o=this.geometry.parameters;if(o!==void 0&&o.shapes!==void 0){const c=o.shapes;if(Array.isArray(c))for(let u=0,d=c.length;u<d;u++){const m=c[u];s(e.shapes,m)}else s(e.shapes,c)}}if(this.isSkinnedMesh&&(a.bindMode=this.bindMode,a.bindMatrix=this.bindMatrix.toArray(),this.skeleton!==void 0&&(s(e.skeletons,this.skeleton),a.skeleton=this.skeleton.uuid)),this.material!==void 0)if(Array.isArray(this.material)){const o=[];for(let c=0,u=this.material.length;c<u;c++)o.push(s(e.materials,this.material[c]));a.material=o}else a.material=s(e.materials,this.material);if(this.children.length>0){a.children=[];for(let o=0;o<this.children.length;o++)a.children.push(this.children[o].toJSON(e).object)}if(this.animations.length>0){a.animations=[];for(let o=0;o<this.animations.length;o++){const c=this.animations[o];a.animations.push(s(e.animations,c))}}if(n){const o=r(e.geometries),c=r(e.materials),u=r(e.textures),d=r(e.images),m=r(e.shapes),f=r(e.skeletons),p=r(e.animations),x=r(e.nodes);o.length>0&&(i.geometries=o),c.length>0&&(i.materials=c),u.length>0&&(i.textures=u),d.length>0&&(i.images=d),m.length>0&&(i.shapes=m),f.length>0&&(i.skeletons=f),p.length>0&&(i.animations=p),x.length>0&&(i.nodes=x)}return i.object=a,i;function r(o){const c=[];for(const u in o){const d=o[u];delete d.metadata,c.push(d)}return c}}clone(e){return new this.constructor().copy(this,e)}copy(e,n=!0){if(this.name=e.name,this.up.copy(e.up),this.position.copy(e.position),this.rotation.order=e.rotation.order,this.quaternion.copy(e.quaternion),this.scale.copy(e.scale),this.pivot=e.pivot!==null?e.pivot.clone():null,this.matrix.copy(e.matrix),this.matrixWorld.copy(e.matrixWorld),this.matrixAutoUpdate=e.matrixAutoUpdate,this.matrixWorldAutoUpdate=e.matrixWorldAutoUpdate,this.matrixWorldNeedsUpdate=e.matrixWorldNeedsUpdate,this.layers.mask=e.layers.mask,this.visible=e.visible,this.castShadow=e.castShadow,this.receiveShadow=e.receiveShadow,this.frustumCulled=e.frustumCulled,this.renderOrder=e.renderOrder,this.static=e.static,this.animations=e.animations.slice(),this.userData=JSON.parse(JSON.stringify(e.userData)),n===!0)for(let i=0;i<e.children.length;i++){const a=e.children[i];this.add(a.clone())}return this}}In.DEFAULT_UP=new Y(0,1,0);In.DEFAULT_MATRIX_AUTO_UPDATE=!0;In.DEFAULT_MATRIX_WORLD_AUTO_UPDATE=!0;class Ac extends In{constructor(){super(),this.isGroup=!0,this.type="Group"}}const EA={type:"move"};class Od{constructor(){this._targetRay=null,this._grip=null,this._hand=null}getHandSpace(){return this._hand===null&&(this._hand=new Ac,this._hand.matrixAutoUpdate=!1,this._hand.visible=!1,this._hand.joints={},this._hand.inputState={pinching:!1}),this._hand}getTargetRaySpace(){return this._targetRay===null&&(this._targetRay=new Ac,this._targetRay.matrixAutoUpdate=!1,this._targetRay.visible=!1,this._targetRay.hasLinearVelocity=!1,this._targetRay.linearVelocity=new Y,this._targetRay.hasAngularVelocity=!1,this._targetRay.angularVelocity=new Y),this._targetRay}getGripSpace(){return this._grip===null&&(this._grip=new Ac,this._grip.matrixAutoUpdate=!1,this._grip.visible=!1,this._grip.hasLinearVelocity=!1,this._grip.linearVelocity=new Y,this._grip.hasAngularVelocity=!1,this._grip.angularVelocity=new Y,this._grip.eventsEnabled=!1),this._grip}dispatchEvent(e){return this._targetRay!==null&&this._targetRay.dispatchEvent(e),this._grip!==null&&this._grip.dispatchEvent(e),this._hand!==null&&this._hand.dispatchEvent(e),this}connect(e){if(e&&e.hand){const n=this._hand;if(n)for(const i of e.hand.values())this._getHandJoint(n,i)}return this.dispatchEvent({type:"connected",data:e}),this}disconnect(e){return this.dispatchEvent({type:"disconnected",data:e}),this._targetRay!==null&&(this._targetRay.visible=!1),this._grip!==null&&(this._grip.visible=!1),this._hand!==null&&(this._hand.visible=!1),this}update(e,n,i){let a=null,s=null,r=null;const o=this._targetRay,c=this._grip,u=this._hand;if(e&&n.session.visibilityState!=="visible-blurred"){if(u&&e.hand){r=!0;for(const _ of e.hand.values()){const g=n.getJointPose(_,i),h=this._getHandJoint(u,_);g!==null&&(h.matrix.fromArray(g.transform.matrix),h.matrix.decompose(h.position,h.rotation,h.scale),h.matrixWorldNeedsUpdate=!0,h.jointRadius=g.radius),h.visible=g!==null}const d=u.joints["index-finger-tip"],m=u.joints["thumb-tip"],f=d.position.distanceTo(m.position),p=.02,x=.005;u.inputState.pinching&&f>p+x?(u.inputState.pinching=!1,this.dispatchEvent({type:"pinchend",handedness:e.handedness,target:this})):!u.inputState.pinching&&f<=p-x&&(u.inputState.pinching=!0,this.dispatchEvent({type:"pinchstart",handedness:e.handedness,target:this}))}else c!==null&&e.gripSpace&&(s=n.getPose(e.gripSpace,i),s!==null&&(c.matrix.fromArray(s.transform.matrix),c.matrix.decompose(c.position,c.rotation,c.scale),c.matrixWorldNeedsUpdate=!0,s.linearVelocity?(c.hasLinearVelocity=!0,c.linearVelocity.copy(s.linearVelocity)):c.hasLinearVelocity=!1,s.angularVelocity?(c.hasAngularVelocity=!0,c.angularVelocity.copy(s.angularVelocity)):c.hasAngularVelocity=!1,c.eventsEnabled&&c.dispatchEvent({type:"gripUpdated",data:e,target:this})));o!==null&&(a=n.getPose(e.targetRaySpace,i),a===null&&s!==null&&(a=s),a!==null&&(o.matrix.fromArray(a.transform.matrix),o.matrix.decompose(o.position,o.rotation,o.scale),o.matrixWorldNeedsUpdate=!0,a.linearVelocity?(o.hasLinearVelocity=!0,o.linearVelocity.copy(a.linearVelocity)):o.hasLinearVelocity=!1,a.angularVelocity?(o.hasAngularVelocity=!0,o.angularVelocity.copy(a.angularVelocity)):o.hasAngularVelocity=!1,this.dispatchEvent(EA)))}return o!==null&&(o.visible=a!==null),c!==null&&(c.visible=s!==null),u!==null&&(u.visible=r!==null),this}_getHandJoint(e,n){if(e.joints[n.jointName]===void 0){const i=new Ac;i.matrixAutoUpdate=!1,i.visible=!1,e.joints[n.jointName]=i,e.add(i)}return e.joints[n.jointName]}}const gS={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074},Wa={h:0,s:0,l:0},wc={h:0,s:0,l:0};function Pd(t,e,n){return n<0&&(n+=1),n>1&&(n-=1),n<1/6?t+(e-t)*6*n:n<1/2?e:n<2/3?t+(e-t)*6*(2/3-n):t}class gt{constructor(e,n,i){return this.isColor=!0,this.r=1,this.g=1,this.b=1,this.set(e,n,i)}set(e,n,i){if(n===void 0&&i===void 0){const a=e;a&&a.isColor?this.copy(a):typeof a=="number"?this.setHex(a):typeof a=="string"&&this.setStyle(a)}else this.setRGB(e,n,i);return this}setScalar(e){return this.r=e,this.g=e,this.b=e,this}setHex(e,n=xi){return e=Math.floor(e),this.r=(e>>16&255)/255,this.g=(e>>8&255)/255,this.b=(e&255)/255,ht.colorSpaceToWorking(this,n),this}setRGB(e,n,i,a=ht.workingColorSpace){return this.r=e,this.g=n,this.b=i,ht.colorSpaceToWorking(this,a),this}setHSL(e,n,i,a=ht.workingColorSpace){if(e=fA(e,1),n=pt(n,0,1),i=pt(i,0,1),n===0)this.r=this.g=this.b=i;else{const s=i<=.5?i*(1+n):i+n-i*n,r=2*i-s;this.r=Pd(r,s,e+1/3),this.g=Pd(r,s,e),this.b=Pd(r,s,e-1/3)}return ht.colorSpaceToWorking(this,a),this}setStyle(e,n=xi){function i(s){s!==void 0&&parseFloat(s)<1&&Je("Color: Alpha component of "+e+" will be ignored.")}let a;if(a=/^(\w+)\(([^\)]*)\)/.exec(e)){let s;const r=a[1],o=a[2];switch(r){case"rgb":case"rgba":if(s=/^\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(o))return i(s[4]),this.setRGB(Math.min(255,parseInt(s[1],10))/255,Math.min(255,parseInt(s[2],10))/255,Math.min(255,parseInt(s[3],10))/255,n);if(s=/^\s*(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(o))return i(s[4]),this.setRGB(Math.min(100,parseInt(s[1],10))/100,Math.min(100,parseInt(s[2],10))/100,Math.min(100,parseInt(s[3],10))/100,n);break;case"hsl":case"hsla":if(s=/^\s*(\d*\.?\d+)\s*,\s*(\d*\.?\d+)\%\s*,\s*(\d*\.?\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(o))return i(s[4]),this.setHSL(parseFloat(s[1])/360,parseFloat(s[2])/100,parseFloat(s[3])/100,n);break;default:Je("Color: Unknown color model "+e)}}else if(a=/^\#([A-Fa-f\d]+)$/.exec(e)){const s=a[1],r=s.length;if(r===3)return this.setRGB(parseInt(s.charAt(0),16)/15,parseInt(s.charAt(1),16)/15,parseInt(s.charAt(2),16)/15,n);if(r===6)return this.setHex(parseInt(s,16),n);Je("Color: Invalid hex color "+e)}else if(e&&e.length>0)return this.setColorName(e,n);return this}setColorName(e,n=xi){const i=gS[e.toLowerCase()];return i!==void 0?this.setHex(i,n):Je("Color: Unknown color "+e),this}clone(){return new this.constructor(this.r,this.g,this.b)}copy(e){return this.r=e.r,this.g=e.g,this.b=e.b,this}copySRGBToLinear(e){return this.r=Ra(e.r),this.g=Ra(e.g),this.b=Ra(e.b),this}copyLinearToSRGB(e){return this.r=al(e.r),this.g=al(e.g),this.b=al(e.b),this}convertSRGBToLinear(){return this.copySRGBToLinear(this),this}convertLinearToSRGB(){return this.copyLinearToSRGB(this),this}getHex(e=xi){return ht.workingToColorSpace(Nn.copy(this),e),Math.round(pt(Nn.r*255,0,255))*65536+Math.round(pt(Nn.g*255,0,255))*256+Math.round(pt(Nn.b*255,0,255))}getHexString(e=xi){return("000000"+this.getHex(e).toString(16)).slice(-6)}getHSL(e,n=ht.workingColorSpace){ht.workingToColorSpace(Nn.copy(this),n);const i=Nn.r,a=Nn.g,s=Nn.b,r=Math.max(i,a,s),o=Math.min(i,a,s);let c,u;const d=(o+r)/2;if(o===r)c=0,u=0;else{const m=r-o;switch(u=d<=.5?m/(r+o):m/(2-r-o),r){case i:c=(a-s)/m+(a<s?6:0);break;case a:c=(s-i)/m+2;break;case s:c=(i-a)/m+4;break}c/=6}return e.h=c,e.s=u,e.l=d,e}getRGB(e,n=ht.workingColorSpace){return ht.workingToColorSpace(Nn.copy(this),n),e.r=Nn.r,e.g=Nn.g,e.b=Nn.b,e}getStyle(e=xi){ht.workingToColorSpace(Nn.copy(this),e);const n=Nn.r,i=Nn.g,a=Nn.b;return e!==xi?`color(${e} ${n.toFixed(3)} ${i.toFixed(3)} ${a.toFixed(3)})`:`rgb(${Math.round(n*255)},${Math.round(i*255)},${Math.round(a*255)})`}offsetHSL(e,n,i){return this.getHSL(Wa),this.setHSL(Wa.h+e,Wa.s+n,Wa.l+i)}add(e){return this.r+=e.r,this.g+=e.g,this.b+=e.b,this}addColors(e,n){return this.r=e.r+n.r,this.g=e.g+n.g,this.b=e.b+n.b,this}addScalar(e){return this.r+=e,this.g+=e,this.b+=e,this}sub(e){return this.r=Math.max(0,this.r-e.r),this.g=Math.max(0,this.g-e.g),this.b=Math.max(0,this.b-e.b),this}multiply(e){return this.r*=e.r,this.g*=e.g,this.b*=e.b,this}multiplyScalar(e){return this.r*=e,this.g*=e,this.b*=e,this}lerp(e,n){return this.r+=(e.r-this.r)*n,this.g+=(e.g-this.g)*n,this.b+=(e.b-this.b)*n,this}lerpColors(e,n,i){return this.r=e.r+(n.r-e.r)*i,this.g=e.g+(n.g-e.g)*i,this.b=e.b+(n.b-e.b)*i,this}lerpHSL(e,n){this.getHSL(Wa),e.getHSL(wc);const i=Cd(Wa.h,wc.h,n),a=Cd(Wa.s,wc.s,n),s=Cd(Wa.l,wc.l,n);return this.setHSL(i,a,s),this}setFromVector3(e){return this.r=e.x,this.g=e.y,this.b=e.z,this}applyMatrix3(e){const n=this.r,i=this.g,a=this.b,s=e.elements;return this.r=s[0]*n+s[3]*i+s[6]*a,this.g=s[1]*n+s[4]*i+s[7]*a,this.b=s[2]*n+s[5]*i+s[8]*a,this}equals(e){return e.r===this.r&&e.g===this.g&&e.b===this.b}fromArray(e,n=0){return this.r=e[n],this.g=e[n+1],this.b=e[n+2],this}toArray(e=[],n=0){return e[n]=this.r,e[n+1]=this.g,e[n+2]=this.b,e}fromBufferAttribute(e,n){return this.r=e.getX(n),this.g=e.getY(n),this.b=e.getZ(n),this}toJSON(){return this.getHex()}*[Symbol.iterator](){yield this.r,yield this.g,yield this.b}}const Nn=new gt;gt.NAMES=gS;class TA extends In{constructor(){super(),this.isScene=!0,this.type="Scene",this.background=null,this.environment=null,this.fog=null,this.backgroundBlurriness=0,this.backgroundIntensity=1,this.backgroundRotation=new tr,this.environmentIntensity=1,this.environmentRotation=new tr,this.overrideMaterial=null,typeof __THREE_DEVTOOLS__<"u"&&__THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe",{detail:this}))}copy(e,n){return super.copy(e,n),e.background!==null&&(this.background=e.background.clone()),e.environment!==null&&(this.environment=e.environment.clone()),e.fog!==null&&(this.fog=e.fog.clone()),this.backgroundBlurriness=e.backgroundBlurriness,this.backgroundIntensity=e.backgroundIntensity,this.backgroundRotation.copy(e.backgroundRotation),this.environmentIntensity=e.environmentIntensity,this.environmentRotation.copy(e.environmentRotation),e.overrideMaterial!==null&&(this.overrideMaterial=e.overrideMaterial.clone()),this.matrixAutoUpdate=e.matrixAutoUpdate,this}toJSON(e){const n=super.toJSON(e);return this.fog!==null&&(n.object.fog=this.fog.toJSON()),this.backgroundBlurriness>0&&(n.object.backgroundBlurriness=this.backgroundBlurriness),this.backgroundIntensity!==1&&(n.object.backgroundIntensity=this.backgroundIntensity),n.object.backgroundRotation=this.backgroundRotation.toArray(),this.environmentIntensity!==1&&(n.object.environmentIntensity=this.environmentIntensity),n.object.environmentRotation=this.environmentRotation.toArray(),n}}const Ui=new Y,ha=new Y,zd=new Y,pa=new Y,br=new Y,Sr=new Y,R0=new Y,Id=new Y,kd=new Y,Bd=new Y,Fd=new Qt,jd=new Qt,Hd=new Qt;class Ti{constructor(e=new Y,n=new Y,i=new Y){this.a=e,this.b=n,this.c=i}static getNormal(e,n,i,a){a.subVectors(i,n),Ui.subVectors(e,n),a.cross(Ui);const s=a.lengthSq();return s>0?a.multiplyScalar(1/Math.sqrt(s)):a.set(0,0,0)}static getBarycoord(e,n,i,a,s){Ui.subVectors(a,n),ha.subVectors(i,n),zd.subVectors(e,n);const r=Ui.dot(Ui),o=Ui.dot(ha),c=Ui.dot(zd),u=ha.dot(ha),d=ha.dot(zd),m=r*u-o*o;if(m===0)return s.set(0,0,0),null;const f=1/m,p=(u*c-o*d)*f,x=(r*d-o*c)*f;return s.set(1-p-x,x,p)}static containsPoint(e,n,i,a){return this.getBarycoord(e,n,i,a,pa)===null?!1:pa.x>=0&&pa.y>=0&&pa.x+pa.y<=1}static getInterpolation(e,n,i,a,s,r,o,c){return this.getBarycoord(e,n,i,a,pa)===null?(c.x=0,c.y=0,"z"in c&&(c.z=0),"w"in c&&(c.w=0),null):(c.setScalar(0),c.addScaledVector(s,pa.x),c.addScaledVector(r,pa.y),c.addScaledVector(o,pa.z),c)}static getInterpolatedAttribute(e,n,i,a,s,r){return Fd.setScalar(0),jd.setScalar(0),Hd.setScalar(0),Fd.fromBufferAttribute(e,n),jd.fromBufferAttribute(e,i),Hd.fromBufferAttribute(e,a),r.setScalar(0),r.addScaledVector(Fd,s.x),r.addScaledVector(jd,s.y),r.addScaledVector(Hd,s.z),r}static isFrontFacing(e,n,i,a){return Ui.subVectors(i,n),ha.subVectors(e,n),Ui.cross(ha).dot(a)<0}set(e,n,i){return this.a.copy(e),this.b.copy(n),this.c.copy(i),this}setFromPointsAndIndices(e,n,i,a){return this.a.copy(e[n]),this.b.copy(e[i]),this.c.copy(e[a]),this}setFromAttributeAndIndices(e,n,i,a){return this.a.fromBufferAttribute(e,n),this.b.fromBufferAttribute(e,i),this.c.fromBufferAttribute(e,a),this}clone(){return new this.constructor().copy(this)}copy(e){return this.a.copy(e.a),this.b.copy(e.b),this.c.copy(e.c),this}getArea(){return Ui.subVectors(this.c,this.b),ha.subVectors(this.a,this.b),Ui.cross(ha).length()*.5}getMidpoint(e){return e.addVectors(this.a,this.b).add(this.c).multiplyScalar(1/3)}getNormal(e){return Ti.getNormal(this.a,this.b,this.c,e)}getPlane(e){return e.setFromCoplanarPoints(this.a,this.b,this.c)}getBarycoord(e,n){return Ti.getBarycoord(e,this.a,this.b,this.c,n)}getInterpolation(e,n,i,a,s){return Ti.getInterpolation(e,this.a,this.b,this.c,n,i,a,s)}containsPoint(e){return Ti.containsPoint(e,this.a,this.b,this.c)}isFrontFacing(e){return Ti.isFrontFacing(this.a,this.b,this.c,e)}intersectsBox(e){return e.intersectsTriangle(this)}closestPointToPoint(e,n){const i=this.a,a=this.b,s=this.c;let r,o;br.subVectors(a,i),Sr.subVectors(s,i),Id.subVectors(e,i);const c=br.dot(Id),u=Sr.dot(Id);if(c<=0&&u<=0)return n.copy(i);kd.subVectors(e,a);const d=br.dot(kd),m=Sr.dot(kd);if(d>=0&&m<=d)return n.copy(a);const f=c*m-d*u;if(f<=0&&c>=0&&d<=0)return r=c/(c-d),n.copy(i).addScaledVector(br,r);Bd.subVectors(e,s);const p=br.dot(Bd),x=Sr.dot(Bd);if(x>=0&&p<=x)return n.copy(s);const _=p*u-c*x;if(_<=0&&u>=0&&x<=0)return o=u/(u-x),n.copy(i).addScaledVector(Sr,o);const g=d*x-p*m;if(g<=0&&m-d>=0&&p-x>=0)return R0.subVectors(s,a),o=(m-d)/(m-d+(p-x)),n.copy(a).addScaledVector(R0,o);const h=1/(g+_+f);return r=_*h,o=f*h,n.copy(i).addScaledVector(br,r).addScaledVector(Sr,o)}equals(e){return e.a.equals(this.a)&&e.b.equals(this.b)&&e.c.equals(this.c)}}class ur{constructor(e=new Y(1/0,1/0,1/0),n=new Y(-1/0,-1/0,-1/0)){this.isBox3=!0,this.min=e,this.max=n}set(e,n){return this.min.copy(e),this.max.copy(n),this}setFromArray(e){this.makeEmpty();for(let n=0,i=e.length;n<i;n+=3)this.expandByPoint(Oi.fromArray(e,n));return this}setFromBufferAttribute(e){this.makeEmpty();for(let n=0,i=e.count;n<i;n++)this.expandByPoint(Oi.fromBufferAttribute(e,n));return this}setFromPoints(e){this.makeEmpty();for(let n=0,i=e.length;n<i;n++)this.expandByPoint(e[n]);return this}setFromCenterAndSize(e,n){const i=Oi.copy(n).multiplyScalar(.5);return this.min.copy(e).sub(i),this.max.copy(e).add(i),this}setFromObject(e,n=!1){return this.makeEmpty(),this.expandByObject(e,n)}clone(){return new this.constructor().copy(this)}copy(e){return this.min.copy(e.min),this.max.copy(e.max),this}makeEmpty(){return this.min.x=this.min.y=this.min.z=1/0,this.max.x=this.max.y=this.max.z=-1/0,this}isEmpty(){return this.max.x<this.min.x||this.max.y<this.min.y||this.max.z<this.min.z}getCenter(e){return this.isEmpty()?e.set(0,0,0):e.addVectors(this.min,this.max).multiplyScalar(.5)}getSize(e){return this.isEmpty()?e.set(0,0,0):e.subVectors(this.max,this.min)}expandByPoint(e){return this.min.min(e),this.max.max(e),this}expandByVector(e){return this.min.sub(e),this.max.add(e),this}expandByScalar(e){return this.min.addScalar(-e),this.max.addScalar(e),this}expandByObject(e,n=!1){e.updateWorldMatrix(!1,!1);const i=e.geometry;if(i!==void 0){const s=i.getAttribute("position");if(n===!0&&s!==void 0&&e.isInstancedMesh!==!0)for(let r=0,o=s.count;r<o;r++)e.isMesh===!0?e.getVertexPosition(r,Oi):Oi.fromBufferAttribute(s,r),Oi.applyMatrix4(e.matrixWorld),this.expandByPoint(Oi);else e.boundingBox!==void 0?(e.boundingBox===null&&e.computeBoundingBox(),Cc.copy(e.boundingBox)):(i.boundingBox===null&&i.computeBoundingBox(),Cc.copy(i.boundingBox)),Cc.applyMatrix4(e.matrixWorld),this.union(Cc)}const a=e.children;for(let s=0,r=a.length;s<r;s++)this.expandByObject(a[s],n);return this}containsPoint(e){return e.x>=this.min.x&&e.x<=this.max.x&&e.y>=this.min.y&&e.y<=this.max.y&&e.z>=this.min.z&&e.z<=this.max.z}containsBox(e){return this.min.x<=e.min.x&&e.max.x<=this.max.x&&this.min.y<=e.min.y&&e.max.y<=this.max.y&&this.min.z<=e.min.z&&e.max.z<=this.max.z}getParameter(e,n){return n.set((e.x-this.min.x)/(this.max.x-this.min.x),(e.y-this.min.y)/(this.max.y-this.min.y),(e.z-this.min.z)/(this.max.z-this.min.z))}intersectsBox(e){return e.max.x>=this.min.x&&e.min.x<=this.max.x&&e.max.y>=this.min.y&&e.min.y<=this.max.y&&e.max.z>=this.min.z&&e.min.z<=this.max.z}intersectsSphere(e){return this.clampPoint(e.center,Oi),Oi.distanceToSquared(e.center)<=e.radius*e.radius}intersectsPlane(e){let n,i;return e.normal.x>0?(n=e.normal.x*this.min.x,i=e.normal.x*this.max.x):(n=e.normal.x*this.max.x,i=e.normal.x*this.min.x),e.normal.y>0?(n+=e.normal.y*this.min.y,i+=e.normal.y*this.max.y):(n+=e.normal.y*this.max.y,i+=e.normal.y*this.min.y),e.normal.z>0?(n+=e.normal.z*this.min.z,i+=e.normal.z*this.max.z):(n+=e.normal.z*this.max.z,i+=e.normal.z*this.min.z),n<=-e.constant&&i>=-e.constant}intersectsTriangle(e){if(this.isEmpty())return!1;this.getCenter(Vl),Nc.subVectors(this.max,Vl),Mr.subVectors(e.a,Vl),Er.subVectors(e.b,Vl),Tr.subVectors(e.c,Vl),Xa.subVectors(Er,Mr),qa.subVectors(Tr,Er),Rs.subVectors(Mr,Tr);let n=[0,-Xa.z,Xa.y,0,-qa.z,qa.y,0,-Rs.z,Rs.y,Xa.z,0,-Xa.x,qa.z,0,-qa.x,Rs.z,0,-Rs.x,-Xa.y,Xa.x,0,-qa.y,qa.x,0,-Rs.y,Rs.x,0];return!Gd(n,Mr,Er,Tr,Nc)||(n=[1,0,0,0,1,0,0,0,1],!Gd(n,Mr,Er,Tr,Nc))?!1:(Rc.crossVectors(Xa,qa),n=[Rc.x,Rc.y,Rc.z],Gd(n,Mr,Er,Tr,Nc))}clampPoint(e,n){return n.copy(e).clamp(this.min,this.max)}distanceToPoint(e){return this.clampPoint(e,Oi).distanceTo(e)}getBoundingSphere(e){return this.isEmpty()?e.makeEmpty():(this.getCenter(e.center),e.radius=this.getSize(Oi).length()*.5),e}intersect(e){return this.min.max(e.min),this.max.min(e.max),this.isEmpty()&&this.makeEmpty(),this}union(e){return this.min.min(e.min),this.max.max(e.max),this}applyMatrix4(e){return this.isEmpty()?this:(ma[0].set(this.min.x,this.min.y,this.min.z).applyMatrix4(e),ma[1].set(this.min.x,this.min.y,this.max.z).applyMatrix4(e),ma[2].set(this.min.x,this.max.y,this.min.z).applyMatrix4(e),ma[3].set(this.min.x,this.max.y,this.max.z).applyMatrix4(e),ma[4].set(this.max.x,this.min.y,this.min.z).applyMatrix4(e),ma[5].set(this.max.x,this.min.y,this.max.z).applyMatrix4(e),ma[6].set(this.max.x,this.max.y,this.min.z).applyMatrix4(e),ma[7].set(this.max.x,this.max.y,this.max.z).applyMatrix4(e),this.setFromPoints(ma),this)}translate(e){return this.min.add(e),this.max.add(e),this}equals(e){return e.min.equals(this.min)&&e.max.equals(this.max)}toJSON(){return{min:this.min.toArray(),max:this.max.toArray()}}fromJSON(e){return this.min.fromArray(e.min),this.max.fromArray(e.max),this}}const ma=[new Y,new Y,new Y,new Y,new Y,new Y,new Y,new Y],Oi=new Y,Cc=new ur,Mr=new Y,Er=new Y,Tr=new Y,Xa=new Y,qa=new Y,Rs=new Y,Vl=new Y,Nc=new Y,Rc=new Y,Ds=new Y;function Gd(t,e,n,i,a){for(let s=0,r=t.length-3;s<=r;s+=3){Ds.fromArray(t,s);const o=a.x*Math.abs(Ds.x)+a.y*Math.abs(Ds.y)+a.z*Math.abs(Ds.z),c=e.dot(Ds),u=n.dot(Ds),d=i.dot(Ds);if(Math.max(-Math.max(c,u,d),Math.min(c,u,d))>o)return!1}return!0}const an=new Y,Dc=new At;let AA=0;class cn extends cr{constructor(e,n,i=!1){if(super(),Array.isArray(e))throw new TypeError("THREE.BufferAttribute: array should be a Typed Array.");this.isBufferAttribute=!0,Object.defineProperty(this,"id",{value:AA++}),this.name="",this.array=e,this.itemSize=n,this.count=e!==void 0?e.length/n:0,this.normalized=i,this.usage=g0,this.updateRanges=[],this.gpuType=Ii,this.version=0}onUploadCallback(){}set needsUpdate(e){e===!0&&this.version++}setUsage(e){return this.usage=e,this}addUpdateRange(e,n){this.updateRanges.push({start:e,count:n})}clearUpdateRanges(){this.updateRanges.length=0}copy(e){return this.name=e.name,this.array=new e.array.constructor(e.array),this.itemSize=e.itemSize,this.count=e.count,this.normalized=e.normalized,this.usage=e.usage,this.gpuType=e.gpuType,this}copyAt(e,n,i){e*=this.itemSize,i*=n.itemSize;for(let a=0,s=this.itemSize;a<s;a++)this.array[e+a]=n.array[i+a];return this}copyArray(e){return this.array.set(e),this}applyMatrix3(e){if(this.itemSize===2)for(let n=0,i=this.count;n<i;n++)Dc.fromBufferAttribute(this,n),Dc.applyMatrix3(e),this.setXY(n,Dc.x,Dc.y);else if(this.itemSize===3)for(let n=0,i=this.count;n<i;n++)an.fromBufferAttribute(this,n),an.applyMatrix3(e),this.setXYZ(n,an.x,an.y,an.z);return this}applyMatrix4(e){for(let n=0,i=this.count;n<i;n++)an.fromBufferAttribute(this,n),an.applyMatrix4(e),this.setXYZ(n,an.x,an.y,an.z);return this}applyNormalMatrix(e){for(let n=0,i=this.count;n<i;n++)an.fromBufferAttribute(this,n),an.applyNormalMatrix(e),this.setXYZ(n,an.x,an.y,an.z);return this}transformDirection(e){for(let n=0,i=this.count;n<i;n++)an.fromBufferAttribute(this,n),an.transformDirection(e),this.setXYZ(n,an.x,an.y,an.z);return this}set(e,n=0){return this.array.set(e,n),this}getComponent(e,n){let i=this.array[e*this.itemSize+n];return this.normalized&&(i=Hl(i,this.array)),i}setComponent(e,n,i){return this.normalized&&(i=Hn(i,this.array)),this.array[e*this.itemSize+n]=i,this}getX(e){let n=this.array[e*this.itemSize];return this.normalized&&(n=Hl(n,this.array)),n}setX(e,n){return this.normalized&&(n=Hn(n,this.array)),this.array[e*this.itemSize]=n,this}getY(e){let n=this.array[e*this.itemSize+1];return this.normalized&&(n=Hl(n,this.array)),n}setY(e,n){return this.normalized&&(n=Hn(n,this.array)),this.array[e*this.itemSize+1]=n,this}getZ(e){let n=this.array[e*this.itemSize+2];return this.normalized&&(n=Hl(n,this.array)),n}setZ(e,n){return this.normalized&&(n=Hn(n,this.array)),this.array[e*this.itemSize+2]=n,this}getW(e){let n=this.array[e*this.itemSize+3];return this.normalized&&(n=Hl(n,this.array)),n}setW(e,n){return this.normalized&&(n=Hn(n,this.array)),this.array[e*this.itemSize+3]=n,this}setXY(e,n,i){return e*=this.itemSize,this.normalized&&(n=Hn(n,this.array),i=Hn(i,this.array)),this.array[e+0]=n,this.array[e+1]=i,this}setXYZ(e,n,i,a){return e*=this.itemSize,this.normalized&&(n=Hn(n,this.array),i=Hn(i,this.array),a=Hn(a,this.array)),this.array[e+0]=n,this.array[e+1]=i,this.array[e+2]=a,this}setXYZW(e,n,i,a,s){return e*=this.itemSize,this.normalized&&(n=Hn(n,this.array),i=Hn(i,this.array),a=Hn(a,this.array),s=Hn(s,this.array)),this.array[e+0]=n,this.array[e+1]=i,this.array[e+2]=a,this.array[e+3]=s,this}onUpload(e){return this.onUploadCallback=e,this}clone(){return new this.constructor(this.array,this.itemSize).copy(this)}toJSON(){const e={itemSize:this.itemSize,type:this.array.constructor.name,array:Array.from(this.array),normalized:this.normalized};return this.name!==""&&(e.name=this.name),this.usage!==g0&&(e.usage=this.usage),e}dispose(){this.dispatchEvent({type:"dispose"})}}class xS extends cn{constructor(e,n,i){super(new Uint16Array(e),n,i)}}class vS extends cn{constructor(e,n,i){super(new Uint32Array(e),n,i)}}class Ci extends cn{constructor(e,n,i){super(new Float32Array(e),n,i)}}const wA=new ur,Wl=new Y,Vd=new Y;class fr{constructor(e=new Y,n=-1){this.isSphere=!0,this.center=e,this.radius=n}set(e,n){return this.center.copy(e),this.radius=n,this}setFromPoints(e,n){const i=this.center;n!==void 0?i.copy(n):wA.setFromPoints(e).getCenter(i);let a=0;for(let s=0,r=e.length;s<r;s++)a=Math.max(a,i.distanceToSquared(e[s]));return this.radius=Math.sqrt(a),this}copy(e){return this.center.copy(e.center),this.radius=e.radius,this}isEmpty(){return this.radius<0}makeEmpty(){return this.center.set(0,0,0),this.radius=-1,this}containsPoint(e){return e.distanceToSquared(this.center)<=this.radius*this.radius}distanceToPoint(e){return e.distanceTo(this.center)-this.radius}intersectsSphere(e){const n=this.radius+e.radius;return e.center.distanceToSquared(this.center)<=n*n}intersectsBox(e){return e.intersectsSphere(this)}intersectsPlane(e){return Math.abs(e.distanceToPoint(this.center))<=this.radius}clampPoint(e,n){const i=this.center.distanceToSquared(e);return n.copy(e),i>this.radius*this.radius&&(n.sub(this.center).normalize(),n.multiplyScalar(this.radius).add(this.center)),n}getBoundingBox(e){return this.isEmpty()?(e.makeEmpty(),e):(e.set(this.center,this.center),e.expandByScalar(this.radius),e)}applyMatrix4(e){return this.center.applyMatrix4(e),this.radius=this.radius*e.getMaxScaleOnAxis(),this}translate(e){return this.center.add(e),this}expandByPoint(e){if(this.isEmpty())return this.center.copy(e),this.radius=0,this;Wl.subVectors(e,this.center);const n=Wl.lengthSq();if(n>this.radius*this.radius){const i=Math.sqrt(n),a=(i-this.radius)*.5;this.center.addScaledVector(Wl,a/i),this.radius+=a}return this}union(e){return e.isEmpty()?this:this.isEmpty()?(this.copy(e),this):(this.center.equals(e.center)===!0?this.radius=Math.max(this.radius,e.radius):(Vd.subVectors(e.center,this.center).setLength(e.radius),this.expandByPoint(Wl.copy(e.center).add(Vd)),this.expandByPoint(Wl.copy(e.center).sub(Vd))),this)}equals(e){return e.center.equals(this.center)&&e.radius===this.radius}clone(){return new this.constructor().copy(this)}toJSON(){return{radius:this.radius,center:this.center.toArray()}}fromJSON(e){return this.radius=e.radius,this.center.fromArray(e.center),this}}let CA=0;const mi=new Ft,Wd=new In,Ar=new Y,ni=new ur,Xl=new ur,pn=new Y;class Zn extends cr{constructor(){super(),this.isBufferGeometry=!0,Object.defineProperty(this,"id",{value:CA++}),this.uuid=sc(),this.name="",this.type="BufferGeometry",this.index=null,this.indirect=null,this.indirectOffset=0,this.attributes={},this.morphAttributes={},this.morphTargetsRelative=!1,this.groups=[],this.boundingBox=null,this.boundingSphere=null,this.drawRange={start:0,count:1/0},this.userData={},this._transformed=!1}getIndex(){return this.index}setIndex(e){return Array.isArray(e)?this.index=new(lA(e)?vS:xS)(e,1):this.index=e,this}setIndirect(e,n=0){return this.indirect=e,this.indirectOffset=n,this}getIndirect(){return this.indirect}getAttribute(e){return this.attributes[e]}setAttribute(e,n){return this.attributes[e]=n,this}deleteAttribute(e){return delete this.attributes[e],this}hasAttribute(e){return this.attributes[e]!==void 0}addGroup(e,n,i=0){this.groups.push({start:e,count:n,materialIndex:i})}clearGroups(){this.groups=[]}setDrawRange(e,n){this.drawRange.start=e,this.drawRange.count=n}applyMatrix4(e){const n=this.attributes.position;n!==void 0&&(n.applyMatrix4(e),n.needsUpdate=!0);const i=this.attributes.normal;if(i!==void 0){const s=new it().getNormalMatrix(e);i.applyNormalMatrix(s),i.needsUpdate=!0}const a=this.attributes.tangent;return a!==void 0&&(a.transformDirection(e),a.needsUpdate=!0),this.boundingBox!==null&&this.computeBoundingBox(),this.boundingSphere!==null&&this.computeBoundingSphere(),this._transformed=!0,this}applyQuaternion(e){return mi.makeRotationFromQuaternion(e),this.applyMatrix4(mi),this}rotateX(e){return mi.makeRotationX(e),this.applyMatrix4(mi),this}rotateY(e){return mi.makeRotationY(e),this.applyMatrix4(mi),this}rotateZ(e){return mi.makeRotationZ(e),this.applyMatrix4(mi),this}translate(e,n,i){return mi.makeTranslation(e,n,i),this.applyMatrix4(mi),this}scale(e,n,i){return mi.makeScale(e,n,i),this.applyMatrix4(mi),this}lookAt(e){return Wd.lookAt(e),Wd.updateMatrix(),this.applyMatrix4(Wd.matrix),this}center(){return this.computeBoundingBox(),this.boundingBox.getCenter(Ar).negate(),this.translate(Ar.x,Ar.y,Ar.z),this}setFromPoints(e){const n=this.getAttribute("position");if(n===void 0){const i=[];for(let a=0,s=e.length;a<s;a++){const r=e[a];i.push(r.x,r.y,r.z||0)}this.setAttribute("position",new Ci(i,3))}else{const i=Math.min(e.length,n.count);for(let a=0;a<i;a++){const s=e[a];n.setXYZ(a,s.x,s.y,s.z||0)}e.length>n.count&&Je("BufferGeometry: Buffer size too small for points data. Use .dispose() and create a new geometry."),n.needsUpdate=!0}return this}computeBoundingBox(){this.boundingBox===null&&(this.boundingBox=new ur);const e=this.attributes.position,n=this.morphAttributes.position;if(e&&e.isGLBufferAttribute){St("BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box.",this),this.boundingBox.set(new Y(-1/0,-1/0,-1/0),new Y(1/0,1/0,1/0));return}if(e!==void 0){if(this.boundingBox.setFromBufferAttribute(e),n)for(let i=0,a=n.length;i<a;i++){const s=n[i];ni.setFromBufferAttribute(s),this.morphTargetsRelative?(pn.addVectors(this.boundingBox.min,ni.min),this.boundingBox.expandByPoint(pn),pn.addVectors(this.boundingBox.max,ni.max),this.boundingBox.expandByPoint(pn)):(this.boundingBox.expandByPoint(ni.min),this.boundingBox.expandByPoint(ni.max))}}else this.boundingBox.makeEmpty();(isNaN(this.boundingBox.min.x)||isNaN(this.boundingBox.min.y)||isNaN(this.boundingBox.min.z))&&St('BufferGeometry.computeBoundingBox(): Computed min/max have NaN values. The "position" attribute is likely to have NaN values.',this)}computeBoundingSphere(){this.boundingSphere===null&&(this.boundingSphere=new fr);const e=this.attributes.position,n=this.morphAttributes.position;if(e&&e.isGLBufferAttribute){St("BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere.",this),this.boundingSphere.set(new Y,1/0);return}if(e){const i=this.boundingSphere.center;if(ni.setFromBufferAttribute(e),n)for(let s=0,r=n.length;s<r;s++){const o=n[s];Xl.setFromBufferAttribute(o),this.morphTargetsRelative?(pn.addVectors(ni.min,Xl.min),ni.expandByPoint(pn),pn.addVectors(ni.max,Xl.max),ni.expandByPoint(pn)):(ni.expandByPoint(Xl.min),ni.expandByPoint(Xl.max))}ni.getCenter(i);let a=0;for(let s=0,r=e.count;s<r;s++)pn.fromBufferAttribute(e,s),a=Math.max(a,i.distanceToSquared(pn));if(n)for(let s=0,r=n.length;s<r;s++){const o=n[s],c=this.morphTargetsRelative;for(let u=0,d=o.count;u<d;u++)pn.fromBufferAttribute(o,u),c&&(Ar.fromBufferAttribute(e,u),pn.add(Ar)),a=Math.max(a,i.distanceToSquared(pn))}this.boundingSphere.radius=Math.sqrt(a),isNaN(this.boundingSphere.radius)&&St('BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.',this)}}computeTangents(){const e=this.index,n=this.attributes;if(e===null||n.position===void 0||n.normal===void 0||n.uv===void 0){St("BufferGeometry: .computeTangents() failed. Missing required attributes (index, position, normal or uv)");return}const i=n.position,a=n.normal,s=n.uv;let r=this.getAttribute("tangent");(r===void 0||r.count!==i.count)&&(r=new cn(new Float32Array(4*i.count),4),this.setAttribute("tangent",r));const o=[],c=[];for(let S=0;S<i.count;S++)o[S]=new Y,c[S]=new Y;const u=new Y,d=new Y,m=new Y,f=new At,p=new At,x=new At,_=new Y,g=new Y;function h(S,C,R){u.fromBufferAttribute(i,S),d.fromBufferAttribute(i,C),m.fromBufferAttribute(i,R),f.fromBufferAttribute(s,S),p.fromBufferAttribute(s,C),x.fromBufferAttribute(s,R),d.sub(u),m.sub(u),p.sub(f),x.sub(f);const L=1/(p.x*x.y-x.x*p.y);isFinite(L)&&(_.copy(d).multiplyScalar(x.y).addScaledVector(m,-p.y).multiplyScalar(L),g.copy(m).multiplyScalar(p.x).addScaledVector(d,-x.x).multiplyScalar(L),o[S].add(_),o[C].add(_),o[R].add(_),c[S].add(g),c[C].add(g),c[R].add(g))}let v=this.groups;v.length===0&&(v=[{start:0,count:e.count}]);for(let S=0,C=v.length;S<C;++S){const R=v[S],L=R.start,O=R.count;for(let W=L,z=L+O;W<z;W+=3)h(e.getX(W+0),e.getX(W+1),e.getX(W+2))}const b=new Y,y=new Y,A=new Y,M=new Y;function T(S){A.fromBufferAttribute(a,S),M.copy(A);const C=o[S];b.copy(C),b.sub(A.multiplyScalar(A.dot(C))).normalize(),y.crossVectors(M,C);const L=y.dot(c[S])<0?-1:1;r.setXYZW(S,b.x,b.y,b.z,L)}for(let S=0,C=v.length;S<C;++S){const R=v[S],L=R.start,O=R.count;for(let W=L,z=L+O;W<z;W+=3)T(e.getX(W+0)),T(e.getX(W+1)),T(e.getX(W+2))}this._transformed=!0}computeVertexNormals(){const e=this.index,n=this.getAttribute("position");if(n!==void 0){let i=this.getAttribute("normal");if(i===void 0||i.count!==n.count)i=new cn(new Float32Array(n.count*3),3),this.setAttribute("normal",i);else for(let f=0,p=i.count;f<p;f++)i.setXYZ(f,0,0,0);const a=new Y,s=new Y,r=new Y,o=new Y,c=new Y,u=new Y,d=new Y,m=new Y;if(e)for(let f=0,p=e.count;f<p;f+=3){const x=e.getX(f+0),_=e.getX(f+1),g=e.getX(f+2);a.fromBufferAttribute(n,x),s.fromBufferAttribute(n,_),r.fromBufferAttribute(n,g),d.subVectors(r,s),m.subVectors(a,s),d.cross(m),o.fromBufferAttribute(i,x),c.fromBufferAttribute(i,_),u.fromBufferAttribute(i,g),o.add(d),c.add(d),u.add(d),i.setXYZ(x,o.x,o.y,o.z),i.setXYZ(_,c.x,c.y,c.z),i.setXYZ(g,u.x,u.y,u.z)}else for(let f=0,p=n.count;f<p;f+=3)a.fromBufferAttribute(n,f+0),s.fromBufferAttribute(n,f+1),r.fromBufferAttribute(n,f+2),d.subVectors(r,s),m.subVectors(a,s),d.cross(m),i.setXYZ(f+0,d.x,d.y,d.z),i.setXYZ(f+1,d.x,d.y,d.z),i.setXYZ(f+2,d.x,d.y,d.z);this.normalizeNormals(),i.needsUpdate=!0}}normalizeNormals(){const e=this.attributes.normal;for(let n=0,i=e.count;n<i;n++)pn.fromBufferAttribute(e,n),pn.normalize(),e.setXYZ(n,pn.x,pn.y,pn.z)}toNonIndexed(){function e(o,c){const u=o.array,d=o.itemSize,m=o.normalized,f=new u.constructor(c.length*d);let p=0,x=0;for(let _=0,g=c.length;_<g;_++){o.isInterleavedBufferAttribute?p=c[_]*o.data.stride+o.offset:p=c[_]*d;for(let h=0;h<d;h++)f[x++]=u[p++]}return new cn(f,d,m)}if(this.index===null)return Je("BufferGeometry.toNonIndexed(): BufferGeometry is already non-indexed."),this;const n=new Zn,i=this.index.array,a=this.attributes;for(const o in a){const c=a[o],u=e(c,i);n.setAttribute(o,u)}const s=this.morphAttributes;for(const o in s){const c=[],u=s[o];for(let d=0,m=u.length;d<m;d++){const f=u[d],p=e(f,i);c.push(p)}n.morphAttributes[o]=c}n.morphTargetsRelative=this.morphTargetsRelative;const r=this.groups;for(let o=0,c=r.length;o<c;o++){const u=r[o];n.addGroup(u.start,u.count,u.materialIndex)}return n}toJSON(){const e={metadata:{version:4.7,type:"BufferGeometry",generator:"BufferGeometry.toJSON"}};if(e.uuid=this.uuid,e.type=this.parameters!==void 0&&this._transformed===!0?"BufferGeometry":this.type,this.name!==""&&(e.name=this.name),Object.keys(this.userData).length>0&&(e.userData=this.userData),this.parameters!==void 0&&this._transformed!==!0){const c=this.parameters;for(const u in c)c[u]!==void 0&&(e[u]=c[u]);return e}e.data={attributes:{}};const n=this.index;n!==null&&(e.data.index={type:n.array.constructor.name,array:Array.prototype.slice.call(n.array)});const i=this.attributes;for(const c in i){const u=i[c];e.data.attributes[c]=u.toJSON(e.data)}const a={};let s=!1;for(const c in this.morphAttributes){const u=this.morphAttributes[c],d=[];for(let m=0,f=u.length;m<f;m++){const p=u[m];d.push(p.toJSON(e.data))}d.length>0&&(a[c]=d,s=!0)}s&&(e.data.morphAttributes=a,e.data.morphTargetsRelative=this.morphTargetsRelative);const r=this.groups;r.length>0&&(e.data.groups=JSON.parse(JSON.stringify(r)));const o=this.boundingSphere;return o!==null&&(e.data.boundingSphere=o.toJSON()),e}clone(){return new this.constructor().copy(this)}copy(e){this.index=null,this.attributes={},this.morphAttributes={},this.groups=[],this.boundingBox=null,this.boundingSphere=null;const n={};this.name=e.name;const i=e.index;i!==null&&this.setIndex(i.clone());const a=e.attributes;for(const u in a){const d=a[u];this.setAttribute(u,d.clone(n))}const s=e.morphAttributes;for(const u in s){const d=[],m=s[u];for(let f=0,p=m.length;f<p;f++)d.push(m[f].clone(n));this.morphAttributes[u]=d}this.morphTargetsRelative=e.morphTargetsRelative;const r=e.groups;for(let u=0,d=r.length;u<d;u++){const m=r[u];this.addGroup(m.start,m.count,m.materialIndex)}const o=e.boundingBox;o!==null&&(this.boundingBox=o.clone());const c=e.boundingSphere;return c!==null&&(this.boundingSphere=c.clone()),this.drawRange.start=e.drawRange.start,this.drawRange.count=e.drawRange.count,this.userData=e.userData,this._transformed=e._transformed,this}dispose(){this.dispatchEvent({type:"dispose"})}}let NA=0;class Ll extends cr{constructor(){super(),this.isMaterial=!0,Object.defineProperty(this,"id",{value:NA++}),this.uuid=sc(),this.name="",this.type="Material",this.blending=nl,this.side=Ts,this.vertexColors=!1,this.opacity=1,this.transparent=!1,this.alphaHash=!1,this.blendSrc=dp,this.blendDst=hp,this.blendEquation=Bs,this.blendSrcAlpha=null,this.blendDstAlpha=null,this.blendEquationAlpha=null,this.blendColor=new gt(0,0,0),this.blendAlpha=0,this.depthFunc=vl,this.depthTest=!0,this.depthWrite=!0,this.stencilWriteMask=255,this.stencilFunc=m0,this.stencilRef=0,this.stencilFuncMask=255,this.stencilFail=gr,this.stencilZFail=gr,this.stencilZPass=gr,this.stencilWrite=!1,this.clippingPlanes=null,this.clipIntersection=!1,this.clipShadows=!1,this.shadowSide=null,this.colorWrite=!0,this.precision=null,this.polygonOffset=!1,this.polygonOffsetFactor=0,this.polygonOffsetUnits=0,this.dithering=!1,this.alphaToCoverage=!1,this.premultipliedAlpha=!1,this.forceSinglePass=!1,this.allowOverride=!0,this.visible=!0,this.toneMapped=!0,this.userData={},this.version=0,this._alphaTest=0}get alphaTest(){return this._alphaTest}set alphaTest(e){this._alphaTest>0!=e>0&&this.version++,this._alphaTest=e}onBeforeRender(){}onBeforeCompile(){}customProgramCacheKey(){return this.onBeforeCompile.toString()}setValues(e){if(e!==void 0)for(const n in e){const i=e[n];if(i===void 0){Je(`Material: parameter '${n}' has value of undefined.`);continue}const a=this[n];if(a===void 0){Je(`Material: '${n}' is not a property of THREE.${this.type}.`);continue}a&&a.isColor?a.set(i):a&&a.isVector2&&i&&i.isVector2||a&&a.isEuler&&i&&i.isEuler||a&&a.isVector3&&i&&i.isVector3?a.copy(i):this[n]=i}}toJSON(e){const n=e===void 0||typeof e=="string";n&&(e={textures:{},images:{}});const i={metadata:{version:4.7,type:"Material",generator:"Material.toJSON"}};i.uuid=this.uuid,i.type=this.type,this.name!==""&&(i.name=this.name),this.color&&this.color.isColor&&(i.color=this.color.getHex()),this.roughness!==void 0&&(i.roughness=this.roughness),this.metalness!==void 0&&(i.metalness=this.metalness),this.sheen!==void 0&&(i.sheen=this.sheen),this.sheenColor&&this.sheenColor.isColor&&(i.sheenColor=this.sheenColor.getHex()),this.sheenRoughness!==void 0&&(i.sheenRoughness=this.sheenRoughness),this.emissive&&this.emissive.isColor&&(i.emissive=this.emissive.getHex()),this.emissiveIntensity!==void 0&&this.emissiveIntensity!==1&&(i.emissiveIntensity=this.emissiveIntensity),this.specular&&this.specular.isColor&&(i.specular=this.specular.getHex()),this.specularIntensity!==void 0&&(i.specularIntensity=this.specularIntensity),this.specularColor&&this.specularColor.isColor&&(i.specularColor=this.specularColor.getHex()),this.shininess!==void 0&&(i.shininess=this.shininess),this.clearcoat!==void 0&&(i.clearcoat=this.clearcoat),this.clearcoatRoughness!==void 0&&(i.clearcoatRoughness=this.clearcoatRoughness),this.clearcoatMap&&this.clearcoatMap.isTexture&&(i.clearcoatMap=this.clearcoatMap.toJSON(e).uuid),this.clearcoatRoughnessMap&&this.clearcoatRoughnessMap.isTexture&&(i.clearcoatRoughnessMap=this.clearcoatRoughnessMap.toJSON(e).uuid),this.clearcoatNormalMap&&this.clearcoatNormalMap.isTexture&&(i.clearcoatNormalMap=this.clearcoatNormalMap.toJSON(e).uuid,i.clearcoatNormalScale=this.clearcoatNormalScale.toArray()),this.sheenColorMap&&this.sheenColorMap.isTexture&&(i.sheenColorMap=this.sheenColorMap.toJSON(e).uuid),this.sheenRoughnessMap&&this.sheenRoughnessMap.isTexture&&(i.sheenRoughnessMap=this.sheenRoughnessMap.toJSON(e).uuid),this.dispersion!==void 0&&(i.dispersion=this.dispersion),this.iridescence!==void 0&&(i.iridescence=this.iridescence),this.iridescenceIOR!==void 0&&(i.iridescenceIOR=this.iridescenceIOR),this.iridescenceThicknessRange!==void 0&&(i.iridescenceThicknessRange=this.iridescenceThicknessRange),this.iridescenceMap&&this.iridescenceMap.isTexture&&(i.iridescenceMap=this.iridescenceMap.toJSON(e).uuid),this.iridescenceThicknessMap&&this.iridescenceThicknessMap.isTexture&&(i.iridescenceThicknessMap=this.iridescenceThicknessMap.toJSON(e).uuid),this.anisotropy!==void 0&&(i.anisotropy=this.anisotropy),this.anisotropyRotation!==void 0&&(i.anisotropyRotation=this.anisotropyRotation),this.anisotropyMap&&this.anisotropyMap.isTexture&&(i.anisotropyMap=this.anisotropyMap.toJSON(e).uuid),this.map&&this.map.isTexture&&(i.map=this.map.toJSON(e).uuid),this.matcap&&this.matcap.isTexture&&(i.matcap=this.matcap.toJSON(e).uuid),this.alphaMap&&this.alphaMap.isTexture&&(i.alphaMap=this.alphaMap.toJSON(e).uuid),this.lightMap&&this.lightMap.isTexture&&(i.lightMap=this.lightMap.toJSON(e).uuid,i.lightMapIntensity=this.lightMapIntensity),this.aoMap&&this.aoMap.isTexture&&(i.aoMap=this.aoMap.toJSON(e).uuid,i.aoMapIntensity=this.aoMapIntensity),this.bumpMap&&this.bumpMap.isTexture&&(i.bumpMap=this.bumpMap.toJSON(e).uuid,i.bumpScale=this.bumpScale),this.normalMap&&this.normalMap.isTexture&&(i.normalMap=this.normalMap.toJSON(e).uuid,i.normalMapType=this.normalMapType,i.normalScale=this.normalScale.toArray()),this.displacementMap&&this.displacementMap.isTexture&&(i.displacementMap=this.displacementMap.toJSON(e).uuid,i.displacementScale=this.displacementScale,i.displacementBias=this.displacementBias),this.roughnessMap&&this.roughnessMap.isTexture&&(i.roughnessMap=this.roughnessMap.toJSON(e).uuid),this.metalnessMap&&this.metalnessMap.isTexture&&(i.metalnessMap=this.metalnessMap.toJSON(e).uuid),this.emissiveMap&&this.emissiveMap.isTexture&&(i.emissiveMap=this.emissiveMap.toJSON(e).uuid),this.specularMap&&this.specularMap.isTexture&&(i.specularMap=this.specularMap.toJSON(e).uuid),this.specularIntensityMap&&this.specularIntensityMap.isTexture&&(i.specularIntensityMap=this.specularIntensityMap.toJSON(e).uuid),this.specularColorMap&&this.specularColorMap.isTexture&&(i.specularColorMap=this.specularColorMap.toJSON(e).uuid),this.envMap&&this.envMap.isTexture&&(i.envMap=this.envMap.toJSON(e).uuid,this.combine!==void 0&&(i.combine=this.combine)),this.envMapRotation!==void 0&&(i.envMapRotation=this.envMapRotation.toArray()),this.envMapIntensity!==void 0&&(i.envMapIntensity=this.envMapIntensity),this.reflectivity!==void 0&&(i.reflectivity=this.reflectivity),this.refractionRatio!==void 0&&(i.refractionRatio=this.refractionRatio),this.gradientMap&&this.gradientMap.isTexture&&(i.gradientMap=this.gradientMap.toJSON(e).uuid),this.transmission!==void 0&&(i.transmission=this.transmission),this.transmissionMap&&this.transmissionMap.isTexture&&(i.transmissionMap=this.transmissionMap.toJSON(e).uuid),this.thickness!==void 0&&(i.thickness=this.thickness),this.thicknessMap&&this.thicknessMap.isTexture&&(i.thicknessMap=this.thicknessMap.toJSON(e).uuid),this.attenuationDistance!==void 0&&this.attenuationDistance!==1/0&&(i.attenuationDistance=this.attenuationDistance),this.attenuationColor!==void 0&&(i.attenuationColor=this.attenuationColor.getHex()),this.size!==void 0&&(i.size=this.size),this.shadowSide!==null&&(i.shadowSide=this.shadowSide),this.sizeAttenuation!==void 0&&(i.sizeAttenuation=this.sizeAttenuation),this.blending!==nl&&(i.blending=this.blending),this.side!==Ts&&(i.side=this.side),this.vertexColors===!0&&(i.vertexColors=!0),this.opacity<1&&(i.opacity=this.opacity),this.transparent===!0&&(i.transparent=!0),this.blendSrc!==dp&&(i.blendSrc=this.blendSrc),this.blendDst!==hp&&(i.blendDst=this.blendDst),this.blendEquation!==Bs&&(i.blendEquation=this.blendEquation),this.blendSrcAlpha!==null&&(i.blendSrcAlpha=this.blendSrcAlpha),this.blendDstAlpha!==null&&(i.blendDstAlpha=this.blendDstAlpha),this.blendEquationAlpha!==null&&(i.blendEquationAlpha=this.blendEquationAlpha),this.blendColor&&this.blendColor.isColor&&(i.blendColor=this.blendColor.getHex()),this.blendAlpha!==0&&(i.blendAlpha=this.blendAlpha),this.depthFunc!==vl&&(i.depthFunc=this.depthFunc),this.depthTest===!1&&(i.depthTest=this.depthTest),this.depthWrite===!1&&(i.depthWrite=this.depthWrite),this.colorWrite===!1&&(i.colorWrite=this.colorWrite),this.stencilWriteMask!==255&&(i.stencilWriteMask=this.stencilWriteMask),this.stencilFunc!==m0&&(i.stencilFunc=this.stencilFunc),this.stencilRef!==0&&(i.stencilRef=this.stencilRef),this.stencilFuncMask!==255&&(i.stencilFuncMask=this.stencilFuncMask),this.stencilFail!==gr&&(i.stencilFail=this.stencilFail),this.stencilZFail!==gr&&(i.stencilZFail=this.stencilZFail),this.stencilZPass!==gr&&(i.stencilZPass=this.stencilZPass),this.stencilWrite===!0&&(i.stencilWrite=this.stencilWrite),this.rotation!==void 0&&this.rotation!==0&&(i.rotation=this.rotation),this.polygonOffset===!0&&(i.polygonOffset=!0),this.polygonOffsetFactor!==0&&(i.polygonOffsetFactor=this.polygonOffsetFactor),this.polygonOffsetUnits!==0&&(i.polygonOffsetUnits=this.polygonOffsetUnits),this.linewidth!==void 0&&this.linewidth!==1&&(i.linewidth=this.linewidth),this.dashSize!==void 0&&(i.dashSize=this.dashSize),this.gapSize!==void 0&&(i.gapSize=this.gapSize),this.scale!==void 0&&(i.scale=this.scale),this.dithering===!0&&(i.dithering=!0),this.alphaTest>0&&(i.alphaTest=this.alphaTest),this.alphaHash===!0&&(i.alphaHash=!0),this.alphaToCoverage===!0&&(i.alphaToCoverage=!0),this.premultipliedAlpha===!0&&(i.premultipliedAlpha=!0),this.forceSinglePass===!0&&(i.forceSinglePass=!0),this.allowOverride===!1&&(i.allowOverride=!1),this.wireframe===!0&&(i.wireframe=!0),this.wireframeLinewidth>1&&(i.wireframeLinewidth=this.wireframeLinewidth),this.wireframeLinecap!=="round"&&(i.wireframeLinecap=this.wireframeLinecap),this.wireframeLinejoin!=="round"&&(i.wireframeLinejoin=this.wireframeLinejoin),this.flatShading===!0&&(i.flatShading=!0),this.visible===!1&&(i.visible=!1),this.toneMapped===!1&&(i.toneMapped=!1),this.fog===!1&&(i.fog=!1),Object.keys(this.userData).length>0&&(i.userData=this.userData);function a(s){const r=[];for(const o in s){const c=s[o];delete c.metadata,r.push(c)}return r}if(n){const s=a(e.textures),r=a(e.images);s.length>0&&(i.textures=s),r.length>0&&(i.images=r)}return i}fromJSON(e,n){if(e.uuid!==void 0&&(this.uuid=e.uuid),e.name!==void 0&&(this.name=e.name),e.color!==void 0&&this.color!==void 0&&this.color.setHex(e.color),e.roughness!==void 0&&(this.roughness=e.roughness),e.metalness!==void 0&&(this.metalness=e.metalness),e.sheen!==void 0&&(this.sheen=e.sheen),e.sheenColor!==void 0&&(this.sheenColor=new gt().setHex(e.sheenColor)),e.sheenRoughness!==void 0&&(this.sheenRoughness=e.sheenRoughness),e.emissive!==void 0&&this.emissive!==void 0&&this.emissive.setHex(e.emissive),e.specular!==void 0&&this.specular!==void 0&&this.specular.setHex(e.specular),e.specularIntensity!==void 0&&(this.specularIntensity=e.specularIntensity),e.specularColor!==void 0&&this.specularColor!==void 0&&this.specularColor.setHex(e.specularColor),e.shininess!==void 0&&(this.shininess=e.shininess),e.clearcoat!==void 0&&(this.clearcoat=e.clearcoat),e.clearcoatRoughness!==void 0&&(this.clearcoatRoughness=e.clearcoatRoughness),e.dispersion!==void 0&&(this.dispersion=e.dispersion),e.iridescence!==void 0&&(this.iridescence=e.iridescence),e.iridescenceIOR!==void 0&&(this.iridescenceIOR=e.iridescenceIOR),e.iridescenceThicknessRange!==void 0&&(this.iridescenceThicknessRange=e.iridescenceThicknessRange),e.transmission!==void 0&&(this.transmission=e.transmission),e.thickness!==void 0&&(this.thickness=e.thickness),e.attenuationDistance!==void 0&&(this.attenuationDistance=e.attenuationDistance),e.attenuationColor!==void 0&&this.attenuationColor!==void 0&&this.attenuationColor.setHex(e.attenuationColor),e.anisotropy!==void 0&&(this.anisotropy=e.anisotropy),e.anisotropyRotation!==void 0&&(this.anisotropyRotation=e.anisotropyRotation),e.fog!==void 0&&(this.fog=e.fog),e.flatShading!==void 0&&(this.flatShading=e.flatShading),e.blending!==void 0&&(this.blending=e.blending),e.combine!==void 0&&(this.combine=e.combine),e.side!==void 0&&(this.side=e.side),e.shadowSide!==void 0&&(this.shadowSide=e.shadowSide),e.opacity!==void 0&&(this.opacity=e.opacity),e.transparent!==void 0&&(this.transparent=e.transparent),e.alphaTest!==void 0&&(this.alphaTest=e.alphaTest),e.alphaHash!==void 0&&(this.alphaHash=e.alphaHash),e.depthFunc!==void 0&&(this.depthFunc=e.depthFunc),e.depthTest!==void 0&&(this.depthTest=e.depthTest),e.depthWrite!==void 0&&(this.depthWrite=e.depthWrite),e.colorWrite!==void 0&&(this.colorWrite=e.colorWrite),e.blendSrc!==void 0&&(this.blendSrc=e.blendSrc),e.blendDst!==void 0&&(this.blendDst=e.blendDst),e.blendEquation!==void 0&&(this.blendEquation=e.blendEquation),e.blendSrcAlpha!==void 0&&(this.blendSrcAlpha=e.blendSrcAlpha),e.blendDstAlpha!==void 0&&(this.blendDstAlpha=e.blendDstAlpha),e.blendEquationAlpha!==void 0&&(this.blendEquationAlpha=e.blendEquationAlpha),e.blendColor!==void 0&&this.blendColor!==void 0&&this.blendColor.setHex(e.blendColor),e.blendAlpha!==void 0&&(this.blendAlpha=e.blendAlpha),e.stencilWriteMask!==void 0&&(this.stencilWriteMask=e.stencilWriteMask),e.stencilFunc!==void 0&&(this.stencilFunc=e.stencilFunc),e.stencilRef!==void 0&&(this.stencilRef=e.stencilRef),e.stencilFuncMask!==void 0&&(this.stencilFuncMask=e.stencilFuncMask),e.stencilFail!==void 0&&(this.stencilFail=e.stencilFail),e.stencilZFail!==void 0&&(this.stencilZFail=e.stencilZFail),e.stencilZPass!==void 0&&(this.stencilZPass=e.stencilZPass),e.stencilWrite!==void 0&&(this.stencilWrite=e.stencilWrite),e.wireframe!==void 0&&(this.wireframe=e.wireframe),e.wireframeLinewidth!==void 0&&(this.wireframeLinewidth=e.wireframeLinewidth),e.wireframeLinecap!==void 0&&(this.wireframeLinecap=e.wireframeLinecap),e.wireframeLinejoin!==void 0&&(this.wireframeLinejoin=e.wireframeLinejoin),e.rotation!==void 0&&(this.rotation=e.rotation),e.linewidth!==void 0&&(this.linewidth=e.linewidth),e.dashSize!==void 0&&(this.dashSize=e.dashSize),e.gapSize!==void 0&&(this.gapSize=e.gapSize),e.scale!==void 0&&(this.scale=e.scale),e.polygonOffset!==void 0&&(this.polygonOffset=e.polygonOffset),e.polygonOffsetFactor!==void 0&&(this.polygonOffsetFactor=e.polygonOffsetFactor),e.polygonOffsetUnits!==void 0&&(this.polygonOffsetUnits=e.polygonOffsetUnits),e.dithering!==void 0&&(this.dithering=e.dithering),e.alphaToCoverage!==void 0&&(this.alphaToCoverage=e.alphaToCoverage),e.premultipliedAlpha!==void 0&&(this.premultipliedAlpha=e.premultipliedAlpha),e.forceSinglePass!==void 0&&(this.forceSinglePass=e.forceSinglePass),e.allowOverride!==void 0&&(this.allowOverride=e.allowOverride),e.visible!==void 0&&(this.visible=e.visible),e.toneMapped!==void 0&&(this.toneMapped=e.toneMapped),e.userData!==void 0&&(this.userData=e.userData),e.vertexColors!==void 0&&(typeof e.vertexColors=="number"?this.vertexColors=e.vertexColors>0:this.vertexColors=e.vertexColors),e.size!==void 0&&(this.size=e.size),e.sizeAttenuation!==void 0&&(this.sizeAttenuation=e.sizeAttenuation),e.map!==void 0&&(this.map=n[e.map]||null),e.matcap!==void 0&&(this.matcap=n[e.matcap]||null),e.alphaMap!==void 0&&(this.alphaMap=n[e.alphaMap]||null),e.bumpMap!==void 0&&(this.bumpMap=n[e.bumpMap]||null),e.bumpScale!==void 0&&(this.bumpScale=e.bumpScale),e.normalMap!==void 0&&(this.normalMap=n[e.normalMap]||null),e.normalMapType!==void 0&&(this.normalMapType=e.normalMapType),e.normalScale!==void 0){let i=e.normalScale;Array.isArray(i)===!1&&(i=[i,i]),this.normalScale=new At().fromArray(i)}return e.displacementMap!==void 0&&(this.displacementMap=n[e.displacementMap]||null),e.displacementScale!==void 0&&(this.displacementScale=e.displacementScale),e.displacementBias!==void 0&&(this.displacementBias=e.displacementBias),e.roughnessMap!==void 0&&(this.roughnessMap=n[e.roughnessMap]||null),e.metalnessMap!==void 0&&(this.metalnessMap=n[e.metalnessMap]||null),e.emissiveMap!==void 0&&(this.emissiveMap=n[e.emissiveMap]||null),e.emissiveIntensity!==void 0&&(this.emissiveIntensity=e.emissiveIntensity),e.specularMap!==void 0&&(this.specularMap=n[e.specularMap]||null),e.specularIntensityMap!==void 0&&(this.specularIntensityMap=n[e.specularIntensityMap]||null),e.specularColorMap!==void 0&&(this.specularColorMap=n[e.specularColorMap]||null),e.envMap!==void 0&&(this.envMap=n[e.envMap]||null),e.envMapRotation!==void 0&&this.envMapRotation.fromArray(e.envMapRotation),e.envMapIntensity!==void 0&&(this.envMapIntensity=e.envMapIntensity),e.reflectivity!==void 0&&(this.reflectivity=e.reflectivity),e.refractionRatio!==void 0&&(this.refractionRatio=e.refractionRatio),e.lightMap!==void 0&&(this.lightMap=n[e.lightMap]||null),e.lightMapIntensity!==void 0&&(this.lightMapIntensity=e.lightMapIntensity),e.aoMap!==void 0&&(this.aoMap=n[e.aoMap]||null),e.aoMapIntensity!==void 0&&(this.aoMapIntensity=e.aoMapIntensity),e.gradientMap!==void 0&&(this.gradientMap=n[e.gradientMap]||null),e.clearcoatMap!==void 0&&(this.clearcoatMap=n[e.clearcoatMap]||null),e.clearcoatRoughnessMap!==void 0&&(this.clearcoatRoughnessMap=n[e.clearcoatRoughnessMap]||null),e.clearcoatNormalMap!==void 0&&(this.clearcoatNormalMap=n[e.clearcoatNormalMap]||null),e.clearcoatNormalScale!==void 0&&(this.clearcoatNormalScale=new At().fromArray(e.clearcoatNormalScale)),e.iridescenceMap!==void 0&&(this.iridescenceMap=n[e.iridescenceMap]||null),e.iridescenceThicknessMap!==void 0&&(this.iridescenceThicknessMap=n[e.iridescenceThicknessMap]||null),e.transmissionMap!==void 0&&(this.transmissionMap=n[e.transmissionMap]||null),e.thicknessMap!==void 0&&(this.thicknessMap=n[e.thicknessMap]||null),e.anisotropyMap!==void 0&&(this.anisotropyMap=n[e.anisotropyMap]||null),e.sheenColorMap!==void 0&&(this.sheenColorMap=n[e.sheenColorMap]||null),e.sheenRoughnessMap!==void 0&&(this.sheenRoughnessMap=n[e.sheenRoughnessMap]||null),this}clone(){return new this.constructor().copy(this)}copy(e){this.name=e.name,this.blending=e.blending,this.side=e.side,this.vertexColors=e.vertexColors,this.opacity=e.opacity,this.transparent=e.transparent,this.blendSrc=e.blendSrc,this.blendDst=e.blendDst,this.blendEquation=e.blendEquation,this.blendSrcAlpha=e.blendSrcAlpha,this.blendDstAlpha=e.blendDstAlpha,this.blendEquationAlpha=e.blendEquationAlpha,this.blendColor.copy(e.blendColor),this.blendAlpha=e.blendAlpha,this.depthFunc=e.depthFunc,this.depthTest=e.depthTest,this.depthWrite=e.depthWrite,this.stencilWriteMask=e.stencilWriteMask,this.stencilFunc=e.stencilFunc,this.stencilRef=e.stencilRef,this.stencilFuncMask=e.stencilFuncMask,this.stencilFail=e.stencilFail,this.stencilZFail=e.stencilZFail,this.stencilZPass=e.stencilZPass,this.stencilWrite=e.stencilWrite;const n=e.clippingPlanes;let i=null;if(n!==null){const a=n.length;i=new Array(a);for(let s=0;s!==a;++s)i[s]=n[s].clone()}return this.clippingPlanes=i,this.clipIntersection=e.clipIntersection,this.clipShadows=e.clipShadows,this.shadowSide=e.shadowSide,this.colorWrite=e.colorWrite,this.precision=e.precision,this.polygonOffset=e.polygonOffset,this.polygonOffsetFactor=e.polygonOffsetFactor,this.polygonOffsetUnits=e.polygonOffsetUnits,this.dithering=e.dithering,this.alphaTest=e.alphaTest,this.alphaHash=e.alphaHash,this.alphaToCoverage=e.alphaToCoverage,this.premultipliedAlpha=e.premultipliedAlpha,this.forceSinglePass=e.forceSinglePass,this.allowOverride=e.allowOverride,this.visible=e.visible,this.toneMapped=e.toneMapped,this.userData=JSON.parse(JSON.stringify(e.userData)),this}dispose(){this.dispatchEvent({type:"dispose"})}set needsUpdate(e){e===!0&&this.version++}}const ga=new Y,Xd=new Y,Lc=new Y,Ya=new Y,qd=new Y,Uc=new Y,Yd=new Y;class _g{constructor(e=new Y,n=new Y(0,0,-1)){this.origin=e,this.direction=n}set(e,n){return this.origin.copy(e),this.direction.copy(n),this}copy(e){return this.origin.copy(e.origin),this.direction.copy(e.direction),this}at(e,n){return n.copy(this.origin).addScaledVector(this.direction,e)}lookAt(e){return this.direction.copy(e).sub(this.origin).normalize(),this}recast(e){return this.origin.copy(this.at(e,ga)),this}closestPointToPoint(e,n){n.subVectors(e,this.origin);const i=n.dot(this.direction);return i<0?n.copy(this.origin):n.copy(this.origin).addScaledVector(this.direction,i)}distanceToPoint(e){return Math.sqrt(this.distanceSqToPoint(e))}distanceSqToPoint(e){const n=ga.subVectors(e,this.origin).dot(this.direction);return n<0?this.origin.distanceToSquared(e):(ga.copy(this.origin).addScaledVector(this.direction,n),ga.distanceToSquared(e))}distanceSqToSegment(e,n,i,a){Xd.copy(e).add(n).multiplyScalar(.5),Lc.copy(n).sub(e).normalize(),Ya.copy(this.origin).sub(Xd);const s=e.distanceTo(n)*.5,r=-this.direction.dot(Lc),o=Ya.dot(this.direction),c=-Ya.dot(Lc),u=Ya.lengthSq(),d=Math.abs(1-r*r);let m,f,p,x;if(d>0)if(m=r*c-o,f=r*o-c,x=s*d,m>=0)if(f>=-x)if(f<=x){const _=1/d;m*=_,f*=_,p=m*(m+r*f+2*o)+f*(r*m+f+2*c)+u}else f=s,m=Math.max(0,-(r*f+o)),p=-m*m+f*(f+2*c)+u;else f=-s,m=Math.max(0,-(r*f+o)),p=-m*m+f*(f+2*c)+u;else f<=-x?(m=Math.max(0,-(-r*s+o)),f=m>0?-s:Math.min(Math.max(-s,-c),s),p=-m*m+f*(f+2*c)+u):f<=x?(m=0,f=Math.min(Math.max(-s,-c),s),p=f*(f+2*c)+u):(m=Math.max(0,-(r*s+o)),f=m>0?s:Math.min(Math.max(-s,-c),s),p=-m*m+f*(f+2*c)+u);else f=r>0?-s:s,m=Math.max(0,-(r*f+o)),p=-m*m+f*(f+2*c)+u;return i&&i.copy(this.origin).addScaledVector(this.direction,m),a&&a.copy(Xd).addScaledVector(Lc,f),p}intersectSphere(e,n){ga.subVectors(e.center,this.origin);const i=ga.dot(this.direction),a=ga.dot(ga)-i*i,s=e.radius*e.radius;if(a>s)return null;const r=Math.sqrt(s-a),o=i-r,c=i+r;return c<0?null:o<0?this.at(c,n):this.at(o,n)}intersectsSphere(e){return e.radius<0?!1:this.distanceSqToPoint(e.center)<=e.radius*e.radius}distanceToPlane(e){const n=e.normal.dot(this.direction);if(n===0)return e.distanceToPoint(this.origin)===0?0:null;const i=-(this.origin.dot(e.normal)+e.constant)/n;return i>=0?i:null}intersectPlane(e,n){const i=this.distanceToPlane(e);return i===null?null:this.at(i,n)}intersectsPlane(e){const n=e.distanceToPoint(this.origin);return n===0||e.normal.dot(this.direction)*n<0}intersectBox(e,n){let i,a,s,r,o,c;const u=1/this.direction.x,d=1/this.direction.y,m=1/this.direction.z,f=this.origin;return u>=0?(i=(e.min.x-f.x)*u,a=(e.max.x-f.x)*u):(i=(e.max.x-f.x)*u,a=(e.min.x-f.x)*u),d>=0?(s=(e.min.y-f.y)*d,r=(e.max.y-f.y)*d):(s=(e.max.y-f.y)*d,r=(e.min.y-f.y)*d),i>r||s>a||((s>i||isNaN(i))&&(i=s),(r<a||isNaN(a))&&(a=r),m>=0?(o=(e.min.z-f.z)*m,c=(e.max.z-f.z)*m):(o=(e.max.z-f.z)*m,c=(e.min.z-f.z)*m),i>c||o>a)||((o>i||i!==i)&&(i=o),(c<a||a!==a)&&(a=c),a<0)?null:this.at(i>=0?i:a,n)}intersectsBox(e){return this.intersectBox(e,ga)!==null}intersectTriangle(e,n,i,a,s){qd.subVectors(n,e),Uc.subVectors(i,e),Yd.crossVectors(qd,Uc);let r=this.direction.dot(Yd),o;if(r>0){if(a)return null;o=1}else if(r<0)o=-1,r=-r;else return null;Ya.subVectors(this.origin,e);const c=o*this.direction.dot(Uc.crossVectors(Ya,Uc));if(c<0)return null;const u=o*this.direction.dot(qd.cross(Ya));if(u<0||c+u>r)return null;const d=-o*Ya.dot(Yd);return d<0?null:this.at(d/r,s)}applyMatrix4(e){return this.origin.applyMatrix4(e),this.direction.transformDirection(e),this}equals(e){return e.origin.equals(this.origin)&&e.direction.equals(this.direction)}clone(){return new this.constructor().copy(this)}}class _S extends Ll{constructor(e){super(),this.isMeshBasicMaterial=!0,this.type="MeshBasicMaterial",this.color=new gt(16777215),this.map=null,this.lightMap=null,this.lightMapIntensity=1,this.aoMap=null,this.aoMapIntensity=1,this.specularMap=null,this.alphaMap=null,this.envMap=null,this.envMapRotation=new tr,this.combine=Qb,this.reflectivity=1,this.refractionRatio=.98,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.fog=!0,this.setValues(e)}copy(e){return super.copy(e),this.color.copy(e.color),this.map=e.map,this.lightMap=e.lightMap,this.lightMapIntensity=e.lightMapIntensity,this.aoMap=e.aoMap,this.aoMapIntensity=e.aoMapIntensity,this.specularMap=e.specularMap,this.alphaMap=e.alphaMap,this.envMap=e.envMap,this.envMapRotation.copy(e.envMapRotation),this.combine=e.combine,this.reflectivity=e.reflectivity,this.refractionRatio=e.refractionRatio,this.wireframe=e.wireframe,this.wireframeLinewidth=e.wireframeLinewidth,this.wireframeLinecap=e.wireframeLinecap,this.wireframeLinejoin=e.wireframeLinejoin,this.fog=e.fog,this}}const D0=new Ft,Ls=new _g,Oc=new fr,L0=new Y,Pc=new Y,zc=new Y,Ic=new Y,Kd=new Y,kc=new Y,U0=new Y,Bc=new Y;class Ri extends In{constructor(e=new Zn,n=new _S){super(),this.isMesh=!0,this.type="Mesh",this.geometry=e,this.material=n,this.morphTargetDictionary=void 0,this.morphTargetInfluences=void 0,this.count=1,this.updateMorphTargets()}copy(e,n){return super.copy(e,n),e.morphTargetInfluences!==void 0&&(this.morphTargetInfluences=e.morphTargetInfluences.slice()),e.morphTargetDictionary!==void 0&&(this.morphTargetDictionary=Object.assign({},e.morphTargetDictionary)),this.material=Array.isArray(e.material)?e.material.slice():e.material,this.geometry=e.geometry,this}updateMorphTargets(){const n=this.geometry.morphAttributes,i=Object.keys(n);if(i.length>0){const a=n[i[0]];if(a!==void 0){this.morphTargetInfluences=[],this.morphTargetDictionary={};for(let s=0,r=a.length;s<r;s++){const o=a[s].name||String(s);this.morphTargetInfluences.push(0),this.morphTargetDictionary[o]=s}}}}getVertexPosition(e,n){const i=this.geometry,a=i.attributes.position,s=i.morphAttributes.position,r=i.morphTargetsRelative;n.fromBufferAttribute(a,e);const o=this.morphTargetInfluences;if(s&&o){kc.set(0,0,0);for(let c=0,u=s.length;c<u;c++){const d=o[c],m=s[c];d!==0&&(Kd.fromBufferAttribute(m,e),r?kc.addScaledVector(Kd,d):kc.addScaledVector(Kd.sub(n),d))}n.add(kc)}return n}raycast(e,n){const i=this.geometry,a=this.material,s=this.matrixWorld;a!==void 0&&(i.boundingSphere===null&&i.computeBoundingSphere(),Oc.copy(i.boundingSphere),Oc.applyMatrix4(s),Ls.copy(e.ray).recast(e.near),!(Oc.containsPoint(Ls.origin)===!1&&(Ls.intersectSphere(Oc,L0)===null||Ls.origin.distanceToSquared(L0)>(e.far-e.near)**2))&&(D0.copy(s).invert(),Ls.copy(e.ray).applyMatrix4(D0),!(i.boundingBox!==null&&Ls.intersectsBox(i.boundingBox)===!1)&&this._computeIntersections(e,n,Ls)))}_computeIntersections(e,n,i){let a;const s=this.geometry,r=this.material,o=s.index,c=s.attributes.position,u=s.attributes.uv,d=s.attributes.uv1,m=s.attributes.normal,f=s.groups,p=s.drawRange;if(o!==null)if(Array.isArray(r))for(let x=0,_=f.length;x<_;x++){const g=f[x],h=r[g.materialIndex],v=Math.max(g.start,p.start),b=Math.min(o.count,Math.min(g.start+g.count,p.start+p.count));for(let y=v,A=b;y<A;y+=3){const M=o.getX(y),T=o.getX(y+1),S=o.getX(y+2);a=Fc(this,h,e,i,u,d,m,M,T,S),a&&(a.faceIndex=Math.floor(y/3),a.face.materialIndex=g.materialIndex,n.push(a))}}else{const x=Math.max(0,p.start),_=Math.min(o.count,p.start+p.count);for(let g=x,h=_;g<h;g+=3){const v=o.getX(g),b=o.getX(g+1),y=o.getX(g+2);a=Fc(this,r,e,i,u,d,m,v,b,y),a&&(a.faceIndex=Math.floor(g/3),n.push(a))}}else if(c!==void 0)if(Array.isArray(r))for(let x=0,_=f.length;x<_;x++){const g=f[x],h=r[g.materialIndex],v=Math.max(g.start,p.start),b=Math.min(c.count,Math.min(g.start+g.count,p.start+p.count));for(let y=v,A=b;y<A;y+=3){const M=y,T=y+1,S=y+2;a=Fc(this,h,e,i,u,d,m,M,T,S),a&&(a.faceIndex=Math.floor(y/3),a.face.materialIndex=g.materialIndex,n.push(a))}}else{const x=Math.max(0,p.start),_=Math.min(c.count,p.start+p.count);for(let g=x,h=_;g<h;g+=3){const v=g,b=g+1,y=g+2;a=Fc(this,r,e,i,u,d,m,v,b,y),a&&(a.faceIndex=Math.floor(g/3),n.push(a))}}}}function RA(t,e,n,i,a,s,r,o){let c;if(e.side===Yn?c=i.intersectTriangle(r,s,a,!0,o):c=i.intersectTriangle(a,s,r,e.side===Ts,o),c===null)return null;Bc.copy(o),Bc.applyMatrix4(t.matrixWorld);const u=n.ray.origin.distanceTo(Bc);return u<n.near||u>n.far?null:{distance:u,point:Bc.clone(),object:t}}function Fc(t,e,n,i,a,s,r,o,c,u){t.getVertexPosition(o,Pc),t.getVertexPosition(c,zc),t.getVertexPosition(u,Ic);const d=RA(t,e,n,i,Pc,zc,Ic,U0);if(d){const m=new Y;Ti.getBarycoord(U0,Pc,zc,Ic,m),a&&(d.uv=Ti.getInterpolatedAttribute(a,o,c,u,m,new At)),s&&(d.uv1=Ti.getInterpolatedAttribute(s,o,c,u,m,new At)),r&&(d.normal=Ti.getInterpolatedAttribute(r,o,c,u,m,new Y),d.normal.dot(i.direction)>0&&d.normal.multiplyScalar(-1));const f={a:o,b:c,c:u,normal:new Y,materialIndex:0};Ti.getNormal(Pc,zc,Ic,f.normal),d.face=f,d.barycoord=m}return d}class yS extends zn{constructor(e=null,n=1,i=1,a,s,r,o,c,u=En,d=En,m,f){super(null,r,o,c,u,d,a,s,m,f),this.isDataTexture=!0,this.image={data:e,width:n,height:i},this.generateMipmaps=!1,this.flipY=!1,this.unpackAlignment=1}}class Yr extends cn{constructor(e,n,i,a=1){super(e,n,i),this.isInstancedBufferAttribute=!0,this.meshPerAttribute=a}copy(e){return super.copy(e),this.meshPerAttribute=e.meshPerAttribute,this}toJSON(){const e=super.toJSON();return e.meshPerAttribute=this.meshPerAttribute,e.isInstancedBufferAttribute=!0,e}}const wr=new Ft,O0=new Ft,jc=[],P0=new ur,DA=new Ft,ql=new Ri,Yl=new fr;class z0 extends Ri{constructor(e,n,i){super(e,n),this.isInstancedMesh=!0,this.instanceMatrix=new Yr(new Float32Array(i*16),16),this.instanceColor=null,this.morphTexture=null,this.count=i,this.boundingBox=null,this.boundingSphere=null;for(let a=0;a<i;a++)this.setMatrixAt(a,DA)}computeBoundingBox(){const e=this.geometry,n=this.count;this.boundingBox===null&&(this.boundingBox=new ur),e.boundingBox===null&&e.computeBoundingBox(),this.boundingBox.makeEmpty();for(let i=0;i<n;i++)this.getMatrixAt(i,wr),P0.copy(e.boundingBox).applyMatrix4(wr),this.boundingBox.union(P0)}computeBoundingSphere(){const e=this.geometry,n=this.count;this.boundingSphere===null&&(this.boundingSphere=new fr),e.boundingSphere===null&&e.computeBoundingSphere(),this.boundingSphere.makeEmpty();for(let i=0;i<n;i++)this.getMatrixAt(i,wr),Yl.copy(e.boundingSphere).applyMatrix4(wr),this.boundingSphere.union(Yl)}copy(e,n){return super.copy(e,n),this.instanceMatrix.copy(e.instanceMatrix),e.morphTexture!==null&&(this.morphTexture=e.morphTexture.clone()),e.instanceColor!==null&&(this.instanceColor=e.instanceColor.clone()),this.count=e.count,e.boundingBox!==null&&(this.boundingBox=e.boundingBox.clone()),e.boundingSphere!==null&&(this.boundingSphere=e.boundingSphere.clone()),this}getColorAt(e,n){return this.instanceColor===null?n.setRGB(1,1,1):n.fromArray(this.instanceColor.array,e*3)}getMatrixAt(e,n){return n.fromArray(this.instanceMatrix.array,e*16)}getMorphAt(e,n){const i=n.morphTargetInfluences,a=this.morphTexture.source.data.data,s=i.length+1,r=e*s+1;for(let o=0;o<i.length;o++)i[o]=a[r+o]}raycast(e,n){const i=this.matrixWorld,a=this.count;if(ql.geometry=this.geometry,ql.material=this.material,ql.material!==void 0&&(this.boundingSphere===null&&this.computeBoundingSphere(),Yl.copy(this.boundingSphere),Yl.applyMatrix4(i),e.ray.intersectsSphere(Yl)!==!1))for(let s=0;s<a;s++){this.getMatrixAt(s,wr),O0.multiplyMatrices(i,wr),ql.matrixWorld=O0,ql.raycast(e,jc);for(let r=0,o=jc.length;r<o;r++){const c=jc[r];c.instanceId=s,c.object=this,n.push(c)}jc.length=0}}setColorAt(e,n){return this.instanceColor===null&&(this.instanceColor=new Yr(new Float32Array(this.instanceMatrix.count*3).fill(1),3)),n.toArray(this.instanceColor.array,e*3),this}setMatrixAt(e,n){return n.toArray(this.instanceMatrix.array,e*16),this}setMorphAt(e,n){const i=n.morphTargetInfluences,a=i.length+1;this.morphTexture===null&&(this.morphTexture=new yS(new Float32Array(a*this.count),a,this.count,dg,Ii));const s=this.morphTexture.source.data.data;let r=0;for(let u=0;u<i.length;u++)r+=i[u];const o=this.geometry.morphTargetsRelative?1:1-r,c=a*e;return s[c]=o,s.set(i,c+1),this}updateMorphTargets(){}dispose(){this.dispatchEvent({type:"dispose"}),this.morphTexture!==null&&(this.morphTexture.dispose(),this.morphTexture=null)}}const Zd=new Y,LA=new Y,UA=new it;class ks{constructor(e=new Y(1,0,0),n=0){this.isPlane=!0,this.normal=e,this.constant=n}set(e,n){return this.normal.copy(e),this.constant=n,this}setComponents(e,n,i,a){return this.normal.set(e,n,i),this.constant=a,this}setFromNormalAndCoplanarPoint(e,n){return this.normal.copy(e),this.constant=-n.dot(this.normal),this}setFromCoplanarPoints(e,n,i){const a=Zd.subVectors(i,n).cross(LA.subVectors(e,n)).normalize();return this.setFromNormalAndCoplanarPoint(a,e),this}copy(e){return this.normal.copy(e.normal),this.constant=e.constant,this}normalize(){const e=1/this.normal.length();return this.normal.multiplyScalar(e),this.constant*=e,this}negate(){return this.constant*=-1,this.normal.negate(),this}distanceToPoint(e){return this.normal.dot(e)+this.constant}distanceToSphere(e){return this.distanceToPoint(e.center)-e.radius}projectPoint(e,n){return n.copy(e).addScaledVector(this.normal,-this.distanceToPoint(e))}intersectLine(e,n,i=!0){const a=e.delta(Zd),s=this.normal.dot(a);if(s===0)return this.distanceToPoint(e.start)===0?n.copy(e.start):null;const r=-(e.start.dot(this.normal)+this.constant)/s;return i===!0&&(r<0||r>1)?null:n.copy(e.start).addScaledVector(a,r)}intersectsLine(e){const n=this.distanceToPoint(e.start),i=this.distanceToPoint(e.end);return n<0&&i>0||i<0&&n>0}intersectsBox(e){return e.intersectsPlane(this)}intersectsSphere(e){return e.intersectsPlane(this)}coplanarPoint(e){return e.copy(this.normal).multiplyScalar(-this.constant)}applyMatrix4(e,n){const i=n||UA.getNormalMatrix(e),a=this.coplanarPoint(Zd).applyMatrix4(e),s=this.normal.applyMatrix3(i).normalize();return this.constant=-a.dot(s),this}translate(e){return this.constant-=e.dot(this.normal),this}equals(e){return e.normal.equals(this.normal)&&e.constant===this.constant}clone(){return new this.constructor().copy(this)}}const Us=new fr,OA=new At(.5,.5),Hc=new Y;class bS{constructor(e=new ks,n=new ks,i=new ks,a=new ks,s=new ks,r=new ks){this.planes=[e,n,i,a,s,r]}set(e,n,i,a,s,r){const o=this.planes;return o[0].copy(e),o[1].copy(n),o[2].copy(i),o[3].copy(a),o[4].copy(s),o[5].copy(r),this}copy(e){const n=this.planes;for(let i=0;i<6;i++)n[i].copy(e.planes[i]);return this}setFromProjectionMatrix(e,n=ta,i=!1){const a=this.planes,s=e.elements,r=s[0],o=s[1],c=s[2],u=s[3],d=s[4],m=s[5],f=s[6],p=s[7],x=s[8],_=s[9],g=s[10],h=s[11],v=s[12],b=s[13],y=s[14],A=s[15];if(a[0].setComponents(u-r,p-d,h-x,A-v).normalize(),a[1].setComponents(u+r,p+d,h+x,A+v).normalize(),a[2].setComponents(u+o,p+m,h+_,A+b).normalize(),a[3].setComponents(u-o,p-m,h-_,A-b).normalize(),i)a[4].setComponents(c,f,g,y).normalize(),a[5].setComponents(u-c,p-f,h-g,A-y).normalize();else if(a[4].setComponents(u-c,p-f,h-g,A-y).normalize(),n===ta)a[5].setComponents(u+c,p+f,h+g,A+y).normalize();else if(n===cf)a[5].setComponents(c,f,g,y).normalize();else throw new Error("THREE.Frustum.setFromProjectionMatrix(): Invalid coordinate system: "+n);return this}intersectsObject(e){if(e.boundingSphere!==void 0)e.boundingSphere===null&&e.computeBoundingSphere(),Us.copy(e.boundingSphere).applyMatrix4(e.matrixWorld);else{const n=e.geometry;n.boundingSphere===null&&n.computeBoundingSphere(),Us.copy(n.boundingSphere).applyMatrix4(e.matrixWorld)}return this.intersectsSphere(Us)}intersectsSprite(e){Us.center.set(0,0,0);const n=OA.distanceTo(e.center);return Us.radius=.7071067811865476+n,Us.applyMatrix4(e.matrixWorld),this.intersectsSphere(Us)}intersectsSphere(e){const n=this.planes,i=e.center,a=-e.radius;for(let s=0;s<6;s++)if(n[s].distanceToPoint(i)<a)return!1;return!0}intersectsBox(e){const n=this.planes;for(let i=0;i<6;i++){const a=n[i];if(Hc.x=a.normal.x>0?e.max.x:e.min.x,Hc.y=a.normal.y>0?e.max.y:e.min.y,Hc.z=a.normal.z>0?e.max.z:e.min.z,a.distanceToPoint(Hc)<0)return!1}return!0}containsPoint(e){const n=this.planes;for(let i=0;i<6;i++)if(n[i].distanceToPoint(e)<0)return!1;return!0}clone(){return new this.constructor().copy(this)}}class SS extends Ll{constructor(e){super(),this.isLineBasicMaterial=!0,this.type="LineBasicMaterial",this.color=new gt(16777215),this.map=null,this.linewidth=1,this.linecap="round",this.linejoin="round",this.fog=!0,this.setValues(e)}copy(e){return super.copy(e),this.color.copy(e.color),this.map=e.map,this.linewidth=e.linewidth,this.linecap=e.linecap,this.linejoin=e.linejoin,this.fog=e.fog,this}}const ff=new Y,df=new Y,I0=new Ft,Kl=new _g,Gc=new fr,$d=new Y,k0=new Y;class PA extends In{constructor(e=new Zn,n=new SS){super(),this.isLine=!0,this.type="Line",this.geometry=e,this.material=n,this.morphTargetDictionary=void 0,this.morphTargetInfluences=void 0,this.updateMorphTargets()}copy(e,n){return super.copy(e,n),this.material=Array.isArray(e.material)?e.material.slice():e.material,this.geometry=e.geometry,this}computeLineDistances(){const e=this.geometry;if(e.index===null){const n=e.attributes.position,i=[0];for(let a=1,s=n.count;a<s;a++)ff.fromBufferAttribute(n,a-1),df.fromBufferAttribute(n,a),i[a]=i[a-1],i[a]+=ff.distanceTo(df);e.setAttribute("lineDistance",new Ci(i,1))}else Je("Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.");return this}raycast(e,n){const i=this.geometry,a=this.matrixWorld,s=e.params.Line.threshold,r=i.drawRange;if(i.boundingSphere===null&&i.computeBoundingSphere(),Gc.copy(i.boundingSphere),Gc.applyMatrix4(a),Gc.radius+=s,e.ray.intersectsSphere(Gc)===!1)return;I0.copy(a).invert(),Kl.copy(e.ray).applyMatrix4(I0);const o=s/((this.scale.x+this.scale.y+this.scale.z)/3),c=o*o,u=this.isLineSegments?2:1,d=i.index,f=i.attributes.position;if(d!==null){const p=Math.max(0,r.start),x=Math.min(d.count,r.start+r.count);for(let _=p,g=x-1;_<g;_+=u){const h=d.getX(_),v=d.getX(_+1),b=Vc(this,e,Kl,c,h,v,_);b&&n.push(b)}if(this.isLineLoop){const _=d.getX(x-1),g=d.getX(p),h=Vc(this,e,Kl,c,_,g,x-1);h&&n.push(h)}}else{const p=Math.max(0,r.start),x=Math.min(f.count,r.start+r.count);for(let _=p,g=x-1;_<g;_+=u){const h=Vc(this,e,Kl,c,_,_+1,_);h&&n.push(h)}if(this.isLineLoop){const _=Vc(this,e,Kl,c,x-1,p,x-1);_&&n.push(_)}}}updateMorphTargets(){const n=this.geometry.morphAttributes,i=Object.keys(n);if(i.length>0){const a=n[i[0]];if(a!==void 0){this.morphTargetInfluences=[],this.morphTargetDictionary={};for(let s=0,r=a.length;s<r;s++){const o=a[s].name||String(s);this.morphTargetInfluences.push(0),this.morphTargetDictionary[o]=s}}}}}function Vc(t,e,n,i,a,s,r){const o=t.geometry.attributes.position;if(ff.fromBufferAttribute(o,a),df.fromBufferAttribute(o,s),n.distanceSqToSegment(ff,df,$d,k0)>i)return;$d.applyMatrix4(t.matrixWorld);const u=e.ray.origin.distanceTo($d);if(!(u<e.near||u>e.far))return{distance:u,point:k0.clone().applyMatrix4(t.matrixWorld),index:r,face:null,faceIndex:null,barycoord:null,object:t}}const B0=new Y,F0=new Y;class Qd extends PA{constructor(e,n){super(e,n),this.isLineSegments=!0,this.type="LineSegments"}computeLineDistances(){const e=this.geometry;if(e.index===null){const n=e.attributes.position,i=[];for(let a=0,s=n.count;a<s;a+=2)B0.fromBufferAttribute(n,a),F0.fromBufferAttribute(n,a+1),i[a]=a===0?0:i[a-1],i[a+1]=i[a]+B0.distanceTo(F0);e.setAttribute("lineDistance",new Ci(i,1))}else Je("LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.");return this}}class zA extends Ll{constructor(e){super(),this.isPointsMaterial=!0,this.type="PointsMaterial",this.color=new gt(16777215),this.map=null,this.alphaMap=null,this.size=1,this.sizeAttenuation=!0,this.fog=!0,this.setValues(e)}copy(e){return super.copy(e),this.color.copy(e.color),this.map=e.map,this.alphaMap=e.alphaMap,this.size=e.size,this.sizeAttenuation=e.sizeAttenuation,this.fog=e.fog,this}}const j0=new Ft,em=new _g,Wc=new fr,Xc=new Y;class IA extends In{constructor(e=new Zn,n=new zA){super(),this.isPoints=!0,this.type="Points",this.geometry=e,this.material=n,this.morphTargetDictionary=void 0,this.morphTargetInfluences=void 0,this.updateMorphTargets()}copy(e,n){return super.copy(e,n),this.material=Array.isArray(e.material)?e.material.slice():e.material,this.geometry=e.geometry,this}raycast(e,n){const i=this.geometry,a=this.matrixWorld,s=e.params.Points.threshold,r=i.drawRange;if(i.boundingSphere===null&&i.computeBoundingSphere(),Wc.copy(i.boundingSphere),Wc.applyMatrix4(a),Wc.radius+=s,e.ray.intersectsSphere(Wc)===!1)return;j0.copy(a).invert(),em.copy(e.ray).applyMatrix4(j0);const o=s/((this.scale.x+this.scale.y+this.scale.z)/3),c=o*o,u=i.index,m=i.attributes.position;if(u!==null){const f=Math.max(0,r.start),p=Math.min(u.count,r.start+r.count);for(let x=f,_=p;x<_;x++){const g=u.getX(x);Xc.fromBufferAttribute(m,g),H0(Xc,g,c,a,e,n,this)}}else{const f=Math.max(0,r.start),p=Math.min(m.count,r.start+r.count);for(let x=f,_=p;x<_;x++)Xc.fromBufferAttribute(m,x),H0(Xc,x,c,a,e,n,this)}}updateMorphTargets(){const n=this.geometry.morphAttributes,i=Object.keys(n);if(i.length>0){const a=n[i[0]];if(a!==void 0){this.morphTargetInfluences=[],this.morphTargetDictionary={};for(let s=0,r=a.length;s<r;s++){const o=a[s].name||String(s);this.morphTargetInfluences.push(0),this.morphTargetDictionary[o]=s}}}}}function H0(t,e,n,i,a,s,r){const o=em.distanceSqToPoint(t);if(o<n){const c=new Y;em.closestPointToPoint(t,c),c.applyMatrix4(i);const u=a.ray.origin.distanceTo(c);if(u<a.near||u>a.far)return;s.push({distance:u,distanceToRay:Math.sqrt(o),point:c,index:e,face:null,faceIndex:null,barycoord:null,object:r})}}class MS extends zn{constructor(e=[],n=Js,i,a,s,r,o,c,u,d){super(e,n,i,a,s,r,o,c,u,d),this.isCubeTexture=!0,this.flipY=!1}get images(){return this.image}set images(e){this.image=e}}class yl extends zn{constructor(e,n,i=sa,a,s,r,o=En,c=En,u,d=Ia,m=1){if(d!==Ia&&d!==Hs)throw new Error("THREE.DepthTexture: format must be either THREE.DepthFormat or THREE.DepthStencilFormat");const f={width:e,height:n,depth:m};super(f,a,s,r,o,c,d,i,u),this.isDepthTexture=!0,this.flipY=!1,this.generateMipmaps=!1,this.compareFunction=null}copy(e){return super.copy(e),this.source=new vg(Object.assign({},e.image)),this.compareFunction=e.compareFunction,this}toJSON(e){const n=super.toJSON(e);return this.compareFunction!==null&&(n.compareFunction=this.compareFunction),n}}class kA extends yl{constructor(e,n=sa,i=Js,a,s,r=En,o=En,c,u=Ia){const d={width:e,height:e,depth:1},m=[d,d,d,d,d,d];super(e,e,n,i,a,s,r,o,c,u),this.image=m,this.isCubeDepthTexture=!0,this.isCubeTexture=!0}get images(){return this.image}set images(e){this.image=e}}class ES extends zn{constructor(e=null){super(),this.sourceTexture=e,this.isExternalTexture=!0}copy(e){return super.copy(e),this.sourceTexture=e.sourceTexture,this}}class nr extends Zn{constructor(e=1,n=1,i=1,a=1,s=1,r=1){super(),this.type="BoxGeometry",this.parameters={width:e,height:n,depth:i,widthSegments:a,heightSegments:s,depthSegments:r};const o=this;a=Math.floor(a),s=Math.floor(s),r=Math.floor(r);const c=[],u=[],d=[],m=[];let f=0,p=0;x("z","y","x",-1,-1,i,n,e,r,s,0),x("z","y","x",1,-1,i,n,-e,r,s,1),x("x","z","y",1,1,e,i,n,a,r,2),x("x","z","y",1,-1,e,i,-n,a,r,3),x("x","y","z",1,-1,e,n,i,a,s,4),x("x","y","z",-1,-1,e,n,-i,a,s,5),this.setIndex(c),this.setAttribute("position",new Ci(u,3)),this.setAttribute("normal",new Ci(d,3)),this.setAttribute("uv",new Ci(m,2));function x(_,g,h,v,b,y,A,M,T,S,C){const R=y/T,L=A/S,O=y/2,W=A/2,z=M/2,D=T+1,B=S+1;let k=0,V=0;const X=new Y;for(let ge=0;ge<B;ge++){const he=ge*L-W;for(let Ae=0;Ae<D;Ae++){const q=Ae*R-O;X[_]=q*v,X[g]=he*b,X[h]=z,u.push(X.x,X.y,X.z),X[_]=0,X[g]=0,X[h]=M>0?1:-1,d.push(X.x,X.y,X.z),m.push(Ae/T),m.push(1-ge/S),k+=1}}for(let ge=0;ge<S;ge++)for(let he=0;he<T;he++){const Ae=f+he+D*ge,q=f+he+D*(ge+1),fe=f+(he+1)+D*(ge+1),Ne=f+(he+1)+D*ge;c.push(Ae,q,Ne),c.push(q,fe,Ne),V+=6}o.addGroup(p,V,C),p+=V,f+=k}}copy(e){return super.copy(e),this.parameters=Object.assign({},e.parameters),this}static fromJSON(e){return new nr(e.width,e.height,e.depth,e.widthSegments,e.heightSegments,e.depthSegments)}}const qc=new Y,Yc=new Y,Jd=new Y,Kc=new Ti;class BA extends Zn{constructor(e=null,n=1){if(super(),this.type="EdgesGeometry",this.parameters={geometry:e,thresholdAngle:n},e!==null){const a=Math.pow(10,4),s=Math.cos(Au*n),r=e.getIndex(),o=e.getAttribute("position"),c=r?r.count:o.count,u=[0,0,0],d=["a","b","c"],m=new Array(3),f={},p=[];for(let x=0;x<c;x+=3){r?(u[0]=r.getX(x),u[1]=r.getX(x+1),u[2]=r.getX(x+2)):(u[0]=x,u[1]=x+1,u[2]=x+2);const{a:_,b:g,c:h}=Kc;if(_.fromBufferAttribute(o,u[0]),g.fromBufferAttribute(o,u[1]),h.fromBufferAttribute(o,u[2]),Kc.getNormal(Jd),m[0]=`${Math.round(_.x*a)},${Math.round(_.y*a)},${Math.round(_.z*a)}`,m[1]=`${Math.round(g.x*a)},${Math.round(g.y*a)},${Math.round(g.z*a)}`,m[2]=`${Math.round(h.x*a)},${Math.round(h.y*a)},${Math.round(h.z*a)}`,!(m[0]===m[1]||m[1]===m[2]||m[2]===m[0]))for(let v=0;v<3;v++){const b=(v+1)%3,y=m[v],A=m[b],M=Kc[d[v]],T=Kc[d[b]],S=`${y}_${A}`,C=`${A}_${y}`;C in f&&f[C]?(Jd.dot(f[C].normal)<=s&&(p.push(M.x,M.y,M.z),p.push(T.x,T.y,T.z)),f[C]=null):S in f||(f[S]={index0:u[v],index1:u[b],normal:Jd.clone()})}}for(const x in f)if(f[x]){const{index0:_,index1:g}=f[x];qc.fromBufferAttribute(o,_),Yc.fromBufferAttribute(o,g),p.push(qc.x,qc.y,qc.z),p.push(Yc.x,Yc.y,Yc.z)}this.setAttribute("position",new Ci(p,3))}}copy(e){return super.copy(e),this.parameters=Object.assign({},e.parameters),this}}class kf extends Zn{constructor(e=1,n=1,i=1,a=1){super(),this.type="PlaneGeometry",this.parameters={width:e,height:n,widthSegments:i,heightSegments:a};const s=e/2,r=n/2,o=Math.floor(i),c=Math.floor(a),u=o+1,d=c+1,m=e/o,f=n/c,p=[],x=[],_=[],g=[];for(let h=0;h<d;h++){const v=h*f-r;for(let b=0;b<u;b++){const y=b*m-s;x.push(y,-v,0),_.push(0,0,1),g.push(b/o),g.push(1-h/c)}}for(let h=0;h<c;h++)for(let v=0;v<o;v++){const b=v+u*h,y=v+u*(h+1),A=v+1+u*(h+1),M=v+1+u*h;p.push(b,y,M),p.push(y,A,M)}this.setIndex(p),this.setAttribute("position",new Ci(x,3)),this.setAttribute("normal",new Ci(_,3)),this.setAttribute("uv",new Ci(g,2))}copy(e){return super.copy(e),this.parameters=Object.assign({},e.parameters),this}static fromJSON(e){return new kf(e.width,e.height,e.widthSegments,e.heightSegments)}}function bl(t){const e={};for(const n in t){e[n]={};for(const i in t[n]){const a=t[n][i];if(G0(a))a.isRenderTargetTexture?(Je("UniformsUtils: Textures of render targets cannot be cloned via cloneUniforms() or mergeUniforms()."),e[n][i]=null):e[n][i]=a.clone();else if(Array.isArray(a))if(G0(a[0])){const s=[];for(let r=0,o=a.length;r<o;r++)s[r]=a[r].clone();e[n][i]=s}else e[n][i]=a.slice();else e[n][i]=a}}return e}function Dn(t){const e={};for(let n=0;n<t.length;n++){const i=bl(t[n]);for(const a in i)e[a]=i[a]}return e}function G0(t){return t&&(t.isColor||t.isMatrix3||t.isMatrix4||t.isVector2||t.isVector3||t.isVector4||t.isTexture||t.isQuaternion)}function FA(t){const e=[];for(let n=0;n<t.length;n++)e.push(t[n].clone());return e}function TS(t){const e=t.getRenderTarget();return e===null?t.outputColorSpace:e.isXRRenderTarget===!0?e.texture.colorSpace:ht.workingColorSpace}const jA={clone:bl,merge:Dn};var HA=`void main() { - gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); -}`,GA=`void main() { - gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 ); -}`;class Xn extends Ll{constructor(e){super(),this.isShaderMaterial=!0,this.type="ShaderMaterial",this.defines={},this.uniforms={},this.uniformsGroups=[],this.vertexShader=HA,this.fragmentShader=GA,this.linewidth=1,this.wireframe=!1,this.wireframeLinewidth=1,this.fog=!1,this.lights=!1,this.clipping=!1,this.forceSinglePass=!0,this.extensions={clipCullDistance:!1,multiDraw:!1},this.defaultAttributeValues={color:[1,1,1],uv:[0,0],uv1:[0,0]},this.index0AttributeName=void 0,this.uniformsNeedUpdate=!1,this.glslVersion=null,e!==void 0&&this.setValues(e)}copy(e){return super.copy(e),this.fragmentShader=e.fragmentShader,this.vertexShader=e.vertexShader,this.uniforms=bl(e.uniforms),this.uniformsGroups=FA(e.uniformsGroups),this.defines=Object.assign({},e.defines),this.wireframe=e.wireframe,this.wireframeLinewidth=e.wireframeLinewidth,this.fog=e.fog,this.lights=e.lights,this.clipping=e.clipping,this.extensions=Object.assign({},e.extensions),this.glslVersion=e.glslVersion,this.defaultAttributeValues=Object.assign({},e.defaultAttributeValues),this.index0AttributeName=e.index0AttributeName,this.uniformsNeedUpdate=e.uniformsNeedUpdate,this}toJSON(e){const n=super.toJSON(e);n.glslVersion=this.glslVersion,n.uniforms={};for(const a in this.uniforms){const r=this.uniforms[a].value;r&&r.isTexture?n.uniforms[a]={type:"t",value:r.toJSON(e).uuid}:r&&r.isColor?n.uniforms[a]={type:"c",value:r.getHex()}:r&&r.isVector2?n.uniforms[a]={type:"v2",value:r.toArray()}:r&&r.isVector3?n.uniforms[a]={type:"v3",value:r.toArray()}:r&&r.isVector4?n.uniforms[a]={type:"v4",value:r.toArray()}:r&&r.isMatrix3?n.uniforms[a]={type:"m3",value:r.toArray()}:r&&r.isMatrix4?n.uniforms[a]={type:"m4",value:r.toArray()}:n.uniforms[a]={value:r}}Object.keys(this.defines).length>0&&(n.defines=this.defines),n.vertexShader=this.vertexShader,n.fragmentShader=this.fragmentShader,n.lights=this.lights,n.clipping=this.clipping;const i={};for(const a in this.extensions)this.extensions[a]===!0&&(i[a]=!0);return Object.keys(i).length>0&&(n.extensions=i),n}fromJSON(e,n){if(super.fromJSON(e,n),e.uniforms!==void 0)for(const i in e.uniforms){const a=e.uniforms[i];switch(this.uniforms[i]={},a.type){case"t":this.uniforms[i].value=n[a.value]||null;break;case"c":this.uniforms[i].value=new gt().setHex(a.value);break;case"v2":this.uniforms[i].value=new At().fromArray(a.value);break;case"v3":this.uniforms[i].value=new Y().fromArray(a.value);break;case"v4":this.uniforms[i].value=new Qt().fromArray(a.value);break;case"m3":this.uniforms[i].value=new it().fromArray(a.value);break;case"m4":this.uniforms[i].value=new Ft().fromArray(a.value);break;default:this.uniforms[i].value=a.value}}if(e.defines!==void 0&&(this.defines=e.defines),e.vertexShader!==void 0&&(this.vertexShader=e.vertexShader),e.fragmentShader!==void 0&&(this.fragmentShader=e.fragmentShader),e.glslVersion!==void 0&&(this.glslVersion=e.glslVersion),e.extensions!==void 0)for(const i in e.extensions)this.extensions[i]=e.extensions[i];return e.lights!==void 0&&(this.lights=e.lights),e.clipping!==void 0&&(this.clipping=e.clipping),this}}class VA extends Xn{constructor(e){super(e),this.isRawShaderMaterial=!0,this.type="RawShaderMaterial"}}class WA extends Ll{constructor(e){super(),this.isMeshDepthMaterial=!0,this.type="MeshDepthMaterial",this.depthPacking=JT,this.map=null,this.alphaMap=null,this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.wireframe=!1,this.wireframeLinewidth=1,this.setValues(e)}copy(e){return super.copy(e),this.depthPacking=e.depthPacking,this.map=e.map,this.alphaMap=e.alphaMap,this.displacementMap=e.displacementMap,this.displacementScale=e.displacementScale,this.displacementBias=e.displacementBias,this.wireframe=e.wireframe,this.wireframeLinewidth=e.wireframeLinewidth,this}}class XA extends Ll{constructor(e){super(),this.isMeshDistanceMaterial=!0,this.type="MeshDistanceMaterial",this.map=null,this.alphaMap=null,this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.setValues(e)}copy(e){return super.copy(e),this.map=e.map,this.alphaMap=e.alphaMap,this.displacementMap=e.displacementMap,this.displacementScale=e.displacementScale,this.displacementBias=e.displacementBias,this}}const Zc=new Y,$c=new Dl,Xi=new Y;class AS extends In{constructor(){super(),this.isCamera=!0,this.type="Camera",this.matrixWorldInverse=new Ft,this.projectionMatrix=new Ft,this.projectionMatrixInverse=new Ft,this.coordinateSystem=ta,this._reversedDepth=!1}get reversedDepth(){return this._reversedDepth}copy(e,n){return super.copy(e,n),this.matrixWorldInverse.copy(e.matrixWorldInverse),this.projectionMatrix.copy(e.projectionMatrix),this.projectionMatrixInverse.copy(e.projectionMatrixInverse),this.coordinateSystem=e.coordinateSystem,this}getWorldDirection(e){return super.getWorldDirection(e).negate()}updateMatrixWorld(e){super.updateMatrixWorld(e),this.matrixWorld.decompose(Zc,$c,Xi),Xi.x===1&&Xi.y===1&&Xi.z===1?this.matrixWorldInverse.copy(this.matrixWorld).invert():this.matrixWorldInverse.compose(Zc,$c,Xi.set(1,1,1)).invert()}updateWorldMatrix(e,n,i=!1){super.updateWorldMatrix(e,n,i),this.matrixWorld.decompose(Zc,$c,Xi),Xi.x===1&&Xi.y===1&&Xi.z===1?this.matrixWorldInverse.copy(this.matrixWorld).invert():this.matrixWorldInverse.compose(Zc,$c,Xi.set(1,1,1)).invert()}clone(){return new this.constructor().copy(this)}}const Ka=new Y,V0=new At,W0=new At;class zi extends AS{constructor(e=50,n=1,i=.1,a=2e3){super(),this.isPerspectiveCamera=!0,this.type="PerspectiveCamera",this.fov=e,this.zoom=1,this.near=i,this.far=a,this.focus=10,this.aspect=n,this.view=null,this.filmGauge=35,this.filmOffset=0,this.updateProjectionMatrix()}copy(e,n){return super.copy(e,n),this.fov=e.fov,this.zoom=e.zoom,this.near=e.near,this.far=e.far,this.focus=e.focus,this.aspect=e.aspect,this.view=e.view===null?null:Object.assign({},e.view),this.filmGauge=e.filmGauge,this.filmOffset=e.filmOffset,this}setFocalLength(e){const n=.5*this.getFilmHeight()/e;this.fov=Jp*2*Math.atan(n),this.updateProjectionMatrix()}getFocalLength(){const e=Math.tan(Au*.5*this.fov);return .5*this.getFilmHeight()/e}getEffectiveFOV(){return Jp*2*Math.atan(Math.tan(Au*.5*this.fov)/this.zoom)}getFilmWidth(){return this.filmGauge*Math.min(this.aspect,1)}getFilmHeight(){return this.filmGauge/Math.max(this.aspect,1)}getViewBounds(e,n,i){Ka.set(-1,-1,.5).applyMatrix4(this.projectionMatrixInverse),n.set(Ka.x,Ka.y).multiplyScalar(-e/Ka.z),Ka.set(1,1,.5).applyMatrix4(this.projectionMatrixInverse),i.set(Ka.x,Ka.y).multiplyScalar(-e/Ka.z)}getViewSize(e,n){return this.getViewBounds(e,V0,W0),n.subVectors(W0,V0)}setViewOffset(e,n,i,a,s,r){this.aspect=e/n,this.view===null&&(this.view={enabled:!0,fullWidth:1,fullHeight:1,offsetX:0,offsetY:0,width:1,height:1}),this.view.enabled=!0,this.view.fullWidth=e,this.view.fullHeight=n,this.view.offsetX=i,this.view.offsetY=a,this.view.width=s,this.view.height=r,this.updateProjectionMatrix()}clearViewOffset(){this.view!==null&&(this.view.enabled=!1),this.updateProjectionMatrix()}updateProjectionMatrix(){const e=this.near;let n=e*Math.tan(Au*.5*this.fov)/this.zoom,i=2*n,a=this.aspect*i,s=-.5*a;const r=this.view;if(this.view!==null&&this.view.enabled){const c=r.fullWidth,u=r.fullHeight;s+=r.offsetX*a/c,n-=r.offsetY*i/u,a*=r.width/c,i*=r.height/u}const o=this.filmOffset;o!==0&&(s+=e*o/this.getFilmWidth()),this.projectionMatrix.makePerspective(s,s+a,n,n-i,e,this.far,this.coordinateSystem,this.reversedDepth),this.projectionMatrixInverse.copy(this.projectionMatrix).invert()}toJSON(e){const n=super.toJSON(e);return n.object.fov=this.fov,n.object.zoom=this.zoom,n.object.near=this.near,n.object.far=this.far,n.object.focus=this.focus,n.object.aspect=this.aspect,this.view!==null&&(n.object.view=Object.assign({},this.view)),n.object.filmGauge=this.filmGauge,n.object.filmOffset=this.filmOffset,n}}class yg extends AS{constructor(e=-1,n=1,i=1,a=-1,s=.1,r=2e3){super(),this.isOrthographicCamera=!0,this.type="OrthographicCamera",this.zoom=1,this.view=null,this.left=e,this.right=n,this.top=i,this.bottom=a,this.near=s,this.far=r,this.updateProjectionMatrix()}copy(e,n){return super.copy(e,n),this.left=e.left,this.right=e.right,this.top=e.top,this.bottom=e.bottom,this.near=e.near,this.far=e.far,this.zoom=e.zoom,this.view=e.view===null?null:Object.assign({},e.view),this}setViewOffset(e,n,i,a,s,r){this.view===null&&(this.view={enabled:!0,fullWidth:1,fullHeight:1,offsetX:0,offsetY:0,width:1,height:1}),this.view.enabled=!0,this.view.fullWidth=e,this.view.fullHeight=n,this.view.offsetX=i,this.view.offsetY=a,this.view.width=s,this.view.height=r,this.updateProjectionMatrix()}clearViewOffset(){this.view!==null&&(this.view.enabled=!1),this.updateProjectionMatrix()}updateProjectionMatrix(){const e=(this.right-this.left)/(2*this.zoom),n=(this.top-this.bottom)/(2*this.zoom),i=(this.right+this.left)/2,a=(this.top+this.bottom)/2;let s=i-e,r=i+e,o=a+n,c=a-n;if(this.view!==null&&this.view.enabled){const u=(this.right-this.left)/this.view.fullWidth/this.zoom,d=(this.top-this.bottom)/this.view.fullHeight/this.zoom;s+=u*this.view.offsetX,r=s+u*this.view.width,o-=d*this.view.offsetY,c=o-d*this.view.height}this.projectionMatrix.makeOrthographic(s,r,o,c,this.near,this.far,this.coordinateSystem,this.reversedDepth),this.projectionMatrixInverse.copy(this.projectionMatrix).invert()}toJSON(e){const n=super.toJSON(e);return n.object.zoom=this.zoom,n.object.left=this.left,n.object.right=this.right,n.object.top=this.top,n.object.bottom=this.bottom,n.object.near=this.near,n.object.far=this.far,this.view!==null&&(n.object.view=Object.assign({},this.view)),n}}const Cr=-90,Nr=1;class qA extends In{constructor(e,n,i){super(),this.type="CubeCamera",this.renderTarget=i,this.coordinateSystem=null,this.activeMipmapLevel=0;const a=new zi(Cr,Nr,e,n);a.layers=this.layers,this.add(a);const s=new zi(Cr,Nr,e,n);s.layers=this.layers,this.add(s);const r=new zi(Cr,Nr,e,n);r.layers=this.layers,this.add(r);const o=new zi(Cr,Nr,e,n);o.layers=this.layers,this.add(o);const c=new zi(Cr,Nr,e,n);c.layers=this.layers,this.add(c);const u=new zi(Cr,Nr,e,n);u.layers=this.layers,this.add(u)}updateCoordinateSystem(){const e=this.coordinateSystem,n=this.children.concat(),[i,a,s,r,o,c]=n;for(const u of n)this.remove(u);if(e===ta)i.up.set(0,1,0),i.lookAt(1,0,0),a.up.set(0,1,0),a.lookAt(-1,0,0),s.up.set(0,0,-1),s.lookAt(0,1,0),r.up.set(0,0,1),r.lookAt(0,-1,0),o.up.set(0,1,0),o.lookAt(0,0,1),c.up.set(0,1,0),c.lookAt(0,0,-1);else if(e===cf)i.up.set(0,-1,0),i.lookAt(-1,0,0),a.up.set(0,-1,0),a.lookAt(1,0,0),s.up.set(0,0,1),s.lookAt(0,1,0),r.up.set(0,0,-1),r.lookAt(0,-1,0),o.up.set(0,-1,0),o.lookAt(0,0,1),c.up.set(0,-1,0),c.lookAt(0,0,-1);else throw new Error("THREE.CubeCamera.updateCoordinateSystem(): Invalid coordinate system: "+e);for(const u of n)this.add(u),u.updateMatrixWorld()}update(e,n){this.parent===null&&this.updateMatrixWorld();const{renderTarget:i,activeMipmapLevel:a}=this;this.coordinateSystem!==e.coordinateSystem&&(this.coordinateSystem=e.coordinateSystem,this.updateCoordinateSystem());const[s,r,o,c,u,d]=this.children,m=e.getRenderTarget(),f=e.getActiveCubeFace(),p=e.getActiveMipmapLevel(),x=e.xr.enabled;e.xr.enabled=!1;const _=i.texture.generateMipmaps;i.texture.generateMipmaps=!1;let g=!1;e.isWebGLRenderer===!0?g=e.state.buffers.depth.getReversed():g=e.reversedDepthBuffer,e.setRenderTarget(i,0,a),g&&e.autoClear===!1&&e.clearDepth(),e.render(n,s),e.setRenderTarget(i,1,a),g&&e.autoClear===!1&&e.clearDepth(),e.render(n,r),e.setRenderTarget(i,2,a),g&&e.autoClear===!1&&e.clearDepth(),e.render(n,o),e.setRenderTarget(i,3,a),g&&e.autoClear===!1&&e.clearDepth(),e.render(n,c),e.setRenderTarget(i,4,a),g&&e.autoClear===!1&&e.clearDepth(),e.render(n,u),i.texture.generateMipmaps=_,e.setRenderTarget(i,5,a),g&&e.autoClear===!1&&e.clearDepth(),e.render(n,d),e.setRenderTarget(m,f,p),e.xr.enabled=x,i.texture.needsPMREMUpdate=!0}}class YA extends zi{constructor(e=[]){super(),this.isArrayCamera=!0,this.isMultiViewCamera=!1,this.cameras=e}}const Cg=class Cg{constructor(e,n,i,a){this.elements=[1,0,0,1],e!==void 0&&this.set(e,n,i,a)}identity(){return this.set(1,0,0,1),this}fromArray(e,n=0){for(let i=0;i<4;i++)this.elements[i]=e[i+n];return this}set(e,n,i,a){const s=this.elements;return s[0]=e,s[2]=n,s[1]=i,s[3]=a,this}};Cg.prototype.isMatrix2=!0;let X0=Cg;function q0(t,e,n,i){const a=KA(i);switch(n){case fS:return t*e;case dg:return t*e/a.components*a.byteLength;case hg:return t*e/a.components*a.byteLength;case er:return t*e*2/a.components*a.byteLength;case pg:return t*e*2/a.components*a.byteLength;case dS:return t*e*3/a.components*a.byteLength;case ki:return t*e*4/a.components*a.byteLength;case mg:return t*e*4/a.components*a.byteLength;case Su:case Mu:return Math.floor((t+3)/4)*Math.floor((e+3)/4)*8;case Eu:case Tu:return Math.floor((t+3)/4)*Math.floor((e+3)/4)*16;case Ep:case Ap:return Math.max(t,16)*Math.max(e,8)/4;case Mp:case Tp:return Math.max(t,8)*Math.max(e,8)/2;case wp:case Cp:case Rp:case Dp:return Math.floor((t+3)/4)*Math.floor((e+3)/4)*8;case Np:case sf:case Lp:return Math.floor((t+3)/4)*Math.floor((e+3)/4)*16;case Up:return Math.floor((t+3)/4)*Math.floor((e+3)/4)*16;case Op:return Math.floor((t+4)/5)*Math.floor((e+3)/4)*16;case Pp:return Math.floor((t+4)/5)*Math.floor((e+4)/5)*16;case zp:return Math.floor((t+5)/6)*Math.floor((e+4)/5)*16;case Ip:return Math.floor((t+5)/6)*Math.floor((e+5)/6)*16;case kp:return Math.floor((t+7)/8)*Math.floor((e+4)/5)*16;case Bp:return Math.floor((t+7)/8)*Math.floor((e+5)/6)*16;case Fp:return Math.floor((t+7)/8)*Math.floor((e+7)/8)*16;case jp:return Math.floor((t+9)/10)*Math.floor((e+4)/5)*16;case Hp:return Math.floor((t+9)/10)*Math.floor((e+5)/6)*16;case Gp:return Math.floor((t+9)/10)*Math.floor((e+7)/8)*16;case Vp:return Math.floor((t+9)/10)*Math.floor((e+9)/10)*16;case Wp:return Math.floor((t+11)/12)*Math.floor((e+9)/10)*16;case Xp:return Math.floor((t+11)/12)*Math.floor((e+11)/12)*16;case qp:case Yp:case Kp:return Math.ceil(t/4)*Math.ceil(e/4)*16;case Zp:case $p:return Math.ceil(t/4)*Math.ceil(e/4)*8;case rf:case Qp:return Math.ceil(t/4)*Math.ceil(e/4)*16}throw new Error(`Unable to determine texture byte length for ${n} format.`)}function KA(t){switch(t){case Ei:case lS:return{byteLength:1,components:1};case Go:case oS:case za:return{byteLength:2,components:1};case ug:case fg:return{byteLength:2,components:4};case sa:case cg:case Ii:return{byteLength:4,components:1};case cS:case uS:return{byteLength:4,components:3}}throw new Error(`THREE.TextureUtils: Unknown texture type ${t}.`)}typeof __THREE_DEVTOOLS__<"u"&&__THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("register",{detail:{revision:og}}));typeof window<"u"&&(window.__THREE__?Je("WARNING: Multiple instances of Three.js being imported."):window.__THREE__=og);/** - * @license - * Copyright 2010-2026 Three.js Authors - * SPDX-License-Identifier: MIT - */function wS(){let t=null,e=!1,n=null,i=null;function a(s,r){n(s,r),i=t.requestAnimationFrame(a)}return{start:function(){e!==!0&&n!==null&&t!==null&&(i=t.requestAnimationFrame(a),e=!0)},stop:function(){t!==null&&t.cancelAnimationFrame(i),e=!1},setAnimationLoop:function(s){n=s},setContext:function(s){t=s}}}function ZA(t){const e=new WeakMap;function n(o,c){const u=o.array,d=o.usage,m=u.byteLength,f=t.createBuffer();t.bindBuffer(c,f),t.bufferData(c,u,d),o.onUploadCallback();let p;if(u instanceof Float32Array)p=t.FLOAT;else if(typeof Float16Array<"u"&&u instanceof Float16Array)p=t.HALF_FLOAT;else if(u instanceof Uint16Array)o.isFloat16BufferAttribute?p=t.HALF_FLOAT:p=t.UNSIGNED_SHORT;else if(u instanceof Int16Array)p=t.SHORT;else if(u instanceof Uint32Array)p=t.UNSIGNED_INT;else if(u instanceof Int32Array)p=t.INT;else if(u instanceof Int8Array)p=t.BYTE;else if(u instanceof Uint8Array)p=t.UNSIGNED_BYTE;else if(u instanceof Uint8ClampedArray)p=t.UNSIGNED_BYTE;else throw new Error("THREE.WebGLAttributes: Unsupported buffer data format: "+u);return{buffer:f,type:p,bytesPerElement:u.BYTES_PER_ELEMENT,version:o.version,size:m}}function i(o,c,u){const d=c.array,m=c.updateRanges;if(t.bindBuffer(u,o),m.length===0)t.bufferSubData(u,0,d);else{m.sort((p,x)=>p.start-x.start);let f=0;for(let p=1;p<m.length;p++){const x=m[f],_=m[p];_.start<=x.start+x.count+1?x.count=Math.max(x.count,_.start+_.count-x.start):(++f,m[f]=_)}m.length=f+1;for(let p=0,x=m.length;p<x;p++){const _=m[p];t.bufferSubData(u,_.start*d.BYTES_PER_ELEMENT,d,_.start,_.count)}c.clearUpdateRanges()}c.onUploadCallback()}function a(o){return o.isInterleavedBufferAttribute&&(o=o.data),e.get(o)}function s(o){o.isInterleavedBufferAttribute&&(o=o.data);const c=e.get(o);c&&(t.deleteBuffer(c.buffer),e.delete(o))}function r(o,c){if(o.isInterleavedBufferAttribute&&(o=o.data),o.isGLBufferAttribute){const d=e.get(o);(!d||d.version<o.version)&&e.set(o,{buffer:o.buffer,type:o.type,bytesPerElement:o.elementSize,version:o.version});return}const u=e.get(o);if(u===void 0)e.set(o,n(o,c));else if(u.version<o.version){if(u.size!==o.array.byteLength)throw new Error("THREE.WebGLAttributes: The size of the buffer attribute's array buffer does not match the original size. Resizing buffer attributes is not supported.");i(u.buffer,o,c),u.version=o.version}}return{get:a,remove:s,update:r}}var $A=`#ifdef USE_ALPHAHASH - if ( diffuseColor.a < getAlphaHashThreshold( vPosition ) ) discard; -#endif`,QA=`#ifdef USE_ALPHAHASH - const float ALPHA_HASH_SCALE = 0.05; - float hash2D( vec2 value ) { - return fract( 1.0e4 * sin( 17.0 * value.x + 0.1 * value.y ) * ( 0.1 + abs( sin( 13.0 * value.y + value.x ) ) ) ); - } - float hash3D( vec3 value ) { - return hash2D( vec2( hash2D( value.xy ), value.z ) ); - } - float getAlphaHashThreshold( vec3 position ) { - float maxDeriv = max( - length( dFdx( position.xyz ) ), - length( dFdy( position.xyz ) ) - ); - float pixScale = 1.0 / ( ALPHA_HASH_SCALE * maxDeriv ); - vec2 pixScales = vec2( - exp2( floor( log2( pixScale ) ) ), - exp2( ceil( log2( pixScale ) ) ) - ); - vec2 alpha = vec2( - hash3D( floor( pixScales.x * position.xyz ) ), - hash3D( floor( pixScales.y * position.xyz ) ) - ); - float lerpFactor = fract( log2( pixScale ) ); - float x = ( 1.0 - lerpFactor ) * alpha.x + lerpFactor * alpha.y; - float a = min( lerpFactor, 1.0 - lerpFactor ); - vec3 cases = vec3( - x * x / ( 2.0 * a * ( 1.0 - a ) ), - ( x - 0.5 * a ) / ( 1.0 - a ), - 1.0 - ( ( 1.0 - x ) * ( 1.0 - x ) / ( 2.0 * a * ( 1.0 - a ) ) ) - ); - float threshold = ( x < ( 1.0 - a ) ) - ? ( ( x < a ) ? cases.x : cases.y ) - : cases.z; - return clamp( threshold , 1.0e-6, 1.0 ); - } -#endif`,JA=`#ifdef USE_ALPHAMAP - diffuseColor.a *= texture2D( alphaMap, vAlphaMapUv ).g; -#endif`,ew=`#ifdef USE_ALPHAMAP - uniform sampler2D alphaMap; -#endif`,tw=`#ifdef USE_ALPHATEST - #ifdef ALPHA_TO_COVERAGE - diffuseColor.a = smoothstep( alphaTest, alphaTest + fwidth( diffuseColor.a ), diffuseColor.a ); - if ( diffuseColor.a == 0.0 ) discard; - #else - if ( diffuseColor.a < alphaTest ) discard; - #endif -#endif`,nw=`#ifdef USE_ALPHATEST - uniform float alphaTest; -#endif`,iw=`#ifdef USE_AOMAP - float ambientOcclusion = ( texture2D( aoMap, vAoMapUv ).r - 1.0 ) * aoMapIntensity + 1.0; - reflectedLight.indirectDiffuse *= ambientOcclusion; - #if defined( USE_CLEARCOAT ) - clearcoatSpecularIndirect *= ambientOcclusion; - #endif - #if defined( USE_SHEEN ) - sheenSpecularIndirect *= ambientOcclusion; - #endif - #if defined( USE_ENVMAP ) && defined( STANDARD ) - float dotNV = saturate( dot( geometryNormal, geometryViewDir ) ); - reflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.roughness ); - #endif -#endif`,aw=`#ifdef USE_AOMAP - uniform sampler2D aoMap; - uniform float aoMapIntensity; -#endif`,sw=`#ifdef USE_BATCHING - #if ! defined( GL_ANGLE_multi_draw ) - #define gl_DrawID _gl_DrawID - uniform int _gl_DrawID; - #endif - uniform highp sampler2D batchingTexture; - uniform highp usampler2D batchingIdTexture; - mat4 getBatchingMatrix( const in float i ) { - int size = textureSize( batchingTexture, 0 ).x; - int j = int( i ) * 4; - int x = j % size; - int y = j / size; - vec4 v1 = texelFetch( batchingTexture, ivec2( x, y ), 0 ); - vec4 v2 = texelFetch( batchingTexture, ivec2( x + 1, y ), 0 ); - vec4 v3 = texelFetch( batchingTexture, ivec2( x + 2, y ), 0 ); - vec4 v4 = texelFetch( batchingTexture, ivec2( x + 3, y ), 0 ); - return mat4( v1, v2, v3, v4 ); - } - float getIndirectIndex( const in int i ) { - int size = textureSize( batchingIdTexture, 0 ).x; - int x = i % size; - int y = i / size; - return float( texelFetch( batchingIdTexture, ivec2( x, y ), 0 ).r ); - } -#endif -#ifdef USE_BATCHING_COLOR - uniform sampler2D batchingColorTexture; - vec4 getBatchingColor( const in float i ) { - int size = textureSize( batchingColorTexture, 0 ).x; - int j = int( i ); - int x = j % size; - int y = j / size; - return texelFetch( batchingColorTexture, ivec2( x, y ), 0 ); - } -#endif`,rw=`#ifdef USE_BATCHING - mat4 batchingMatrix = getBatchingMatrix( getIndirectIndex( gl_DrawID ) ); -#endif`,lw=`vec3 transformed = vec3( position ); -#ifdef USE_ALPHAHASH - vPosition = vec3( position ); -#endif`,ow=`vec3 objectNormal = vec3( normal ); -#ifdef USE_TANGENT - vec3 objectTangent = vec3( tangent.xyz ); -#endif`,cw=`float G_BlinnPhong_Implicit( ) { - return 0.25; -} -float D_BlinnPhong( const in float shininess, const in float dotNH ) { - return RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess ); -} -vec3 BRDF_BlinnPhong( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float shininess ) { - vec3 halfDir = normalize( lightDir + viewDir ); - float dotNH = saturate( dot( normal, halfDir ) ); - float dotVH = saturate( dot( viewDir, halfDir ) ); - vec3 F = F_Schlick( specularColor, 1.0, dotVH ); - float G = G_BlinnPhong_Implicit( ); - float D = D_BlinnPhong( shininess, dotNH ); - return F * ( G * D ); -} // validated`,uw=`#ifdef USE_IRIDESCENCE - const mat3 XYZ_TO_REC709 = mat3( - 3.2404542, -0.9692660, 0.0556434, - -1.5371385, 1.8760108, -0.2040259, - -0.4985314, 0.0415560, 1.0572252 - ); - vec3 Fresnel0ToIor( vec3 fresnel0 ) { - vec3 sqrtF0 = sqrt( fresnel0 ); - return ( vec3( 1.0 ) + sqrtF0 ) / ( vec3( 1.0 ) - sqrtF0 ); - } - vec3 IorToFresnel0( vec3 transmittedIor, float incidentIor ) { - return pow2( ( transmittedIor - vec3( incidentIor ) ) / ( transmittedIor + vec3( incidentIor ) ) ); - } - float IorToFresnel0( float transmittedIor, float incidentIor ) { - return pow2( ( transmittedIor - incidentIor ) / ( transmittedIor + incidentIor )); - } - vec3 evalSensitivity( float OPD, vec3 shift ) { - float phase = 2.0 * PI * OPD * 1.0e-9; - vec3 val = vec3( 5.4856e-13, 4.4201e-13, 5.2481e-13 ); - vec3 pos = vec3( 1.6810e+06, 1.7953e+06, 2.2084e+06 ); - vec3 var = vec3( 4.3278e+09, 9.3046e+09, 6.6121e+09 ); - vec3 xyz = val * sqrt( 2.0 * PI * var ) * cos( pos * phase + shift ) * exp( - pow2( phase ) * var ); - xyz.x += 9.7470e-14 * sqrt( 2.0 * PI * 4.5282e+09 ) * cos( 2.2399e+06 * phase + shift[ 0 ] ) * exp( - 4.5282e+09 * pow2( phase ) ); - xyz /= 1.0685e-7; - vec3 rgb = XYZ_TO_REC709 * xyz; - return rgb; - } - vec3 evalIridescence( float outsideIOR, float eta2, float cosTheta1, float thinFilmThickness, vec3 baseF0 ) { - vec3 I; - float iridescenceIOR = mix( outsideIOR, eta2, smoothstep( 0.0, 0.03, thinFilmThickness ) ); - float sinTheta2Sq = pow2( outsideIOR / iridescenceIOR ) * ( 1.0 - pow2( cosTheta1 ) ); - float cosTheta2Sq = 1.0 - sinTheta2Sq; - if ( cosTheta2Sq < 0.0 ) { - return vec3( 1.0 ); - } - float cosTheta2 = sqrt( cosTheta2Sq ); - float R0 = IorToFresnel0( iridescenceIOR, outsideIOR ); - float R12 = F_Schlick( R0, 1.0, cosTheta1 ); - float T121 = 1.0 - R12; - float phi12 = 0.0; - if ( iridescenceIOR < outsideIOR ) phi12 = PI; - float phi21 = PI - phi12; - vec3 baseIOR = Fresnel0ToIor( clamp( baseF0, 0.0, 0.9999 ) ); vec3 R1 = IorToFresnel0( baseIOR, iridescenceIOR ); - vec3 R23 = F_Schlick( R1, 1.0, cosTheta2 ); - vec3 phi23 = vec3( 0.0 ); - if ( baseIOR[ 0 ] < iridescenceIOR ) phi23[ 0 ] = PI; - if ( baseIOR[ 1 ] < iridescenceIOR ) phi23[ 1 ] = PI; - if ( baseIOR[ 2 ] < iridescenceIOR ) phi23[ 2 ] = PI; - float OPD = 2.0 * iridescenceIOR * thinFilmThickness * cosTheta2; - vec3 phi = vec3( phi21 ) + phi23; - vec3 R123 = clamp( R12 * R23, 1e-5, 0.9999 ); - vec3 r123 = sqrt( R123 ); - vec3 Rs = pow2( T121 ) * R23 / ( vec3( 1.0 ) - R123 ); - vec3 C0 = R12 + Rs; - I = C0; - vec3 Cm = Rs - T121; - for ( int m = 1; m <= 2; ++ m ) { - Cm *= r123; - vec3 Sm = 2.0 * evalSensitivity( float( m ) * OPD, float( m ) * phi ); - I += Cm * Sm; - } - return max( I, vec3( 0.0 ) ); - } -#endif`,fw=`#ifdef USE_BUMPMAP - uniform sampler2D bumpMap; - uniform float bumpScale; - vec2 dHdxy_fwd() { - vec2 dSTdx = dFdx( vBumpMapUv ); - vec2 dSTdy = dFdy( vBumpMapUv ); - float Hll = bumpScale * texture2D( bumpMap, vBumpMapUv ).x; - float dBx = bumpScale * texture2D( bumpMap, vBumpMapUv + dSTdx ).x - Hll; - float dBy = bumpScale * texture2D( bumpMap, vBumpMapUv + dSTdy ).x - Hll; - return vec2( dBx, dBy ); - } - vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy, float faceDirection ) { - vec3 vSigmaX = normalize( dFdx( surf_pos.xyz ) ); - vec3 vSigmaY = normalize( dFdy( surf_pos.xyz ) ); - vec3 vN = surf_norm; - vec3 R1 = cross( vSigmaY, vN ); - vec3 R2 = cross( vN, vSigmaX ); - float fDet = dot( vSigmaX, R1 ) * faceDirection; - vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 ); - return normalize( abs( fDet ) * surf_norm - vGrad ); - } -#endif`,dw=`#if NUM_CLIPPING_PLANES > 0 - vec4 plane; - #ifdef ALPHA_TO_COVERAGE - float distanceToPlane, distanceGradient; - float clipOpacity = 1.0; - #pragma unroll_loop_start - for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) { - plane = clippingPlanes[ i ]; - distanceToPlane = - dot( vClipPosition, plane.xyz ) + plane.w; - distanceGradient = fwidth( distanceToPlane ) / 2.0; - clipOpacity *= smoothstep( - distanceGradient, distanceGradient, distanceToPlane ); - if ( clipOpacity == 0.0 ) discard; - } - #pragma unroll_loop_end - #if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES - float unionClipOpacity = 1.0; - #pragma unroll_loop_start - for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) { - plane = clippingPlanes[ i ]; - distanceToPlane = - dot( vClipPosition, plane.xyz ) + plane.w; - distanceGradient = fwidth( distanceToPlane ) / 2.0; - unionClipOpacity *= 1.0 - smoothstep( - distanceGradient, distanceGradient, distanceToPlane ); - } - #pragma unroll_loop_end - clipOpacity *= 1.0 - unionClipOpacity; - #endif - diffuseColor.a *= clipOpacity; - if ( diffuseColor.a == 0.0 ) discard; - #else - #pragma unroll_loop_start - for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) { - plane = clippingPlanes[ i ]; - if ( dot( vClipPosition, plane.xyz ) > plane.w ) discard; - } - #pragma unroll_loop_end - #if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES - bool clipped = true; - #pragma unroll_loop_start - for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) { - plane = clippingPlanes[ i ]; - clipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped; - } - #pragma unroll_loop_end - if ( clipped ) discard; - #endif - #endif -#endif`,hw=`#if NUM_CLIPPING_PLANES > 0 - varying vec3 vClipPosition; - uniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ]; -#endif`,pw=`#if NUM_CLIPPING_PLANES > 0 - varying vec3 vClipPosition; -#endif`,mw=`#if NUM_CLIPPING_PLANES > 0 - vClipPosition = - mvPosition.xyz; -#endif`,gw=`#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA ) - diffuseColor *= vColor; -#endif`,xw=`#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA ) - varying vec4 vColor; -#endif`,vw=`#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA ) || defined( USE_INSTANCING_COLOR ) || defined( USE_BATCHING_COLOR ) - varying vec4 vColor; -#endif`,_w=`#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA ) || defined( USE_INSTANCING_COLOR ) || defined( USE_BATCHING_COLOR ) - vColor = vec4( 1.0 ); -#endif -#ifdef USE_COLOR_ALPHA - vColor *= color; -#elif defined( USE_COLOR ) - vColor.rgb *= color; -#endif -#ifdef USE_INSTANCING_COLOR - vColor.rgb *= instanceColor.rgb; -#endif -#ifdef USE_BATCHING_COLOR - vColor *= getBatchingColor( getIndirectIndex( gl_DrawID ) ); -#endif`,yw=`#define PI 3.141592653589793 -#define PI2 6.283185307179586 -#define PI_HALF 1.5707963267948966 -#define RECIPROCAL_PI 0.3183098861837907 -#define RECIPROCAL_PI2 0.15915494309189535 -#define EPSILON 1e-6 -#ifndef saturate -#define saturate( a ) clamp( a, 0.0, 1.0 ) -#endif -#define whiteComplement( a ) ( 1.0 - saturate( a ) ) -float pow2( const in float x ) { return x*x; } -vec3 pow2( const in vec3 x ) { return x*x; } -float pow3( const in float x ) { return x*x*x; } -float pow4( const in float x ) { float x2 = x*x; return x2*x2; } -float max3( const in vec3 v ) { return max( max( v.x, v.y ), v.z ); } -float average( const in vec3 v ) { return dot( v, vec3( 0.3333333 ) ); } -highp float rand( const in vec2 uv ) { - const highp float a = 12.9898, b = 78.233, c = 43758.5453; - highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI ); - return fract( sin( sn ) * c ); -} -#ifdef HIGH_PRECISION - float precisionSafeLength( vec3 v ) { return length( v ); } -#else - float precisionSafeLength( vec3 v ) { - float maxComponent = max3( abs( v ) ); - return length( v / maxComponent ) * maxComponent; - } -#endif -struct IncidentLight { - vec3 color; - vec3 direction; - bool visible; -}; -struct ReflectedLight { - vec3 directDiffuse; - vec3 directSpecular; - vec3 indirectDiffuse; - vec3 indirectSpecular; -}; -#ifdef USE_ALPHAHASH - varying vec3 vPosition; -#endif -vec3 transformDirection( in vec3 dir, in mat4 matrix ) { - return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); -} -#define inverseTransformDirection transformDirectionByInverseViewMatrix -vec3 transformNormalByInverseViewMatrix( in vec3 normal, in mat4 viewMatrix ) { - return normalize( ( vec4( normal, 0.0 ) * viewMatrix ).xyz ); -} -vec3 transformDirectionByInverseViewMatrix( in vec3 dir, in mat4 viewMatrix ) { - return normalize( ( vec4( dir, 0.0 ) * viewMatrix ).xyz ); -} -bool isPerspectiveMatrix( mat4 m ) { - return m[ 2 ][ 3 ] == - 1.0; -} -vec2 equirectUv( in vec3 dir ) { - float u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5; - float v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5; - return vec2( u, v ); -} -vec3 BRDF_Lambert( const in vec3 diffuseColor ) { - return RECIPROCAL_PI * diffuseColor; -} -vec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) { - float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH ); - return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel ); -} -float F_Schlick( const in float f0, const in float f90, const in float dotVH ) { - float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH ); - return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel ); -} // validated`,bw=`#ifdef ENVMAP_TYPE_CUBE_UV - #define cubeUV_minMipLevel 4.0 - #define cubeUV_minTileSize 16.0 - float getFace( vec3 direction ) { - vec3 absDirection = abs( direction ); - float face = - 1.0; - if ( absDirection.x > absDirection.z ) { - if ( absDirection.x > absDirection.y ) - face = direction.x > 0.0 ? 0.0 : 3.0; - else - face = direction.y > 0.0 ? 1.0 : 4.0; - } else { - if ( absDirection.z > absDirection.y ) - face = direction.z > 0.0 ? 2.0 : 5.0; - else - face = direction.y > 0.0 ? 1.0 : 4.0; - } - return face; - } - vec2 getUV( vec3 direction, float face ) { - vec2 uv; - if ( face == 0.0 ) { - uv = vec2( direction.z, direction.y ) / abs( direction.x ); - } else if ( face == 1.0 ) { - uv = vec2( - direction.x, - direction.z ) / abs( direction.y ); - } else if ( face == 2.0 ) { - uv = vec2( - direction.x, direction.y ) / abs( direction.z ); - } else if ( face == 3.0 ) { - uv = vec2( - direction.z, direction.y ) / abs( direction.x ); - } else if ( face == 4.0 ) { - uv = vec2( - direction.x, direction.z ) / abs( direction.y ); - } else { - uv = vec2( direction.x, direction.y ) / abs( direction.z ); - } - return 0.5 * ( uv + 1.0 ); - } - vec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) { - float face = getFace( direction ); - float filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 ); - mipInt = max( mipInt, cubeUV_minMipLevel ); - float faceSize = exp2( mipInt ); - highp vec2 uv = getUV( direction, face ) * ( faceSize - 2.0 ) + 1.0; - if ( face > 2.0 ) { - uv.y += faceSize; - face -= 3.0; - } - uv.x += face * faceSize; - uv.x += filterInt * 3.0 * cubeUV_minTileSize; - uv.y += 4.0 * ( exp2( CUBEUV_MAX_MIP ) - faceSize ); - uv.x *= CUBEUV_TEXEL_WIDTH; - uv.y *= CUBEUV_TEXEL_HEIGHT; - #ifdef texture2DGradEXT - return texture2DGradEXT( envMap, uv, vec2( 0.0 ), vec2( 0.0 ) ).rgb; - #else - return texture2D( envMap, uv ).rgb; - #endif - } - #define cubeUV_r0 1.0 - #define cubeUV_m0 - 2.0 - #define cubeUV_r1 0.8 - #define cubeUV_m1 - 1.0 - #define cubeUV_r4 0.4 - #define cubeUV_m4 2.0 - #define cubeUV_r5 0.305 - #define cubeUV_m5 3.0 - #define cubeUV_r6 0.21 - #define cubeUV_m6 4.0 - float roughnessToMip( float roughness ) { - float mip = 0.0; - if ( roughness >= cubeUV_r1 ) { - mip = ( cubeUV_r0 - roughness ) * ( cubeUV_m1 - cubeUV_m0 ) / ( cubeUV_r0 - cubeUV_r1 ) + cubeUV_m0; - } else if ( roughness >= cubeUV_r4 ) { - mip = ( cubeUV_r1 - roughness ) * ( cubeUV_m4 - cubeUV_m1 ) / ( cubeUV_r1 - cubeUV_r4 ) + cubeUV_m1; - } else if ( roughness >= cubeUV_r5 ) { - mip = ( cubeUV_r4 - roughness ) * ( cubeUV_m5 - cubeUV_m4 ) / ( cubeUV_r4 - cubeUV_r5 ) + cubeUV_m4; - } else if ( roughness >= cubeUV_r6 ) { - mip = ( cubeUV_r5 - roughness ) * ( cubeUV_m6 - cubeUV_m5 ) / ( cubeUV_r5 - cubeUV_r6 ) + cubeUV_m5; - } else { - mip = - 2.0 * log2( 1.16 * roughness ); } - return mip; - } - vec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) { - float mip = clamp( roughnessToMip( roughness ), cubeUV_m0, CUBEUV_MAX_MIP ); - float mipF = fract( mip ); - float mipInt = floor( mip ); - vec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt ); - if ( mipF == 0.0 ) { - return vec4( color0, 1.0 ); - } else { - vec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 ); - return vec4( mix( color0, color1, mipF ), 1.0 ); - } - } -#endif`,Sw=`vec3 transformedNormal = objectNormal; -#ifdef USE_TANGENT - vec3 transformedTangent = objectTangent; -#endif -#ifdef USE_BATCHING - mat3 bm = mat3( batchingMatrix ); - transformedNormal /= vec3( dot( bm[ 0 ], bm[ 0 ] ), dot( bm[ 1 ], bm[ 1 ] ), dot( bm[ 2 ], bm[ 2 ] ) ); - transformedNormal = bm * transformedNormal; - #ifdef USE_TANGENT - transformedTangent = bm * transformedTangent; - #endif -#endif -#ifdef USE_INSTANCING - mat3 im = mat3( instanceMatrix ); - transformedNormal /= vec3( dot( im[ 0 ], im[ 0 ] ), dot( im[ 1 ], im[ 1 ] ), dot( im[ 2 ], im[ 2 ] ) ); - transformedNormal = im * transformedNormal; - #ifdef USE_TANGENT - transformedTangent = im * transformedTangent; - #endif -#endif -transformedNormal = normalMatrix * transformedNormal; -#ifdef FLIP_SIDED - transformedNormal = - transformedNormal; -#endif -#ifdef USE_TANGENT - transformedTangent = ( modelViewMatrix * vec4( transformedTangent, 0.0 ) ).xyz; -#endif`,Mw=`#ifdef USE_DISPLACEMENTMAP - uniform sampler2D displacementMap; - uniform float displacementScale; - uniform float displacementBias; -#endif`,Ew=`#ifdef USE_DISPLACEMENTMAP - transformed += normalize( objectNormal ) * ( texture2D( displacementMap, vDisplacementMapUv ).x * displacementScale + displacementBias ); -#endif`,Tw=`#ifdef USE_EMISSIVEMAP - vec4 emissiveColor = texture2D( emissiveMap, vEmissiveMapUv ); - #ifdef DECODE_VIDEO_TEXTURE_EMISSIVE - emissiveColor = sRGBTransferEOTF( emissiveColor ); - #endif - totalEmissiveRadiance *= emissiveColor.rgb; -#endif`,Aw=`#ifdef USE_EMISSIVEMAP - uniform sampler2D emissiveMap; -#endif`,ww="gl_FragColor = linearToOutputTexel( gl_FragColor );",Cw=`vec4 LinearTransferOETF( in vec4 value ) { - return value; -} -vec4 sRGBTransferEOTF( in vec4 value ) { - return vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.a ); -} -vec4 sRGBTransferOETF( in vec4 value ) { - return vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a ); -}`,Nw=`#ifdef USE_ENVMAP - #ifdef ENV_WORLDPOS - vec3 cameraToFrag; - if ( isOrthographic ) { - cameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); - } else { - cameraToFrag = normalize( vWorldPosition - cameraPosition ); - } - vec3 worldNormal = transformNormalByInverseViewMatrix( normal, viewMatrix ); - #ifdef ENVMAP_MODE_REFLECTION - vec3 reflectVec = reflect( cameraToFrag, worldNormal ); - #else - vec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio ); - #endif - #else - vec3 reflectVec = vReflect; - #endif - #ifdef ENVMAP_TYPE_CUBE - vec4 envColor = textureCube( envMap, envMapRotation * reflectVec ); - #ifdef ENVMAP_BLENDING_MULTIPLY - outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity ); - #elif defined( ENVMAP_BLENDING_MIX ) - outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity ); - #elif defined( ENVMAP_BLENDING_ADD ) - outgoingLight += envColor.xyz * specularStrength * reflectivity; - #endif - #endif -#endif`,Rw=`#ifdef USE_ENVMAP - uniform float envMapIntensity; - uniform mat3 envMapRotation; - #ifdef ENVMAP_TYPE_CUBE - uniform samplerCube envMap; - #else - uniform sampler2D envMap; - #endif -#endif`,Dw=`#ifdef USE_ENVMAP - uniform float reflectivity; - #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( LAMBERT ) - #define ENV_WORLDPOS - #endif - #ifdef ENV_WORLDPOS - varying vec3 vWorldPosition; - uniform float refractionRatio; - #else - varying vec3 vReflect; - #endif -#endif`,Lw=`#ifdef USE_ENVMAP - #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( LAMBERT ) - #define ENV_WORLDPOS - #endif - #ifdef ENV_WORLDPOS - - varying vec3 vWorldPosition; - #else - varying vec3 vReflect; - uniform float refractionRatio; - #endif -#endif`,Uw=`#ifdef USE_ENVMAP - #ifdef ENV_WORLDPOS - vWorldPosition = worldPosition.xyz; - #else - vec3 cameraToVertex; - if ( isOrthographic ) { - cameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); - } else { - cameraToVertex = normalize( worldPosition.xyz - cameraPosition ); - } - vec3 worldNormal = transformNormalByInverseViewMatrix( transformedNormal, viewMatrix ); - #ifdef ENVMAP_MODE_REFLECTION - vReflect = reflect( cameraToVertex, worldNormal ); - #else - vReflect = refract( cameraToVertex, worldNormal, refractionRatio ); - #endif - #endif -#endif`,Ow=`#ifdef USE_FOG - vFogDepth = - mvPosition.z; -#endif`,Pw=`#ifdef USE_FOG - varying float vFogDepth; -#endif`,zw=`#ifdef USE_FOG - #ifdef FOG_EXP2 - float fogFactor = 1.0 - exp( - fogDensity * fogDensity * vFogDepth * vFogDepth ); - #else - float fogFactor = smoothstep( fogNear, fogFar, vFogDepth ); - #endif - gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor ); -#endif`,Iw=`#ifdef USE_FOG - uniform vec3 fogColor; - varying float vFogDepth; - #ifdef FOG_EXP2 - uniform float fogDensity; - #else - uniform float fogNear; - uniform float fogFar; - #endif -#endif`,kw=`#ifdef USE_GRADIENTMAP - uniform sampler2D gradientMap; -#endif -vec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) { - float dotNL = dot( normal, lightDirection ); - vec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 ); - #ifdef USE_GRADIENTMAP - return vec3( texture2D( gradientMap, coord ).r ); - #else - vec2 fw = fwidth( coord ) * 0.5; - return mix( vec3( 0.7 ), vec3( 1.0 ), smoothstep( 0.7 - fw.x, 0.7 + fw.x, coord.x ) ); - #endif -}`,Bw=`#ifdef USE_LIGHTMAP - uniform sampler2D lightMap; - uniform float lightMapIntensity; -#endif`,Fw=`LambertMaterial material; -material.diffuseColor = diffuseColor.rgb; -material.specularStrength = specularStrength;`,jw=`varying vec3 vViewPosition; -struct LambertMaterial { - vec3 diffuseColor; - float specularStrength; -}; -void RE_Direct_Lambert( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in LambertMaterial material, inout ReflectedLight reflectedLight ) { - float dotNL = saturate( dot( geometryNormal, directLight.direction ) ); - vec3 irradiance = dotNL * directLight.color; - reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); -} -void RE_IndirectDiffuse_Lambert( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in LambertMaterial material, inout ReflectedLight reflectedLight ) { - reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); -} -#define RE_Direct RE_Direct_Lambert -#define RE_IndirectDiffuse RE_IndirectDiffuse_Lambert`,Hw=`uniform bool receiveShadow; -uniform vec3 ambientLightColor; -#if defined( USE_LIGHT_PROBES ) - uniform vec3 lightProbe[ 9 ]; -#endif -vec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) { - float x = normal.x, y = normal.y, z = normal.z; - vec3 result = shCoefficients[ 0 ] * 0.886227; - result += shCoefficients[ 1 ] * 2.0 * 0.511664 * y; - result += shCoefficients[ 2 ] * 2.0 * 0.511664 * z; - result += shCoefficients[ 3 ] * 2.0 * 0.511664 * x; - result += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y; - result += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z; - result += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 ); - result += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z; - result += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y ); - return result; -} -vec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in vec3 normal ) { - vec3 worldNormal = transformNormalByInverseViewMatrix( normal, viewMatrix ); - vec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe ); - return irradiance; -} -vec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) { - vec3 irradiance = ambientLightColor; - return irradiance; -} -float getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) { - float distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 ); - if ( cutoffDistance > 0.0 ) { - distanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) ); - } - return distanceFalloff; -} -float getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) { - return smoothstep( coneCosine, penumbraCosine, angleCosine ); -} -#if NUM_DIR_LIGHTS > 0 - struct DirectionalLight { - vec3 direction; - vec3 color; - }; - uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ]; - void getDirectionalLightInfo( const in DirectionalLight directionalLight, out IncidentLight light ) { - light.color = directionalLight.color; - light.direction = directionalLight.direction; - light.visible = true; - } -#endif -#if NUM_POINT_LIGHTS > 0 - struct PointLight { - vec3 position; - vec3 color; - float distance; - float decay; - }; - uniform PointLight pointLights[ NUM_POINT_LIGHTS ]; - void getPointLightInfo( const in PointLight pointLight, const in vec3 geometryPosition, out IncidentLight light ) { - vec3 lVector = pointLight.position - geometryPosition; - light.direction = normalize( lVector ); - float lightDistance = length( lVector ); - light.color = pointLight.color; - light.color *= getDistanceAttenuation( lightDistance, pointLight.distance, pointLight.decay ); - light.visible = ( light.color != vec3( 0.0 ) ); - } -#endif -#if NUM_SPOT_LIGHTS > 0 - struct SpotLight { - vec3 position; - vec3 direction; - vec3 color; - float distance; - float decay; - float coneCos; - float penumbraCos; - }; - uniform SpotLight spotLights[ NUM_SPOT_LIGHTS ]; - void getSpotLightInfo( const in SpotLight spotLight, const in vec3 geometryPosition, out IncidentLight light ) { - vec3 lVector = spotLight.position - geometryPosition; - light.direction = normalize( lVector ); - float angleCos = dot( light.direction, spotLight.direction ); - float spotAttenuation = getSpotAttenuation( spotLight.coneCos, spotLight.penumbraCos, angleCos ); - if ( spotAttenuation > 0.0 ) { - float lightDistance = length( lVector ); - light.color = spotLight.color * spotAttenuation; - light.color *= getDistanceAttenuation( lightDistance, spotLight.distance, spotLight.decay ); - light.visible = ( light.color != vec3( 0.0 ) ); - } else { - light.color = vec3( 0.0 ); - light.visible = false; - } - } -#endif -#if NUM_RECT_AREA_LIGHTS > 0 - struct RectAreaLight { - vec3 color; - vec3 position; - vec3 halfWidth; - vec3 halfHeight; - }; - uniform sampler2D ltc_1; uniform sampler2D ltc_2; - uniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ]; -#endif -#if NUM_HEMI_LIGHTS > 0 - struct HemisphereLight { - vec3 direction; - vec3 skyColor; - vec3 groundColor; - }; - uniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ]; - vec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in vec3 normal ) { - float dotNL = dot( normal, hemiLight.direction ); - float hemiDiffuseWeight = 0.5 * dotNL + 0.5; - vec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight ); - return irradiance; - } -#endif -#include <lightprobes_pars_fragment>`,Gw=`#ifdef USE_ENVMAP - vec3 getIBLIrradiance( const in vec3 normal ) { - #ifdef ENVMAP_TYPE_CUBE_UV - vec3 worldNormal = transformNormalByInverseViewMatrix( normal, viewMatrix ); - vec4 envMapColor = textureCubeUV( envMap, envMapRotation * worldNormal, 1.0 ); - return PI * envMapColor.rgb * envMapIntensity; - #else - return vec3( 0.0 ); - #endif - } - vec3 getIBLRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness ) { - #ifdef ENVMAP_TYPE_CUBE_UV - vec3 reflectVec = reflect( - viewDir, normal ); - reflectVec = normalize( mix( reflectVec, normal, pow4( roughness ) ) ); - reflectVec = transformDirectionByInverseViewMatrix( reflectVec, viewMatrix ); - vec4 envMapColor = textureCubeUV( envMap, envMapRotation * reflectVec, roughness ); - return envMapColor.rgb * envMapIntensity; - #else - return vec3( 0.0 ); - #endif - } - #ifdef USE_ANISOTROPY - vec3 getIBLAnisotropyRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness, const in vec3 bitangent, const in float anisotropy ) { - #ifdef ENVMAP_TYPE_CUBE_UV - vec3 bentNormal = cross( bitangent, viewDir ); - bentNormal = normalize( cross( bentNormal, bitangent ) ); - bentNormal = normalize( mix( bentNormal, normal, pow2( pow2( 1.0 - anisotropy * ( 1.0 - roughness ) ) ) ) ); - return getIBLRadiance( viewDir, bentNormal, roughness ); - #else - return vec3( 0.0 ); - #endif - } - #endif -#endif`,Vw=`ToonMaterial material; -material.diffuseColor = diffuseColor.rgb;`,Ww=`varying vec3 vViewPosition; -struct ToonMaterial { - vec3 diffuseColor; -}; -void RE_Direct_Toon( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { - vec3 irradiance = getGradientIrradiance( geometryNormal, directLight.direction ) * directLight.color; - reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); -} -void RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { - reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); -} -#define RE_Direct RE_Direct_Toon -#define RE_IndirectDiffuse RE_IndirectDiffuse_Toon`,Xw=`BlinnPhongMaterial material; -material.diffuseColor = diffuseColor.rgb; -material.specularColor = specular; -material.specularShininess = shininess; -material.specularStrength = specularStrength;`,qw=`varying vec3 vViewPosition; -struct BlinnPhongMaterial { - vec3 diffuseColor; - vec3 specularColor; - float specularShininess; - float specularStrength; -}; -void RE_Direct_BlinnPhong( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { - float dotNL = saturate( dot( geometryNormal, directLight.direction ) ); - vec3 irradiance = dotNL * directLight.color; - reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); - reflectedLight.directSpecular += irradiance * BRDF_BlinnPhong( directLight.direction, geometryViewDir, geometryNormal, material.specularColor, material.specularShininess ) * material.specularStrength; -} -void RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { - reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); -} -#define RE_Direct RE_Direct_BlinnPhong -#define RE_IndirectDiffuse RE_IndirectDiffuse_BlinnPhong`,Yw=`PhysicalMaterial material; -material.diffuseColor = diffuseColor.rgb; -material.diffuseContribution = diffuseColor.rgb * ( 1.0 - metalnessFactor ); -material.metalness = metalnessFactor; -vec3 dxy = max( abs( dFdx( nonPerturbedNormal ) ), abs( dFdy( nonPerturbedNormal ) ) ); -float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z ); -material.roughness = max( roughnessFactor, 0.0525 );material.roughness += geometryRoughness; -material.roughness = min( material.roughness, 1.0 ); -#ifdef IOR - material.ior = ior; - #ifdef USE_SPECULAR - float specularIntensityFactor = specularIntensity; - vec3 specularColorFactor = specularColor; - #ifdef USE_SPECULAR_COLORMAP - specularColorFactor *= texture2D( specularColorMap, vSpecularColorMapUv ).rgb; - #endif - #ifdef USE_SPECULAR_INTENSITYMAP - specularIntensityFactor *= texture2D( specularIntensityMap, vSpecularIntensityMapUv ).a; - #endif - material.specularF90 = mix( specularIntensityFactor, 1.0, metalnessFactor ); - #else - float specularIntensityFactor = 1.0; - vec3 specularColorFactor = vec3( 1.0 ); - material.specularF90 = 1.0; - #endif - material.specularColor = min( pow2( ( material.ior - 1.0 ) / ( material.ior + 1.0 ) ) * specularColorFactor, vec3( 1.0 ) ) * specularIntensityFactor; - material.specularColorBlended = mix( material.specularColor, diffuseColor.rgb, metalnessFactor ); -#else - material.specularColor = vec3( 0.04 ); - material.specularColorBlended = mix( material.specularColor, diffuseColor.rgb, metalnessFactor ); - material.specularF90 = 1.0; -#endif -#ifdef USE_CLEARCOAT - material.clearcoat = clearcoat; - material.clearcoatRoughness = clearcoatRoughness; - material.clearcoatF0 = vec3( 0.04 ); - material.clearcoatF90 = 1.0; - #ifdef USE_CLEARCOATMAP - material.clearcoat *= texture2D( clearcoatMap, vClearcoatMapUv ).x; - #endif - #ifdef USE_CLEARCOAT_ROUGHNESSMAP - material.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vClearcoatRoughnessMapUv ).y; - #endif - material.clearcoat = saturate( material.clearcoat ); material.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 ); - material.clearcoatRoughness += geometryRoughness; - material.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 ); -#endif -#ifdef USE_DISPERSION - material.dispersion = dispersion; -#endif -#ifdef USE_IRIDESCENCE - material.iridescence = iridescence; - material.iridescenceIOR = iridescenceIOR; - #ifdef USE_IRIDESCENCEMAP - material.iridescence *= texture2D( iridescenceMap, vIridescenceMapUv ).r; - #endif - #ifdef USE_IRIDESCENCE_THICKNESSMAP - material.iridescenceThickness = (iridescenceThicknessMaximum - iridescenceThicknessMinimum) * texture2D( iridescenceThicknessMap, vIridescenceThicknessMapUv ).g + iridescenceThicknessMinimum; - #else - material.iridescenceThickness = iridescenceThicknessMaximum; - #endif -#endif -#ifdef USE_SHEEN - material.sheenColor = sheenColor; - #ifdef USE_SHEEN_COLORMAP - material.sheenColor *= texture2D( sheenColorMap, vSheenColorMapUv ).rgb; - #endif - material.sheenRoughness = clamp( sheenRoughness, 0.0001, 1.0 ); - #ifdef USE_SHEEN_ROUGHNESSMAP - material.sheenRoughness *= texture2D( sheenRoughnessMap, vSheenRoughnessMapUv ).a; - #endif -#endif -#ifdef USE_ANISOTROPY - #ifdef USE_ANISOTROPYMAP - mat2 anisotropyMat = mat2( anisotropyVector.x, anisotropyVector.y, - anisotropyVector.y, anisotropyVector.x ); - vec3 anisotropyPolar = texture2D( anisotropyMap, vAnisotropyMapUv ).rgb; - vec2 anisotropyV = anisotropyMat * normalize( 2.0 * anisotropyPolar.rg - vec2( 1.0 ) ) * anisotropyPolar.b; - #else - vec2 anisotropyV = anisotropyVector; - #endif - material.anisotropy = length( anisotropyV ); - if( material.anisotropy == 0.0 ) { - anisotropyV = vec2( 1.0, 0.0 ); - } else { - anisotropyV /= material.anisotropy; - material.anisotropy = saturate( material.anisotropy ); - } - material.alphaT = mix( pow2( material.roughness ), 1.0, pow2( material.anisotropy ) ); - material.anisotropyT = tbn[ 0 ] * anisotropyV.x + tbn[ 1 ] * anisotropyV.y; - material.anisotropyB = tbn[ 1 ] * anisotropyV.x - tbn[ 0 ] * anisotropyV.y; -#endif`,Kw=`uniform sampler2D dfgLUT; -struct PhysicalMaterial { - vec3 diffuseColor; - vec3 diffuseContribution; - vec3 specularColor; - vec3 specularColorBlended; - float roughness; - float metalness; - float specularF90; - float dispersion; - #ifdef USE_CLEARCOAT - float clearcoat; - float clearcoatRoughness; - vec3 clearcoatF0; - float clearcoatF90; - #endif - #ifdef USE_IRIDESCENCE - float iridescence; - float iridescenceIOR; - float iridescenceThickness; - vec3 iridescenceFresnel; - vec3 iridescenceF0; - vec3 iridescenceFresnelDielectric; - vec3 iridescenceFresnelMetallic; - #endif - #ifdef USE_SHEEN - vec3 sheenColor; - float sheenRoughness; - #endif - #ifdef IOR - float ior; - #endif - #ifdef USE_TRANSMISSION - float transmission; - float transmissionAlpha; - float thickness; - float attenuationDistance; - vec3 attenuationColor; - #endif - #ifdef USE_ANISOTROPY - float anisotropy; - float alphaT; - vec3 anisotropyT; - vec3 anisotropyB; - #endif -}; -vec3 clearcoatSpecularDirect = vec3( 0.0 ); -vec3 clearcoatSpecularIndirect = vec3( 0.0 ); -vec3 sheenSpecularDirect = vec3( 0.0 ); -vec3 sheenSpecularIndirect = vec3(0.0 ); -vec3 Schlick_to_F0( const in vec3 f, const in float f90, const in float dotVH ) { - float x = clamp( 1.0 - dotVH, 0.0, 1.0 ); - float x2 = x * x; - float x5 = clamp( x * x2 * x2, 0.0, 0.9999 ); - return ( f - vec3( f90 ) * x5 ) / ( 1.0 - x5 ); -} -float V_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) { - float a2 = pow2( alpha ); - float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) ); - float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) ); - return 0.5 / max( gv + gl, EPSILON ); -} -float D_GGX( const in float alpha, const in float dotNH ) { - float a2 = pow2( alpha ); - float denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0; - return RECIPROCAL_PI * a2 / pow2( denom ); -} -#ifdef USE_ANISOTROPY - float V_GGX_SmithCorrelated_Anisotropic( const in float alphaT, const in float alphaB, const in float dotTV, const in float dotBV, const in float dotTL, const in float dotBL, const in float dotNV, const in float dotNL ) { - float gv = dotNL * length( vec3( alphaT * dotTV, alphaB * dotBV, dotNV ) ); - float gl = dotNV * length( vec3( alphaT * dotTL, alphaB * dotBL, dotNL ) ); - return 0.5 / max( gv + gl, EPSILON ); - } - float D_GGX_Anisotropic( const in float alphaT, const in float alphaB, const in float dotNH, const in float dotTH, const in float dotBH ) { - float a2 = alphaT * alphaB; - highp vec3 v = vec3( alphaB * dotTH, alphaT * dotBH, a2 * dotNH ); - highp float v2 = dot( v, v ); - float w2 = a2 / v2; - return RECIPROCAL_PI * a2 * pow2 ( w2 ); - } -#endif -#ifdef USE_CLEARCOAT - vec3 BRDF_GGX_Clearcoat( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material) { - vec3 f0 = material.clearcoatF0; - float f90 = material.clearcoatF90; - float roughness = material.clearcoatRoughness; - float alpha = pow2( roughness ); - vec3 halfDir = normalize( lightDir + viewDir ); - float dotNL = saturate( dot( normal, lightDir ) ); - float dotNV = saturate( dot( normal, viewDir ) ); - float dotNH = saturate( dot( normal, halfDir ) ); - float dotVH = saturate( dot( viewDir, halfDir ) ); - vec3 F = F_Schlick( f0, f90, dotVH ); - float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV ); - float D = D_GGX( alpha, dotNH ); - return F * ( V * D ); - } -#endif -vec3 BRDF_GGX( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material ) { - vec3 f0 = material.specularColorBlended; - float f90 = material.specularF90; - float roughness = material.roughness; - float alpha = pow2( roughness ); - vec3 halfDir = normalize( lightDir + viewDir ); - float dotNL = saturate( dot( normal, lightDir ) ); - float dotNV = saturate( dot( normal, viewDir ) ); - float dotNH = saturate( dot( normal, halfDir ) ); - float dotVH = saturate( dot( viewDir, halfDir ) ); - vec3 F = F_Schlick( f0, f90, dotVH ); - #ifdef USE_IRIDESCENCE - F = mix( F, material.iridescenceFresnel, material.iridescence ); - #endif - #ifdef USE_ANISOTROPY - float dotTL = dot( material.anisotropyT, lightDir ); - float dotTV = dot( material.anisotropyT, viewDir ); - float dotTH = dot( material.anisotropyT, halfDir ); - float dotBL = dot( material.anisotropyB, lightDir ); - float dotBV = dot( material.anisotropyB, viewDir ); - float dotBH = dot( material.anisotropyB, halfDir ); - float V = V_GGX_SmithCorrelated_Anisotropic( material.alphaT, alpha, dotTV, dotBV, dotTL, dotBL, dotNV, dotNL ); - float D = D_GGX_Anisotropic( material.alphaT, alpha, dotNH, dotTH, dotBH ); - #else - float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV ); - float D = D_GGX( alpha, dotNH ); - #endif - return F * ( V * D ); -} -vec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) { - const float LUT_SIZE = 64.0; - const float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE; - const float LUT_BIAS = 0.5 / LUT_SIZE; - float dotNV = saturate( dot( N, V ) ); - vec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) ); - uv = uv * LUT_SCALE + LUT_BIAS; - return uv; -} -float LTC_ClippedSphereFormFactor( const in vec3 f ) { - float l = length( f ); - return max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 ); -} -vec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) { - float x = dot( v1, v2 ); - float y = abs( x ); - float a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y; - float b = 3.4175940 + ( 4.1616724 + y ) * y; - float v = a / b; - float theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v; - return cross( v1, v2 ) * theta_sintheta; -} -vec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) { - vec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ]; - vec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ]; - vec3 lightNormal = cross( v1, v2 ); - if( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 ); - vec3 T1, T2; - T1 = normalize( V - N * dot( V, N ) ); - T2 = - cross( N, T1 ); - mat3 mat = mInv * transpose( mat3( T1, T2, N ) ); - vec3 coords[ 4 ]; - coords[ 0 ] = mat * ( rectCoords[ 0 ] - P ); - coords[ 1 ] = mat * ( rectCoords[ 1 ] - P ); - coords[ 2 ] = mat * ( rectCoords[ 2 ] - P ); - coords[ 3 ] = mat * ( rectCoords[ 3 ] - P ); - coords[ 0 ] = normalize( coords[ 0 ] ); - coords[ 1 ] = normalize( coords[ 1 ] ); - coords[ 2 ] = normalize( coords[ 2 ] ); - coords[ 3 ] = normalize( coords[ 3 ] ); - vec3 vectorFormFactor = vec3( 0.0 ); - vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] ); - vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] ); - vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] ); - vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] ); - float result = LTC_ClippedSphereFormFactor( vectorFormFactor ); - return vec3( result ); -} -#if defined( USE_SHEEN ) -float D_Charlie( float roughness, float dotNH ) { - float alpha = pow2( roughness ); - float invAlpha = 1.0 / alpha; - float cos2h = dotNH * dotNH; - float sin2h = max( 1.0 - cos2h, 0.0078125 ); - return ( 2.0 + invAlpha ) * pow( sin2h, invAlpha * 0.5 ) / ( 2.0 * PI ); -} -float V_Neubelt( float dotNV, float dotNL ) { - return saturate( 1.0 / ( 4.0 * ( dotNL + dotNV - dotNL * dotNV ) ) ); -} -vec3 BRDF_Sheen( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, vec3 sheenColor, const in float sheenRoughness ) { - vec3 halfDir = normalize( lightDir + viewDir ); - float dotNL = saturate( dot( normal, lightDir ) ); - float dotNV = saturate( dot( normal, viewDir ) ); - float dotNH = saturate( dot( normal, halfDir ) ); - float D = D_Charlie( sheenRoughness, dotNH ); - float V = V_Neubelt( dotNV, dotNL ); - return sheenColor * ( D * V ); -} -#endif -float IBLSheenBRDF( const in vec3 normal, const in vec3 viewDir, const in float roughness ) { - float dotNV = saturate( dot( normal, viewDir ) ); - float r2 = roughness * roughness; - float rInv = 1.0 / ( roughness + 0.1 ); - float a = -1.9362 + 1.0678 * roughness + 0.4573 * r2 - 0.8469 * rInv; - float b = -0.6014 + 0.5538 * roughness - 0.4670 * r2 - 0.1255 * rInv; - float DG = exp( a * dotNV + b ); - return saturate( DG ); -} -vec3 EnvironmentBRDF( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness ) { - float dotNV = saturate( dot( normal, viewDir ) ); - vec2 fab = texture2D( dfgLUT, vec2( roughness, dotNV ) ).rg; - return specularColor * fab.x + specularF90 * fab.y; -} -#ifdef USE_IRIDESCENCE -void computeMultiscatteringIridescence( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float iridescence, const in vec3 iridescenceF0, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) { -#else -void computeMultiscattering( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) { -#endif - float dotNV = saturate( dot( normal, viewDir ) ); - vec2 fab = texture2D( dfgLUT, vec2( roughness, dotNV ) ).rg; - #ifdef USE_IRIDESCENCE - vec3 Fr = mix( specularColor, iridescenceF0, iridescence ); - #else - vec3 Fr = specularColor; - #endif - vec3 FssEss = Fr * fab.x + specularF90 * fab.y; - float Ess = fab.x + fab.y; - float Ems = 1.0 - Ess; - vec3 Favg = Fr + ( 1.0 - Fr ) * 0.047619; vec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg ); - singleScatter += FssEss; - multiScatter += Fms * Ems; -} -vec3 BRDF_GGX_Multiscatter( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material ) { - vec3 singleScatter = BRDF_GGX( lightDir, viewDir, normal, material ); - float dotNL = saturate( dot( normal, lightDir ) ); - float dotNV = saturate( dot( normal, viewDir ) ); - vec2 dfgV = texture2D( dfgLUT, vec2( material.roughness, dotNV ) ).rg; - vec2 dfgL = texture2D( dfgLUT, vec2( material.roughness, dotNL ) ).rg; - vec3 FssEss_V = material.specularColorBlended * dfgV.x + material.specularF90 * dfgV.y; - vec3 FssEss_L = material.specularColorBlended * dfgL.x + material.specularF90 * dfgL.y; - float Ess_V = dfgV.x + dfgV.y; - float Ess_L = dfgL.x + dfgL.y; - float Ems_V = 1.0 - Ess_V; - float Ems_L = 1.0 - Ess_L; - vec3 Favg = material.specularColorBlended + ( 1.0 - material.specularColorBlended ) * 0.047619; - vec3 Fms = FssEss_V * FssEss_L * Favg / ( 1.0 - Ems_V * Ems_L * Favg + EPSILON ); - float compensationFactor = Ems_V * Ems_L; - vec3 multiScatter = Fms * compensationFactor; - return singleScatter + multiScatter; -} -#if NUM_RECT_AREA_LIGHTS > 0 - void RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { - vec3 normal = geometryNormal; - vec3 viewDir = geometryViewDir; - vec3 position = geometryPosition; - vec3 lightPos = rectAreaLight.position; - vec3 halfWidth = rectAreaLight.halfWidth; - vec3 halfHeight = rectAreaLight.halfHeight; - vec3 lightColor = rectAreaLight.color; - float roughness = material.roughness; - vec3 rectCoords[ 4 ]; - rectCoords[ 0 ] = lightPos + halfWidth - halfHeight; rectCoords[ 1 ] = lightPos - halfWidth - halfHeight; - rectCoords[ 2 ] = lightPos - halfWidth + halfHeight; - rectCoords[ 3 ] = lightPos + halfWidth + halfHeight; - vec2 uv = LTC_Uv( normal, viewDir, roughness ); - vec4 t1 = texture2D( ltc_1, uv ); - vec4 t2 = texture2D( ltc_2, uv ); - mat3 mInv = mat3( - vec3( t1.x, 0, t1.y ), - vec3( 0, 1, 0 ), - vec3( t1.z, 0, t1.w ) - ); - vec3 fresnel = ( material.specularColorBlended * t2.x + ( material.specularF90 - material.specularColorBlended ) * t2.y ); - reflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords ); - reflectedLight.directDiffuse += lightColor * material.diffuseContribution * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords ); - #ifdef USE_CLEARCOAT - vec3 Ncc = geometryClearcoatNormal; - vec2 uvClearcoat = LTC_Uv( Ncc, viewDir, material.clearcoatRoughness ); - vec4 t1Clearcoat = texture2D( ltc_1, uvClearcoat ); - vec4 t2Clearcoat = texture2D( ltc_2, uvClearcoat ); - mat3 mInvClearcoat = mat3( - vec3( t1Clearcoat.x, 0, t1Clearcoat.y ), - vec3( 0, 1, 0 ), - vec3( t1Clearcoat.z, 0, t1Clearcoat.w ) - ); - vec3 fresnelClearcoat = material.clearcoatF0 * t2Clearcoat.x + ( material.clearcoatF90 - material.clearcoatF0 ) * t2Clearcoat.y; - clearcoatSpecularDirect += lightColor * fresnelClearcoat * LTC_Evaluate( Ncc, viewDir, position, mInvClearcoat, rectCoords ); - #endif - } -#endif -void RE_Direct_Physical( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { - float dotNL = saturate( dot( geometryNormal, directLight.direction ) ); - vec3 irradiance = dotNL * directLight.color; - #ifdef USE_CLEARCOAT - float dotNLcc = saturate( dot( geometryClearcoatNormal, directLight.direction ) ); - vec3 ccIrradiance = dotNLcc * directLight.color; - clearcoatSpecularDirect += ccIrradiance * BRDF_GGX_Clearcoat( directLight.direction, geometryViewDir, geometryClearcoatNormal, material ); - #endif - #ifdef USE_SHEEN - - sheenSpecularDirect += irradiance * BRDF_Sheen( directLight.direction, geometryViewDir, geometryNormal, material.sheenColor, material.sheenRoughness ); - - float sheenAlbedoV = IBLSheenBRDF( geometryNormal, geometryViewDir, material.sheenRoughness ); - float sheenAlbedoL = IBLSheenBRDF( geometryNormal, directLight.direction, material.sheenRoughness ); - - float sheenEnergyComp = 1.0 - max3( material.sheenColor ) * max( sheenAlbedoV, sheenAlbedoL ); - - irradiance *= sheenEnergyComp; - - #endif - reflectedLight.directSpecular += irradiance * BRDF_GGX_Multiscatter( directLight.direction, geometryViewDir, geometryNormal, material ); - reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseContribution ); -} -void RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { - vec3 diffuse = irradiance * BRDF_Lambert( material.diffuseContribution ); - #ifdef USE_SHEEN - float sheenAlbedo = IBLSheenBRDF( geometryNormal, geometryViewDir, material.sheenRoughness ); - float sheenEnergyComp = 1.0 - max3( material.sheenColor ) * sheenAlbedo; - diffuse *= sheenEnergyComp; - #endif - reflectedLight.indirectDiffuse += diffuse; -} -void RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearcoatRadiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) { - #ifdef USE_CLEARCOAT - clearcoatSpecularIndirect += clearcoatRadiance * EnvironmentBRDF( geometryClearcoatNormal, geometryViewDir, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness ); - #endif - #ifdef USE_SHEEN - sheenSpecularIndirect += irradiance * material.sheenColor * IBLSheenBRDF( geometryNormal, geometryViewDir, material.sheenRoughness ) * RECIPROCAL_PI; - #endif - vec3 singleScatteringDielectric = vec3( 0.0 ); - vec3 multiScatteringDielectric = vec3( 0.0 ); - vec3 singleScatteringMetallic = vec3( 0.0 ); - vec3 multiScatteringMetallic = vec3( 0.0 ); - #ifdef USE_IRIDESCENCE - computeMultiscatteringIridescence( geometryNormal, geometryViewDir, material.specularColor, material.specularF90, material.iridescence, material.iridescenceFresnelDielectric, material.roughness, singleScatteringDielectric, multiScatteringDielectric ); - computeMultiscatteringIridescence( geometryNormal, geometryViewDir, material.diffuseColor, material.specularF90, material.iridescence, material.iridescenceFresnelMetallic, material.roughness, singleScatteringMetallic, multiScatteringMetallic ); - #else - computeMultiscattering( geometryNormal, geometryViewDir, material.specularColor, material.specularF90, material.roughness, singleScatteringDielectric, multiScatteringDielectric ); - computeMultiscattering( geometryNormal, geometryViewDir, material.diffuseColor, material.specularF90, material.roughness, singleScatteringMetallic, multiScatteringMetallic ); - #endif - vec3 singleScattering = mix( singleScatteringDielectric, singleScatteringMetallic, material.metalness ); - vec3 multiScattering = mix( multiScatteringDielectric, multiScatteringMetallic, material.metalness ); - vec3 totalScatteringDielectric = singleScatteringDielectric + multiScatteringDielectric; - vec3 diffuse = material.diffuseContribution * ( 1.0 - totalScatteringDielectric ); - vec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI; - vec3 indirectSpecular = radiance * singleScattering; - indirectSpecular += multiScattering * cosineWeightedIrradiance; - vec3 indirectDiffuse = diffuse * cosineWeightedIrradiance; - #ifdef USE_SHEEN - float sheenAlbedo = IBLSheenBRDF( geometryNormal, geometryViewDir, material.sheenRoughness ); - float sheenEnergyComp = 1.0 - max3( material.sheenColor ) * sheenAlbedo; - indirectSpecular *= sheenEnergyComp; - indirectDiffuse *= sheenEnergyComp; - #endif - reflectedLight.indirectSpecular += indirectSpecular; - reflectedLight.indirectDiffuse += indirectDiffuse; -} -#define RE_Direct RE_Direct_Physical -#define RE_Direct_RectArea RE_Direct_RectArea_Physical -#define RE_IndirectDiffuse RE_IndirectDiffuse_Physical -#define RE_IndirectSpecular RE_IndirectSpecular_Physical -float computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) { - return saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion ); -}`,Zw=` -vec3 geometryPosition = - vViewPosition; -vec3 geometryNormal = normal; -vec3 geometryViewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition ); -vec3 geometryClearcoatNormal = vec3( 0.0 ); -#ifdef USE_CLEARCOAT - geometryClearcoatNormal = clearcoatNormal; -#endif -#ifdef USE_IRIDESCENCE - float dotNVi = saturate( dot( normal, geometryViewDir ) ); - if ( material.iridescenceThickness == 0.0 ) { - material.iridescence = 0.0; - } else { - material.iridescence = saturate( material.iridescence ); - } - if ( material.iridescence > 0.0 ) { - material.iridescenceFresnelDielectric = evalIridescence( 1.0, material.iridescenceIOR, dotNVi, material.iridescenceThickness, material.specularColor ); - material.iridescenceFresnelMetallic = evalIridescence( 1.0, material.iridescenceIOR, dotNVi, material.iridescenceThickness, material.diffuseColor ); - material.iridescenceFresnel = mix( material.iridescenceFresnelDielectric, material.iridescenceFresnelMetallic, material.metalness ); - material.iridescenceF0 = Schlick_to_F0( material.iridescenceFresnel, 1.0, dotNVi ); - } -#endif -IncidentLight directLight; -#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct ) - PointLight pointLight; - #if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0 - PointLightShadow pointLightShadow; - #endif - #pragma unroll_loop_start - for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { - pointLight = pointLights[ i ]; - getPointLightInfo( pointLight, geometryPosition, directLight ); - #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS ) && ( defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_BASIC ) ) - pointLightShadow = pointLightShadows[ i ]; - directLight.color *= ( directLight.visible && receiveShadow ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowIntensity, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0; - #endif - RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); - } - #pragma unroll_loop_end -#endif -#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct ) - SpotLight spotLight; - vec4 spotColor; - vec3 spotLightCoord; - bool inSpotLightMap; - #if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0 - SpotLightShadow spotLightShadow; - #endif - #pragma unroll_loop_start - for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { - spotLight = spotLights[ i ]; - getSpotLightInfo( spotLight, geometryPosition, directLight ); - #if ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS ) - #define SPOT_LIGHT_MAP_INDEX UNROLLED_LOOP_INDEX - #elif ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) - #define SPOT_LIGHT_MAP_INDEX NUM_SPOT_LIGHT_MAPS - #else - #define SPOT_LIGHT_MAP_INDEX ( UNROLLED_LOOP_INDEX - NUM_SPOT_LIGHT_SHADOWS + NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS ) - #endif - #if ( SPOT_LIGHT_MAP_INDEX < NUM_SPOT_LIGHT_MAPS ) - spotLightCoord = vSpotLightCoord[ i ].xyz / vSpotLightCoord[ i ].w; - inSpotLightMap = all( lessThan( abs( spotLightCoord * 2. - 1. ), vec3( 1.0 ) ) ); - spotColor = texture2D( spotLightMap[ SPOT_LIGHT_MAP_INDEX ], spotLightCoord.xy ); - directLight.color = inSpotLightMap ? directLight.color * spotColor.rgb : directLight.color; - #endif - #undef SPOT_LIGHT_MAP_INDEX - #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) - spotLightShadow = spotLightShadows[ i ]; - directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowIntensity, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotLightCoord[ i ] ) : 1.0; - #endif - RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); - } - #pragma unroll_loop_end -#endif -#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct ) - DirectionalLight directionalLight; - #if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0 - DirectionalLightShadow directionalLightShadow; - #endif - #pragma unroll_loop_start - for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { - directionalLight = directionalLights[ i ]; - getDirectionalLightInfo( directionalLight, directLight ); - #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS ) - directionalLightShadow = directionalLightShadows[ i ]; - directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowIntensity, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; - #endif - RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); - } - #pragma unroll_loop_end -#endif -#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea ) - RectAreaLight rectAreaLight; - #pragma unroll_loop_start - for ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) { - rectAreaLight = rectAreaLights[ i ]; - RE_Direct_RectArea( rectAreaLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); - } - #pragma unroll_loop_end -#endif -#if defined( RE_IndirectDiffuse ) - vec3 iblIrradiance = vec3( 0.0 ); - vec3 irradiance = getAmbientLightIrradiance( ambientLightColor ); - #if defined( USE_LIGHT_PROBES ) - irradiance += getLightProbeIrradiance( lightProbe, geometryNormal ); - #endif - #if ( NUM_HEMI_LIGHTS > 0 ) - #pragma unroll_loop_start - for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { - irradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometryNormal ); - } - #pragma unroll_loop_end - #endif - #ifdef USE_LIGHT_PROBES_GRID - vec3 probeWorldPos = ( ( vec4( geometryPosition, 1.0 ) - viewMatrix[ 3 ] ) * viewMatrix ).xyz; - vec3 probeWorldNormal = transformNormalByInverseViewMatrix( geometryNormal, viewMatrix ); - irradiance += getLightProbeGridIrradiance( probeWorldPos, probeWorldNormal ); - #endif -#endif -#if defined( RE_IndirectSpecular ) - vec3 radiance = vec3( 0.0 ); - vec3 clearcoatRadiance = vec3( 0.0 ); -#endif`,$w=`#if defined( RE_IndirectDiffuse ) - #ifdef USE_LIGHTMAP - vec4 lightMapTexel = texture2D( lightMap, vLightMapUv ); - vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity; - irradiance += lightMapIrradiance; - #endif - #if defined( USE_ENVMAP ) && defined( ENVMAP_TYPE_CUBE_UV ) - #if defined( STANDARD ) || defined( LAMBERT ) || defined( PHONG ) - iblIrradiance += getIBLIrradiance( geometryNormal ); - #endif - #endif -#endif -#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular ) - #ifdef USE_ANISOTROPY - radiance += getIBLAnisotropyRadiance( geometryViewDir, geometryNormal, material.roughness, material.anisotropyB, material.anisotropy ); - #else - radiance += getIBLRadiance( geometryViewDir, geometryNormal, material.roughness ); - #endif - #ifdef USE_CLEARCOAT - clearcoatRadiance += getIBLRadiance( geometryViewDir, geometryClearcoatNormal, material.clearcoatRoughness ); - #endif -#endif`,Qw=`#if defined( RE_IndirectDiffuse ) - #if defined( LAMBERT ) || defined( PHONG ) - irradiance += iblIrradiance; - #endif - RE_IndirectDiffuse( irradiance, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); -#endif -#if defined( RE_IndirectSpecular ) - RE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); -#endif`,Jw=`#ifdef USE_LIGHT_PROBES_GRID -uniform highp sampler3D probesSH; -uniform vec3 probesMin; -uniform vec3 probesMax; -uniform vec3 probesResolution; -vec3 getLightProbeGridIrradiance( vec3 worldPos, vec3 worldNormal ) { - vec3 res = probesResolution; - vec3 gridRange = probesMax - probesMin; - vec3 resMinusOne = res - 1.0; - vec3 probeSpacing = gridRange / resMinusOne; - vec3 samplePos = worldPos + worldNormal * probeSpacing * 0.5; - vec3 uvw = clamp( ( samplePos - probesMin ) / gridRange, 0.0, 1.0 ); - uvw = uvw * resMinusOne / res + 0.5 / res; - float nz = res.z; - float paddedSlices = nz + 2.0; - float atlasDepth = 7.0 * paddedSlices; - float uvZBase = uvw.z * nz + 1.0; - vec4 s0 = texture( probesSH, vec3( uvw.xy, ( uvZBase ) / atlasDepth ) ); - vec4 s1 = texture( probesSH, vec3( uvw.xy, ( uvZBase + paddedSlices ) / atlasDepth ) ); - vec4 s2 = texture( probesSH, vec3( uvw.xy, ( uvZBase + 2.0 * paddedSlices ) / atlasDepth ) ); - vec4 s3 = texture( probesSH, vec3( uvw.xy, ( uvZBase + 3.0 * paddedSlices ) / atlasDepth ) ); - vec4 s4 = texture( probesSH, vec3( uvw.xy, ( uvZBase + 4.0 * paddedSlices ) / atlasDepth ) ); - vec4 s5 = texture( probesSH, vec3( uvw.xy, ( uvZBase + 5.0 * paddedSlices ) / atlasDepth ) ); - vec4 s6 = texture( probesSH, vec3( uvw.xy, ( uvZBase + 6.0 * paddedSlices ) / atlasDepth ) ); - vec3 c0 = s0.xyz; - vec3 c1 = vec3( s0.w, s1.xy ); - vec3 c2 = vec3( s1.zw, s2.x ); - vec3 c3 = s2.yzw; - vec3 c4 = s3.xyz; - vec3 c5 = vec3( s3.w, s4.xy ); - vec3 c6 = vec3( s4.zw, s5.x ); - vec3 c7 = s5.yzw; - vec3 c8 = s6.xyz; - float x = worldNormal.x, y = worldNormal.y, z = worldNormal.z; - vec3 result = c0 * 0.886227; - result += c1 * 2.0 * 0.511664 * y; - result += c2 * 2.0 * 0.511664 * z; - result += c3 * 2.0 * 0.511664 * x; - result += c4 * 2.0 * 0.429043 * x * y; - result += c5 * 2.0 * 0.429043 * y * z; - result += c6 * ( 0.743125 * z * z - 0.247708 ); - result += c7 * 2.0 * 0.429043 * x * z; - result += c8 * 0.429043 * ( x * x - y * y ); - return max( result, vec3( 0.0 ) ); -} -#endif`,eC=`#if defined( USE_LOGARITHMIC_DEPTH_BUFFER ) - gl_FragDepth = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5; -#endif`,tC=`#if defined( USE_LOGARITHMIC_DEPTH_BUFFER ) - uniform float logDepthBufFC; - varying float vFragDepth; - varying float vIsPerspective; -#endif`,nC=`#ifdef USE_LOGARITHMIC_DEPTH_BUFFER - varying float vFragDepth; - varying float vIsPerspective; -#endif`,iC=`#ifdef USE_LOGARITHMIC_DEPTH_BUFFER - vFragDepth = 1.0 + gl_Position.w; - vIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) ); -#endif`,aC=`#ifdef USE_MAP - vec4 sampledDiffuseColor = texture2D( map, vMapUv ); - #ifdef DECODE_VIDEO_TEXTURE - sampledDiffuseColor = sRGBTransferEOTF( sampledDiffuseColor ); - #endif - diffuseColor *= sampledDiffuseColor; -#endif`,sC=`#ifdef USE_MAP - uniform sampler2D map; -#endif`,rC=`#if defined( USE_MAP ) || defined( USE_ALPHAMAP ) - #if defined( USE_POINTS_UV ) - vec2 uv = vUv; - #else - vec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy; - #endif -#endif -#ifdef USE_MAP - diffuseColor *= texture2D( map, uv ); -#endif -#ifdef USE_ALPHAMAP - diffuseColor.a *= texture2D( alphaMap, uv ).g; -#endif`,lC=`#if defined( USE_POINTS_UV ) - varying vec2 vUv; -#else - #if defined( USE_MAP ) || defined( USE_ALPHAMAP ) - uniform mat3 uvTransform; - #endif -#endif -#ifdef USE_MAP - uniform sampler2D map; -#endif -#ifdef USE_ALPHAMAP - uniform sampler2D alphaMap; -#endif`,oC=`float metalnessFactor = metalness; -#ifdef USE_METALNESSMAP - vec4 texelMetalness = texture2D( metalnessMap, vMetalnessMapUv ); - metalnessFactor *= texelMetalness.b; -#endif`,cC=`#ifdef USE_METALNESSMAP - uniform sampler2D metalnessMap; -#endif`,uC=`#ifdef USE_INSTANCING_MORPH - float morphTargetInfluences[ MORPHTARGETS_COUNT ]; - float morphTargetBaseInfluence = texelFetch( morphTexture, ivec2( 0, gl_InstanceID ), 0 ).r; - for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { - morphTargetInfluences[i] = texelFetch( morphTexture, ivec2( i + 1, gl_InstanceID ), 0 ).r; - } -#endif`,fC=`#if defined( USE_MORPHCOLORS ) - vColor *= morphTargetBaseInfluence; - for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { - #if defined( USE_COLOR_ALPHA ) - if ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ) * morphTargetInfluences[ i ]; - #elif defined( USE_COLOR ) - if ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ).rgb * morphTargetInfluences[ i ]; - #endif - } -#endif`,dC=`#ifdef USE_MORPHNORMALS - objectNormal *= morphTargetBaseInfluence; - for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { - if ( morphTargetInfluences[ i ] != 0.0 ) objectNormal += getMorph( gl_VertexID, i, 1 ).xyz * morphTargetInfluences[ i ]; - } -#endif`,hC=`#ifdef USE_MORPHTARGETS - #ifndef USE_INSTANCING_MORPH - uniform float morphTargetBaseInfluence; - uniform float morphTargetInfluences[ MORPHTARGETS_COUNT ]; - #endif - uniform sampler2DArray morphTargetsTexture; - uniform ivec2 morphTargetsTextureSize; - vec4 getMorph( const in int vertexIndex, const in int morphTargetIndex, const in int offset ) { - int texelIndex = vertexIndex * MORPHTARGETS_TEXTURE_STRIDE + offset; - int y = texelIndex / morphTargetsTextureSize.x; - int x = texelIndex - y * morphTargetsTextureSize.x; - ivec3 morphUV = ivec3( x, y, morphTargetIndex ); - return texelFetch( morphTargetsTexture, morphUV, 0 ); - } -#endif`,pC=`#ifdef USE_MORPHTARGETS - transformed *= morphTargetBaseInfluence; - for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { - if ( morphTargetInfluences[ i ] != 0.0 ) transformed += getMorph( gl_VertexID, i, 0 ).xyz * morphTargetInfluences[ i ]; - } -#endif`,mC=`float faceDirection = gl_FrontFacing ? 1.0 : - 1.0; -#ifdef FLAT_SHADED - vec3 fdx = dFdx( vViewPosition ); - vec3 fdy = dFdy( vViewPosition ); - vec3 normal = normalize( cross( fdx, fdy ) ); -#else - vec3 normal = normalize( vNormal ); - #ifdef DOUBLE_SIDED - normal *= faceDirection; - #endif -#endif -#if defined( USE_NORMALMAP_TANGENTSPACE ) || defined( USE_CLEARCOAT_NORMALMAP ) || defined( USE_ANISOTROPY ) - #ifdef USE_TANGENT - mat3 tbn = mat3( normalize( vTangent ), normalize( vBitangent ), normal ); - #else - mat3 tbn = getTangentFrame( - vViewPosition, normal, - #if defined( USE_NORMALMAP ) - vNormalMapUv - #elif defined( USE_CLEARCOAT_NORMALMAP ) - vClearcoatNormalMapUv - #else - vUv - #endif - ); - #endif - #ifdef DOUBLE_SIDED - tbn[0] *= faceDirection; - tbn[1] *= faceDirection; - #endif -#endif -#ifdef USE_CLEARCOAT_NORMALMAP - #ifdef USE_TANGENT - mat3 tbn2 = mat3( normalize( vTangent ), normalize( vBitangent ), normal ); - #else - mat3 tbn2 = getTangentFrame( - vViewPosition, normal, vClearcoatNormalMapUv ); - #endif - #ifdef DOUBLE_SIDED - tbn2[0] *= faceDirection; - tbn2[1] *= faceDirection; - #endif -#endif -vec3 nonPerturbedNormal = normal;`,gC=`#ifdef USE_NORMALMAP_OBJECTSPACE - normal = texture2D( normalMap, vNormalMapUv ).xyz * 2.0 - 1.0; - #ifdef FLIP_SIDED - normal = - normal; - #endif - #ifdef DOUBLE_SIDED - normal = normal * faceDirection; - #endif - normal = normalize( normalMatrix * normal ); -#elif defined( USE_NORMALMAP_TANGENTSPACE ) - vec3 mapN = texture2D( normalMap, vNormalMapUv ).xyz * 2.0 - 1.0; - #if defined( USE_PACKED_NORMALMAP ) - mapN = vec3( mapN.xy, sqrt( saturate( 1.0 - dot( mapN.xy, mapN.xy ) ) ) ); - #endif - mapN.xy *= normalScale; - normal = normalize( tbn * mapN ); -#elif defined( USE_BUMPMAP ) - normal = perturbNormalArb( - vViewPosition, normal, dHdxy_fwd(), faceDirection ); -#endif`,xC=`#ifndef FLAT_SHADED - varying vec3 vNormal; - #ifdef USE_TANGENT - varying vec3 vTangent; - varying vec3 vBitangent; - #endif -#endif`,vC=`#ifndef FLAT_SHADED - varying vec3 vNormal; - #ifdef USE_TANGENT - varying vec3 vTangent; - varying vec3 vBitangent; - #endif -#endif`,_C=`#ifndef FLAT_SHADED - vNormal = normalize( transformedNormal ); - #ifdef USE_TANGENT - vTangent = normalize( transformedTangent ); - vBitangent = normalize( cross( vNormal, vTangent ) * tangent.w ); - #ifdef FLIP_SIDED - vBitangent = - vBitangent; - #endif - #endif -#endif`,yC=`#ifdef USE_NORMALMAP - uniform sampler2D normalMap; - uniform vec2 normalScale; -#endif -#ifdef USE_NORMALMAP_OBJECTSPACE - uniform mat3 normalMatrix; -#endif -#if ! defined ( USE_TANGENT ) && ( defined ( USE_NORMALMAP_TANGENTSPACE ) || defined ( USE_CLEARCOAT_NORMALMAP ) || defined( USE_ANISOTROPY ) ) - mat3 getTangentFrame( vec3 eye_pos, vec3 surf_norm, vec2 uv ) { - vec3 q0 = dFdx( eye_pos.xyz ); - vec3 q1 = dFdy( eye_pos.xyz ); - vec2 st0 = dFdx( uv.st ); - vec2 st1 = dFdy( uv.st ); - vec3 N = surf_norm; - vec3 q1perp = cross( q1, N ); - vec3 q0perp = cross( N, q0 ); - vec3 T = q1perp * st0.x + q0perp * st1.x; - vec3 B = q1perp * st0.y + q0perp * st1.y; - float det = max( dot( T, T ), dot( B, B ) ); - float scale = ( det == 0.0 ) ? 0.0 : inversesqrt( det ); - return mat3( T * scale, B * scale, N ); - } -#endif`,bC=`#ifdef USE_CLEARCOAT - vec3 clearcoatNormal = nonPerturbedNormal; -#endif`,SC=`#ifdef USE_CLEARCOAT_NORMALMAP - vec3 clearcoatMapN = texture2D( clearcoatNormalMap, vClearcoatNormalMapUv ).xyz * 2.0 - 1.0; - clearcoatMapN.xy *= clearcoatNormalScale; - clearcoatNormal = normalize( tbn2 * clearcoatMapN ); -#endif`,MC=`#ifdef USE_CLEARCOATMAP - uniform sampler2D clearcoatMap; -#endif -#ifdef USE_CLEARCOAT_NORMALMAP - uniform sampler2D clearcoatNormalMap; - uniform vec2 clearcoatNormalScale; -#endif -#ifdef USE_CLEARCOAT_ROUGHNESSMAP - uniform sampler2D clearcoatRoughnessMap; -#endif`,EC=`#ifdef USE_IRIDESCENCEMAP - uniform sampler2D iridescenceMap; -#endif -#ifdef USE_IRIDESCENCE_THICKNESSMAP - uniform sampler2D iridescenceThicknessMap; -#endif`,TC=`#ifdef OPAQUE -diffuseColor.a = 1.0; -#endif -#ifdef USE_TRANSMISSION -diffuseColor.a *= material.transmissionAlpha; -#endif -gl_FragColor = vec4( outgoingLight, diffuseColor.a );`,AC=`vec3 packNormalToRGB( const in vec3 normal ) { - return normalize( normal ) * 0.5 + 0.5; -} -vec3 unpackRGBToNormal( const in vec3 rgb ) { - return 2.0 * rgb.xyz - 1.0; -} -const float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;const float ShiftRight8 = 1. / 256.; -const float Inv255 = 1. / 255.; -const vec4 PackFactors = vec4( 1.0, 256.0, 256.0 * 256.0, 256.0 * 256.0 * 256.0 ); -const vec2 UnpackFactors2 = vec2( UnpackDownscale, 1.0 / PackFactors.g ); -const vec3 UnpackFactors3 = vec3( UnpackDownscale / PackFactors.rg, 1.0 / PackFactors.b ); -const vec4 UnpackFactors4 = vec4( UnpackDownscale / PackFactors.rgb, 1.0 / PackFactors.a ); -vec4 packDepthToRGBA( const in float v ) { - if( v <= 0.0 ) - return vec4( 0., 0., 0., 0. ); - if( v >= 1.0 ) - return vec4( 1., 1., 1., 1. ); - float vuf; - float af = modf( v * PackFactors.a, vuf ); - float bf = modf( vuf * ShiftRight8, vuf ); - float gf = modf( vuf * ShiftRight8, vuf ); - return vec4( vuf * Inv255, gf * PackUpscale, bf * PackUpscale, af ); -} -vec3 packDepthToRGB( const in float v ) { - if( v <= 0.0 ) - return vec3( 0., 0., 0. ); - if( v >= 1.0 ) - return vec3( 1., 1., 1. ); - float vuf; - float bf = modf( v * PackFactors.b, vuf ); - float gf = modf( vuf * ShiftRight8, vuf ); - return vec3( vuf * Inv255, gf * PackUpscale, bf ); -} -vec2 packDepthToRG( const in float v ) { - if( v <= 0.0 ) - return vec2( 0., 0. ); - if( v >= 1.0 ) - return vec2( 1., 1. ); - float vuf; - float gf = modf( v * 256., vuf ); - return vec2( vuf * Inv255, gf ); -} -float unpackRGBAToDepth( const in vec4 v ) { - return dot( v, UnpackFactors4 ); -} -float unpackRGBToDepth( const in vec3 v ) { - return dot( v, UnpackFactors3 ); -} -float unpackRGToDepth( const in vec2 v ) { - return v.r * UnpackFactors2.r + v.g * UnpackFactors2.g; -} -vec4 pack2HalfToRGBA( const in vec2 v ) { - vec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ) ); - return vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w ); -} -vec2 unpackRGBATo2Half( const in vec4 v ) { - return vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) ); -} -float viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) { - return ( viewZ + near ) / ( near - far ); -} -float orthographicDepthToViewZ( const in float depth, const in float near, const in float far ) { - #ifdef USE_REVERSED_DEPTH_BUFFER - - return depth * ( far - near ) - far; - #else - return depth * ( near - far ) - near; - #endif -} -float viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) { - return ( ( near + viewZ ) * far ) / ( ( far - near ) * viewZ ); -} -float perspectiveDepthToViewZ( const in float depth, const in float near, const in float far ) { - - #ifdef USE_REVERSED_DEPTH_BUFFER - return ( near * far ) / ( ( near - far ) * depth - near ); - #else - return ( near * far ) / ( ( far - near ) * depth - far ); - #endif -}`,wC=`#ifdef PREMULTIPLIED_ALPHA - gl_FragColor.rgb *= gl_FragColor.a; -#endif`,CC=`vec4 mvPosition = vec4( transformed, 1.0 ); -#ifdef USE_BATCHING - mvPosition = batchingMatrix * mvPosition; -#endif -#ifdef USE_INSTANCING - mvPosition = instanceMatrix * mvPosition; -#endif -mvPosition = modelViewMatrix * mvPosition; -gl_Position = projectionMatrix * mvPosition;`,NC=`#ifdef DITHERING - gl_FragColor.rgb = dithering( gl_FragColor.rgb ); -#endif`,RC=`#ifdef DITHERING - vec3 dithering( vec3 color ) { - float grid_position = rand( gl_FragCoord.xy ); - vec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 ); - dither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position ); - return color + dither_shift_RGB; - } -#endif`,DC=`float roughnessFactor = roughness; -#ifdef USE_ROUGHNESSMAP - vec4 texelRoughness = texture2D( roughnessMap, vRoughnessMapUv ); - roughnessFactor *= texelRoughness.g; -#endif`,LC=`#ifdef USE_ROUGHNESSMAP - uniform sampler2D roughnessMap; -#endif`,UC=`#if NUM_SPOT_LIGHT_COORDS > 0 - varying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ]; -#endif -#if NUM_SPOT_LIGHT_MAPS > 0 - uniform sampler2D spotLightMap[ NUM_SPOT_LIGHT_MAPS ]; -#endif -#ifdef USE_SHADOWMAP - #if NUM_DIR_LIGHT_SHADOWS > 0 - #if defined( SHADOWMAP_TYPE_PCF ) - uniform sampler2DShadow directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ]; - #else - uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ]; - #endif - varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; - struct DirectionalLightShadow { - float shadowIntensity; - float shadowBias; - float shadowNormalBias; - float shadowRadius; - vec2 shadowMapSize; - }; - uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; - #endif - #if NUM_SPOT_LIGHT_SHADOWS > 0 - #if defined( SHADOWMAP_TYPE_PCF ) - uniform sampler2DShadow spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ]; - #else - uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ]; - #endif - struct SpotLightShadow { - float shadowIntensity; - float shadowBias; - float shadowNormalBias; - float shadowRadius; - vec2 shadowMapSize; - }; - uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; - #endif - #if NUM_POINT_LIGHT_SHADOWS > 0 - #if defined( SHADOWMAP_TYPE_PCF ) - uniform samplerCubeShadow pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ]; - #elif defined( SHADOWMAP_TYPE_BASIC ) - uniform samplerCube pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ]; - #endif - varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; - struct PointLightShadow { - float shadowIntensity; - float shadowBias; - float shadowNormalBias; - float shadowRadius; - vec2 shadowMapSize; - float shadowCameraNear; - float shadowCameraFar; - }; - uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; - #endif - #if defined( SHADOWMAP_TYPE_PCF ) - float interleavedGradientNoise( vec2 position ) { - return fract( 52.9829189 * fract( dot( position, vec2( 0.06711056, 0.00583715 ) ) ) ); - } - vec2 vogelDiskSample( int sampleIndex, int samplesCount, float phi ) { - const float goldenAngle = 2.399963229728653; - float r = sqrt( ( float( sampleIndex ) + 0.5 ) / float( samplesCount ) ); - float theta = float( sampleIndex ) * goldenAngle + phi; - return vec2( cos( theta ), sin( theta ) ) * r; - } - #endif - #if defined( SHADOWMAP_TYPE_PCF ) - float getShadow( sampler2DShadow shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord ) { - float shadow = 1.0; - shadowCoord.xyz /= shadowCoord.w; - shadowCoord.z += shadowBias; - bool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0; - bool frustumTest = inFrustum && shadowCoord.z <= 1.0; - if ( frustumTest ) { - vec2 texelSize = vec2( 1.0 ) / shadowMapSize; - float radius = shadowRadius * texelSize.x; - float phi = interleavedGradientNoise( gl_FragCoord.xy ) * PI2; - shadow = ( - texture( shadowMap, vec3( shadowCoord.xy + vogelDiskSample( 0, 5, phi ) * radius, shadowCoord.z ) ) + - texture( shadowMap, vec3( shadowCoord.xy + vogelDiskSample( 1, 5, phi ) * radius, shadowCoord.z ) ) + - texture( shadowMap, vec3( shadowCoord.xy + vogelDiskSample( 2, 5, phi ) * radius, shadowCoord.z ) ) + - texture( shadowMap, vec3( shadowCoord.xy + vogelDiskSample( 3, 5, phi ) * radius, shadowCoord.z ) ) + - texture( shadowMap, vec3( shadowCoord.xy + vogelDiskSample( 4, 5, phi ) * radius, shadowCoord.z ) ) - ) * 0.2; - } - return mix( 1.0, shadow, shadowIntensity ); - } - #elif defined( SHADOWMAP_TYPE_VSM ) - float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord ) { - float shadow = 1.0; - shadowCoord.xyz /= shadowCoord.w; - #ifdef USE_REVERSED_DEPTH_BUFFER - shadowCoord.z -= shadowBias; - #else - shadowCoord.z += shadowBias; - #endif - bool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0; - bool frustumTest = inFrustum && shadowCoord.z <= 1.0; - if ( frustumTest ) { - vec2 distribution = texture2D( shadowMap, shadowCoord.xy ).rg; - float mean = distribution.x; - float variance = distribution.y * distribution.y; - #ifdef USE_REVERSED_DEPTH_BUFFER - float hard_shadow = step( mean, shadowCoord.z ); - #else - float hard_shadow = step( shadowCoord.z, mean ); - #endif - - if ( hard_shadow == 1.0 ) { - shadow = 1.0; - } else { - variance = max( variance, 0.0000001 ); - float d = shadowCoord.z - mean; - float p_max = variance / ( variance + d * d ); - p_max = clamp( ( p_max - 0.3 ) / 0.65, 0.0, 1.0 ); - shadow = max( hard_shadow, p_max ); - } - } - return mix( 1.0, shadow, shadowIntensity ); - } - #else - float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord ) { - float shadow = 1.0; - shadowCoord.xyz /= shadowCoord.w; - #ifdef USE_REVERSED_DEPTH_BUFFER - shadowCoord.z -= shadowBias; - #else - shadowCoord.z += shadowBias; - #endif - bool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0; - bool frustumTest = inFrustum && shadowCoord.z <= 1.0; - if ( frustumTest ) { - float depth = texture2D( shadowMap, shadowCoord.xy ).r; - #ifdef USE_REVERSED_DEPTH_BUFFER - shadow = step( depth, shadowCoord.z ); - #else - shadow = step( shadowCoord.z, depth ); - #endif - } - return mix( 1.0, shadow, shadowIntensity ); - } - #endif - #if NUM_POINT_LIGHT_SHADOWS > 0 - #if defined( SHADOWMAP_TYPE_PCF ) - float getPointShadow( samplerCubeShadow shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) { - float shadow = 1.0; - vec3 lightToPosition = shadowCoord.xyz; - vec3 bd3D = normalize( lightToPosition ); - vec3 absVec = abs( lightToPosition ); - float viewSpaceZ = max( max( absVec.x, absVec.y ), absVec.z ); - if ( viewSpaceZ - shadowCameraFar <= 0.0 && viewSpaceZ - shadowCameraNear >= 0.0 ) { - #ifdef USE_REVERSED_DEPTH_BUFFER - float dp = ( shadowCameraNear * ( shadowCameraFar - viewSpaceZ ) ) / ( viewSpaceZ * ( shadowCameraFar - shadowCameraNear ) ); - dp -= shadowBias; - #else - float dp = ( shadowCameraFar * ( viewSpaceZ - shadowCameraNear ) ) / ( viewSpaceZ * ( shadowCameraFar - shadowCameraNear ) ); - dp += shadowBias; - #endif - float texelSize = shadowRadius / shadowMapSize.x; - vec3 absDir = abs( bd3D ); - vec3 tangent = absDir.x > absDir.z ? vec3( 0.0, 1.0, 0.0 ) : vec3( 1.0, 0.0, 0.0 ); - tangent = normalize( cross( bd3D, tangent ) ); - vec3 bitangent = cross( bd3D, tangent ); - float phi = interleavedGradientNoise( gl_FragCoord.xy ) * PI2; - vec2 sample0 = vogelDiskSample( 0, 5, phi ); - vec2 sample1 = vogelDiskSample( 1, 5, phi ); - vec2 sample2 = vogelDiskSample( 2, 5, phi ); - vec2 sample3 = vogelDiskSample( 3, 5, phi ); - vec2 sample4 = vogelDiskSample( 4, 5, phi ); - shadow = ( - texture( shadowMap, vec4( bd3D + ( tangent * sample0.x + bitangent * sample0.y ) * texelSize, dp ) ) + - texture( shadowMap, vec4( bd3D + ( tangent * sample1.x + bitangent * sample1.y ) * texelSize, dp ) ) + - texture( shadowMap, vec4( bd3D + ( tangent * sample2.x + bitangent * sample2.y ) * texelSize, dp ) ) + - texture( shadowMap, vec4( bd3D + ( tangent * sample3.x + bitangent * sample3.y ) * texelSize, dp ) ) + - texture( shadowMap, vec4( bd3D + ( tangent * sample4.x + bitangent * sample4.y ) * texelSize, dp ) ) - ) * 0.2; - } - return mix( 1.0, shadow, shadowIntensity ); - } - #elif defined( SHADOWMAP_TYPE_BASIC ) - float getPointShadow( samplerCube shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) { - float shadow = 1.0; - vec3 lightToPosition = shadowCoord.xyz; - vec3 absVec = abs( lightToPosition ); - float viewSpaceZ = max( max( absVec.x, absVec.y ), absVec.z ); - if ( viewSpaceZ - shadowCameraFar <= 0.0 && viewSpaceZ - shadowCameraNear >= 0.0 ) { - float dp = ( shadowCameraFar * ( viewSpaceZ - shadowCameraNear ) ) / ( viewSpaceZ * ( shadowCameraFar - shadowCameraNear ) ); - dp += shadowBias; - vec3 bd3D = normalize( lightToPosition ); - float depth = textureCube( shadowMap, bd3D ).r; - #ifdef USE_REVERSED_DEPTH_BUFFER - depth = 1.0 - depth; - #endif - shadow = step( dp, depth ); - } - return mix( 1.0, shadow, shadowIntensity ); - } - #endif - #endif -#endif`,OC=`#if NUM_SPOT_LIGHT_COORDS > 0 - uniform mat4 spotLightMatrix[ NUM_SPOT_LIGHT_COORDS ]; - varying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ]; -#endif -#ifdef USE_SHADOWMAP - #if NUM_DIR_LIGHT_SHADOWS > 0 - uniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ]; - varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; - struct DirectionalLightShadow { - float shadowIntensity; - float shadowBias; - float shadowNormalBias; - float shadowRadius; - vec2 shadowMapSize; - }; - uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; - #endif - #if NUM_SPOT_LIGHT_SHADOWS > 0 - struct SpotLightShadow { - float shadowIntensity; - float shadowBias; - float shadowNormalBias; - float shadowRadius; - vec2 shadowMapSize; - }; - uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; - #endif - #if NUM_POINT_LIGHT_SHADOWS > 0 - uniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ]; - varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; - struct PointLightShadow { - float shadowIntensity; - float shadowBias; - float shadowNormalBias; - float shadowRadius; - vec2 shadowMapSize; - float shadowCameraNear; - float shadowCameraFar; - }; - uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; - #endif -#endif`,PC=`#if ( defined( USE_SHADOWMAP ) && ( NUM_DIR_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0 ) ) || ( NUM_SPOT_LIGHT_COORDS > 0 ) - #ifdef HAS_NORMAL - vec3 shadowWorldNormal = transformNormalByInverseViewMatrix( transformedNormal, viewMatrix ); - #else - vec3 shadowWorldNormal = vec3( 0.0 ); - #endif - vec4 shadowWorldPosition; -#endif -#if defined( USE_SHADOWMAP ) - #if NUM_DIR_LIGHT_SHADOWS > 0 - #pragma unroll_loop_start - for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { - shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 ); - vDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition; - } - #pragma unroll_loop_end - #endif - #if NUM_POINT_LIGHT_SHADOWS > 0 - #pragma unroll_loop_start - for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { - shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalBias, 0 ); - vPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition; - } - #pragma unroll_loop_end - #endif -#endif -#if NUM_SPOT_LIGHT_COORDS > 0 - #pragma unroll_loop_start - for ( int i = 0; i < NUM_SPOT_LIGHT_COORDS; i ++ ) { - shadowWorldPosition = worldPosition; - #if ( defined( USE_SHADOWMAP ) && UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) - shadowWorldPosition.xyz += shadowWorldNormal * spotLightShadows[ i ].shadowNormalBias; - #endif - vSpotLightCoord[ i ] = spotLightMatrix[ i ] * shadowWorldPosition; - } - #pragma unroll_loop_end -#endif`,zC=`float getShadowMask() { - float shadow = 1.0; - #ifdef USE_SHADOWMAP - #if NUM_DIR_LIGHT_SHADOWS > 0 - DirectionalLightShadow directionalLight; - #pragma unroll_loop_start - for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { - directionalLight = directionalLightShadows[ i ]; - shadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowIntensity, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; - } - #pragma unroll_loop_end - #endif - #if NUM_SPOT_LIGHT_SHADOWS > 0 - SpotLightShadow spotLight; - #pragma unroll_loop_start - for ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) { - spotLight = spotLightShadows[ i ]; - shadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowIntensity, spotLight.shadowBias, spotLight.shadowRadius, vSpotLightCoord[ i ] ) : 1.0; - } - #pragma unroll_loop_end - #endif - #if NUM_POINT_LIGHT_SHADOWS > 0 && ( defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_BASIC ) ) - PointLightShadow pointLight; - #pragma unroll_loop_start - for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { - pointLight = pointLightShadows[ i ]; - shadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowIntensity, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0; - } - #pragma unroll_loop_end - #endif - #endif - return shadow; -}`,IC=`#ifdef USE_SKINNING - mat4 boneMatX = getBoneMatrix( skinIndex.x ); - mat4 boneMatY = getBoneMatrix( skinIndex.y ); - mat4 boneMatZ = getBoneMatrix( skinIndex.z ); - mat4 boneMatW = getBoneMatrix( skinIndex.w ); -#endif`,kC=`#ifdef USE_SKINNING - uniform mat4 bindMatrix; - uniform mat4 bindMatrixInverse; - uniform highp sampler2D boneTexture; - mat4 getBoneMatrix( const in float i ) { - int size = textureSize( boneTexture, 0 ).x; - int j = int( i ) * 4; - int x = j % size; - int y = j / size; - vec4 v1 = texelFetch( boneTexture, ivec2( x, y ), 0 ); - vec4 v2 = texelFetch( boneTexture, ivec2( x + 1, y ), 0 ); - vec4 v3 = texelFetch( boneTexture, ivec2( x + 2, y ), 0 ); - vec4 v4 = texelFetch( boneTexture, ivec2( x + 3, y ), 0 ); - return mat4( v1, v2, v3, v4 ); - } -#endif`,BC=`#ifdef USE_SKINNING - vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 ); - vec4 skinned = vec4( 0.0 ); - skinned += boneMatX * skinVertex * skinWeight.x; - skinned += boneMatY * skinVertex * skinWeight.y; - skinned += boneMatZ * skinVertex * skinWeight.z; - skinned += boneMatW * skinVertex * skinWeight.w; - transformed = ( bindMatrixInverse * skinned ).xyz; -#endif`,FC=`#ifdef USE_SKINNING - mat4 skinMatrix = mat4( 0.0 ); - skinMatrix += skinWeight.x * boneMatX; - skinMatrix += skinWeight.y * boneMatY; - skinMatrix += skinWeight.z * boneMatZ; - skinMatrix += skinWeight.w * boneMatW; - skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix; - objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz; - #ifdef USE_TANGENT - objectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz; - #endif -#endif`,jC=`float specularStrength; -#ifdef USE_SPECULARMAP - vec4 texelSpecular = texture2D( specularMap, vSpecularMapUv ); - specularStrength = texelSpecular.r; -#else - specularStrength = 1.0; -#endif`,HC=`#ifdef USE_SPECULARMAP - uniform sampler2D specularMap; -#endif`,GC=`#if defined( TONE_MAPPING ) - gl_FragColor.rgb = toneMapping( gl_FragColor.rgb ); -#endif`,VC=`#ifndef saturate -#define saturate( a ) clamp( a, 0.0, 1.0 ) -#endif -uniform float toneMappingExposure; -vec3 LinearToneMapping( vec3 color ) { - return saturate( toneMappingExposure * color ); -} -vec3 ReinhardToneMapping( vec3 color ) { - color *= toneMappingExposure; - return saturate( color / ( vec3( 1.0 ) + color ) ); -} -vec3 CineonToneMapping( vec3 color ) { - color *= toneMappingExposure; - color = max( vec3( 0.0 ), color - 0.004 ); - return pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) ); -} -vec3 RRTAndODTFit( vec3 v ) { - vec3 a = v * ( v + 0.0245786 ) - 0.000090537; - vec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081; - return a / b; -} -vec3 ACESFilmicToneMapping( vec3 color ) { - const mat3 ACESInputMat = mat3( - vec3( 0.59719, 0.07600, 0.02840 ), vec3( 0.35458, 0.90834, 0.13383 ), - vec3( 0.04823, 0.01566, 0.83777 ) - ); - const mat3 ACESOutputMat = mat3( - vec3( 1.60475, -0.10208, -0.00327 ), vec3( -0.53108, 1.10813, -0.07276 ), - vec3( -0.07367, -0.00605, 1.07602 ) - ); - color *= toneMappingExposure / 0.6; - color = ACESInputMat * color; - color = RRTAndODTFit( color ); - color = ACESOutputMat * color; - return saturate( color ); -} -const mat3 LINEAR_REC2020_TO_LINEAR_SRGB = mat3( - vec3( 1.6605, - 0.1246, - 0.0182 ), - vec3( - 0.5876, 1.1329, - 0.1006 ), - vec3( - 0.0728, - 0.0083, 1.1187 ) -); -const mat3 LINEAR_SRGB_TO_LINEAR_REC2020 = mat3( - vec3( 0.6274, 0.0691, 0.0164 ), - vec3( 0.3293, 0.9195, 0.0880 ), - vec3( 0.0433, 0.0113, 0.8956 ) -); -vec3 agxDefaultContrastApprox( vec3 x ) { - vec3 x2 = x * x; - vec3 x4 = x2 * x2; - return + 15.5 * x4 * x2 - - 40.14 * x4 * x - + 31.96 * x4 - - 6.868 * x2 * x - + 0.4298 * x2 - + 0.1191 * x - - 0.00232; -} -vec3 AgXToneMapping( vec3 color ) { - const mat3 AgXInsetMatrix = mat3( - vec3( 0.856627153315983, 0.137318972929847, 0.11189821299995 ), - vec3( 0.0951212405381588, 0.761241990602591, 0.0767994186031903 ), - vec3( 0.0482516061458583, 0.101439036467562, 0.811302368396859 ) - ); - const mat3 AgXOutsetMatrix = mat3( - vec3( 1.1271005818144368, - 0.1413297634984383, - 0.14132976349843826 ), - vec3( - 0.11060664309660323, 1.157823702216272, - 0.11060664309660294 ), - vec3( - 0.016493938717834573, - 0.016493938717834257, 1.2519364065950405 ) - ); - const float AgxMinEv = - 12.47393; const float AgxMaxEv = 4.026069; - color *= toneMappingExposure; - color = LINEAR_SRGB_TO_LINEAR_REC2020 * color; - color = AgXInsetMatrix * color; - color = max( color, 1e-10 ); color = log2( color ); - color = ( color - AgxMinEv ) / ( AgxMaxEv - AgxMinEv ); - color = clamp( color, 0.0, 1.0 ); - color = agxDefaultContrastApprox( color ); - color = AgXOutsetMatrix * color; - color = pow( max( vec3( 0.0 ), color ), vec3( 2.2 ) ); - color = LINEAR_REC2020_TO_LINEAR_SRGB * color; - color = clamp( color, 0.0, 1.0 ); - return color; -} -vec3 NeutralToneMapping( vec3 color ) { - const float StartCompression = 0.8 - 0.04; - const float Desaturation = 0.15; - color *= toneMappingExposure; - float x = min( color.r, min( color.g, color.b ) ); - float offset = x < 0.08 ? x - 6.25 * x * x : 0.04; - color -= offset; - float peak = max( color.r, max( color.g, color.b ) ); - if ( peak < StartCompression ) return color; - float d = 1. - StartCompression; - float newPeak = 1. - d * d / ( peak + d - StartCompression ); - color *= newPeak / peak; - float g = 1. - 1. / ( Desaturation * ( peak - newPeak ) + 1. ); - return mix( color, vec3( newPeak ), g ); -} -vec3 CustomToneMapping( vec3 color ) { return color; }`,WC=`#ifdef USE_TRANSMISSION - material.transmission = transmission; - material.transmissionAlpha = 1.0; - material.thickness = thickness; - material.attenuationDistance = attenuationDistance; - material.attenuationColor = attenuationColor; - #ifdef USE_TRANSMISSIONMAP - material.transmission *= texture2D( transmissionMap, vTransmissionMapUv ).r; - #endif - #ifdef USE_THICKNESSMAP - material.thickness *= texture2D( thicknessMap, vThicknessMapUv ).g; - #endif - vec3 pos = vWorldPosition; - vec3 v = normalize( cameraPosition - pos ); - vec3 n = transformNormalByInverseViewMatrix( normal, viewMatrix ); - vec4 transmitted = getIBLVolumeRefraction( - n, v, material.roughness, material.diffuseContribution, material.specularColorBlended, material.specularF90, - pos, modelMatrix, viewMatrix, projectionMatrix, material.dispersion, material.ior, material.thickness, - material.attenuationColor, material.attenuationDistance ); - material.transmissionAlpha = mix( material.transmissionAlpha, transmitted.a, material.transmission ); - totalDiffuse = mix( totalDiffuse, transmitted.rgb, material.transmission ); -#endif`,XC=`#ifdef USE_TRANSMISSION - uniform float transmission; - uniform float thickness; - uniform float attenuationDistance; - uniform vec3 attenuationColor; - #ifdef USE_TRANSMISSIONMAP - uniform sampler2D transmissionMap; - #endif - #ifdef USE_THICKNESSMAP - uniform sampler2D thicknessMap; - #endif - uniform vec2 transmissionSamplerSize; - uniform sampler2D transmissionSamplerMap; - uniform mat4 modelMatrix; - uniform mat4 projectionMatrix; - varying vec3 vWorldPosition; - float w0( float a ) { - return ( 1.0 / 6.0 ) * ( a * ( a * ( - a + 3.0 ) - 3.0 ) + 1.0 ); - } - float w1( float a ) { - return ( 1.0 / 6.0 ) * ( a * a * ( 3.0 * a - 6.0 ) + 4.0 ); - } - float w2( float a ){ - return ( 1.0 / 6.0 ) * ( a * ( a * ( - 3.0 * a + 3.0 ) + 3.0 ) + 1.0 ); - } - float w3( float a ) { - return ( 1.0 / 6.0 ) * ( a * a * a ); - } - float g0( float a ) { - return w0( a ) + w1( a ); - } - float g1( float a ) { - return w2( a ) + w3( a ); - } - float h0( float a ) { - return - 1.0 + w1( a ) / ( w0( a ) + w1( a ) ); - } - float h1( float a ) { - return 1.0 + w3( a ) / ( w2( a ) + w3( a ) ); - } - vec4 bicubic( sampler2D tex, vec2 uv, vec4 texelSize, float lod ) { - uv = uv * texelSize.zw + 0.5; - vec2 iuv = floor( uv ); - vec2 fuv = fract( uv ); - float g0x = g0( fuv.x ); - float g1x = g1( fuv.x ); - float h0x = h0( fuv.x ); - float h1x = h1( fuv.x ); - float h0y = h0( fuv.y ); - float h1y = h1( fuv.y ); - vec2 p0 = ( vec2( iuv.x + h0x, iuv.y + h0y ) - 0.5 ) * texelSize.xy; - vec2 p1 = ( vec2( iuv.x + h1x, iuv.y + h0y ) - 0.5 ) * texelSize.xy; - vec2 p2 = ( vec2( iuv.x + h0x, iuv.y + h1y ) - 0.5 ) * texelSize.xy; - vec2 p3 = ( vec2( iuv.x + h1x, iuv.y + h1y ) - 0.5 ) * texelSize.xy; - return g0( fuv.y ) * ( g0x * textureLod( tex, p0, lod ) + g1x * textureLod( tex, p1, lod ) ) + - g1( fuv.y ) * ( g0x * textureLod( tex, p2, lod ) + g1x * textureLod( tex, p3, lod ) ); - } - vec4 textureBicubic( sampler2D sampler, vec2 uv, float lod ) { - vec2 fLodSize = vec2( textureSize( sampler, int( lod ) ) ); - vec2 cLodSize = vec2( textureSize( sampler, int( lod + 1.0 ) ) ); - vec2 fLodSizeInv = 1.0 / fLodSize; - vec2 cLodSizeInv = 1.0 / cLodSize; - vec4 fSample = bicubic( sampler, uv, vec4( fLodSizeInv, fLodSize ), floor( lod ) ); - vec4 cSample = bicubic( sampler, uv, vec4( cLodSizeInv, cLodSize ), ceil( lod ) ); - return mix( fSample, cSample, fract( lod ) ); - } - vec3 getVolumeTransmissionRay( const in vec3 n, const in vec3 v, const in float thickness, const in float ior, const in mat4 modelMatrix ) { - vec3 refractionVector = refract( - v, normalize( n ), 1.0 / ior ); - vec3 modelScale; - modelScale.x = length( vec3( modelMatrix[ 0 ].xyz ) ); - modelScale.y = length( vec3( modelMatrix[ 1 ].xyz ) ); - modelScale.z = length( vec3( modelMatrix[ 2 ].xyz ) ); - return normalize( refractionVector ) * thickness * modelScale; - } - float applyIorToRoughness( const in float roughness, const in float ior ) { - return roughness * clamp( ior * 2.0 - 2.0, 0.0, 1.0 ); - } - vec4 getTransmissionSample( const in vec2 fragCoord, const in float roughness, const in float ior ) { - float lod = log2( transmissionSamplerSize.x ) * applyIorToRoughness( roughness, ior ); - return textureBicubic( transmissionSamplerMap, fragCoord.xy, lod ); - } - vec3 volumeAttenuation( const in float transmissionDistance, const in vec3 attenuationColor, const in float attenuationDistance ) { - if ( isinf( attenuationDistance ) ) { - return vec3( 1.0 ); - } else { - vec3 attenuationCoefficient = -log( attenuationColor ) / attenuationDistance; - vec3 transmittance = exp( - attenuationCoefficient * transmissionDistance ); return transmittance; - } - } - vec4 getIBLVolumeRefraction( const in vec3 n, const in vec3 v, const in float roughness, const in vec3 diffuseColor, - const in vec3 specularColor, const in float specularF90, const in vec3 position, const in mat4 modelMatrix, - const in mat4 viewMatrix, const in mat4 projMatrix, const in float dispersion, const in float ior, const in float thickness, - const in vec3 attenuationColor, const in float attenuationDistance ) { - vec4 transmittedLight; - vec3 transmittance; - #ifdef USE_DISPERSION - float halfSpread = ( ior - 1.0 ) * 0.025 * dispersion; - vec3 iors = vec3( ior - halfSpread, ior, ior + halfSpread ); - for ( int i = 0; i < 3; i ++ ) { - vec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, iors[ i ], modelMatrix ); - vec3 refractedRayExit = position + transmissionRay; - vec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 ); - vec2 refractionCoords = ndcPos.xy / ndcPos.w; - refractionCoords += 1.0; - refractionCoords /= 2.0; - vec4 transmissionSample = getTransmissionSample( refractionCoords, roughness, iors[ i ] ); - transmittedLight[ i ] = transmissionSample[ i ]; - transmittedLight.a += transmissionSample.a; - transmittance[ i ] = diffuseColor[ i ] * volumeAttenuation( length( transmissionRay ), attenuationColor, attenuationDistance )[ i ]; - } - transmittedLight.a /= 3.0; - #else - vec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, ior, modelMatrix ); - vec3 refractedRayExit = position + transmissionRay; - vec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 ); - vec2 refractionCoords = ndcPos.xy / ndcPos.w; - refractionCoords += 1.0; - refractionCoords /= 2.0; - transmittedLight = getTransmissionSample( refractionCoords, roughness, ior ); - transmittance = diffuseColor * volumeAttenuation( length( transmissionRay ), attenuationColor, attenuationDistance ); - #endif - vec3 attenuatedColor = transmittance * transmittedLight.rgb; - vec3 F = EnvironmentBRDF( n, v, specularColor, specularF90, roughness ); - float transmittanceFactor = ( transmittance.r + transmittance.g + transmittance.b ) / 3.0; - return vec4( ( 1.0 - F ) * attenuatedColor, 1.0 - ( 1.0 - transmittedLight.a ) * transmittanceFactor ); - } -#endif`,qC=`#if defined( USE_UV ) || defined( USE_ANISOTROPY ) - varying vec2 vUv; -#endif -#ifdef USE_MAP - varying vec2 vMapUv; -#endif -#ifdef USE_ALPHAMAP - varying vec2 vAlphaMapUv; -#endif -#ifdef USE_LIGHTMAP - varying vec2 vLightMapUv; -#endif -#ifdef USE_AOMAP - varying vec2 vAoMapUv; -#endif -#ifdef USE_BUMPMAP - varying vec2 vBumpMapUv; -#endif -#ifdef USE_NORMALMAP - varying vec2 vNormalMapUv; -#endif -#ifdef USE_EMISSIVEMAP - varying vec2 vEmissiveMapUv; -#endif -#ifdef USE_METALNESSMAP - varying vec2 vMetalnessMapUv; -#endif -#ifdef USE_ROUGHNESSMAP - varying vec2 vRoughnessMapUv; -#endif -#ifdef USE_ANISOTROPYMAP - varying vec2 vAnisotropyMapUv; -#endif -#ifdef USE_CLEARCOATMAP - varying vec2 vClearcoatMapUv; -#endif -#ifdef USE_CLEARCOAT_NORMALMAP - varying vec2 vClearcoatNormalMapUv; -#endif -#ifdef USE_CLEARCOAT_ROUGHNESSMAP - varying vec2 vClearcoatRoughnessMapUv; -#endif -#ifdef USE_IRIDESCENCEMAP - varying vec2 vIridescenceMapUv; -#endif -#ifdef USE_IRIDESCENCE_THICKNESSMAP - varying vec2 vIridescenceThicknessMapUv; -#endif -#ifdef USE_SHEEN_COLORMAP - varying vec2 vSheenColorMapUv; -#endif -#ifdef USE_SHEEN_ROUGHNESSMAP - varying vec2 vSheenRoughnessMapUv; -#endif -#ifdef USE_SPECULARMAP - varying vec2 vSpecularMapUv; -#endif -#ifdef USE_SPECULAR_COLORMAP - varying vec2 vSpecularColorMapUv; -#endif -#ifdef USE_SPECULAR_INTENSITYMAP - varying vec2 vSpecularIntensityMapUv; -#endif -#ifdef USE_TRANSMISSIONMAP - uniform mat3 transmissionMapTransform; - varying vec2 vTransmissionMapUv; -#endif -#ifdef USE_THICKNESSMAP - uniform mat3 thicknessMapTransform; - varying vec2 vThicknessMapUv; -#endif`,YC=`#if defined( USE_UV ) || defined( USE_ANISOTROPY ) - varying vec2 vUv; -#endif -#ifdef USE_MAP - uniform mat3 mapTransform; - varying vec2 vMapUv; -#endif -#ifdef USE_ALPHAMAP - uniform mat3 alphaMapTransform; - varying vec2 vAlphaMapUv; -#endif -#ifdef USE_LIGHTMAP - uniform mat3 lightMapTransform; - varying vec2 vLightMapUv; -#endif -#ifdef USE_AOMAP - uniform mat3 aoMapTransform; - varying vec2 vAoMapUv; -#endif -#ifdef USE_BUMPMAP - uniform mat3 bumpMapTransform; - varying vec2 vBumpMapUv; -#endif -#ifdef USE_NORMALMAP - uniform mat3 normalMapTransform; - varying vec2 vNormalMapUv; -#endif -#ifdef USE_DISPLACEMENTMAP - uniform mat3 displacementMapTransform; - varying vec2 vDisplacementMapUv; -#endif -#ifdef USE_EMISSIVEMAP - uniform mat3 emissiveMapTransform; - varying vec2 vEmissiveMapUv; -#endif -#ifdef USE_METALNESSMAP - uniform mat3 metalnessMapTransform; - varying vec2 vMetalnessMapUv; -#endif -#ifdef USE_ROUGHNESSMAP - uniform mat3 roughnessMapTransform; - varying vec2 vRoughnessMapUv; -#endif -#ifdef USE_ANISOTROPYMAP - uniform mat3 anisotropyMapTransform; - varying vec2 vAnisotropyMapUv; -#endif -#ifdef USE_CLEARCOATMAP - uniform mat3 clearcoatMapTransform; - varying vec2 vClearcoatMapUv; -#endif -#ifdef USE_CLEARCOAT_NORMALMAP - uniform mat3 clearcoatNormalMapTransform; - varying vec2 vClearcoatNormalMapUv; -#endif -#ifdef USE_CLEARCOAT_ROUGHNESSMAP - uniform mat3 clearcoatRoughnessMapTransform; - varying vec2 vClearcoatRoughnessMapUv; -#endif -#ifdef USE_SHEEN_COLORMAP - uniform mat3 sheenColorMapTransform; - varying vec2 vSheenColorMapUv; -#endif -#ifdef USE_SHEEN_ROUGHNESSMAP - uniform mat3 sheenRoughnessMapTransform; - varying vec2 vSheenRoughnessMapUv; -#endif -#ifdef USE_IRIDESCENCEMAP - uniform mat3 iridescenceMapTransform; - varying vec2 vIridescenceMapUv; -#endif -#ifdef USE_IRIDESCENCE_THICKNESSMAP - uniform mat3 iridescenceThicknessMapTransform; - varying vec2 vIridescenceThicknessMapUv; -#endif -#ifdef USE_SPECULARMAP - uniform mat3 specularMapTransform; - varying vec2 vSpecularMapUv; -#endif -#ifdef USE_SPECULAR_COLORMAP - uniform mat3 specularColorMapTransform; - varying vec2 vSpecularColorMapUv; -#endif -#ifdef USE_SPECULAR_INTENSITYMAP - uniform mat3 specularIntensityMapTransform; - varying vec2 vSpecularIntensityMapUv; -#endif -#ifdef USE_TRANSMISSIONMAP - uniform mat3 transmissionMapTransform; - varying vec2 vTransmissionMapUv; -#endif -#ifdef USE_THICKNESSMAP - uniform mat3 thicknessMapTransform; - varying vec2 vThicknessMapUv; -#endif`,KC=`#if defined( USE_UV ) || defined( USE_ANISOTROPY ) - vUv = vec3( uv, 1 ).xy; -#endif -#ifdef USE_MAP - vMapUv = ( mapTransform * vec3( MAP_UV, 1 ) ).xy; -#endif -#ifdef USE_ALPHAMAP - vAlphaMapUv = ( alphaMapTransform * vec3( ALPHAMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_LIGHTMAP - vLightMapUv = ( lightMapTransform * vec3( LIGHTMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_AOMAP - vAoMapUv = ( aoMapTransform * vec3( AOMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_BUMPMAP - vBumpMapUv = ( bumpMapTransform * vec3( BUMPMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_NORMALMAP - vNormalMapUv = ( normalMapTransform * vec3( NORMALMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_DISPLACEMENTMAP - vDisplacementMapUv = ( displacementMapTransform * vec3( DISPLACEMENTMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_EMISSIVEMAP - vEmissiveMapUv = ( emissiveMapTransform * vec3( EMISSIVEMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_METALNESSMAP - vMetalnessMapUv = ( metalnessMapTransform * vec3( METALNESSMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_ROUGHNESSMAP - vRoughnessMapUv = ( roughnessMapTransform * vec3( ROUGHNESSMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_ANISOTROPYMAP - vAnisotropyMapUv = ( anisotropyMapTransform * vec3( ANISOTROPYMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_CLEARCOATMAP - vClearcoatMapUv = ( clearcoatMapTransform * vec3( CLEARCOATMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_CLEARCOAT_NORMALMAP - vClearcoatNormalMapUv = ( clearcoatNormalMapTransform * vec3( CLEARCOAT_NORMALMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_CLEARCOAT_ROUGHNESSMAP - vClearcoatRoughnessMapUv = ( clearcoatRoughnessMapTransform * vec3( CLEARCOAT_ROUGHNESSMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_IRIDESCENCEMAP - vIridescenceMapUv = ( iridescenceMapTransform * vec3( IRIDESCENCEMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_IRIDESCENCE_THICKNESSMAP - vIridescenceThicknessMapUv = ( iridescenceThicknessMapTransform * vec3( IRIDESCENCE_THICKNESSMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_SHEEN_COLORMAP - vSheenColorMapUv = ( sheenColorMapTransform * vec3( SHEEN_COLORMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_SHEEN_ROUGHNESSMAP - vSheenRoughnessMapUv = ( sheenRoughnessMapTransform * vec3( SHEEN_ROUGHNESSMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_SPECULARMAP - vSpecularMapUv = ( specularMapTransform * vec3( SPECULARMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_SPECULAR_COLORMAP - vSpecularColorMapUv = ( specularColorMapTransform * vec3( SPECULAR_COLORMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_SPECULAR_INTENSITYMAP - vSpecularIntensityMapUv = ( specularIntensityMapTransform * vec3( SPECULAR_INTENSITYMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_TRANSMISSIONMAP - vTransmissionMapUv = ( transmissionMapTransform * vec3( TRANSMISSIONMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_THICKNESSMAP - vThicknessMapUv = ( thicknessMapTransform * vec3( THICKNESSMAP_UV, 1 ) ).xy; -#endif`,ZC=`#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) || defined ( USE_TRANSMISSION ) || NUM_SPOT_LIGHT_COORDS > 0 - vec4 worldPosition = vec4( transformed, 1.0 ); - #ifdef USE_BATCHING - worldPosition = batchingMatrix * worldPosition; - #endif - #ifdef USE_INSTANCING - worldPosition = instanceMatrix * worldPosition; - #endif - worldPosition = modelMatrix * worldPosition; -#endif`;const $C=`varying vec2 vUv; -uniform mat3 uvTransform; -void main() { - vUv = ( uvTransform * vec3( uv, 1 ) ).xy; - gl_Position = vec4( position.xy, 1.0, 1.0 ); -}`,QC=`uniform sampler2D t2D; -uniform float backgroundIntensity; -varying vec2 vUv; -void main() { - vec4 texColor = texture2D( t2D, vUv ); - #ifdef DECODE_VIDEO_TEXTURE - texColor = vec4( mix( pow( texColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), texColor.rgb * 0.0773993808, vec3( lessThanEqual( texColor.rgb, vec3( 0.04045 ) ) ) ), texColor.w ); - #endif - texColor.rgb *= backgroundIntensity; - gl_FragColor = texColor; - #include <tonemapping_fragment> - #include <colorspace_fragment> -}`,JC=`varying vec3 vWorldDirection; -#include <common> -void main() { - vWorldDirection = transformDirection( position, modelMatrix ); - #include <begin_vertex> - #include <project_vertex> - gl_Position.z = gl_Position.w; -}`,eN=`#ifdef ENVMAP_TYPE_CUBE - uniform samplerCube envMap; -#elif defined( ENVMAP_TYPE_CUBE_UV ) - uniform sampler2D envMap; -#endif -uniform float backgroundBlurriness; -uniform float backgroundIntensity; -uniform mat3 backgroundRotation; -varying vec3 vWorldDirection; -#include <cube_uv_reflection_fragment> -void main() { - #ifdef ENVMAP_TYPE_CUBE - vec4 texColor = textureCube( envMap, backgroundRotation * vWorldDirection ); - #elif defined( ENVMAP_TYPE_CUBE_UV ) - vec4 texColor = textureCubeUV( envMap, backgroundRotation * vWorldDirection, backgroundBlurriness ); - #else - vec4 texColor = vec4( 0.0, 0.0, 0.0, 1.0 ); - #endif - texColor.rgb *= backgroundIntensity; - gl_FragColor = texColor; - #include <tonemapping_fragment> - #include <colorspace_fragment> -}`,tN=`varying vec3 vWorldDirection; -#include <common> -void main() { - vWorldDirection = transformDirection( position, modelMatrix ); - #include <begin_vertex> - #include <project_vertex> - gl_Position.z = gl_Position.w; -}`,nN=`uniform samplerCube tCube; -uniform float tFlip; -uniform float opacity; -varying vec3 vWorldDirection; -void main() { - vec4 texColor = textureCube( tCube, vec3( tFlip * vWorldDirection.x, vWorldDirection.yz ) ); - gl_FragColor = texColor; - gl_FragColor.a *= opacity; - #include <tonemapping_fragment> - #include <colorspace_fragment> -}`,iN=`#include <common> -#include <batching_pars_vertex> -#include <uv_pars_vertex> -#include <displacementmap_pars_vertex> -#include <morphtarget_pars_vertex> -#include <skinning_pars_vertex> -#include <logdepthbuf_pars_vertex> -#include <clipping_planes_pars_vertex> -varying vec2 vHighPrecisionZW; -void main() { - #include <uv_vertex> - #include <batching_vertex> - #include <skinbase_vertex> - #include <morphinstance_vertex> - #ifdef USE_DISPLACEMENTMAP - #include <beginnormal_vertex> - #include <morphnormal_vertex> - #include <skinnormal_vertex> - #endif - #include <begin_vertex> - #include <morphtarget_vertex> - #include <skinning_vertex> - #include <displacementmap_vertex> - #include <project_vertex> - #include <logdepthbuf_vertex> - #include <clipping_planes_vertex> - vHighPrecisionZW = gl_Position.zw; -}`,aN=`#if DEPTH_PACKING == 3200 - uniform float opacity; -#endif -#include <common> -#include <packing> -#include <uv_pars_fragment> -#include <map_pars_fragment> -#include <alphamap_pars_fragment> -#include <alphatest_pars_fragment> -#include <alphahash_pars_fragment> -#include <logdepthbuf_pars_fragment> -#include <clipping_planes_pars_fragment> -varying vec2 vHighPrecisionZW; -void main() { - vec4 diffuseColor = vec4( 1.0 ); - #include <clipping_planes_fragment> - #if DEPTH_PACKING == 3200 - diffuseColor.a = opacity; - #endif - #include <map_fragment> - #include <alphamap_fragment> - #include <alphatest_fragment> - #include <alphahash_fragment> - #include <logdepthbuf_fragment> - #ifdef USE_REVERSED_DEPTH_BUFFER - float fragCoordZ = vHighPrecisionZW[ 0 ] / vHighPrecisionZW[ 1 ]; - #else - float fragCoordZ = 0.5 * vHighPrecisionZW[ 0 ] / vHighPrecisionZW[ 1 ] + 0.5; - #endif - #if DEPTH_PACKING == 3200 - gl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity ); - #elif DEPTH_PACKING == 3201 - gl_FragColor = packDepthToRGBA( fragCoordZ ); - #elif DEPTH_PACKING == 3202 - gl_FragColor = vec4( packDepthToRGB( fragCoordZ ), 1.0 ); - #elif DEPTH_PACKING == 3203 - gl_FragColor = vec4( packDepthToRG( fragCoordZ ), 0.0, 1.0 ); - #endif -}`,sN=`#define DISTANCE -varying vec3 vWorldPosition; -#include <common> -#include <batching_pars_vertex> -#include <uv_pars_vertex> -#include <displacementmap_pars_vertex> -#include <morphtarget_pars_vertex> -#include <skinning_pars_vertex> -#include <clipping_planes_pars_vertex> -void main() { - #include <uv_vertex> - #include <batching_vertex> - #include <skinbase_vertex> - #include <morphinstance_vertex> - #ifdef USE_DISPLACEMENTMAP - #include <beginnormal_vertex> - #include <morphnormal_vertex> - #include <skinnormal_vertex> - #endif - #include <begin_vertex> - #include <morphtarget_vertex> - #include <skinning_vertex> - #include <displacementmap_vertex> - #include <project_vertex> - #include <worldpos_vertex> - #include <clipping_planes_vertex> - vWorldPosition = worldPosition.xyz; -}`,rN=`#define DISTANCE -uniform vec3 referencePosition; -uniform float nearDistance; -uniform float farDistance; -varying vec3 vWorldPosition; -#include <common> -#include <uv_pars_fragment> -#include <map_pars_fragment> -#include <alphamap_pars_fragment> -#include <alphatest_pars_fragment> -#include <alphahash_pars_fragment> -#include <clipping_planes_pars_fragment> -void main() { - vec4 diffuseColor = vec4( 1.0 ); - #include <clipping_planes_fragment> - #include <map_fragment> - #include <alphamap_fragment> - #include <alphatest_fragment> - #include <alphahash_fragment> - float dist = length( vWorldPosition - referencePosition ); - dist = ( dist - nearDistance ) / ( farDistance - nearDistance ); - dist = saturate( dist ); - gl_FragColor = vec4( dist, 0.0, 0.0, 1.0 ); -}`,lN=`varying vec3 vWorldDirection; -#include <common> -void main() { - vWorldDirection = transformDirection( position, modelMatrix ); - #include <begin_vertex> - #include <project_vertex> -}`,oN=`uniform sampler2D tEquirect; -varying vec3 vWorldDirection; -#include <common> -void main() { - vec3 direction = normalize( vWorldDirection ); - vec2 sampleUV = equirectUv( direction ); - gl_FragColor = texture2D( tEquirect, sampleUV ); - #include <tonemapping_fragment> - #include <colorspace_fragment> -}`,cN=`uniform float scale; -attribute float lineDistance; -varying float vLineDistance; -#include <common> -#include <uv_pars_vertex> -#include <color_pars_vertex> -#include <fog_pars_vertex> -#include <morphtarget_pars_vertex> -#include <logdepthbuf_pars_vertex> -#include <clipping_planes_pars_vertex> -void main() { - vLineDistance = scale * lineDistance; - #include <uv_vertex> - #include <color_vertex> - #include <morphinstance_vertex> - #include <morphcolor_vertex> - #include <begin_vertex> - #include <morphtarget_vertex> - #include <project_vertex> - #include <logdepthbuf_vertex> - #include <clipping_planes_vertex> - #include <fog_vertex> -}`,uN=`uniform vec3 diffuse; -uniform float opacity; -uniform float dashSize; -uniform float totalSize; -varying float vLineDistance; -#include <common> -#include <color_pars_fragment> -#include <uv_pars_fragment> -#include <map_pars_fragment> -#include <fog_pars_fragment> -#include <logdepthbuf_pars_fragment> -#include <clipping_planes_pars_fragment> -void main() { - vec4 diffuseColor = vec4( diffuse, opacity ); - #include <clipping_planes_fragment> - if ( mod( vLineDistance, totalSize ) > dashSize ) { - discard; - } - vec3 outgoingLight = vec3( 0.0 ); - #include <logdepthbuf_fragment> - #include <map_fragment> - #include <color_fragment> - outgoingLight = diffuseColor.rgb; - #include <opaque_fragment> - #include <tonemapping_fragment> - #include <colorspace_fragment> - #include <fog_fragment> - #include <premultiplied_alpha_fragment> -}`,fN=`#include <common> -#include <batching_pars_vertex> -#include <uv_pars_vertex> -#include <envmap_pars_vertex> -#include <color_pars_vertex> -#include <fog_pars_vertex> -#include <morphtarget_pars_vertex> -#include <skinning_pars_vertex> -#include <logdepthbuf_pars_vertex> -#include <clipping_planes_pars_vertex> -void main() { - #include <uv_vertex> - #include <color_vertex> - #include <morphinstance_vertex> - #include <morphcolor_vertex> - #include <batching_vertex> - #if defined ( USE_ENVMAP ) || defined ( USE_SKINNING ) - #include <beginnormal_vertex> - #include <morphnormal_vertex> - #include <skinbase_vertex> - #include <skinnormal_vertex> - #include <defaultnormal_vertex> - #endif - #include <begin_vertex> - #include <morphtarget_vertex> - #include <skinning_vertex> - #include <project_vertex> - #include <logdepthbuf_vertex> - #include <clipping_planes_vertex> - #include <worldpos_vertex> - #include <envmap_vertex> - #include <fog_vertex> -}`,dN=`uniform vec3 diffuse; -uniform float opacity; -#ifndef FLAT_SHADED - varying vec3 vNormal; -#endif -#include <common> -#include <dithering_pars_fragment> -#include <color_pars_fragment> -#include <uv_pars_fragment> -#include <map_pars_fragment> -#include <alphamap_pars_fragment> -#include <alphatest_pars_fragment> -#include <alphahash_pars_fragment> -#include <aomap_pars_fragment> -#include <lightmap_pars_fragment> -#include <envmap_common_pars_fragment> -#include <envmap_pars_fragment> -#include <fog_pars_fragment> -#include <specularmap_pars_fragment> -#include <logdepthbuf_pars_fragment> -#include <clipping_planes_pars_fragment> -void main() { - vec4 diffuseColor = vec4( diffuse, opacity ); - #include <clipping_planes_fragment> - #include <logdepthbuf_fragment> - #include <map_fragment> - #include <color_fragment> - #include <alphamap_fragment> - #include <alphatest_fragment> - #include <alphahash_fragment> - #include <specularmap_fragment> - ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); - #ifdef USE_LIGHTMAP - vec4 lightMapTexel = texture2D( lightMap, vLightMapUv ); - reflectedLight.indirectDiffuse += lightMapTexel.rgb * lightMapIntensity * RECIPROCAL_PI; - #else - reflectedLight.indirectDiffuse += vec3( 1.0 ); - #endif - #include <aomap_fragment> - reflectedLight.indirectDiffuse *= diffuseColor.rgb; - vec3 outgoingLight = reflectedLight.indirectDiffuse; - #include <envmap_fragment> - #include <opaque_fragment> - #include <tonemapping_fragment> - #include <colorspace_fragment> - #include <fog_fragment> - #include <premultiplied_alpha_fragment> - #include <dithering_fragment> -}`,hN=`#define LAMBERT -varying vec3 vViewPosition; -#include <common> -#include <batching_pars_vertex> -#include <uv_pars_vertex> -#include <displacementmap_pars_vertex> -#include <envmap_pars_vertex> -#include <color_pars_vertex> -#include <fog_pars_vertex> -#include <normal_pars_vertex> -#include <morphtarget_pars_vertex> -#include <skinning_pars_vertex> -#include <shadowmap_pars_vertex> -#include <logdepthbuf_pars_vertex> -#include <clipping_planes_pars_vertex> -void main() { - #include <uv_vertex> - #include <color_vertex> - #include <morphinstance_vertex> - #include <morphcolor_vertex> - #include <batching_vertex> - #include <beginnormal_vertex> - #include <morphnormal_vertex> - #include <skinbase_vertex> - #include <skinnormal_vertex> - #include <defaultnormal_vertex> - #include <normal_vertex> - #include <begin_vertex> - #include <morphtarget_vertex> - #include <skinning_vertex> - #include <displacementmap_vertex> - #include <project_vertex> - #include <logdepthbuf_vertex> - #include <clipping_planes_vertex> - vViewPosition = - mvPosition.xyz; - #include <worldpos_vertex> - #include <envmap_vertex> - #include <shadowmap_vertex> - #include <fog_vertex> -}`,pN=`#define LAMBERT -uniform vec3 diffuse; -uniform vec3 emissive; -uniform float opacity; -#include <common> -#include <dithering_pars_fragment> -#include <color_pars_fragment> -#include <uv_pars_fragment> -#include <map_pars_fragment> -#include <alphamap_pars_fragment> -#include <alphatest_pars_fragment> -#include <alphahash_pars_fragment> -#include <aomap_pars_fragment> -#include <lightmap_pars_fragment> -#include <emissivemap_pars_fragment> -#include <cube_uv_reflection_fragment> -#include <envmap_common_pars_fragment> -#include <envmap_pars_fragment> -#include <envmap_physical_pars_fragment> -#include <fog_pars_fragment> -#include <bsdfs> -#include <lights_pars_begin> -#include <normal_pars_fragment> -#include <lights_lambert_pars_fragment> -#include <shadowmap_pars_fragment> -#include <bumpmap_pars_fragment> -#include <normalmap_pars_fragment> -#include <specularmap_pars_fragment> -#include <logdepthbuf_pars_fragment> -#include <clipping_planes_pars_fragment> -void main() { - vec4 diffuseColor = vec4( diffuse, opacity ); - #include <clipping_planes_fragment> - ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); - vec3 totalEmissiveRadiance = emissive; - #include <logdepthbuf_fragment> - #include <map_fragment> - #include <color_fragment> - #include <alphamap_fragment> - #include <alphatest_fragment> - #include <alphahash_fragment> - #include <specularmap_fragment> - #include <normal_fragment_begin> - #include <normal_fragment_maps> - #include <emissivemap_fragment> - #include <lights_lambert_fragment> - #include <lights_fragment_begin> - #include <lights_fragment_maps> - #include <lights_fragment_end> - #include <aomap_fragment> - vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; - #include <envmap_fragment> - #include <opaque_fragment> - #include <tonemapping_fragment> - #include <colorspace_fragment> - #include <fog_fragment> - #include <premultiplied_alpha_fragment> - #include <dithering_fragment> -}`,mN=`#define MATCAP -varying vec3 vViewPosition; -#include <common> -#include <batching_pars_vertex> -#include <uv_pars_vertex> -#include <color_pars_vertex> -#include <displacementmap_pars_vertex> -#include <fog_pars_vertex> -#include <normal_pars_vertex> -#include <morphtarget_pars_vertex> -#include <skinning_pars_vertex> -#include <logdepthbuf_pars_vertex> -#include <clipping_planes_pars_vertex> -void main() { - #include <uv_vertex> - #include <color_vertex> - #include <morphinstance_vertex> - #include <morphcolor_vertex> - #include <batching_vertex> - #include <beginnormal_vertex> - #include <morphnormal_vertex> - #include <skinbase_vertex> - #include <skinnormal_vertex> - #include <defaultnormal_vertex> - #include <normal_vertex> - #include <begin_vertex> - #include <morphtarget_vertex> - #include <skinning_vertex> - #include <displacementmap_vertex> - #include <project_vertex> - #include <logdepthbuf_vertex> - #include <clipping_planes_vertex> - #include <fog_vertex> - vViewPosition = - mvPosition.xyz; -}`,gN=`#define MATCAP -uniform vec3 diffuse; -uniform float opacity; -uniform sampler2D matcap; -varying vec3 vViewPosition; -#include <common> -#include <dithering_pars_fragment> -#include <color_pars_fragment> -#include <uv_pars_fragment> -#include <map_pars_fragment> -#include <alphamap_pars_fragment> -#include <alphatest_pars_fragment> -#include <alphahash_pars_fragment> -#include <fog_pars_fragment> -#include <normal_pars_fragment> -#include <bumpmap_pars_fragment> -#include <normalmap_pars_fragment> -#include <logdepthbuf_pars_fragment> -#include <clipping_planes_pars_fragment> -void main() { - vec4 diffuseColor = vec4( diffuse, opacity ); - #include <clipping_planes_fragment> - #include <logdepthbuf_fragment> - #include <map_fragment> - #include <color_fragment> - #include <alphamap_fragment> - #include <alphatest_fragment> - #include <alphahash_fragment> - #include <normal_fragment_begin> - #include <normal_fragment_maps> - vec3 viewDir = normalize( vViewPosition ); - vec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) ); - vec3 y = cross( viewDir, x ); - vec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5; - #ifdef USE_MATCAP - vec4 matcapColor = texture2D( matcap, uv ); - #else - vec4 matcapColor = vec4( vec3( mix( 0.2, 0.8, uv.y ) ), 1.0 ); - #endif - vec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb; - #include <opaque_fragment> - #include <tonemapping_fragment> - #include <colorspace_fragment> - #include <fog_fragment> - #include <premultiplied_alpha_fragment> - #include <dithering_fragment> -}`,xN=`#define NORMAL -#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE ) - varying vec3 vViewPosition; -#endif -#include <common> -#include <batching_pars_vertex> -#include <uv_pars_vertex> -#include <displacementmap_pars_vertex> -#include <normal_pars_vertex> -#include <morphtarget_pars_vertex> -#include <skinning_pars_vertex> -#include <logdepthbuf_pars_vertex> -#include <clipping_planes_pars_vertex> -void main() { - #include <uv_vertex> - #include <batching_vertex> - #include <beginnormal_vertex> - #include <morphinstance_vertex> - #include <morphnormal_vertex> - #include <skinbase_vertex> - #include <skinnormal_vertex> - #include <defaultnormal_vertex> - #include <normal_vertex> - #include <begin_vertex> - #include <morphtarget_vertex> - #include <skinning_vertex> - #include <displacementmap_vertex> - #include <project_vertex> - #include <logdepthbuf_vertex> - #include <clipping_planes_vertex> -#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE ) - vViewPosition = - mvPosition.xyz; -#endif -}`,vN=`#define NORMAL -uniform float opacity; -#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE ) - varying vec3 vViewPosition; -#endif -#include <uv_pars_fragment> -#include <normal_pars_fragment> -#include <bumpmap_pars_fragment> -#include <normalmap_pars_fragment> -#include <logdepthbuf_pars_fragment> -#include <clipping_planes_pars_fragment> -void main() { - vec4 diffuseColor = vec4( 0.0, 0.0, 0.0, opacity ); - #include <clipping_planes_fragment> - #include <logdepthbuf_fragment> - #include <normal_fragment_begin> - #include <normal_fragment_maps> - gl_FragColor = vec4( normalize( normal ) * 0.5 + 0.5, diffuseColor.a ); - #ifdef OPAQUE - gl_FragColor.a = 1.0; - #endif -}`,_N=`#define PHONG -varying vec3 vViewPosition; -#include <common> -#include <batching_pars_vertex> -#include <uv_pars_vertex> -#include <displacementmap_pars_vertex> -#include <envmap_pars_vertex> -#include <color_pars_vertex> -#include <fog_pars_vertex> -#include <normal_pars_vertex> -#include <morphtarget_pars_vertex> -#include <skinning_pars_vertex> -#include <shadowmap_pars_vertex> -#include <logdepthbuf_pars_vertex> -#include <clipping_planes_pars_vertex> -void main() { - #include <uv_vertex> - #include <color_vertex> - #include <morphcolor_vertex> - #include <batching_vertex> - #include <beginnormal_vertex> - #include <morphinstance_vertex> - #include <morphnormal_vertex> - #include <skinbase_vertex> - #include <skinnormal_vertex> - #include <defaultnormal_vertex> - #include <normal_vertex> - #include <begin_vertex> - #include <morphtarget_vertex> - #include <skinning_vertex> - #include <displacementmap_vertex> - #include <project_vertex> - #include <logdepthbuf_vertex> - #include <clipping_planes_vertex> - vViewPosition = - mvPosition.xyz; - #include <worldpos_vertex> - #include <envmap_vertex> - #include <shadowmap_vertex> - #include <fog_vertex> -}`,yN=`#define PHONG -uniform vec3 diffuse; -uniform vec3 emissive; -uniform vec3 specular; -uniform float shininess; -uniform float opacity; -#include <common> -#include <dithering_pars_fragment> -#include <color_pars_fragment> -#include <uv_pars_fragment> -#include <map_pars_fragment> -#include <alphamap_pars_fragment> -#include <alphatest_pars_fragment> -#include <alphahash_pars_fragment> -#include <aomap_pars_fragment> -#include <lightmap_pars_fragment> -#include <emissivemap_pars_fragment> -#include <cube_uv_reflection_fragment> -#include <envmap_common_pars_fragment> -#include <envmap_pars_fragment> -#include <envmap_physical_pars_fragment> -#include <fog_pars_fragment> -#include <bsdfs> -#include <lights_pars_begin> -#include <normal_pars_fragment> -#include <lights_phong_pars_fragment> -#include <shadowmap_pars_fragment> -#include <bumpmap_pars_fragment> -#include <normalmap_pars_fragment> -#include <specularmap_pars_fragment> -#include <logdepthbuf_pars_fragment> -#include <clipping_planes_pars_fragment> -void main() { - vec4 diffuseColor = vec4( diffuse, opacity ); - #include <clipping_planes_fragment> - ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); - vec3 totalEmissiveRadiance = emissive; - #include <logdepthbuf_fragment> - #include <map_fragment> - #include <color_fragment> - #include <alphamap_fragment> - #include <alphatest_fragment> - #include <alphahash_fragment> - #include <specularmap_fragment> - #include <normal_fragment_begin> - #include <normal_fragment_maps> - #include <emissivemap_fragment> - #include <lights_phong_fragment> - #include <lights_fragment_begin> - #include <lights_fragment_maps> - #include <lights_fragment_end> - #include <aomap_fragment> - vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance; - #include <envmap_fragment> - #include <opaque_fragment> - #include <tonemapping_fragment> - #include <colorspace_fragment> - #include <fog_fragment> - #include <premultiplied_alpha_fragment> - #include <dithering_fragment> -}`,bN=`#define STANDARD -varying vec3 vViewPosition; -#ifdef USE_TRANSMISSION - varying vec3 vWorldPosition; -#endif -#include <common> -#include <batching_pars_vertex> -#include <uv_pars_vertex> -#include <displacementmap_pars_vertex> -#include <color_pars_vertex> -#include <fog_pars_vertex> -#include <normal_pars_vertex> -#include <morphtarget_pars_vertex> -#include <skinning_pars_vertex> -#include <shadowmap_pars_vertex> -#include <logdepthbuf_pars_vertex> -#include <clipping_planes_pars_vertex> -void main() { - #include <uv_vertex> - #include <color_vertex> - #include <morphinstance_vertex> - #include <morphcolor_vertex> - #include <batching_vertex> - #include <beginnormal_vertex> - #include <morphnormal_vertex> - #include <skinbase_vertex> - #include <skinnormal_vertex> - #include <defaultnormal_vertex> - #include <normal_vertex> - #include <begin_vertex> - #include <morphtarget_vertex> - #include <skinning_vertex> - #include <displacementmap_vertex> - #include <project_vertex> - #include <logdepthbuf_vertex> - #include <clipping_planes_vertex> - vViewPosition = - mvPosition.xyz; - #include <worldpos_vertex> - #include <shadowmap_vertex> - #include <fog_vertex> -#ifdef USE_TRANSMISSION - vWorldPosition = worldPosition.xyz; -#endif -}`,SN=`#define STANDARD -#ifdef PHYSICAL - #define IOR - #define USE_SPECULAR -#endif -uniform vec3 diffuse; -uniform vec3 emissive; -uniform float roughness; -uniform float metalness; -uniform float opacity; -#ifdef IOR - uniform float ior; -#endif -#ifdef USE_SPECULAR - uniform float specularIntensity; - uniform vec3 specularColor; - #ifdef USE_SPECULAR_COLORMAP - uniform sampler2D specularColorMap; - #endif - #ifdef USE_SPECULAR_INTENSITYMAP - uniform sampler2D specularIntensityMap; - #endif -#endif -#ifdef USE_CLEARCOAT - uniform float clearcoat; - uniform float clearcoatRoughness; -#endif -#ifdef USE_DISPERSION - uniform float dispersion; -#endif -#ifdef USE_IRIDESCENCE - uniform float iridescence; - uniform float iridescenceIOR; - uniform float iridescenceThicknessMinimum; - uniform float iridescenceThicknessMaximum; -#endif -#ifdef USE_SHEEN - uniform vec3 sheenColor; - uniform float sheenRoughness; - #ifdef USE_SHEEN_COLORMAP - uniform sampler2D sheenColorMap; - #endif - #ifdef USE_SHEEN_ROUGHNESSMAP - uniform sampler2D sheenRoughnessMap; - #endif -#endif -#ifdef USE_ANISOTROPY - uniform vec2 anisotropyVector; - #ifdef USE_ANISOTROPYMAP - uniform sampler2D anisotropyMap; - #endif -#endif -varying vec3 vViewPosition; -#include <common> -#include <dithering_pars_fragment> -#include <color_pars_fragment> -#include <uv_pars_fragment> -#include <map_pars_fragment> -#include <alphamap_pars_fragment> -#include <alphatest_pars_fragment> -#include <alphahash_pars_fragment> -#include <aomap_pars_fragment> -#include <lightmap_pars_fragment> -#include <emissivemap_pars_fragment> -#include <iridescence_fragment> -#include <cube_uv_reflection_fragment> -#include <envmap_common_pars_fragment> -#include <envmap_physical_pars_fragment> -#include <fog_pars_fragment> -#include <lights_pars_begin> -#include <normal_pars_fragment> -#include <lights_physical_pars_fragment> -#include <transmission_pars_fragment> -#include <shadowmap_pars_fragment> -#include <bumpmap_pars_fragment> -#include <normalmap_pars_fragment> -#include <clearcoat_pars_fragment> -#include <iridescence_pars_fragment> -#include <roughnessmap_pars_fragment> -#include <metalnessmap_pars_fragment> -#include <logdepthbuf_pars_fragment> -#include <clipping_planes_pars_fragment> -void main() { - vec4 diffuseColor = vec4( diffuse, opacity ); - #include <clipping_planes_fragment> - ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); - vec3 totalEmissiveRadiance = emissive; - #include <logdepthbuf_fragment> - #include <map_fragment> - #include <color_fragment> - #include <alphamap_fragment> - #include <alphatest_fragment> - #include <alphahash_fragment> - #include <roughnessmap_fragment> - #include <metalnessmap_fragment> - #include <normal_fragment_begin> - #include <normal_fragment_maps> - #include <clearcoat_normal_fragment_begin> - #include <clearcoat_normal_fragment_maps> - #include <emissivemap_fragment> - #include <lights_physical_fragment> - #include <lights_fragment_begin> - #include <lights_fragment_maps> - #include <lights_fragment_end> - #include <aomap_fragment> - vec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse; - vec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular; - #include <transmission_fragment> - vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance; - #ifdef USE_SHEEN - - outgoingLight = outgoingLight + sheenSpecularDirect + sheenSpecularIndirect; - - #endif - #ifdef USE_CLEARCOAT - float dotNVcc = saturate( dot( geometryClearcoatNormal, geometryViewDir ) ); - vec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc ); - outgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + ( clearcoatSpecularDirect + clearcoatSpecularIndirect ) * material.clearcoat; - #endif - #include <opaque_fragment> - #include <tonemapping_fragment> - #include <colorspace_fragment> - #include <fog_fragment> - #include <premultiplied_alpha_fragment> - #include <dithering_fragment> -}`,MN=`#define TOON -varying vec3 vViewPosition; -#include <common> -#include <batching_pars_vertex> -#include <uv_pars_vertex> -#include <displacementmap_pars_vertex> -#include <color_pars_vertex> -#include <fog_pars_vertex> -#include <normal_pars_vertex> -#include <morphtarget_pars_vertex> -#include <skinning_pars_vertex> -#include <shadowmap_pars_vertex> -#include <logdepthbuf_pars_vertex> -#include <clipping_planes_pars_vertex> -void main() { - #include <uv_vertex> - #include <color_vertex> - #include <morphinstance_vertex> - #include <morphcolor_vertex> - #include <batching_vertex> - #include <beginnormal_vertex> - #include <morphnormal_vertex> - #include <skinbase_vertex> - #include <skinnormal_vertex> - #include <defaultnormal_vertex> - #include <normal_vertex> - #include <begin_vertex> - #include <morphtarget_vertex> - #include <skinning_vertex> - #include <displacementmap_vertex> - #include <project_vertex> - #include <logdepthbuf_vertex> - #include <clipping_planes_vertex> - vViewPosition = - mvPosition.xyz; - #include <worldpos_vertex> - #include <shadowmap_vertex> - #include <fog_vertex> -}`,EN=`#define TOON -uniform vec3 diffuse; -uniform vec3 emissive; -uniform float opacity; -#include <common> -#include <dithering_pars_fragment> -#include <color_pars_fragment> -#include <uv_pars_fragment> -#include <map_pars_fragment> -#include <alphamap_pars_fragment> -#include <alphatest_pars_fragment> -#include <alphahash_pars_fragment> -#include <aomap_pars_fragment> -#include <lightmap_pars_fragment> -#include <emissivemap_pars_fragment> -#include <gradientmap_pars_fragment> -#include <fog_pars_fragment> -#include <bsdfs> -#include <lights_pars_begin> -#include <normal_pars_fragment> -#include <lights_toon_pars_fragment> -#include <shadowmap_pars_fragment> -#include <bumpmap_pars_fragment> -#include <normalmap_pars_fragment> -#include <logdepthbuf_pars_fragment> -#include <clipping_planes_pars_fragment> -void main() { - vec4 diffuseColor = vec4( diffuse, opacity ); - #include <clipping_planes_fragment> - ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); - vec3 totalEmissiveRadiance = emissive; - #include <logdepthbuf_fragment> - #include <map_fragment> - #include <color_fragment> - #include <alphamap_fragment> - #include <alphatest_fragment> - #include <alphahash_fragment> - #include <normal_fragment_begin> - #include <normal_fragment_maps> - #include <emissivemap_fragment> - #include <lights_toon_fragment> - #include <lights_fragment_begin> - #include <lights_fragment_maps> - #include <lights_fragment_end> - #include <aomap_fragment> - vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; - #include <opaque_fragment> - #include <tonemapping_fragment> - #include <colorspace_fragment> - #include <fog_fragment> - #include <premultiplied_alpha_fragment> - #include <dithering_fragment> -}`,TN=`uniform float size; -uniform float scale; -#include <common> -#include <color_pars_vertex> -#include <fog_pars_vertex> -#include <morphtarget_pars_vertex> -#include <logdepthbuf_pars_vertex> -#include <clipping_planes_pars_vertex> -#ifdef USE_POINTS_UV - varying vec2 vUv; - uniform mat3 uvTransform; -#endif -void main() { - #ifdef USE_POINTS_UV - vUv = ( uvTransform * vec3( uv, 1 ) ).xy; - #endif - #include <color_vertex> - #include <morphinstance_vertex> - #include <morphcolor_vertex> - #include <begin_vertex> - #include <morphtarget_vertex> - #include <project_vertex> - gl_PointSize = size; - #ifdef USE_SIZEATTENUATION - bool isPerspective = isPerspectiveMatrix( projectionMatrix ); - if ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z ); - #endif - #include <logdepthbuf_vertex> - #include <clipping_planes_vertex> - #include <worldpos_vertex> - #include <fog_vertex> -}`,AN=`uniform vec3 diffuse; -uniform float opacity; -#include <common> -#include <color_pars_fragment> -#include <map_particle_pars_fragment> -#include <alphatest_pars_fragment> -#include <alphahash_pars_fragment> -#include <fog_pars_fragment> -#include <logdepthbuf_pars_fragment> -#include <clipping_planes_pars_fragment> -void main() { - vec4 diffuseColor = vec4( diffuse, opacity ); - #include <clipping_planes_fragment> - vec3 outgoingLight = vec3( 0.0 ); - #include <logdepthbuf_fragment> - #include <map_particle_fragment> - #include <color_fragment> - #include <alphatest_fragment> - #include <alphahash_fragment> - outgoingLight = diffuseColor.rgb; - #include <opaque_fragment> - #include <tonemapping_fragment> - #include <colorspace_fragment> - #include <fog_fragment> - #include <premultiplied_alpha_fragment> -}`,wN=`#include <common> -#include <batching_pars_vertex> -#include <fog_pars_vertex> -#include <morphtarget_pars_vertex> -#include <skinning_pars_vertex> -#include <logdepthbuf_pars_vertex> -#include <shadowmap_pars_vertex> -void main() { - #include <batching_vertex> - #include <beginnormal_vertex> - #include <morphinstance_vertex> - #include <morphnormal_vertex> - #include <skinbase_vertex> - #include <skinnormal_vertex> - #include <defaultnormal_vertex> - #include <begin_vertex> - #include <morphtarget_vertex> - #include <skinning_vertex> - #include <project_vertex> - #include <logdepthbuf_vertex> - #include <worldpos_vertex> - #include <shadowmap_vertex> - #include <fog_vertex> -}`,CN=`uniform vec3 color; -uniform float opacity; -#include <common> -#include <fog_pars_fragment> -#include <bsdfs> -#include <lights_pars_begin> -#include <logdepthbuf_pars_fragment> -#include <shadowmap_pars_fragment> -#include <shadowmask_pars_fragment> -void main() { - #include <logdepthbuf_fragment> - gl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) ); - #include <tonemapping_fragment> - #include <colorspace_fragment> - #include <fog_fragment> - #include <premultiplied_alpha_fragment> -}`,NN=`uniform float rotation; -uniform vec2 center; -#include <common> -#include <uv_pars_vertex> -#include <fog_pars_vertex> -#include <logdepthbuf_pars_vertex> -#include <clipping_planes_pars_vertex> -void main() { - #include <uv_vertex> - vec4 mvPosition = modelViewMatrix[ 3 ]; - vec2 scale = vec2( length( modelMatrix[ 0 ].xyz ), length( modelMatrix[ 1 ].xyz ) ); - #ifndef USE_SIZEATTENUATION - bool isPerspective = isPerspectiveMatrix( projectionMatrix ); - if ( isPerspective ) scale *= - mvPosition.z; - #endif - vec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale; - vec2 rotatedPosition; - rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y; - rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y; - mvPosition.xy += rotatedPosition; - gl_Position = projectionMatrix * mvPosition; - #include <logdepthbuf_vertex> - #include <clipping_planes_vertex> - #include <fog_vertex> -}`,RN=`uniform vec3 diffuse; -uniform float opacity; -#include <common> -#include <uv_pars_fragment> -#include <map_pars_fragment> -#include <alphamap_pars_fragment> -#include <alphatest_pars_fragment> -#include <alphahash_pars_fragment> -#include <fog_pars_fragment> -#include <logdepthbuf_pars_fragment> -#include <clipping_planes_pars_fragment> -void main() { - vec4 diffuseColor = vec4( diffuse, opacity ); - #include <clipping_planes_fragment> - vec3 outgoingLight = vec3( 0.0 ); - #include <logdepthbuf_fragment> - #include <map_fragment> - #include <alphamap_fragment> - #include <alphatest_fragment> - #include <alphahash_fragment> - outgoingLight = diffuseColor.rgb; - #include <opaque_fragment> - #include <tonemapping_fragment> - #include <colorspace_fragment> - #include <fog_fragment> -}`,lt={alphahash_fragment:$A,alphahash_pars_fragment:QA,alphamap_fragment:JA,alphamap_pars_fragment:ew,alphatest_fragment:tw,alphatest_pars_fragment:nw,aomap_fragment:iw,aomap_pars_fragment:aw,batching_pars_vertex:sw,batching_vertex:rw,begin_vertex:lw,beginnormal_vertex:ow,bsdfs:cw,iridescence_fragment:uw,bumpmap_pars_fragment:fw,clipping_planes_fragment:dw,clipping_planes_pars_fragment:hw,clipping_planes_pars_vertex:pw,clipping_planes_vertex:mw,color_fragment:gw,color_pars_fragment:xw,color_pars_vertex:vw,color_vertex:_w,common:yw,cube_uv_reflection_fragment:bw,defaultnormal_vertex:Sw,displacementmap_pars_vertex:Mw,displacementmap_vertex:Ew,emissivemap_fragment:Tw,emissivemap_pars_fragment:Aw,colorspace_fragment:ww,colorspace_pars_fragment:Cw,envmap_fragment:Nw,envmap_common_pars_fragment:Rw,envmap_pars_fragment:Dw,envmap_pars_vertex:Lw,envmap_physical_pars_fragment:Gw,envmap_vertex:Uw,fog_vertex:Ow,fog_pars_vertex:Pw,fog_fragment:zw,fog_pars_fragment:Iw,gradientmap_pars_fragment:kw,lightmap_pars_fragment:Bw,lights_lambert_fragment:Fw,lights_lambert_pars_fragment:jw,lights_pars_begin:Hw,lights_toon_fragment:Vw,lights_toon_pars_fragment:Ww,lights_phong_fragment:Xw,lights_phong_pars_fragment:qw,lights_physical_fragment:Yw,lights_physical_pars_fragment:Kw,lights_fragment_begin:Zw,lights_fragment_maps:$w,lights_fragment_end:Qw,lightprobes_pars_fragment:Jw,logdepthbuf_fragment:eC,logdepthbuf_pars_fragment:tC,logdepthbuf_pars_vertex:nC,logdepthbuf_vertex:iC,map_fragment:aC,map_pars_fragment:sC,map_particle_fragment:rC,map_particle_pars_fragment:lC,metalnessmap_fragment:oC,metalnessmap_pars_fragment:cC,morphinstance_vertex:uC,morphcolor_vertex:fC,morphnormal_vertex:dC,morphtarget_pars_vertex:hC,morphtarget_vertex:pC,normal_fragment_begin:mC,normal_fragment_maps:gC,normal_pars_fragment:xC,normal_pars_vertex:vC,normal_vertex:_C,normalmap_pars_fragment:yC,clearcoat_normal_fragment_begin:bC,clearcoat_normal_fragment_maps:SC,clearcoat_pars_fragment:MC,iridescence_pars_fragment:EC,opaque_fragment:TC,packing:AC,premultiplied_alpha_fragment:wC,project_vertex:CC,dithering_fragment:NC,dithering_pars_fragment:RC,roughnessmap_fragment:DC,roughnessmap_pars_fragment:LC,shadowmap_pars_fragment:UC,shadowmap_pars_vertex:OC,shadowmap_vertex:PC,shadowmask_pars_fragment:zC,skinbase_vertex:IC,skinning_pars_vertex:kC,skinning_vertex:BC,skinnormal_vertex:FC,specularmap_fragment:jC,specularmap_pars_fragment:HC,tonemapping_fragment:GC,tonemapping_pars_fragment:VC,transmission_fragment:WC,transmission_pars_fragment:XC,uv_pars_fragment:qC,uv_pars_vertex:YC,uv_vertex:KC,worldpos_vertex:ZC,background_vert:$C,background_frag:QC,backgroundCube_vert:JC,backgroundCube_frag:eN,cube_vert:tN,cube_frag:nN,depth_vert:iN,depth_frag:aN,distance_vert:sN,distance_frag:rN,equirect_vert:lN,equirect_frag:oN,linedashed_vert:cN,linedashed_frag:uN,meshbasic_vert:fN,meshbasic_frag:dN,meshlambert_vert:hN,meshlambert_frag:pN,meshmatcap_vert:mN,meshmatcap_frag:gN,meshnormal_vert:xN,meshnormal_frag:vN,meshphong_vert:_N,meshphong_frag:yN,meshphysical_vert:bN,meshphysical_frag:SN,meshtoon_vert:MN,meshtoon_frag:EN,points_vert:TN,points_frag:AN,shadow_vert:wN,shadow_frag:CN,sprite_vert:NN,sprite_frag:RN},De={common:{diffuse:{value:new gt(16777215)},opacity:{value:1},map:{value:null},mapTransform:{value:new it},alphaMap:{value:null},alphaMapTransform:{value:new it},alphaTest:{value:0}},specularmap:{specularMap:{value:null},specularMapTransform:{value:new it}},envmap:{envMap:{value:null},envMapRotation:{value:new it},reflectivity:{value:1},ior:{value:1.5},refractionRatio:{value:.98},dfgLUT:{value:null}},aomap:{aoMap:{value:null},aoMapIntensity:{value:1},aoMapTransform:{value:new it}},lightmap:{lightMap:{value:null},lightMapIntensity:{value:1},lightMapTransform:{value:new it}},bumpmap:{bumpMap:{value:null},bumpMapTransform:{value:new it},bumpScale:{value:1}},normalmap:{normalMap:{value:null},normalMapTransform:{value:new it},normalScale:{value:new At(1,1)}},displacementmap:{displacementMap:{value:null},displacementMapTransform:{value:new it},displacementScale:{value:1},displacementBias:{value:0}},emissivemap:{emissiveMap:{value:null},emissiveMapTransform:{value:new it}},metalnessmap:{metalnessMap:{value:null},metalnessMapTransform:{value:new it}},roughnessmap:{roughnessMap:{value:null},roughnessMapTransform:{value:new it}},gradientmap:{gradientMap:{value:null}},fog:{fogDensity:{value:25e-5},fogNear:{value:1},fogFar:{value:2e3},fogColor:{value:new gt(16777215)}},lights:{ambientLightColor:{value:[]},lightProbe:{value:[]},directionalLights:{value:[],properties:{direction:{},color:{}}},directionalLightShadows:{value:[],properties:{shadowIntensity:1,shadowBias:{},shadowNormalBias:{},shadowRadius:{},shadowMapSize:{}}},directionalShadowMatrix:{value:[]},spotLights:{value:[],properties:{color:{},position:{},direction:{},distance:{},coneCos:{},penumbraCos:{},decay:{}}},spotLightShadows:{value:[],properties:{shadowIntensity:1,shadowBias:{},shadowNormalBias:{},shadowRadius:{},shadowMapSize:{}}},spotLightMap:{value:[]},spotLightMatrix:{value:[]},pointLights:{value:[],properties:{color:{},position:{},decay:{},distance:{}}},pointLightShadows:{value:[],properties:{shadowIntensity:1,shadowBias:{},shadowNormalBias:{},shadowRadius:{},shadowMapSize:{},shadowCameraNear:{},shadowCameraFar:{}}},pointShadowMatrix:{value:[]},hemisphereLights:{value:[],properties:{direction:{},skyColor:{},groundColor:{}}},rectAreaLights:{value:[],properties:{color:{},position:{},width:{},height:{}}},ltc_1:{value:null},ltc_2:{value:null},probesSH:{value:null},probesMin:{value:new Y},probesMax:{value:new Y},probesResolution:{value:new Y}},points:{diffuse:{value:new gt(16777215)},opacity:{value:1},size:{value:1},scale:{value:1},map:{value:null},alphaMap:{value:null},alphaMapTransform:{value:new it},alphaTest:{value:0},uvTransform:{value:new it}},sprite:{diffuse:{value:new gt(16777215)},opacity:{value:1},center:{value:new At(.5,.5)},rotation:{value:0},map:{value:null},mapTransform:{value:new it},alphaMap:{value:null},alphaMapTransform:{value:new it},alphaTest:{value:0}}},$i={basic:{uniforms:Dn([De.common,De.specularmap,De.envmap,De.aomap,De.lightmap,De.fog]),vertexShader:lt.meshbasic_vert,fragmentShader:lt.meshbasic_frag},lambert:{uniforms:Dn([De.common,De.specularmap,De.envmap,De.aomap,De.lightmap,De.emissivemap,De.bumpmap,De.normalmap,De.displacementmap,De.fog,De.lights,{emissive:{value:new gt(0)},envMapIntensity:{value:1}}]),vertexShader:lt.meshlambert_vert,fragmentShader:lt.meshlambert_frag},phong:{uniforms:Dn([De.common,De.specularmap,De.envmap,De.aomap,De.lightmap,De.emissivemap,De.bumpmap,De.normalmap,De.displacementmap,De.fog,De.lights,{emissive:{value:new gt(0)},specular:{value:new gt(1118481)},shininess:{value:30},envMapIntensity:{value:1}}]),vertexShader:lt.meshphong_vert,fragmentShader:lt.meshphong_frag},standard:{uniforms:Dn([De.common,De.envmap,De.aomap,De.lightmap,De.emissivemap,De.bumpmap,De.normalmap,De.displacementmap,De.roughnessmap,De.metalnessmap,De.fog,De.lights,{emissive:{value:new gt(0)},roughness:{value:1},metalness:{value:0},envMapIntensity:{value:1}}]),vertexShader:lt.meshphysical_vert,fragmentShader:lt.meshphysical_frag},toon:{uniforms:Dn([De.common,De.aomap,De.lightmap,De.emissivemap,De.bumpmap,De.normalmap,De.displacementmap,De.gradientmap,De.fog,De.lights,{emissive:{value:new gt(0)}}]),vertexShader:lt.meshtoon_vert,fragmentShader:lt.meshtoon_frag},matcap:{uniforms:Dn([De.common,De.bumpmap,De.normalmap,De.displacementmap,De.fog,{matcap:{value:null}}]),vertexShader:lt.meshmatcap_vert,fragmentShader:lt.meshmatcap_frag},points:{uniforms:Dn([De.points,De.fog]),vertexShader:lt.points_vert,fragmentShader:lt.points_frag},dashed:{uniforms:Dn([De.common,De.fog,{scale:{value:1},dashSize:{value:1},totalSize:{value:2}}]),vertexShader:lt.linedashed_vert,fragmentShader:lt.linedashed_frag},depth:{uniforms:Dn([De.common,De.displacementmap]),vertexShader:lt.depth_vert,fragmentShader:lt.depth_frag},normal:{uniforms:Dn([De.common,De.bumpmap,De.normalmap,De.displacementmap,{opacity:{value:1}}]),vertexShader:lt.meshnormal_vert,fragmentShader:lt.meshnormal_frag},sprite:{uniforms:Dn([De.sprite,De.fog]),vertexShader:lt.sprite_vert,fragmentShader:lt.sprite_frag},background:{uniforms:{uvTransform:{value:new it},t2D:{value:null},backgroundIntensity:{value:1}},vertexShader:lt.background_vert,fragmentShader:lt.background_frag},backgroundCube:{uniforms:{envMap:{value:null},backgroundBlurriness:{value:0},backgroundIntensity:{value:1},backgroundRotation:{value:new it}},vertexShader:lt.backgroundCube_vert,fragmentShader:lt.backgroundCube_frag},cube:{uniforms:{tCube:{value:null},tFlip:{value:-1},opacity:{value:1}},vertexShader:lt.cube_vert,fragmentShader:lt.cube_frag},equirect:{uniforms:{tEquirect:{value:null}},vertexShader:lt.equirect_vert,fragmentShader:lt.equirect_frag},distance:{uniforms:Dn([De.common,De.displacementmap,{referencePosition:{value:new Y},nearDistance:{value:1},farDistance:{value:1e3}}]),vertexShader:lt.distance_vert,fragmentShader:lt.distance_frag},shadow:{uniforms:Dn([De.lights,De.fog,{color:{value:new gt(0)},opacity:{value:1}}]),vertexShader:lt.shadow_vert,fragmentShader:lt.shadow_frag}};$i.physical={uniforms:Dn([$i.standard.uniforms,{clearcoat:{value:0},clearcoatMap:{value:null},clearcoatMapTransform:{value:new it},clearcoatNormalMap:{value:null},clearcoatNormalMapTransform:{value:new it},clearcoatNormalScale:{value:new At(1,1)},clearcoatRoughness:{value:0},clearcoatRoughnessMap:{value:null},clearcoatRoughnessMapTransform:{value:new it},dispersion:{value:0},iridescence:{value:0},iridescenceMap:{value:null},iridescenceMapTransform:{value:new it},iridescenceIOR:{value:1.3},iridescenceThicknessMinimum:{value:100},iridescenceThicknessMaximum:{value:400},iridescenceThicknessMap:{value:null},iridescenceThicknessMapTransform:{value:new it},sheen:{value:0},sheenColor:{value:new gt(0)},sheenColorMap:{value:null},sheenColorMapTransform:{value:new it},sheenRoughness:{value:1},sheenRoughnessMap:{value:null},sheenRoughnessMapTransform:{value:new it},transmission:{value:0},transmissionMap:{value:null},transmissionMapTransform:{value:new it},transmissionSamplerSize:{value:new At},transmissionSamplerMap:{value:null},thickness:{value:0},thicknessMap:{value:null},thicknessMapTransform:{value:new it},attenuationDistance:{value:0},attenuationColor:{value:new gt(0)},specularColor:{value:new gt(1,1,1)},specularColorMap:{value:null},specularColorMapTransform:{value:new it},specularIntensity:{value:1},specularIntensityMap:{value:null},specularIntensityMapTransform:{value:new it},anisotropyVector:{value:new At},anisotropyMap:{value:null},anisotropyMapTransform:{value:new it}}]),vertexShader:lt.meshphysical_vert,fragmentShader:lt.meshphysical_frag};const Qc={r:0,b:0,g:0},DN=new Ft,CS=new it;CS.set(-1,0,0,0,1,0,0,0,1);function LN(t,e,n,i,a,s){const r=new gt(0);let o=a===!0?0:1,c,u,d=null,m=0,f=null;function p(v){let b=v.isScene===!0?v.background:null;if(b&&b.isTexture){const y=v.backgroundBlurriness>0;b=e.get(b,y)}return b}function x(v){let b=!1;const y=p(v);y===null?g(r,o):y&&y.isColor&&(g(y,1),b=!0);const A=t.xr.getEnvironmentBlendMode();A==="additive"?n.buffers.color.setClear(0,0,0,1,s):A==="alpha-blend"&&n.buffers.color.setClear(0,0,0,0,s),(t.autoClear||b)&&(n.buffers.depth.setTest(!0),n.buffers.depth.setMask(!0),n.buffers.color.setMask(!0),t.clear(t.autoClearColor,t.autoClearDepth,t.autoClearStencil))}function _(v,b){const y=p(b);y&&(y.isCubeTexture||y.mapping===If)?(u===void 0&&(u=new Ri(new nr(1,1,1),new Xn({name:"BackgroundCubeMaterial",uniforms:bl($i.backgroundCube.uniforms),vertexShader:$i.backgroundCube.vertexShader,fragmentShader:$i.backgroundCube.fragmentShader,side:Yn,depthTest:!1,depthWrite:!1,fog:!1,allowOverride:!1})),u.geometry.deleteAttribute("normal"),u.geometry.deleteAttribute("uv"),u.onBeforeRender=function(A,M,T){this.matrixWorld.copyPosition(T.matrixWorld)},Object.defineProperty(u.material,"envMap",{get:function(){return this.uniforms.envMap.value}}),i.update(u)),u.material.uniforms.envMap.value=y,u.material.uniforms.backgroundBlurriness.value=b.backgroundBlurriness,u.material.uniforms.backgroundIntensity.value=b.backgroundIntensity,u.material.uniforms.backgroundRotation.value.setFromMatrix4(DN.makeRotationFromEuler(b.backgroundRotation)).transpose(),y.isCubeTexture&&y.isRenderTargetTexture===!1&&u.material.uniforms.backgroundRotation.value.premultiply(CS),u.material.toneMapped=ht.getTransfer(y.colorSpace)!==Ct,(d!==y||m!==y.version||f!==t.toneMapping)&&(u.material.needsUpdate=!0,d=y,m=y.version,f=t.toneMapping),u.layers.enableAll(),v.unshift(u,u.geometry,u.material,0,0,null)):y&&y.isTexture&&(c===void 0&&(c=new Ri(new kf(2,2),new Xn({name:"BackgroundMaterial",uniforms:bl($i.background.uniforms),vertexShader:$i.background.vertexShader,fragmentShader:$i.background.fragmentShader,side:Ts,depthTest:!1,depthWrite:!1,fog:!1,allowOverride:!1})),c.geometry.deleteAttribute("normal"),Object.defineProperty(c.material,"map",{get:function(){return this.uniforms.t2D.value}}),i.update(c)),c.material.uniforms.t2D.value=y,c.material.uniforms.backgroundIntensity.value=b.backgroundIntensity,c.material.toneMapped=ht.getTransfer(y.colorSpace)!==Ct,y.matrixAutoUpdate===!0&&y.updateMatrix(),c.material.uniforms.uvTransform.value.copy(y.matrix),(d!==y||m!==y.version||f!==t.toneMapping)&&(c.material.needsUpdate=!0,d=y,m=y.version,f=t.toneMapping),c.layers.enableAll(),v.unshift(c,c.geometry,c.material,0,0,null))}function g(v,b){v.getRGB(Qc,TS(t)),n.buffers.color.setClear(Qc.r,Qc.g,Qc.b,b,s)}function h(){u!==void 0&&(u.geometry.dispose(),u.material.dispose(),u=void 0),c!==void 0&&(c.geometry.dispose(),c.material.dispose(),c=void 0)}return{getClearColor:function(){return r},setClearColor:function(v,b=1){r.set(v),o=b,g(r,o)},getClearAlpha:function(){return o},setClearAlpha:function(v){o=v,g(r,o)},render:x,addToRenderList:_,dispose:h}}function UN(t,e){const n=t.getParameter(t.MAX_VERTEX_ATTRIBS),i={},a=f(null);let s=a,r=!1;function o(L,O,W,z,D){let B=!1;const k=m(L,z,W,O);s!==k&&(s=k,u(s.object)),B=p(L,z,W,D),B&&x(L,z,W,D),D!==null&&e.update(D,t.ELEMENT_ARRAY_BUFFER),(B||r)&&(r=!1,y(L,O,W,z),D!==null&&t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,e.get(D).buffer))}function c(){return t.createVertexArray()}function u(L){return t.bindVertexArray(L)}function d(L){return t.deleteVertexArray(L)}function m(L,O,W,z){const D=z.wireframe===!0;let B=i[O.id];B===void 0&&(B={},i[O.id]=B);const k=L.isInstancedMesh===!0?L.id:0;let V=B[k];V===void 0&&(V={},B[k]=V);let X=V[W.id];X===void 0&&(X={},V[W.id]=X);let ge=X[D];return ge===void 0&&(ge=f(c()),X[D]=ge),ge}function f(L){const O=[],W=[],z=[];for(let D=0;D<n;D++)O[D]=0,W[D]=0,z[D]=0;return{geometry:null,program:null,wireframe:!1,newAttributes:O,enabledAttributes:W,attributeDivisors:z,object:L,attributes:{},index:null}}function p(L,O,W,z){const D=s.attributes,B=O.attributes;let k=0;const V=W.getAttributes();for(const X in V)if(V[X].location>=0){const he=D[X];let Ae=B[X];if(Ae===void 0&&(X==="instanceMatrix"&&L.instanceMatrix&&(Ae=L.instanceMatrix),X==="instanceColor"&&L.instanceColor&&(Ae=L.instanceColor)),he===void 0||he.attribute!==Ae||Ae&&he.data!==Ae.data)return!0;k++}return s.attributesNum!==k||s.index!==z}function x(L,O,W,z){const D={},B=O.attributes;let k=0;const V=W.getAttributes();for(const X in V)if(V[X].location>=0){let he=B[X];he===void 0&&(X==="instanceMatrix"&&L.instanceMatrix&&(he=L.instanceMatrix),X==="instanceColor"&&L.instanceColor&&(he=L.instanceColor));const Ae={};Ae.attribute=he,he&&he.data&&(Ae.data=he.data),D[X]=Ae,k++}s.attributes=D,s.attributesNum=k,s.index=z}function _(){const L=s.newAttributes;for(let O=0,W=L.length;O<W;O++)L[O]=0}function g(L){h(L,0)}function h(L,O){const W=s.newAttributes,z=s.enabledAttributes,D=s.attributeDivisors;W[L]=1,z[L]===0&&(t.enableVertexAttribArray(L),z[L]=1),D[L]!==O&&(t.vertexAttribDivisor(L,O),D[L]=O)}function v(){const L=s.newAttributes,O=s.enabledAttributes;for(let W=0,z=O.length;W<z;W++)O[W]!==L[W]&&(t.disableVertexAttribArray(W),O[W]=0)}function b(L,O,W,z,D,B,k){k===!0?t.vertexAttribIPointer(L,O,W,D,B):t.vertexAttribPointer(L,O,W,z,D,B)}function y(L,O,W,z){_();const D=z.attributes,B=W.getAttributes(),k=O.defaultAttributeValues;for(const V in B){const X=B[V];if(X.location>=0){let ge=D[V];if(ge===void 0&&(V==="instanceMatrix"&&L.instanceMatrix&&(ge=L.instanceMatrix),V==="instanceColor"&&L.instanceColor&&(ge=L.instanceColor)),ge!==void 0){const he=ge.normalized,Ae=ge.itemSize,q=e.get(ge);if(q===void 0)continue;const fe=q.buffer,Ne=q.type,ie=q.bytesPerElement,xe=Ne===t.INT||Ne===t.UNSIGNED_INT||ge.gpuType===cg;if(ge.isInterleavedBufferAttribute){const oe=ge.data,Ee=oe.stride,Oe=ge.offset;if(oe.isInstancedInterleavedBuffer){for(let ke=0;ke<X.locationSize;ke++)h(X.location+ke,oe.meshPerAttribute);L.isInstancedMesh!==!0&&z._maxInstanceCount===void 0&&(z._maxInstanceCount=oe.meshPerAttribute*oe.count)}else for(let ke=0;ke<X.locationSize;ke++)g(X.location+ke);t.bindBuffer(t.ARRAY_BUFFER,fe);for(let ke=0;ke<X.locationSize;ke++)b(X.location+ke,Ae/X.locationSize,Ne,he,Ee*ie,(Oe+Ae/X.locationSize*ke)*ie,xe)}else{if(ge.isInstancedBufferAttribute){for(let oe=0;oe<X.locationSize;oe++)h(X.location+oe,ge.meshPerAttribute);L.isInstancedMesh!==!0&&z._maxInstanceCount===void 0&&(z._maxInstanceCount=ge.meshPerAttribute*ge.count)}else for(let oe=0;oe<X.locationSize;oe++)g(X.location+oe);t.bindBuffer(t.ARRAY_BUFFER,fe);for(let oe=0;oe<X.locationSize;oe++)b(X.location+oe,Ae/X.locationSize,Ne,he,Ae*ie,Ae/X.locationSize*oe*ie,xe)}}else if(k!==void 0){const he=k[V];if(he!==void 0)switch(he.length){case 2:t.vertexAttrib2fv(X.location,he);break;case 3:t.vertexAttrib3fv(X.location,he);break;case 4:t.vertexAttrib4fv(X.location,he);break;default:t.vertexAttrib1fv(X.location,he)}}}}v()}function A(){C();for(const L in i){const O=i[L];for(const W in O){const z=O[W];for(const D in z){const B=z[D];for(const k in B)d(B[k].object),delete B[k];delete z[D]}}delete i[L]}}function M(L){if(i[L.id]===void 0)return;const O=i[L.id];for(const W in O){const z=O[W];for(const D in z){const B=z[D];for(const k in B)d(B[k].object),delete B[k];delete z[D]}}delete i[L.id]}function T(L){for(const O in i){const W=i[O];for(const z in W){const D=W[z];if(D[L.id]===void 0)continue;const B=D[L.id];for(const k in B)d(B[k].object),delete B[k];delete D[L.id]}}}function S(L){for(const O in i){const W=i[O],z=L.isInstancedMesh===!0?L.id:0,D=W[z];if(D!==void 0){for(const B in D){const k=D[B];for(const V in k)d(k[V].object),delete k[V];delete D[B]}delete W[z],Object.keys(W).length===0&&delete i[O]}}}function C(){R(),r=!0,s!==a&&(s=a,u(s.object))}function R(){a.geometry=null,a.program=null,a.wireframe=!1}return{setup:o,reset:C,resetDefaultState:R,dispose:A,releaseStatesOfGeometry:M,releaseStatesOfObject:S,releaseStatesOfProgram:T,initAttributes:_,enableAttribute:g,disableUnusedAttributes:v}}function ON(t,e,n){let i;function a(c){i=c}function s(c,u){t.drawArrays(i,c,u),n.update(u,i,1)}function r(c,u,d){d!==0&&(t.drawArraysInstanced(i,c,u,d),n.update(u,i,d))}function o(c,u,d){if(d===0)return;e.get("WEBGL_multi_draw").multiDrawArraysWEBGL(i,c,0,u,0,d);let f=0;for(let p=0;p<d;p++)f+=u[p];n.update(f,i,1)}this.setMode=a,this.render=s,this.renderInstances=r,this.renderMultiDraw=o}function PN(t,e,n,i){let a;function s(){if(a!==void 0)return a;if(e.has("EXT_texture_filter_anisotropic")===!0){const T=e.get("EXT_texture_filter_anisotropic");a=t.getParameter(T.MAX_TEXTURE_MAX_ANISOTROPY_EXT)}else a=0;return a}function r(T){return!(T!==ki&&i.convert(T)!==t.getParameter(t.IMPLEMENTATION_COLOR_READ_FORMAT))}function o(T){const S=T===za&&(e.has("EXT_color_buffer_half_float")||e.has("EXT_color_buffer_float"));return!(T!==Ei&&i.convert(T)!==t.getParameter(t.IMPLEMENTATION_COLOR_READ_TYPE)&&T!==Ii&&!S)}function c(T){if(T==="highp"){if(t.getShaderPrecisionFormat(t.VERTEX_SHADER,t.HIGH_FLOAT).precision>0&&t.getShaderPrecisionFormat(t.FRAGMENT_SHADER,t.HIGH_FLOAT).precision>0)return"highp";T="mediump"}return T==="mediump"&&t.getShaderPrecisionFormat(t.VERTEX_SHADER,t.MEDIUM_FLOAT).precision>0&&t.getShaderPrecisionFormat(t.FRAGMENT_SHADER,t.MEDIUM_FLOAT).precision>0?"mediump":"lowp"}let u=n.precision!==void 0?n.precision:"highp";const d=c(u);d!==u&&(Je("WebGLRenderer:",u,"not supported, using",d,"instead."),u=d);const m=n.logarithmicDepthBuffer===!0,f=n.reversedDepthBuffer===!0&&e.has("EXT_clip_control");n.reversedDepthBuffer===!0&&f===!1&&Je("WebGLRenderer: Unable to use reversed depth buffer due to missing EXT_clip_control extension. Fallback to default depth buffer.");const p=t.getParameter(t.MAX_TEXTURE_IMAGE_UNITS),x=t.getParameter(t.MAX_VERTEX_TEXTURE_IMAGE_UNITS),_=t.getParameter(t.MAX_TEXTURE_SIZE),g=t.getParameter(t.MAX_CUBE_MAP_TEXTURE_SIZE),h=t.getParameter(t.MAX_VERTEX_ATTRIBS),v=t.getParameter(t.MAX_VERTEX_UNIFORM_VECTORS),b=t.getParameter(t.MAX_VARYING_VECTORS),y=t.getParameter(t.MAX_FRAGMENT_UNIFORM_VECTORS),A=t.getParameter(t.MAX_SAMPLES),M=t.getParameter(t.SAMPLES);return{isWebGL2:!0,getMaxAnisotropy:s,getMaxPrecision:c,textureFormatReadable:r,textureTypeReadable:o,precision:u,logarithmicDepthBuffer:m,reversedDepthBuffer:f,maxTextures:p,maxVertexTextures:x,maxTextureSize:_,maxCubemapSize:g,maxAttributes:h,maxVertexUniforms:v,maxVaryings:b,maxFragmentUniforms:y,maxSamples:A,samples:M}}function zN(t){const e=this;let n=null,i=0,a=!1,s=!1;const r=new ks,o=new it,c={value:null,needsUpdate:!1};this.uniform=c,this.numPlanes=0,this.numIntersection=0,this.init=function(m,f){const p=m.length!==0||f||i!==0||a;return a=f,i=m.length,p},this.beginShadows=function(){s=!0,d(null)},this.endShadows=function(){s=!1},this.setGlobalState=function(m,f){n=d(m,f,0)},this.setState=function(m,f,p){const x=m.clippingPlanes,_=m.clipIntersection,g=m.clipShadows,h=t.get(m);if(!a||x===null||x.length===0||s&&!g)s?d(null):u();else{const v=s?0:i,b=v*4;let y=h.clippingState||null;c.value=y,y=d(x,f,b,p);for(let A=0;A!==b;++A)y[A]=n[A];h.clippingState=y,this.numIntersection=_?this.numPlanes:0,this.numPlanes+=v}};function u(){c.value!==n&&(c.value=n,c.needsUpdate=i>0),e.numPlanes=i,e.numIntersection=0}function d(m,f,p,x){const _=m!==null?m.length:0;let g=null;if(_!==0){if(g=c.value,x!==!0||g===null){const h=p+_*4,v=f.matrixWorldInverse;o.getNormalMatrix(v),(g===null||g.length<h)&&(g=new Float32Array(h));for(let b=0,y=p;b!==_;++b,y+=4)r.copy(m[b]).applyMatrix4(v,o),r.normal.toArray(g,y),g[y+3]=r.constant}c.value=g,c.needsUpdate=!0}return e.numPlanes=_,e.numIntersection=0,g}}const us=4,Y0=[.125,.215,.35,.446,.526,.582],Fs=20,IN=256,Zl=new yg,K0=new gt;let eh=null,th=0,nh=0,ih=!1;const kN=new Y;class Z0{constructor(e){this._renderer=e,this._pingPongRenderTarget=null,this._lodMax=0,this._cubeSize=0,this._sizeLods=[],this._sigmas=[],this._lodMeshes=[],this._backgroundBox=null,this._cubemapMaterial=null,this._equirectMaterial=null,this._blurMaterial=null,this._ggxMaterial=null}fromScene(e,n=0,i=.1,a=100,s={}){const{size:r=256,position:o=kN}=s;eh=this._renderer.getRenderTarget(),th=this._renderer.getActiveCubeFace(),nh=this._renderer.getActiveMipmapLevel(),ih=this._renderer.xr.enabled,this._renderer.xr.enabled=!1,this._setSize(r);const c=this._allocateTargets();return c.depthBuffer=!0,this._sceneToCubeUV(e,i,a,c,o),n>0&&this._blur(c,0,0,n),this._applyPMREM(c),this._cleanup(c),c}fromEquirectangular(e,n=null){return this._fromTexture(e,n)}fromCubemap(e,n=null){return this._fromTexture(e,n)}compileCubemapShader(){this._cubemapMaterial===null&&(this._cubemapMaterial=J0(),this._compileMaterial(this._cubemapMaterial))}compileEquirectangularShader(){this._equirectMaterial===null&&(this._equirectMaterial=Q0(),this._compileMaterial(this._equirectMaterial))}dispose(){this._dispose(),this._cubemapMaterial!==null&&this._cubemapMaterial.dispose(),this._equirectMaterial!==null&&this._equirectMaterial.dispose(),this._backgroundBox!==null&&(this._backgroundBox.geometry.dispose(),this._backgroundBox.material.dispose())}_setSize(e){this._lodMax=Math.floor(Math.log2(e)),this._cubeSize=Math.pow(2,this._lodMax)}_dispose(){this._blurMaterial!==null&&this._blurMaterial.dispose(),this._ggxMaterial!==null&&this._ggxMaterial.dispose(),this._pingPongRenderTarget!==null&&this._pingPongRenderTarget.dispose();for(let e=0;e<this._lodMeshes.length;e++)this._lodMeshes[e].geometry.dispose()}_cleanup(e){this._renderer.setRenderTarget(eh,th,nh),this._renderer.xr.enabled=ih,e.scissorTest=!1,Rr(e,0,0,e.width,e.height)}_fromTexture(e,n){e.mapping===Js||e.mapping===_l?this._setSize(e.image.length===0?16:e.image[0].width||e.image[0].image.width):this._setSize(e.image.width/4),eh=this._renderer.getRenderTarget(),th=this._renderer.getActiveCubeFace(),nh=this._renderer.getActiveMipmapLevel(),ih=this._renderer.xr.enabled,this._renderer.xr.enabled=!1;const i=n||this._allocateTargets();return this._textureToCubeUV(e,i),this._applyPMREM(i),this._cleanup(i),i}_allocateTargets(){const e=3*Math.max(this._cubeSize,112),n=4*this._cubeSize,i={magFilter:Rn,minFilter:Rn,generateMipmaps:!1,type:za,format:ki,colorSpace:lf,depthBuffer:!1},a=$0(e,n,i);if(this._pingPongRenderTarget===null||this._pingPongRenderTarget.width!==e||this._pingPongRenderTarget.height!==n){this._pingPongRenderTarget!==null&&this._dispose(),this._pingPongRenderTarget=$0(e,n,i);const{_lodMax:s}=this;({lodMeshes:this._lodMeshes,sizeLods:this._sizeLods,sigmas:this._sigmas}=BN(s)),this._blurMaterial=jN(s,e,n),this._ggxMaterial=FN(s,e,n)}return a}_compileMaterial(e){const n=new Ri(new Zn,e);this._renderer.compile(n,Zl)}_sceneToCubeUV(e,n,i,a,s){const c=new zi(90,1,n,i),u=[1,-1,1,1,1,1],d=[1,1,1,-1,-1,-1],m=this._renderer,f=m.autoClear,p=m.toneMapping;m.getClearColor(K0),m.toneMapping=ia,m.autoClear=!1,m.state.buffers.depth.getReversed()&&(m.setRenderTarget(a),m.clearDepth(),m.setRenderTarget(null)),this._backgroundBox===null&&(this._backgroundBox=new Ri(new nr,new _S({name:"PMREM.Background",side:Yn,depthWrite:!1,depthTest:!1})));const _=this._backgroundBox,g=_.material;let h=!1;const v=e.background;v?v.isColor&&(g.color.copy(v),e.background=null,h=!0):(g.color.copy(K0),h=!0);for(let b=0;b<6;b++){const y=b%3;y===0?(c.up.set(0,u[b],0),c.position.set(s.x,s.y,s.z),c.lookAt(s.x+d[b],s.y,s.z)):y===1?(c.up.set(0,0,u[b]),c.position.set(s.x,s.y,s.z),c.lookAt(s.x,s.y+d[b],s.z)):(c.up.set(0,u[b],0),c.position.set(s.x,s.y,s.z),c.lookAt(s.x,s.y,s.z+d[b]));const A=this._cubeSize;Rr(a,y*A,b>2?A:0,A,A),m.setRenderTarget(a),h&&m.render(_,c),m.render(e,c)}m.toneMapping=p,m.autoClear=f,e.background=v}_textureToCubeUV(e,n){const i=this._renderer,a=e.mapping===Js||e.mapping===_l;a?(this._cubemapMaterial===null&&(this._cubemapMaterial=J0()),this._cubemapMaterial.uniforms.flipEnvMap.value=e.isRenderTargetTexture===!1?-1:1):this._equirectMaterial===null&&(this._equirectMaterial=Q0());const s=a?this._cubemapMaterial:this._equirectMaterial,r=this._lodMeshes[0];r.material=s;const o=s.uniforms;o.envMap.value=e;const c=this._cubeSize;Rr(n,0,0,3*c,2*c),i.setRenderTarget(n),i.render(r,Zl)}_applyPMREM(e){const n=this._renderer,i=n.autoClear;n.autoClear=!1;const a=this._lodMeshes.length;for(let s=1;s<a;s++)this._applyGGXFilter(e,s-1,s);n.autoClear=i}_applyGGXFilter(e,n,i){const a=this._renderer,s=this._pingPongRenderTarget,r=this._ggxMaterial,o=this._lodMeshes[i];o.material=r;const c=r.uniforms,u=i/(this._lodMeshes.length-1),d=n/(this._lodMeshes.length-1),m=Math.sqrt(u*u-d*d),f=0+u*1.25,p=m*f,{_lodMax:x}=this,_=this._sizeLods[i],g=3*_*(i>x-us?i-x+us:0),h=4*(this._cubeSize-_);c.envMap.value=e.texture,c.roughness.value=p,c.mipInt.value=x-n,Rr(s,g,h,3*_,2*_),a.setRenderTarget(s),a.render(o,Zl),c.envMap.value=s.texture,c.roughness.value=0,c.mipInt.value=x-i,Rr(e,g,h,3*_,2*_),a.setRenderTarget(e),a.render(o,Zl)}_blur(e,n,i,a,s){const r=this._pingPongRenderTarget;this._halfBlur(e,r,n,i,a,"latitudinal",s),this._halfBlur(r,e,i,i,a,"longitudinal",s)}_halfBlur(e,n,i,a,s,r,o){const c=this._renderer,u=this._blurMaterial;r!=="latitudinal"&&r!=="longitudinal"&&St("blur direction must be either latitudinal or longitudinal!");const d=3,m=this._lodMeshes[a];m.material=u;const f=u.uniforms,p=this._sizeLods[i]-1,x=isFinite(s)?Math.PI/(2*p):2*Math.PI/(2*Fs-1),_=s/x,g=isFinite(s)?1+Math.floor(d*_):Fs;g>Fs&&Je(`sigmaRadians, ${s}, is too large and will clip, as it requested ${g} samples when the maximum is set to ${Fs}`);const h=[];let v=0;for(let T=0;T<Fs;++T){const S=T/_,C=Math.exp(-S*S/2);h.push(C),T===0?v+=C:T<g&&(v+=2*C)}for(let T=0;T<h.length;T++)h[T]=h[T]/v;f.envMap.value=e.texture,f.samples.value=g,f.weights.value=h,f.latitudinal.value=r==="latitudinal",o&&(f.poleAxis.value=o);const{_lodMax:b}=this;f.dTheta.value=x,f.mipInt.value=b-i;const y=this._sizeLods[a],A=3*y*(a>b-us?a-b+us:0),M=4*(this._cubeSize-y);Rr(n,A,M,3*y,2*y),c.setRenderTarget(n),c.render(m,Zl)}}function BN(t){const e=[],n=[],i=[];let a=t;const s=t-us+1+Y0.length;for(let r=0;r<s;r++){const o=Math.pow(2,a);e.push(o);let c=1/o;r>t-us?c=Y0[r-t+us-1]:r===0&&(c=0),n.push(c);const u=1/(o-2),d=-u,m=1+u,f=[d,d,m,d,m,m,d,d,m,m,d,m],p=6,x=6,_=3,g=2,h=1,v=new Float32Array(_*x*p),b=new Float32Array(g*x*p),y=new Float32Array(h*x*p);for(let M=0;M<p;M++){const T=M%3*2/3-1,S=M>2?0:-1,C=[T,S,0,T+2/3,S,0,T+2/3,S+1,0,T,S,0,T+2/3,S+1,0,T,S+1,0];v.set(C,_*x*M),b.set(f,g*x*M);const R=[M,M,M,M,M,M];y.set(R,h*x*M)}const A=new Zn;A.setAttribute("position",new cn(v,_)),A.setAttribute("uv",new cn(b,g)),A.setAttribute("faceIndex",new cn(y,h)),i.push(new Ri(A,null)),a>us&&a--}return{lodMeshes:i,sizeLods:e,sigmas:n}}function $0(t,e,n){const i=new aa(t,e,n);return i.texture.mapping=If,i.texture.name="PMREM.cubeUv",i.scissorTest=!0,i}function Rr(t,e,n,i,a){t.viewport.set(e,n,i,a),t.scissor.set(e,n,i,a)}function FN(t,e,n){return new Xn({name:"PMREMGGXConvolution",defines:{GGX_SAMPLES:IN,CUBEUV_TEXEL_WIDTH:1/e,CUBEUV_TEXEL_HEIGHT:1/n,CUBEUV_MAX_MIP:`${t}.0`},uniforms:{envMap:{value:null},roughness:{value:0},mipInt:{value:0}},vertexShader:Bf(),fragmentShader:` - - precision highp float; - precision highp int; - - varying vec3 vOutputDirection; - - uniform sampler2D envMap; - uniform float roughness; - uniform float mipInt; - - #define ENVMAP_TYPE_CUBE_UV - #include <cube_uv_reflection_fragment> - - #define PI 3.14159265359 - - // Van der Corput radical inverse - float radicalInverse_VdC(uint bits) { - bits = (bits << 16u) | (bits >> 16u); - bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); - bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); - bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); - bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); - return float(bits) * 2.3283064365386963e-10; // / 0x100000000 - } - - // Hammersley sequence - vec2 hammersley(uint i, uint N) { - return vec2(float(i) / float(N), radicalInverse_VdC(i)); - } - - // GGX VNDF importance sampling (Eric Heitz 2018) - // "Sampling the GGX Distribution of Visible Normals" - // https://jcgt.org/published/0007/04/01/ - vec3 importanceSampleGGX_VNDF(vec2 Xi, vec3 V, float roughness) { - float alpha = roughness * roughness; - - // Section 4.1: Orthonormal basis - vec3 T1 = vec3(1.0, 0.0, 0.0); - vec3 T2 = cross(V, T1); - - // Section 4.2: Parameterization of projected area - float r = sqrt(Xi.x); - float phi = 2.0 * PI * Xi.y; - float t1 = r * cos(phi); - float t2 = r * sin(phi); - float s = 0.5 * (1.0 + V.z); - t2 = (1.0 - s) * sqrt(1.0 - t1 * t1) + s * t2; - - // Section 4.3: Reprojection onto hemisphere - vec3 Nh = t1 * T1 + t2 * T2 + sqrt(max(0.0, 1.0 - t1 * t1 - t2 * t2)) * V; - - // Section 3.4: Transform back to ellipsoid configuration - return normalize(vec3(alpha * Nh.x, alpha * Nh.y, max(0.0, Nh.z))); - } - - void main() { - vec3 N = normalize(vOutputDirection); - vec3 V = N; // Assume view direction equals normal for pre-filtering - - vec3 prefilteredColor = vec3(0.0); - float totalWeight = 0.0; - - // For very low roughness, just sample the environment directly - if (roughness < 0.001) { - gl_FragColor = vec4(bilinearCubeUV(envMap, N, mipInt), 1.0); - return; - } - - // Tangent space basis for VNDF sampling - vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); - vec3 tangent = normalize(cross(up, N)); - vec3 bitangent = cross(N, tangent); - - for(uint i = 0u; i < uint(GGX_SAMPLES); i++) { - vec2 Xi = hammersley(i, uint(GGX_SAMPLES)); - - // For PMREM, V = N, so in tangent space V is always (0, 0, 1) - vec3 H_tangent = importanceSampleGGX_VNDF(Xi, vec3(0.0, 0.0, 1.0), roughness); - - // Transform H back to world space - vec3 H = normalize(tangent * H_tangent.x + bitangent * H_tangent.y + N * H_tangent.z); - vec3 L = normalize(2.0 * dot(V, H) * H - V); - - float NdotL = max(dot(N, L), 0.0); - - if(NdotL > 0.0) { - // Sample environment at fixed mip level - // VNDF importance sampling handles the distribution filtering - vec3 sampleColor = bilinearCubeUV(envMap, L, mipInt); - - // Weight by NdotL for the split-sum approximation - // VNDF PDF naturally accounts for the visible microfacet distribution - prefilteredColor += sampleColor * NdotL; - totalWeight += NdotL; - } - } - - if (totalWeight > 0.0) { - prefilteredColor = prefilteredColor / totalWeight; - } - - gl_FragColor = vec4(prefilteredColor, 1.0); - } - `,blending:Na,depthTest:!1,depthWrite:!1})}function jN(t,e,n){const i=new Float32Array(Fs),a=new Y(0,1,0);return new Xn({name:"SphericalGaussianBlur",defines:{n:Fs,CUBEUV_TEXEL_WIDTH:1/e,CUBEUV_TEXEL_HEIGHT:1/n,CUBEUV_MAX_MIP:`${t}.0`},uniforms:{envMap:{value:null},samples:{value:1},weights:{value:i},latitudinal:{value:!1},dTheta:{value:0},mipInt:{value:0},poleAxis:{value:a}},vertexShader:Bf(),fragmentShader:` - - precision mediump float; - precision mediump int; - - varying vec3 vOutputDirection; - - uniform sampler2D envMap; - uniform int samples; - uniform float weights[ n ]; - uniform bool latitudinal; - uniform float dTheta; - uniform float mipInt; - uniform vec3 poleAxis; - - #define ENVMAP_TYPE_CUBE_UV - #include <cube_uv_reflection_fragment> - - vec3 getSample( float theta, vec3 axis ) { - - float cosTheta = cos( theta ); - // Rodrigues' axis-angle rotation - vec3 sampleDirection = vOutputDirection * cosTheta - + cross( axis, vOutputDirection ) * sin( theta ) - + axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta ); - - return bilinearCubeUV( envMap, sampleDirection, mipInt ); - - } - - void main() { - - vec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection ); - - if ( all( equal( axis, vec3( 0.0 ) ) ) ) { - - axis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x ); - - } - - axis = normalize( axis ); - - gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); - gl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis ); - - for ( int i = 1; i < n; i++ ) { - - if ( i >= samples ) { - - break; - - } - - float theta = dTheta * float( i ); - gl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis ); - gl_FragColor.rgb += weights[ i ] * getSample( theta, axis ); - - } - - } - `,blending:Na,depthTest:!1,depthWrite:!1})}function Q0(){return new Xn({name:"EquirectangularToCubeUV",uniforms:{envMap:{value:null}},vertexShader:Bf(),fragmentShader:` - - precision mediump float; - precision mediump int; - - varying vec3 vOutputDirection; - - uniform sampler2D envMap; - - #include <common> - - void main() { - - vec3 outputDirection = normalize( vOutputDirection ); - vec2 uv = equirectUv( outputDirection ); - - gl_FragColor = vec4( texture2D ( envMap, uv ).rgb, 1.0 ); - - } - `,blending:Na,depthTest:!1,depthWrite:!1})}function J0(){return new Xn({name:"CubemapToCubeUV",uniforms:{envMap:{value:null},flipEnvMap:{value:-1}},vertexShader:Bf(),fragmentShader:` - - precision mediump float; - precision mediump int; - - uniform float flipEnvMap; - - varying vec3 vOutputDirection; - - uniform samplerCube envMap; - - void main() { - - gl_FragColor = textureCube( envMap, vec3( flipEnvMap * vOutputDirection.x, vOutputDirection.yz ) ); - - } - `,blending:Na,depthTest:!1,depthWrite:!1})}function Bf(){return` - - precision mediump float; - precision mediump int; - - attribute float faceIndex; - - varying vec3 vOutputDirection; - - // RH coordinate system; PMREM face-indexing convention - vec3 getDirection( vec2 uv, float face ) { - - uv = 2.0 * uv - 1.0; - - vec3 direction = vec3( uv, 1.0 ); - - if ( face == 0.0 ) { - - direction = direction.zyx; // ( 1, v, u ) pos x - - } else if ( face == 1.0 ) { - - direction = direction.xzy; - direction.xz *= -1.0; // ( -u, 1, -v ) pos y - - } else if ( face == 2.0 ) { - - direction.x *= -1.0; // ( -u, v, 1 ) pos z - - } else if ( face == 3.0 ) { - - direction = direction.zyx; - direction.xz *= -1.0; // ( -1, v, -u ) neg x - - } else if ( face == 4.0 ) { - - direction = direction.xzy; - direction.xy *= -1.0; // ( -u, -1, v ) neg y - - } else if ( face == 5.0 ) { - - direction.z *= -1.0; // ( u, v, -1 ) neg z - - } - - return direction; - - } - - void main() { - - vOutputDirection = getDirection( uv, faceIndex ); - gl_Position = vec4( position, 1.0 ); - - } - `}class NS extends aa{constructor(e=1,n={}){super(e,e,n),this.isWebGLCubeRenderTarget=!0;const i={width:e,height:e,depth:1},a=[i,i,i,i,i,i];this.texture=new MS(a),this._setTextureOptions(n),this.texture.isRenderTargetTexture=!0}fromEquirectangularTexture(e,n){this.texture.type=n.type,this.texture.colorSpace=n.colorSpace,this.texture.generateMipmaps=n.generateMipmaps,this.texture.minFilter=n.minFilter,this.texture.magFilter=n.magFilter;const i={uniforms:{tEquirect:{value:null}},vertexShader:` - - varying vec3 vWorldDirection; - - vec3 transformDirection( in vec3 dir, in mat4 matrix ) { - - return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); - - } - - void main() { - - vWorldDirection = transformDirection( position, modelMatrix ); - - #include <begin_vertex> - #include <project_vertex> - - } - `,fragmentShader:` - - uniform sampler2D tEquirect; - - varying vec3 vWorldDirection; - - #include <common> - - void main() { - - vec3 direction = normalize( vWorldDirection ); - - vec2 sampleUV = equirectUv( direction ); - - gl_FragColor = texture2D( tEquirect, sampleUV ); - - } - `},a=new nr(5,5,5),s=new Xn({name:"CubemapFromEquirect",uniforms:bl(i.uniforms),vertexShader:i.vertexShader,fragmentShader:i.fragmentShader,side:Yn,blending:Na});s.uniforms.tEquirect.value=n;const r=new Ri(a,s),o=n.minFilter;return n.minFilter===js&&(n.minFilter=Rn),new qA(1,10,this).update(e,r),n.minFilter=o,r.geometry.dispose(),r.material.dispose(),this}clear(e,n=!0,i=!0,a=!0){const s=e.getRenderTarget();for(let r=0;r<6;r++)e.setRenderTarget(this,r),e.clear(n,i,a);e.setRenderTarget(s)}}function HN(t){let e=new WeakMap,n=new WeakMap,i=null;function a(f,p=!1){return f==null?null:p?r(f):s(f)}function s(f){if(f&&f.isTexture){const p=f.mapping;if(p===Td||p===Ad)if(e.has(f)){const x=e.get(f).texture;return o(x,f.mapping)}else{const x=f.image;if(x&&x.height>0){const _=new NS(x.height);return _.fromEquirectangularTexture(t,f),e.set(f,_),f.addEventListener("dispose",u),o(_.texture,f.mapping)}else return null}}return f}function r(f){if(f&&f.isTexture){const p=f.mapping,x=p===Td||p===Ad,_=p===Js||p===_l;if(x||_){let g=n.get(f);const h=g!==void 0?g.texture.pmremVersion:0;if(f.isRenderTargetTexture&&f.pmremVersion!==h)return i===null&&(i=new Z0(t)),g=x?i.fromEquirectangular(f,g):i.fromCubemap(f,g),g.texture.pmremVersion=f.pmremVersion,n.set(f,g),g.texture;if(g!==void 0)return g.texture;{const v=f.image;return x&&v&&v.height>0||_&&v&&c(v)?(i===null&&(i=new Z0(t)),g=x?i.fromEquirectangular(f):i.fromCubemap(f),g.texture.pmremVersion=f.pmremVersion,n.set(f,g),f.addEventListener("dispose",d),g.texture):null}}}return f}function o(f,p){return p===Td?f.mapping=Js:p===Ad&&(f.mapping=_l),f}function c(f){let p=0;const x=6;for(let _=0;_<x;_++)f[_]!==void 0&&p++;return p===x}function u(f){const p=f.target;p.removeEventListener("dispose",u);const x=e.get(p);x!==void 0&&(e.delete(p),x.dispose())}function d(f){const p=f.target;p.removeEventListener("dispose",d);const x=n.get(p);x!==void 0&&(n.delete(p),x.dispose())}function m(){e=new WeakMap,n=new WeakMap,i!==null&&(i.dispose(),i=null)}return{get:a,dispose:m}}function GN(t){const e={};function n(i){if(e[i]!==void 0)return e[i];const a=t.getExtension(i);return e[i]=a,a}return{has:function(i){return n(i)!==null},init:function(){n("EXT_color_buffer_float"),n("WEBGL_clip_cull_distance"),n("OES_texture_float_linear"),n("EXT_color_buffer_half_float"),n("WEBGL_multisampled_render_to_texture"),n("WEBGL_render_shared_exponent")},get:function(i){const a=n(i);return a===null&&il("WebGLRenderer: "+i+" extension not supported."),a}}}function VN(t,e,n,i){const a={},s=new WeakMap;function r(m){const f=m.target;f.index!==null&&e.remove(f.index);for(const x in f.attributes)e.remove(f.attributes[x]);f.removeEventListener("dispose",r),delete a[f.id];const p=s.get(f);p&&(e.remove(p),s.delete(f)),i.releaseStatesOfGeometry(f),f.isInstancedBufferGeometry===!0&&delete f._maxInstanceCount,n.memory.geometries--}function o(m,f){return a[f.id]===!0||(f.addEventListener("dispose",r),a[f.id]=!0,n.memory.geometries++),f}function c(m){const f=m.attributes;for(const p in f)e.update(f[p],t.ARRAY_BUFFER)}function u(m){const f=[],p=m.index,x=m.attributes.position;let _=0;if(x===void 0)return;if(p!==null){const v=p.array;_=p.version;for(let b=0,y=v.length;b<y;b+=3){const A=v[b+0],M=v[b+1],T=v[b+2];f.push(A,M,M,T,T,A)}}else{const v=x.array;_=x.version;for(let b=0,y=v.length/3-1;b<y;b+=3){const A=b+0,M=b+1,T=b+2;f.push(A,M,M,T,T,A)}}const g=new(x.count>=65535?vS:xS)(f,1);g.version=_;const h=s.get(m);h&&e.remove(h),s.set(m,g)}function d(m){const f=s.get(m);if(f){const p=m.index;p!==null&&f.version<p.version&&u(m)}else u(m);return s.get(m)}return{get:o,update:c,getWireframeAttribute:d}}function WN(t,e,n){let i;function a(m){i=m}let s,r;function o(m){s=m.type,r=m.bytesPerElement}function c(m,f){t.drawElements(i,f,s,m*r),n.update(f,i,1)}function u(m,f,p){p!==0&&(t.drawElementsInstanced(i,f,s,m*r,p),n.update(f,i,p))}function d(m,f,p){if(p===0)return;e.get("WEBGL_multi_draw").multiDrawElementsWEBGL(i,f,0,s,m,0,p);let _=0;for(let g=0;g<p;g++)_+=f[g];n.update(_,i,1)}this.setMode=a,this.setIndex=o,this.render=c,this.renderInstances=u,this.renderMultiDraw=d}function XN(t){const e={geometries:0,textures:0},n={frame:0,calls:0,triangles:0,points:0,lines:0};function i(s,r,o){switch(n.calls++,r){case t.TRIANGLES:n.triangles+=o*(s/3);break;case t.LINES:n.lines+=o*(s/2);break;case t.LINE_STRIP:n.lines+=o*(s-1);break;case t.LINE_LOOP:n.lines+=o*s;break;case t.POINTS:n.points+=o*s;break;default:St("WebGLInfo: Unknown draw mode:",r);break}}function a(){n.calls=0,n.triangles=0,n.points=0,n.lines=0}return{memory:e,render:n,programs:null,autoReset:!0,reset:a,update:i}}function qN(t,e,n){const i=new WeakMap,a=new Qt;function s(r,o,c){const u=r.morphTargetInfluences,d=o.morphAttributes.position||o.morphAttributes.normal||o.morphAttributes.color,m=d!==void 0?d.length:0;let f=i.get(o);if(f===void 0||f.count!==m){let R=function(){S.dispose(),i.delete(o),o.removeEventListener("dispose",R)};var p=R;f!==void 0&&f.texture.dispose();const x=o.morphAttributes.position!==void 0,_=o.morphAttributes.normal!==void 0,g=o.morphAttributes.color!==void 0,h=o.morphAttributes.position||[],v=o.morphAttributes.normal||[],b=o.morphAttributes.color||[];let y=0;x===!0&&(y=1),_===!0&&(y=2),g===!0&&(y=3);let A=o.attributes.position.count*y,M=1;A>e.maxTextureSize&&(M=Math.ceil(A/e.maxTextureSize),A=e.maxTextureSize);const T=new Float32Array(A*M*4*m),S=new pS(T,A,M,m);S.type=Ii,S.needsUpdate=!0;const C=y*4;for(let L=0;L<m;L++){const O=h[L],W=v[L],z=b[L],D=A*M*4*L;for(let B=0;B<O.count;B++){const k=B*C;x===!0&&(a.fromBufferAttribute(O,B),T[D+k+0]=a.x,T[D+k+1]=a.y,T[D+k+2]=a.z,T[D+k+3]=0),_===!0&&(a.fromBufferAttribute(W,B),T[D+k+4]=a.x,T[D+k+5]=a.y,T[D+k+6]=a.z,T[D+k+7]=0),g===!0&&(a.fromBufferAttribute(z,B),T[D+k+8]=a.x,T[D+k+9]=a.y,T[D+k+10]=a.z,T[D+k+11]=z.itemSize===4?a.w:1)}}f={count:m,texture:S,size:new At(A,M)},i.set(o,f),o.addEventListener("dispose",R)}if(r.isInstancedMesh===!0&&r.morphTexture!==null)c.getUniforms().setValue(t,"morphTexture",r.morphTexture,n);else{let x=0;for(let g=0;g<u.length;g++)x+=u[g];const _=o.morphTargetsRelative?1:1-x;c.getUniforms().setValue(t,"morphTargetBaseInfluence",_),c.getUniforms().setValue(t,"morphTargetInfluences",u)}c.getUniforms().setValue(t,"morphTargetsTexture",f.texture,n),c.getUniforms().setValue(t,"morphTargetsTextureSize",f.size)}return{update:s}}function YN(t,e,n,i,a){let s=new WeakMap;function r(u){const d=a.render.frame,m=u.geometry,f=e.get(u,m);if(s.get(f)!==d&&(e.update(f),s.set(f,d)),u.isInstancedMesh&&(u.hasEventListener("dispose",c)===!1&&u.addEventListener("dispose",c),s.get(u)!==d&&(n.update(u.instanceMatrix,t.ARRAY_BUFFER),u.instanceColor!==null&&n.update(u.instanceColor,t.ARRAY_BUFFER),s.set(u,d))),u.isSkinnedMesh){const p=u.skeleton;s.get(p)!==d&&(p.update(),s.set(p,d))}return f}function o(){s=new WeakMap}function c(u){const d=u.target;d.removeEventListener("dispose",c),i.releaseStatesOfObject(d),n.remove(d.instanceMatrix),d.instanceColor!==null&&n.remove(d.instanceColor)}return{update:r,dispose:o}}const KN={[Jb]:"LINEAR_TONE_MAPPING",[eS]:"REINHARD_TONE_MAPPING",[tS]:"CINEON_TONE_MAPPING",[nS]:"ACES_FILMIC_TONE_MAPPING",[aS]:"AGX_TONE_MAPPING",[sS]:"NEUTRAL_TONE_MAPPING",[iS]:"CUSTOM_TONE_MAPPING"};function ZN(t,e,n,i,a,s){const r=new aa(e,n,{type:t,depthBuffer:a,stencilBuffer:s,samples:i?4:0,depthTexture:a?new yl(e,n):void 0}),o=new aa(e,n,{type:za,depthBuffer:!1,stencilBuffer:!1}),c=new Zn;c.setAttribute("position",new Ci([-1,3,0,-1,-1,0,3,-1,0],3)),c.setAttribute("uv",new Ci([0,2,0,0,2,0],2));const u=new VA({uniforms:{tDiffuse:{value:null}},vertexShader:` - precision highp float; - - uniform mat4 modelViewMatrix; - uniform mat4 projectionMatrix; - - attribute vec3 position; - attribute vec2 uv; - - varying vec2 vUv; - - void main() { - vUv = uv; - gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); - }`,fragmentShader:` - precision highp float; - - uniform sampler2D tDiffuse; - - varying vec2 vUv; - - #include <tonemapping_pars_fragment> - #include <colorspace_pars_fragment> - - void main() { - gl_FragColor = texture2D( tDiffuse, vUv ); - - #ifdef LINEAR_TONE_MAPPING - gl_FragColor.rgb = LinearToneMapping( gl_FragColor.rgb ); - #elif defined( REINHARD_TONE_MAPPING ) - gl_FragColor.rgb = ReinhardToneMapping( gl_FragColor.rgb ); - #elif defined( CINEON_TONE_MAPPING ) - gl_FragColor.rgb = CineonToneMapping( gl_FragColor.rgb ); - #elif defined( ACES_FILMIC_TONE_MAPPING ) - gl_FragColor.rgb = ACESFilmicToneMapping( gl_FragColor.rgb ); - #elif defined( AGX_TONE_MAPPING ) - gl_FragColor.rgb = AgXToneMapping( gl_FragColor.rgb ); - #elif defined( NEUTRAL_TONE_MAPPING ) - gl_FragColor.rgb = NeutralToneMapping( gl_FragColor.rgb ); - #elif defined( CUSTOM_TONE_MAPPING ) - gl_FragColor.rgb = CustomToneMapping( gl_FragColor.rgb ); - #endif - - #ifdef SRGB_TRANSFER - gl_FragColor = sRGBTransferOETF( gl_FragColor ); - #endif - }`,depthTest:!1,depthWrite:!1}),d=new Ri(c,u),m=new yg(-1,1,1,-1,0,1);let f=null,p=null,x=!1,_,g=null,h=[],v=!1;this.setSize=function(b,y){r.setSize(b,y),o.setSize(b,y);for(let A=0;A<h.length;A++){const M=h[A];M.setSize&&M.setSize(b,y)}},this.setEffects=function(b){h=b,v=h.length>0&&h[0].isRenderPass===!0;const y=r.width,A=r.height;for(let M=0;M<h.length;M++){const T=h[M];T.setSize&&T.setSize(y,A)}},this.begin=function(b,y){if(x||b.toneMapping===ia&&h.length===0)return!1;if(g=y,y!==null){const A=y.width,M=y.height;(r.width!==A||r.height!==M)&&this.setSize(A,M)}return v===!1&&b.setRenderTarget(r),_=b.toneMapping,b.toneMapping=ia,!0},this.hasRenderPass=function(){return v},this.end=function(b,y){b.toneMapping=_,x=!0;let A=r,M=o;for(let T=0;T<h.length;T++){const S=h[T];if(S.enabled!==!1&&(S.render(b,M,A,y),S.needsSwap!==!1)){const C=A;A=M,M=C}}if(f!==b.outputColorSpace||p!==b.toneMapping){f=b.outputColorSpace,p=b.toneMapping,u.defines={},ht.getTransfer(f)===Ct&&(u.defines.SRGB_TRANSFER="");const T=KN[p];T&&(u.defines[T]=""),u.needsUpdate=!0}u.uniforms.tDiffuse.value=A.texture,b.setRenderTarget(g),b.render(d,m),g=null,x=!1},this.isCompositing=function(){return x},this.dispose=function(){r.depthTexture&&r.depthTexture.dispose(),r.dispose(),o.dispose(),c.dispose(),u.dispose()}}const RS=new zn,tm=new yl(1,1),DS=new pS,LS=new xA,US=new MS,ev=[],tv=[],nv=new Float32Array(16),iv=new Float32Array(9),av=new Float32Array(4);function Ul(t,e,n){const i=t[0];if(i<=0||i>0)return t;const a=e*n;let s=ev[a];if(s===void 0&&(s=new Float32Array(a),ev[a]=s),e!==0){i.toArray(s,0);for(let r=1,o=0;r!==e;++r)o+=n,t[r].toArray(s,o)}return s}function dn(t,e){if(t.length!==e.length)return!1;for(let n=0,i=t.length;n<i;n++)if(t[n]!==e[n])return!1;return!0}function hn(t,e){for(let n=0,i=e.length;n<i;n++)t[n]=e[n]}function Ff(t,e){let n=tv[e];n===void 0&&(n=new Int32Array(e),tv[e]=n);for(let i=0;i!==e;++i)n[i]=t.allocateTextureUnit();return n}function $N(t,e){const n=this.cache;n[0]!==e&&(t.uniform1f(this.addr,e),n[0]=e)}function QN(t,e){const n=this.cache;if(e.x!==void 0)(n[0]!==e.x||n[1]!==e.y)&&(t.uniform2f(this.addr,e.x,e.y),n[0]=e.x,n[1]=e.y);else{if(dn(n,e))return;t.uniform2fv(this.addr,e),hn(n,e)}}function JN(t,e){const n=this.cache;if(e.x!==void 0)(n[0]!==e.x||n[1]!==e.y||n[2]!==e.z)&&(t.uniform3f(this.addr,e.x,e.y,e.z),n[0]=e.x,n[1]=e.y,n[2]=e.z);else if(e.r!==void 0)(n[0]!==e.r||n[1]!==e.g||n[2]!==e.b)&&(t.uniform3f(this.addr,e.r,e.g,e.b),n[0]=e.r,n[1]=e.g,n[2]=e.b);else{if(dn(n,e))return;t.uniform3fv(this.addr,e),hn(n,e)}}function eR(t,e){const n=this.cache;if(e.x!==void 0)(n[0]!==e.x||n[1]!==e.y||n[2]!==e.z||n[3]!==e.w)&&(t.uniform4f(this.addr,e.x,e.y,e.z,e.w),n[0]=e.x,n[1]=e.y,n[2]=e.z,n[3]=e.w);else{if(dn(n,e))return;t.uniform4fv(this.addr,e),hn(n,e)}}function tR(t,e){const n=this.cache,i=e.elements;if(i===void 0){if(dn(n,e))return;t.uniformMatrix2fv(this.addr,!1,e),hn(n,e)}else{if(dn(n,i))return;av.set(i),t.uniformMatrix2fv(this.addr,!1,av),hn(n,i)}}function nR(t,e){const n=this.cache,i=e.elements;if(i===void 0){if(dn(n,e))return;t.uniformMatrix3fv(this.addr,!1,e),hn(n,e)}else{if(dn(n,i))return;iv.set(i),t.uniformMatrix3fv(this.addr,!1,iv),hn(n,i)}}function iR(t,e){const n=this.cache,i=e.elements;if(i===void 0){if(dn(n,e))return;t.uniformMatrix4fv(this.addr,!1,e),hn(n,e)}else{if(dn(n,i))return;nv.set(i),t.uniformMatrix4fv(this.addr,!1,nv),hn(n,i)}}function aR(t,e){const n=this.cache;n[0]!==e&&(t.uniform1i(this.addr,e),n[0]=e)}function sR(t,e){const n=this.cache;if(e.x!==void 0)(n[0]!==e.x||n[1]!==e.y)&&(t.uniform2i(this.addr,e.x,e.y),n[0]=e.x,n[1]=e.y);else{if(dn(n,e))return;t.uniform2iv(this.addr,e),hn(n,e)}}function rR(t,e){const n=this.cache;if(e.x!==void 0)(n[0]!==e.x||n[1]!==e.y||n[2]!==e.z)&&(t.uniform3i(this.addr,e.x,e.y,e.z),n[0]=e.x,n[1]=e.y,n[2]=e.z);else{if(dn(n,e))return;t.uniform3iv(this.addr,e),hn(n,e)}}function lR(t,e){const n=this.cache;if(e.x!==void 0)(n[0]!==e.x||n[1]!==e.y||n[2]!==e.z||n[3]!==e.w)&&(t.uniform4i(this.addr,e.x,e.y,e.z,e.w),n[0]=e.x,n[1]=e.y,n[2]=e.z,n[3]=e.w);else{if(dn(n,e))return;t.uniform4iv(this.addr,e),hn(n,e)}}function oR(t,e){const n=this.cache;n[0]!==e&&(t.uniform1ui(this.addr,e),n[0]=e)}function cR(t,e){const n=this.cache;if(e.x!==void 0)(n[0]!==e.x||n[1]!==e.y)&&(t.uniform2ui(this.addr,e.x,e.y),n[0]=e.x,n[1]=e.y);else{if(dn(n,e))return;t.uniform2uiv(this.addr,e),hn(n,e)}}function uR(t,e){const n=this.cache;if(e.x!==void 0)(n[0]!==e.x||n[1]!==e.y||n[2]!==e.z)&&(t.uniform3ui(this.addr,e.x,e.y,e.z),n[0]=e.x,n[1]=e.y,n[2]=e.z);else{if(dn(n,e))return;t.uniform3uiv(this.addr,e),hn(n,e)}}function fR(t,e){const n=this.cache;if(e.x!==void 0)(n[0]!==e.x||n[1]!==e.y||n[2]!==e.z||n[3]!==e.w)&&(t.uniform4ui(this.addr,e.x,e.y,e.z,e.w),n[0]=e.x,n[1]=e.y,n[2]=e.z,n[3]=e.w);else{if(dn(n,e))return;t.uniform4uiv(this.addr,e),hn(n,e)}}function dR(t,e,n){const i=this.cache,a=n.allocateTextureUnit();i[0]!==a&&(t.uniform1i(this.addr,a),i[0]=a);let s;this.type===t.SAMPLER_2D_SHADOW?(tm.compareFunction=n.isReversedDepthBuffer()?xg:gg,s=tm):s=RS,n.setTexture2D(e||s,a)}function hR(t,e,n){const i=this.cache,a=n.allocateTextureUnit();i[0]!==a&&(t.uniform1i(this.addr,a),i[0]=a),n.setTexture3D(e||LS,a)}function pR(t,e,n){const i=this.cache,a=n.allocateTextureUnit();i[0]!==a&&(t.uniform1i(this.addr,a),i[0]=a),n.setTextureCube(e||US,a)}function mR(t,e,n){const i=this.cache,a=n.allocateTextureUnit();i[0]!==a&&(t.uniform1i(this.addr,a),i[0]=a),n.setTexture2DArray(e||DS,a)}function gR(t){switch(t){case 5126:return $N;case 35664:return QN;case 35665:return JN;case 35666:return eR;case 35674:return tR;case 35675:return nR;case 35676:return iR;case 5124:case 35670:return aR;case 35667:case 35671:return sR;case 35668:case 35672:return rR;case 35669:case 35673:return lR;case 5125:return oR;case 36294:return cR;case 36295:return uR;case 36296:return fR;case 35678:case 36198:case 36298:case 36306:case 35682:return dR;case 35679:case 36299:case 36307:return hR;case 35680:case 36300:case 36308:case 36293:return pR;case 36289:case 36303:case 36311:case 36292:return mR}}function xR(t,e){t.uniform1fv(this.addr,e)}function vR(t,e){const n=Ul(e,this.size,2);t.uniform2fv(this.addr,n)}function _R(t,e){const n=Ul(e,this.size,3);t.uniform3fv(this.addr,n)}function yR(t,e){const n=Ul(e,this.size,4);t.uniform4fv(this.addr,n)}function bR(t,e){const n=Ul(e,this.size,4);t.uniformMatrix2fv(this.addr,!1,n)}function SR(t,e){const n=Ul(e,this.size,9);t.uniformMatrix3fv(this.addr,!1,n)}function MR(t,e){const n=Ul(e,this.size,16);t.uniformMatrix4fv(this.addr,!1,n)}function ER(t,e){t.uniform1iv(this.addr,e)}function TR(t,e){t.uniform2iv(this.addr,e)}function AR(t,e){t.uniform3iv(this.addr,e)}function wR(t,e){t.uniform4iv(this.addr,e)}function CR(t,e){t.uniform1uiv(this.addr,e)}function NR(t,e){t.uniform2uiv(this.addr,e)}function RR(t,e){t.uniform3uiv(this.addr,e)}function DR(t,e){t.uniform4uiv(this.addr,e)}function LR(t,e,n){const i=this.cache,a=e.length,s=Ff(n,a);dn(i,s)||(t.uniform1iv(this.addr,s),hn(i,s));let r;this.type===t.SAMPLER_2D_SHADOW?r=tm:r=RS;for(let o=0;o!==a;++o)n.setTexture2D(e[o]||r,s[o])}function UR(t,e,n){const i=this.cache,a=e.length,s=Ff(n,a);dn(i,s)||(t.uniform1iv(this.addr,s),hn(i,s));for(let r=0;r!==a;++r)n.setTexture3D(e[r]||LS,s[r])}function OR(t,e,n){const i=this.cache,a=e.length,s=Ff(n,a);dn(i,s)||(t.uniform1iv(this.addr,s),hn(i,s));for(let r=0;r!==a;++r)n.setTextureCube(e[r]||US,s[r])}function PR(t,e,n){const i=this.cache,a=e.length,s=Ff(n,a);dn(i,s)||(t.uniform1iv(this.addr,s),hn(i,s));for(let r=0;r!==a;++r)n.setTexture2DArray(e[r]||DS,s[r])}function zR(t){switch(t){case 5126:return xR;case 35664:return vR;case 35665:return _R;case 35666:return yR;case 35674:return bR;case 35675:return SR;case 35676:return MR;case 5124:case 35670:return ER;case 35667:case 35671:return TR;case 35668:case 35672:return AR;case 35669:case 35673:return wR;case 5125:return CR;case 36294:return NR;case 36295:return RR;case 36296:return DR;case 35678:case 36198:case 36298:case 36306:case 35682:return LR;case 35679:case 36299:case 36307:return UR;case 35680:case 36300:case 36308:case 36293:return OR;case 36289:case 36303:case 36311:case 36292:return PR}}class IR{constructor(e,n,i){this.id=e,this.addr=i,this.cache=[],this.type=n.type,this.setValue=gR(n.type)}}class kR{constructor(e,n,i){this.id=e,this.addr=i,this.cache=[],this.type=n.type,this.size=n.size,this.setValue=zR(n.type)}}class BR{constructor(e){this.id=e,this.seq=[],this.map={}}setValue(e,n,i){const a=this.seq;for(let s=0,r=a.length;s!==r;++s){const o=a[s];o.setValue(e,n[o.id],i)}}}const ah=/(\w+)(\])?(\[|\.)?/g;function sv(t,e){t.seq.push(e),t.map[e.id]=e}function FR(t,e,n){const i=t.name,a=i.length;for(ah.lastIndex=0;;){const s=ah.exec(i),r=ah.lastIndex;let o=s[1];const c=s[2]==="]",u=s[3];if(c&&(o=o|0),u===void 0||u==="["&&r+2===a){sv(n,u===void 0?new IR(o,t,e):new kR(o,t,e));break}else{let m=n.map[o];m===void 0&&(m=new BR(o),sv(n,m)),n=m}}}class wu{constructor(e,n){this.seq=[],this.map={};const i=e.getProgramParameter(n,e.ACTIVE_UNIFORMS);for(let r=0;r<i;++r){const o=e.getActiveUniform(n,r),c=e.getUniformLocation(n,o.name);FR(o,c,this)}const a=[],s=[];for(const r of this.seq)r.type===e.SAMPLER_2D_SHADOW||r.type===e.SAMPLER_CUBE_SHADOW||r.type===e.SAMPLER_2D_ARRAY_SHADOW?a.push(r):s.push(r);a.length>0&&(this.seq=a.concat(s))}setValue(e,n,i,a){const s=this.map[n];s!==void 0&&s.setValue(e,i,a)}setOptional(e,n,i){const a=n[i];a!==void 0&&this.setValue(e,i,a)}static upload(e,n,i,a){for(let s=0,r=n.length;s!==r;++s){const o=n[s],c=i[o.id];c.needsUpdate!==!1&&o.setValue(e,c.value,a)}}static seqWithValue(e,n){const i=[];for(let a=0,s=e.length;a!==s;++a){const r=e[a];r.id in n&&i.push(r)}return i}}function rv(t,e,n){const i=t.createShader(e);return t.shaderSource(i,n),t.compileShader(i),i}const jR=37297;let HR=0;function GR(t,e){const n=t.split(` -`),i=[],a=Math.max(e-6,0),s=Math.min(e+6,n.length);for(let r=a;r<s;r++){const o=r+1;i.push(`${o===e?">":" "} ${o}: ${n[r]}`)}return i.join(` -`)}const lv=new it;function VR(t){ht._getMatrix(lv,ht.workingColorSpace,t);const e=`mat3( ${lv.elements.map(n=>n.toFixed(4))} )`;switch(ht.getTransfer(t)){case of:return[e,"LinearTransferOETF"];case Ct:return[e,"sRGBTransferOETF"];default:return Je("WebGLProgram: Unsupported color space: ",t),[e,"LinearTransferOETF"]}}function ov(t,e,n){const i=t.getShaderParameter(e,t.COMPILE_STATUS),s=(t.getShaderInfoLog(e)||"").trim();if(i&&s==="")return"";const r=/ERROR: 0:(\d+)/.exec(s);if(r){const o=parseInt(r[1]);return n.toUpperCase()+` - -`+s+` - -`+GR(t.getShaderSource(e),o)}else return s}function WR(t,e){const n=VR(e);return[`vec4 ${t}( vec4 value ) {`,` return ${n[1]}( vec4( value.rgb * ${n[0]}, value.a ) );`,"}"].join(` -`)}const XR={[Jb]:"Linear",[eS]:"Reinhard",[tS]:"Cineon",[nS]:"ACESFilmic",[aS]:"AgX",[sS]:"Neutral",[iS]:"Custom"};function qR(t,e){const n=XR[e];return n===void 0?(Je("WebGLProgram: Unsupported toneMapping:",e),"vec3 "+t+"( vec3 color ) { return LinearToneMapping( color ); }"):"vec3 "+t+"( vec3 color ) { return "+n+"ToneMapping( color ); }"}const Jc=new Y;function YR(){ht.getLuminanceCoefficients(Jc);const t=Jc.x.toFixed(4),e=Jc.y.toFixed(4),n=Jc.z.toFixed(4);return["float luminance( const in vec3 rgb ) {",` const vec3 weights = vec3( ${t}, ${e}, ${n} );`," return dot( weights, rgb );","}"].join(` -`)}function KR(t){return[t.extensionClipCullDistance?"#extension GL_ANGLE_clip_cull_distance : require":"",t.extensionMultiDraw?"#extension GL_ANGLE_multi_draw : require":""].filter(ro).join(` -`)}function ZR(t){const e=[];for(const n in t){const i=t[n];i!==!1&&e.push("#define "+n+" "+i)}return e.join(` -`)}function $R(t,e){const n={},i=t.getProgramParameter(e,t.ACTIVE_ATTRIBUTES);for(let a=0;a<i;a++){const s=t.getActiveAttrib(e,a),r=s.name;let o=1;s.type===t.FLOAT_MAT2&&(o=2),s.type===t.FLOAT_MAT3&&(o=3),s.type===t.FLOAT_MAT4&&(o=4),n[r]={type:s.type,location:t.getAttribLocation(e,r),locationSize:o}}return n}function ro(t){return t!==""}function cv(t,e){const n=e.numSpotLightShadows+e.numSpotLightMaps-e.numSpotLightShadowsWithMaps;return t.replace(/NUM_DIR_LIGHTS/g,e.numDirLights).replace(/NUM_SPOT_LIGHTS/g,e.numSpotLights).replace(/NUM_SPOT_LIGHT_MAPS/g,e.numSpotLightMaps).replace(/NUM_SPOT_LIGHT_COORDS/g,n).replace(/NUM_RECT_AREA_LIGHTS/g,e.numRectAreaLights).replace(/NUM_POINT_LIGHTS/g,e.numPointLights).replace(/NUM_HEMI_LIGHTS/g,e.numHemiLights).replace(/NUM_DIR_LIGHT_SHADOWS/g,e.numDirLightShadows).replace(/NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS/g,e.numSpotLightShadowsWithMaps).replace(/NUM_SPOT_LIGHT_SHADOWS/g,e.numSpotLightShadows).replace(/NUM_POINT_LIGHT_SHADOWS/g,e.numPointLightShadows)}function uv(t,e){return t.replace(/NUM_CLIPPING_PLANES/g,e.numClippingPlanes).replace(/UNION_CLIPPING_PLANES/g,e.numClippingPlanes-e.numClipIntersection)}const QR=/^[ \t]*#include +<([\w\d./]+)>/gm;function nm(t){return t.replace(QR,e3)}const JR=new Map;function e3(t,e){let n=lt[e];if(n===void 0){const i=JR.get(e);if(i!==void 0)n=lt[i],Je('WebGLRenderer: Shader chunk "%s" has been deprecated. Use "%s" instead.',e,i);else throw new Error("THREE.WebGLProgram: Can not resolve #include <"+e+">")}return nm(n)}const t3=/#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*i\s*\+\+\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end/g;function fv(t){return t.replace(t3,n3)}function n3(t,e,n,i){let a="";for(let s=parseInt(e);s<parseInt(n);s++)a+=i.replace(/\[\s*i\s*\]/g,"[ "+s+" ]").replace(/UNROLLED_LOOP_INDEX/g,s);return a}function dv(t){let e=`precision ${t.precision} float; - precision ${t.precision} int; - precision ${t.precision} sampler2D; - precision ${t.precision} samplerCube; - precision ${t.precision} sampler3D; - precision ${t.precision} sampler2DArray; - precision ${t.precision} sampler2DShadow; - precision ${t.precision} samplerCubeShadow; - precision ${t.precision} sampler2DArrayShadow; - precision ${t.precision} isampler2D; - precision ${t.precision} isampler3D; - precision ${t.precision} isamplerCube; - precision ${t.precision} isampler2DArray; - precision ${t.precision} usampler2D; - precision ${t.precision} usampler3D; - precision ${t.precision} usamplerCube; - precision ${t.precision} usampler2DArray; - `;return t.precision==="highp"?e+=` -#define HIGH_PRECISION`:t.precision==="mediump"?e+=` -#define MEDIUM_PRECISION`:t.precision==="lowp"&&(e+=` -#define LOW_PRECISION`),e}const i3={[bu]:"SHADOWMAP_TYPE_PCF",[so]:"SHADOWMAP_TYPE_VSM"};function a3(t){return i3[t.shadowMapType]||"SHADOWMAP_TYPE_BASIC"}const s3={[Js]:"ENVMAP_TYPE_CUBE",[_l]:"ENVMAP_TYPE_CUBE",[If]:"ENVMAP_TYPE_CUBE_UV"};function r3(t){return t.envMap===!1?"ENVMAP_TYPE_CUBE":s3[t.envMapMode]||"ENVMAP_TYPE_CUBE"}const l3={[_l]:"ENVMAP_MODE_REFRACTION"};function o3(t){return t.envMap===!1?"ENVMAP_MODE_REFLECTION":l3[t.envMapMode]||"ENVMAP_MODE_REFLECTION"}const c3={[Qb]:"ENVMAP_BLENDING_MULTIPLY",[ZT]:"ENVMAP_BLENDING_MIX",[$T]:"ENVMAP_BLENDING_ADD"};function u3(t){return t.envMap===!1?"ENVMAP_BLENDING_NONE":c3[t.combine]||"ENVMAP_BLENDING_NONE"}function f3(t){const e=t.envMapCubeUVHeight;if(e===null)return null;const n=Math.log2(e)-2,i=1/e;return{texelWidth:1/(3*Math.max(Math.pow(2,n),7*16)),texelHeight:i,maxMip:n}}function d3(t,e,n,i){const a=t.getContext(),s=n.defines;let r=n.vertexShader,o=n.fragmentShader;const c=a3(n),u=r3(n),d=o3(n),m=u3(n),f=f3(n),p=KR(n),x=ZR(s),_=a.createProgram();let g,h,v=n.glslVersion?"#version "+n.glslVersion+` -`:"";n.isRawShaderMaterial?(g=["#define SHADER_TYPE "+n.shaderType,"#define SHADER_NAME "+n.shaderName,x].filter(ro).join(` -`),g.length>0&&(g+=` -`),h=["#define SHADER_TYPE "+n.shaderType,"#define SHADER_NAME "+n.shaderName,x].filter(ro).join(` -`),h.length>0&&(h+=` -`)):(g=[dv(n),"#define SHADER_TYPE "+n.shaderType,"#define SHADER_NAME "+n.shaderName,x,n.extensionClipCullDistance?"#define USE_CLIP_DISTANCE":"",n.batching?"#define USE_BATCHING":"",n.batchingColor?"#define USE_BATCHING_COLOR":"",n.instancing?"#define USE_INSTANCING":"",n.instancingColor?"#define USE_INSTANCING_COLOR":"",n.instancingMorph?"#define USE_INSTANCING_MORPH":"",n.useFog&&n.fog?"#define USE_FOG":"",n.useFog&&n.fogExp2?"#define FOG_EXP2":"",n.map?"#define USE_MAP":"",n.envMap?"#define USE_ENVMAP":"",n.envMap?"#define "+d:"",n.lightMap?"#define USE_LIGHTMAP":"",n.aoMap?"#define USE_AOMAP":"",n.bumpMap?"#define USE_BUMPMAP":"",n.normalMap?"#define USE_NORMALMAP":"",n.normalMapObjectSpace?"#define USE_NORMALMAP_OBJECTSPACE":"",n.normalMapTangentSpace?"#define USE_NORMALMAP_TANGENTSPACE":"",n.displacementMap?"#define USE_DISPLACEMENTMAP":"",n.emissiveMap?"#define USE_EMISSIVEMAP":"",n.anisotropy?"#define USE_ANISOTROPY":"",n.anisotropyMap?"#define USE_ANISOTROPYMAP":"",n.clearcoatMap?"#define USE_CLEARCOATMAP":"",n.clearcoatRoughnessMap?"#define USE_CLEARCOAT_ROUGHNESSMAP":"",n.clearcoatNormalMap?"#define USE_CLEARCOAT_NORMALMAP":"",n.iridescenceMap?"#define USE_IRIDESCENCEMAP":"",n.iridescenceThicknessMap?"#define USE_IRIDESCENCE_THICKNESSMAP":"",n.specularMap?"#define USE_SPECULARMAP":"",n.specularColorMap?"#define USE_SPECULAR_COLORMAP":"",n.specularIntensityMap?"#define USE_SPECULAR_INTENSITYMAP":"",n.roughnessMap?"#define USE_ROUGHNESSMAP":"",n.metalnessMap?"#define USE_METALNESSMAP":"",n.alphaMap?"#define USE_ALPHAMAP":"",n.alphaHash?"#define USE_ALPHAHASH":"",n.transmission?"#define USE_TRANSMISSION":"",n.transmissionMap?"#define USE_TRANSMISSIONMAP":"",n.thicknessMap?"#define USE_THICKNESSMAP":"",n.sheenColorMap?"#define USE_SHEEN_COLORMAP":"",n.sheenRoughnessMap?"#define USE_SHEEN_ROUGHNESSMAP":"",n.mapUv?"#define MAP_UV "+n.mapUv:"",n.alphaMapUv?"#define ALPHAMAP_UV "+n.alphaMapUv:"",n.lightMapUv?"#define LIGHTMAP_UV "+n.lightMapUv:"",n.aoMapUv?"#define AOMAP_UV "+n.aoMapUv:"",n.emissiveMapUv?"#define EMISSIVEMAP_UV "+n.emissiveMapUv:"",n.bumpMapUv?"#define BUMPMAP_UV "+n.bumpMapUv:"",n.normalMapUv?"#define NORMALMAP_UV "+n.normalMapUv:"",n.displacementMapUv?"#define DISPLACEMENTMAP_UV "+n.displacementMapUv:"",n.metalnessMapUv?"#define METALNESSMAP_UV "+n.metalnessMapUv:"",n.roughnessMapUv?"#define ROUGHNESSMAP_UV "+n.roughnessMapUv:"",n.anisotropyMapUv?"#define ANISOTROPYMAP_UV "+n.anisotropyMapUv:"",n.clearcoatMapUv?"#define CLEARCOATMAP_UV "+n.clearcoatMapUv:"",n.clearcoatNormalMapUv?"#define CLEARCOAT_NORMALMAP_UV "+n.clearcoatNormalMapUv:"",n.clearcoatRoughnessMapUv?"#define CLEARCOAT_ROUGHNESSMAP_UV "+n.clearcoatRoughnessMapUv:"",n.iridescenceMapUv?"#define IRIDESCENCEMAP_UV "+n.iridescenceMapUv:"",n.iridescenceThicknessMapUv?"#define IRIDESCENCE_THICKNESSMAP_UV "+n.iridescenceThicknessMapUv:"",n.sheenColorMapUv?"#define SHEEN_COLORMAP_UV "+n.sheenColorMapUv:"",n.sheenRoughnessMapUv?"#define SHEEN_ROUGHNESSMAP_UV "+n.sheenRoughnessMapUv:"",n.specularMapUv?"#define SPECULARMAP_UV "+n.specularMapUv:"",n.specularColorMapUv?"#define SPECULAR_COLORMAP_UV "+n.specularColorMapUv:"",n.specularIntensityMapUv?"#define SPECULAR_INTENSITYMAP_UV "+n.specularIntensityMapUv:"",n.transmissionMapUv?"#define TRANSMISSIONMAP_UV "+n.transmissionMapUv:"",n.thicknessMapUv?"#define THICKNESSMAP_UV "+n.thicknessMapUv:"",n.vertexTangents&&n.flatShading===!1?"#define USE_TANGENT":"",n.vertexNormals?"#define HAS_NORMAL":"",n.vertexColors?"#define USE_COLOR":"",n.vertexAlphas?"#define USE_COLOR_ALPHA":"",n.vertexUv1s?"#define USE_UV1":"",n.vertexUv2s?"#define USE_UV2":"",n.vertexUv3s?"#define USE_UV3":"",n.pointsUvs?"#define USE_POINTS_UV":"",n.flatShading?"#define FLAT_SHADED":"",n.skinning?"#define USE_SKINNING":"",n.morphTargets?"#define USE_MORPHTARGETS":"",n.morphNormals&&n.flatShading===!1?"#define USE_MORPHNORMALS":"",n.morphColors?"#define USE_MORPHCOLORS":"",n.morphTargetsCount>0?"#define MORPHTARGETS_TEXTURE_STRIDE "+n.morphTextureStride:"",n.morphTargetsCount>0?"#define MORPHTARGETS_COUNT "+n.morphTargetsCount:"",n.doubleSided?"#define DOUBLE_SIDED":"",n.flipSided?"#define FLIP_SIDED":"",n.shadowMapEnabled?"#define USE_SHADOWMAP":"",n.shadowMapEnabled?"#define "+c:"",n.sizeAttenuation?"#define USE_SIZEATTENUATION":"",n.numLightProbes>0?"#define USE_LIGHT_PROBES":"",n.logarithmicDepthBuffer?"#define USE_LOGARITHMIC_DEPTH_BUFFER":"",n.reversedDepthBuffer?"#define USE_REVERSED_DEPTH_BUFFER":"","uniform mat4 modelMatrix;","uniform mat4 modelViewMatrix;","uniform mat4 projectionMatrix;","uniform mat4 viewMatrix;","uniform mat3 normalMatrix;","uniform vec3 cameraPosition;","uniform bool isOrthographic;","#ifdef USE_INSTANCING"," attribute mat4 instanceMatrix;","#endif","#ifdef USE_INSTANCING_COLOR"," attribute vec3 instanceColor;","#endif","#ifdef USE_INSTANCING_MORPH"," uniform sampler2D morphTexture;","#endif","attribute vec3 position;","attribute vec3 normal;","attribute vec2 uv;","#ifdef USE_UV1"," attribute vec2 uv1;","#endif","#ifdef USE_UV2"," attribute vec2 uv2;","#endif","#ifdef USE_UV3"," attribute vec2 uv3;","#endif","#ifdef USE_TANGENT"," attribute vec4 tangent;","#endif","#if defined( USE_COLOR_ALPHA )"," attribute vec4 color;","#elif defined( USE_COLOR )"," attribute vec3 color;","#endif","#ifdef USE_SKINNING"," attribute vec4 skinIndex;"," attribute vec4 skinWeight;","#endif",` -`].filter(ro).join(` -`),h=[dv(n),"#define SHADER_TYPE "+n.shaderType,"#define SHADER_NAME "+n.shaderName,x,n.useFog&&n.fog?"#define USE_FOG":"",n.useFog&&n.fogExp2?"#define FOG_EXP2":"",n.alphaToCoverage?"#define ALPHA_TO_COVERAGE":"",n.map?"#define USE_MAP":"",n.matcap?"#define USE_MATCAP":"",n.envMap?"#define USE_ENVMAP":"",n.envMap?"#define "+u:"",n.envMap?"#define "+d:"",n.envMap?"#define "+m:"",f?"#define CUBEUV_TEXEL_WIDTH "+f.texelWidth:"",f?"#define CUBEUV_TEXEL_HEIGHT "+f.texelHeight:"",f?"#define CUBEUV_MAX_MIP "+f.maxMip+".0":"",n.lightMap?"#define USE_LIGHTMAP":"",n.aoMap?"#define USE_AOMAP":"",n.bumpMap?"#define USE_BUMPMAP":"",n.normalMap?"#define USE_NORMALMAP":"",n.normalMapObjectSpace?"#define USE_NORMALMAP_OBJECTSPACE":"",n.normalMapTangentSpace?"#define USE_NORMALMAP_TANGENTSPACE":"",n.packedNormalMap?"#define USE_PACKED_NORMALMAP":"",n.emissiveMap?"#define USE_EMISSIVEMAP":"",n.anisotropy?"#define USE_ANISOTROPY":"",n.anisotropyMap?"#define USE_ANISOTROPYMAP":"",n.clearcoat?"#define USE_CLEARCOAT":"",n.clearcoatMap?"#define USE_CLEARCOATMAP":"",n.clearcoatRoughnessMap?"#define USE_CLEARCOAT_ROUGHNESSMAP":"",n.clearcoatNormalMap?"#define USE_CLEARCOAT_NORMALMAP":"",n.dispersion?"#define USE_DISPERSION":"",n.iridescence?"#define USE_IRIDESCENCE":"",n.iridescenceMap?"#define USE_IRIDESCENCEMAP":"",n.iridescenceThicknessMap?"#define USE_IRIDESCENCE_THICKNESSMAP":"",n.specularMap?"#define USE_SPECULARMAP":"",n.specularColorMap?"#define USE_SPECULAR_COLORMAP":"",n.specularIntensityMap?"#define USE_SPECULAR_INTENSITYMAP":"",n.roughnessMap?"#define USE_ROUGHNESSMAP":"",n.metalnessMap?"#define USE_METALNESSMAP":"",n.alphaMap?"#define USE_ALPHAMAP":"",n.alphaTest?"#define USE_ALPHATEST":"",n.alphaHash?"#define USE_ALPHAHASH":"",n.sheen?"#define USE_SHEEN":"",n.sheenColorMap?"#define USE_SHEEN_COLORMAP":"",n.sheenRoughnessMap?"#define USE_SHEEN_ROUGHNESSMAP":"",n.transmission?"#define USE_TRANSMISSION":"",n.transmissionMap?"#define USE_TRANSMISSIONMAP":"",n.thicknessMap?"#define USE_THICKNESSMAP":"",n.vertexTangents&&n.flatShading===!1?"#define USE_TANGENT":"",n.vertexColors||n.instancingColor?"#define USE_COLOR":"",n.vertexAlphas||n.batchingColor?"#define USE_COLOR_ALPHA":"",n.vertexUv1s?"#define USE_UV1":"",n.vertexUv2s?"#define USE_UV2":"",n.vertexUv3s?"#define USE_UV3":"",n.pointsUvs?"#define USE_POINTS_UV":"",n.gradientMap?"#define USE_GRADIENTMAP":"",n.flatShading?"#define FLAT_SHADED":"",n.doubleSided?"#define DOUBLE_SIDED":"",n.flipSided?"#define FLIP_SIDED":"",n.shadowMapEnabled?"#define USE_SHADOWMAP":"",n.shadowMapEnabled?"#define "+c:"",n.premultipliedAlpha?"#define PREMULTIPLIED_ALPHA":"",n.numLightProbes>0?"#define USE_LIGHT_PROBES":"",n.numLightProbeGrids>0?"#define USE_LIGHT_PROBES_GRID":"",n.decodeVideoTexture?"#define DECODE_VIDEO_TEXTURE":"",n.decodeVideoTextureEmissive?"#define DECODE_VIDEO_TEXTURE_EMISSIVE":"",n.logarithmicDepthBuffer?"#define USE_LOGARITHMIC_DEPTH_BUFFER":"",n.reversedDepthBuffer?"#define USE_REVERSED_DEPTH_BUFFER":"","uniform mat4 viewMatrix;","uniform vec3 cameraPosition;","uniform bool isOrthographic;",n.toneMapping!==ia?"#define TONE_MAPPING":"",n.toneMapping!==ia?lt.tonemapping_pars_fragment:"",n.toneMapping!==ia?qR("toneMapping",n.toneMapping):"",n.dithering?"#define DITHERING":"",n.opaque?"#define OPAQUE":"",lt.colorspace_pars_fragment,WR("linearToOutputTexel",n.outputColorSpace),YR(),n.useDepthPacking?"#define DEPTH_PACKING "+n.depthPacking:"",` -`].filter(ro).join(` -`)),r=nm(r),r=cv(r,n),r=uv(r,n),o=nm(o),o=cv(o,n),o=uv(o,n),r=fv(r),o=fv(o),n.isRawShaderMaterial!==!0&&(v=`#version 300 es -`,g=[p,"#define attribute in","#define varying out","#define texture2D texture"].join(` -`)+` -`+g,h=["#define varying in",n.glslVersion===x0?"":"layout(location = 0) out highp vec4 pc_fragColor;",n.glslVersion===x0?"":"#define gl_FragColor pc_fragColor","#define gl_FragDepthEXT gl_FragDepth","#define texture2D texture","#define textureCube texture","#define texture2DProj textureProj","#define texture2DLodEXT textureLod","#define texture2DProjLodEXT textureProjLod","#define textureCubeLodEXT textureLod","#define texture2DGradEXT textureGrad","#define texture2DProjGradEXT textureProjGrad","#define textureCubeGradEXT textureGrad"].join(` -`)+` -`+h);const b=v+g+r,y=v+h+o,A=rv(a,a.VERTEX_SHADER,b),M=rv(a,a.FRAGMENT_SHADER,y);a.attachShader(_,A),a.attachShader(_,M),n.index0AttributeName!==void 0?a.bindAttribLocation(_,0,n.index0AttributeName):n.hasPositionAttribute===!0&&a.bindAttribLocation(_,0,"position"),a.linkProgram(_);function T(L){if(t.debug.checkShaderErrors){const O=a.getProgramInfoLog(_)||"",W=a.getShaderInfoLog(A)||"",z=a.getShaderInfoLog(M)||"",D=O.trim(),B=W.trim(),k=z.trim();let V=!0,X=!0;if(a.getProgramParameter(_,a.LINK_STATUS)===!1)if(V=!1,typeof t.debug.onShaderError=="function")t.debug.onShaderError(a,_,A,M);else{const ge=ov(a,A,"vertex"),he=ov(a,M,"fragment");St("WebGLProgram: Shader Error "+a.getError()+" - VALIDATE_STATUS "+a.getProgramParameter(_,a.VALIDATE_STATUS)+` - -Material Name: `+L.name+` -Material Type: `+L.type+` - -Program Info Log: `+D+` -`+ge+` -`+he)}else D!==""?Je("WebGLProgram: Program Info Log:",D):(B===""||k==="")&&(X=!1);X&&(L.diagnostics={runnable:V,programLog:D,vertexShader:{log:B,prefix:g},fragmentShader:{log:k,prefix:h}})}a.deleteShader(A),a.deleteShader(M),S=new wu(a,_),C=$R(a,_)}let S;this.getUniforms=function(){return S===void 0&&T(this),S};let C;this.getAttributes=function(){return C===void 0&&T(this),C};let R=n.rendererExtensionParallelShaderCompile===!1;return this.isReady=function(){return R===!1&&(R=a.getProgramParameter(_,jR)),R},this.destroy=function(){i.releaseStatesOfProgram(this),a.deleteProgram(_),this.program=void 0},this.type=n.shaderType,this.name=n.shaderName,this.id=HR++,this.cacheKey=e,this.usedTimes=1,this.program=_,this.vertexShader=A,this.fragmentShader=M,this}let h3=0;class p3{constructor(){this.shaderCache=new Map,this.materialCache=new Map}update(e,n,i){const a=this._getShaderCacheForMaterial(e);return a.has(n)===!1&&(a.add(n),n.usedTimes++),a.has(i)===!1&&(a.add(i),i.usedTimes++),this}remove(e){const n=this.materialCache.get(e);for(const i of n)i.usedTimes--,i.usedTimes===0&&this.shaderCache.delete(i.code);return this.materialCache.delete(e),this}getVertexShaderStage(e){return this._getShaderStage(e.vertexShader)}getFragmentShaderStage(e){return this._getShaderStage(e.fragmentShader)}dispose(){this.shaderCache.clear(),this.materialCache.clear()}_getShaderCacheForMaterial(e){const n=this.materialCache;let i=n.get(e);return i===void 0&&(i=new Set,n.set(e,i)),i}_getShaderStage(e){const n=this.shaderCache;let i=n.get(e);return i===void 0&&(i=new m3(e),n.set(e,i)),i}}class m3{constructor(e){this.id=h3++,this.code=e,this.usedTimes=0}}function g3(t){return t===er||t===sf||t===rf}function x3(t,e,n,i,a,s){const r=new mS,o=new p3,c=new Set,u=[],d=new Map,m=i.logarithmicDepthBuffer;let f=i.precision;const p={MeshDepthMaterial:"depth",MeshDistanceMaterial:"distance",MeshNormalMaterial:"normal",MeshBasicMaterial:"basic",MeshLambertMaterial:"lambert",MeshPhongMaterial:"phong",MeshToonMaterial:"toon",MeshStandardMaterial:"physical",MeshPhysicalMaterial:"physical",MeshMatcapMaterial:"matcap",LineBasicMaterial:"basic",LineDashedMaterial:"dashed",PointsMaterial:"points",ShadowMaterial:"shadow",SpriteMaterial:"sprite"};function x(S){return c.add(S),S===0?"uv":`uv${S}`}function _(S,C,R,L,O,W){const z=L.fog,D=O.geometry,B=S.isMeshStandardMaterial||S.isMeshLambertMaterial||S.isMeshPhongMaterial?L.environment:null,k=S.isMeshStandardMaterial||S.isMeshLambertMaterial&&!S.envMap||S.isMeshPhongMaterial&&!S.envMap,V=e.get(S.envMap||B,k),X=V&&V.mapping===If?V.image.height:null,ge=p[S.type];S.precision!==null&&(f=i.getMaxPrecision(S.precision),f!==S.precision&&Je("WebGLProgram.getParameters:",S.precision,"not supported, using",f,"instead."));const he=D.morphAttributes.position||D.morphAttributes.normal||D.morphAttributes.color,Ae=he!==void 0?he.length:0;let q=0;D.morphAttributes.position!==void 0&&(q=1),D.morphAttributes.normal!==void 0&&(q=2),D.morphAttributes.color!==void 0&&(q=3);let fe,Ne,ie,xe;if(ge){const je=$i[ge];fe=je.vertexShader,Ne=je.fragmentShader}else{fe=S.vertexShader,Ne=S.fragmentShader;const je=o.getVertexShaderStage(S),_t=o.getFragmentShaderStage(S);o.update(S,je,_t),ie=je.id,xe=_t.id}const oe=t.getRenderTarget(),Ee=t.state.buffers.depth.getReversed(),Oe=O.isInstancedMesh===!0,ke=O.isBatchedMesh===!0,xt=!!S.map,re=!!S.matcap,Se=!!V,Ce=!!S.aoMap,be=!!S.lightMap,Ve=!!S.bumpMap&&S.wireframe===!1,Ge=!!S.normalMap,Pe=!!S.displacementMap,we=!!S.emissiveMap,We=!!S.metalnessMap,ve=!!S.roughnessMap,I=S.anisotropy>0,nt=S.clearcoat>0,Ke=S.dispersion>0,N=S.iridescence>0,E=S.sheen>0,H=S.transmission>0,K=I&&!!S.anisotropyMap,J=nt&&!!S.clearcoatMap,_e=nt&&!!S.clearcoatNormalMap,pe=nt&&!!S.clearcoatRoughnessMap,te=N&&!!S.iridescenceMap,Z=N&&!!S.iridescenceThicknessMap,ae=E&&!!S.sheenColorMap,ce=E&&!!S.sheenRoughnessMap,me=!!S.specularMap,ye=!!S.specularColorMap,Fe=!!S.specularIntensityMap,Xe=H&&!!S.transmissionMap,et=H&&!!S.thicknessMap,j=!!S.gradientMap,Me=!!S.alphaMap,se=S.alphaTest>0,Te=!!S.alphaHash,Re=!!S.extensions;let de=ia;S.toneMapped&&(oe===null||oe.isXRRenderTarget===!0)&&(de=t.toneMapping);const ze={shaderID:ge,shaderType:S.type,shaderName:S.name,vertexShader:fe,fragmentShader:Ne,defines:S.defines,customVertexShaderID:ie,customFragmentShaderID:xe,isRawShaderMaterial:S.isRawShaderMaterial===!0,glslVersion:S.glslVersion,precision:f,batching:ke,batchingColor:ke&&O._colorsTexture!==null,instancing:Oe,instancingColor:Oe&&O.instanceColor!==null,instancingMorph:Oe&&O.morphTexture!==null,outputColorSpace:oe===null?t.outputColorSpace:oe.isXRRenderTarget===!0?oe.texture.colorSpace:ht.workingColorSpace,alphaToCoverage:!!S.alphaToCoverage,map:xt,matcap:re,envMap:Se,envMapMode:Se&&V.mapping,envMapCubeUVHeight:X,aoMap:Ce,lightMap:be,bumpMap:Ve,normalMap:Ge,displacementMap:Pe,emissiveMap:we,normalMapObjectSpace:Ge&&S.normalMapType===eA,normalMapTangentSpace:Ge&&S.normalMapType===p0,packedNormalMap:Ge&&S.normalMapType===p0&&g3(S.normalMap.format),metalnessMap:We,roughnessMap:ve,anisotropy:I,anisotropyMap:K,clearcoat:nt,clearcoatMap:J,clearcoatNormalMap:_e,clearcoatRoughnessMap:pe,dispersion:Ke,iridescence:N,iridescenceMap:te,iridescenceThicknessMap:Z,sheen:E,sheenColorMap:ae,sheenRoughnessMap:ce,specularMap:me,specularColorMap:ye,specularIntensityMap:Fe,transmission:H,transmissionMap:Xe,thicknessMap:et,gradientMap:j,opaque:S.transparent===!1&&S.blending===nl&&S.alphaToCoverage===!1,alphaMap:Me,alphaTest:se,alphaHash:Te,combine:S.combine,mapUv:xt&&x(S.map.channel),aoMapUv:Ce&&x(S.aoMap.channel),lightMapUv:be&&x(S.lightMap.channel),bumpMapUv:Ve&&x(S.bumpMap.channel),normalMapUv:Ge&&x(S.normalMap.channel),displacementMapUv:Pe&&x(S.displacementMap.channel),emissiveMapUv:we&&x(S.emissiveMap.channel),metalnessMapUv:We&&x(S.metalnessMap.channel),roughnessMapUv:ve&&x(S.roughnessMap.channel),anisotropyMapUv:K&&x(S.anisotropyMap.channel),clearcoatMapUv:J&&x(S.clearcoatMap.channel),clearcoatNormalMapUv:_e&&x(S.clearcoatNormalMap.channel),clearcoatRoughnessMapUv:pe&&x(S.clearcoatRoughnessMap.channel),iridescenceMapUv:te&&x(S.iridescenceMap.channel),iridescenceThicknessMapUv:Z&&x(S.iridescenceThicknessMap.channel),sheenColorMapUv:ae&&x(S.sheenColorMap.channel),sheenRoughnessMapUv:ce&&x(S.sheenRoughnessMap.channel),specularMapUv:me&&x(S.specularMap.channel),specularColorMapUv:ye&&x(S.specularColorMap.channel),specularIntensityMapUv:Fe&&x(S.specularIntensityMap.channel),transmissionMapUv:Xe&&x(S.transmissionMap.channel),thicknessMapUv:et&&x(S.thicknessMap.channel),alphaMapUv:Me&&x(S.alphaMap.channel),vertexTangents:!!D.attributes.tangent&&(Ge||I),vertexNormals:!!D.attributes.normal,vertexColors:S.vertexColors,vertexAlphas:S.vertexColors===!0&&!!D.attributes.color&&D.attributes.color.itemSize===4,pointsUvs:O.isPoints===!0&&!!D.attributes.uv&&(xt||Me),fog:!!z,useFog:S.fog===!0,fogExp2:!!z&&z.isFogExp2,flatShading:S.wireframe===!1&&(S.flatShading===!0||D.attributes.normal===void 0&&Ge===!1&&(S.isMeshLambertMaterial||S.isMeshPhongMaterial||S.isMeshStandardMaterial||S.isMeshPhysicalMaterial)),sizeAttenuation:S.sizeAttenuation===!0,logarithmicDepthBuffer:m,reversedDepthBuffer:Ee,skinning:O.isSkinnedMesh===!0,hasPositionAttribute:D.attributes.position!==void 0,morphTargets:D.morphAttributes.position!==void 0,morphNormals:D.morphAttributes.normal!==void 0,morphColors:D.morphAttributes.color!==void 0,morphTargetsCount:Ae,morphTextureStride:q,numDirLights:C.directional.length,numPointLights:C.point.length,numSpotLights:C.spot.length,numSpotLightMaps:C.spotLightMap.length,numRectAreaLights:C.rectArea.length,numHemiLights:C.hemi.length,numDirLightShadows:C.directionalShadowMap.length,numPointLightShadows:C.pointShadowMap.length,numSpotLightShadows:C.spotShadowMap.length,numSpotLightShadowsWithMaps:C.numSpotLightShadowsWithMaps,numLightProbes:C.numLightProbes,numLightProbeGrids:W.length,numClippingPlanes:s.numPlanes,numClipIntersection:s.numIntersection,dithering:S.dithering,shadowMapEnabled:t.shadowMap.enabled&&R.length>0,shadowMapType:t.shadowMap.type,toneMapping:de,decodeVideoTexture:xt&&S.map.isVideoTexture===!0&&ht.getTransfer(S.map.colorSpace)===Ct,decodeVideoTextureEmissive:we&&S.emissiveMap.isVideoTexture===!0&&ht.getTransfer(S.emissiveMap.colorSpace)===Ct,premultipliedAlpha:S.premultipliedAlpha,doubleSided:S.side===ba,flipSided:S.side===Yn,useDepthPacking:S.depthPacking>=0,depthPacking:S.depthPacking||0,index0AttributeName:S.index0AttributeName,extensionClipCullDistance:Re&&S.extensions.clipCullDistance===!0&&n.has("WEBGL_clip_cull_distance"),extensionMultiDraw:(Re&&S.extensions.multiDraw===!0||ke)&&n.has("WEBGL_multi_draw"),rendererExtensionParallelShaderCompile:n.has("KHR_parallel_shader_compile"),customProgramCacheKey:S.customProgramCacheKey()};return ze.vertexUv1s=c.has(1),ze.vertexUv2s=c.has(2),ze.vertexUv3s=c.has(3),c.clear(),ze}function g(S){const C=[];if(S.shaderID?C.push(S.shaderID):(C.push(S.customVertexShaderID),C.push(S.customFragmentShaderID)),S.defines!==void 0)for(const R in S.defines)C.push(R),C.push(S.defines[R]);return S.isRawShaderMaterial===!1&&(h(C,S),v(C,S),C.push(t.outputColorSpace)),C.push(S.customProgramCacheKey),C.join()}function h(S,C){S.push(C.precision),S.push(C.outputColorSpace),S.push(C.envMapMode),S.push(C.envMapCubeUVHeight),S.push(C.mapUv),S.push(C.alphaMapUv),S.push(C.lightMapUv),S.push(C.aoMapUv),S.push(C.bumpMapUv),S.push(C.normalMapUv),S.push(C.displacementMapUv),S.push(C.emissiveMapUv),S.push(C.metalnessMapUv),S.push(C.roughnessMapUv),S.push(C.anisotropyMapUv),S.push(C.clearcoatMapUv),S.push(C.clearcoatNormalMapUv),S.push(C.clearcoatRoughnessMapUv),S.push(C.iridescenceMapUv),S.push(C.iridescenceThicknessMapUv),S.push(C.sheenColorMapUv),S.push(C.sheenRoughnessMapUv),S.push(C.specularMapUv),S.push(C.specularColorMapUv),S.push(C.specularIntensityMapUv),S.push(C.transmissionMapUv),S.push(C.thicknessMapUv),S.push(C.combine),S.push(C.fogExp2),S.push(C.sizeAttenuation),S.push(C.morphTargetsCount),S.push(C.morphAttributeCount),S.push(C.numDirLights),S.push(C.numPointLights),S.push(C.numSpotLights),S.push(C.numSpotLightMaps),S.push(C.numHemiLights),S.push(C.numRectAreaLights),S.push(C.numDirLightShadows),S.push(C.numPointLightShadows),S.push(C.numSpotLightShadows),S.push(C.numSpotLightShadowsWithMaps),S.push(C.numLightProbes),S.push(C.shadowMapType),S.push(C.toneMapping),S.push(C.numClippingPlanes),S.push(C.numClipIntersection),S.push(C.depthPacking)}function v(S,C){r.disableAll(),C.instancing&&r.enable(0),C.instancingColor&&r.enable(1),C.instancingMorph&&r.enable(2),C.matcap&&r.enable(3),C.envMap&&r.enable(4),C.normalMapObjectSpace&&r.enable(5),C.normalMapTangentSpace&&r.enable(6),C.clearcoat&&r.enable(7),C.iridescence&&r.enable(8),C.alphaTest&&r.enable(9),C.vertexColors&&r.enable(10),C.vertexAlphas&&r.enable(11),C.vertexUv1s&&r.enable(12),C.vertexUv2s&&r.enable(13),C.vertexUv3s&&r.enable(14),C.vertexTangents&&r.enable(15),C.anisotropy&&r.enable(16),C.alphaHash&&r.enable(17),C.batching&&r.enable(18),C.dispersion&&r.enable(19),C.batchingColor&&r.enable(20),C.gradientMap&&r.enable(21),C.packedNormalMap&&r.enable(22),C.vertexNormals&&r.enable(23),S.push(r.mask),r.disableAll(),C.fog&&r.enable(0),C.useFog&&r.enable(1),C.flatShading&&r.enable(2),C.logarithmicDepthBuffer&&r.enable(3),C.reversedDepthBuffer&&r.enable(4),C.skinning&&r.enable(5),C.morphTargets&&r.enable(6),C.morphNormals&&r.enable(7),C.morphColors&&r.enable(8),C.premultipliedAlpha&&r.enable(9),C.shadowMapEnabled&&r.enable(10),C.doubleSided&&r.enable(11),C.flipSided&&r.enable(12),C.useDepthPacking&&r.enable(13),C.dithering&&r.enable(14),C.transmission&&r.enable(15),C.sheen&&r.enable(16),C.opaque&&r.enable(17),C.pointsUvs&&r.enable(18),C.decodeVideoTexture&&r.enable(19),C.decodeVideoTextureEmissive&&r.enable(20),C.alphaToCoverage&&r.enable(21),C.numLightProbeGrids>0&&r.enable(22),C.hasPositionAttribute&&r.enable(23),S.push(r.mask)}function b(S){const C=p[S.type];let R;if(C){const L=$i[C];R=jA.clone(L.uniforms)}else R=S.uniforms;return R}function y(S,C){let R=d.get(C);return R!==void 0?++R.usedTimes:(R=new d3(t,C,S,a),u.push(R),d.set(C,R)),R}function A(S){if(--S.usedTimes===0){const C=u.indexOf(S);u[C]=u[u.length-1],u.pop(),d.delete(S.cacheKey),S.destroy()}}function M(S){o.remove(S)}function T(){o.dispose()}return{getParameters:_,getProgramCacheKey:g,getUniforms:b,acquireProgram:y,releaseProgram:A,releaseShaderCache:M,programs:u,dispose:T}}function v3(){let t=new WeakMap;function e(r){return t.has(r)}function n(r){let o=t.get(r);return o===void 0&&(o={},t.set(r,o)),o}function i(r){t.delete(r)}function a(r,o,c){t.get(r)[o]=c}function s(){t=new WeakMap}return{has:e,get:n,remove:i,update:a,dispose:s}}function _3(t,e){return t.groupOrder!==e.groupOrder?t.groupOrder-e.groupOrder:t.renderOrder!==e.renderOrder?t.renderOrder-e.renderOrder:t.material.id!==e.material.id?t.material.id-e.material.id:t.materialVariant!==e.materialVariant?t.materialVariant-e.materialVariant:t.z!==e.z?t.z-e.z:t.id-e.id}function hv(t,e){return t.groupOrder!==e.groupOrder?t.groupOrder-e.groupOrder:t.renderOrder!==e.renderOrder?t.renderOrder-e.renderOrder:t.z!==e.z?e.z-t.z:t.id-e.id}function pv(){const t=[];let e=0;const n=[],i=[],a=[];function s(){e=0,n.length=0,i.length=0,a.length=0}function r(f){let p=0;return f.isInstancedMesh&&(p+=2),f.isSkinnedMesh&&(p+=1),p}function o(f,p,x,_,g,h){let v=t[e];return v===void 0?(v={id:f.id,object:f,geometry:p,material:x,materialVariant:r(f),groupOrder:_,renderOrder:f.renderOrder,z:g,group:h},t[e]=v):(v.id=f.id,v.object=f,v.geometry=p,v.material=x,v.materialVariant=r(f),v.groupOrder=_,v.renderOrder=f.renderOrder,v.z=g,v.group=h),e++,v}function c(f,p,x,_,g,h){const v=o(f,p,x,_,g,h);x.transmission>0?i.push(v):x.transparent===!0?a.push(v):n.push(v)}function u(f,p,x,_,g,h){const v=o(f,p,x,_,g,h);x.transmission>0?i.unshift(v):x.transparent===!0?a.unshift(v):n.unshift(v)}function d(f,p,x){n.length>1&&n.sort(f||_3),i.length>1&&i.sort(p||hv),a.length>1&&a.sort(p||hv),x&&(n.reverse(),i.reverse(),a.reverse())}function m(){for(let f=e,p=t.length;f<p;f++){const x=t[f];if(x.id===null)break;x.id=null,x.object=null,x.geometry=null,x.material=null,x.group=null}}return{opaque:n,transmissive:i,transparent:a,init:s,push:c,unshift:u,finish:m,sort:d}}function y3(){let t=new WeakMap;function e(i,a){const s=t.get(i);let r;return s===void 0?(r=new pv,t.set(i,[r])):a>=s.length?(r=new pv,s.push(r)):r=s[a],r}function n(){t=new WeakMap}return{get:e,dispose:n}}function b3(){const t={};return{get:function(e){if(t[e.id]!==void 0)return t[e.id];let n;switch(e.type){case"DirectionalLight":n={direction:new Y,color:new gt};break;case"SpotLight":n={position:new Y,direction:new Y,color:new gt,distance:0,coneCos:0,penumbraCos:0,decay:0};break;case"PointLight":n={position:new Y,color:new gt,distance:0,decay:0};break;case"HemisphereLight":n={direction:new Y,skyColor:new gt,groundColor:new gt};break;case"RectAreaLight":n={color:new gt,position:new Y,halfWidth:new Y,halfHeight:new Y};break}return t[e.id]=n,n}}}function S3(){const t={};return{get:function(e){if(t[e.id]!==void 0)return t[e.id];let n;switch(e.type){case"DirectionalLight":n={shadowIntensity:1,shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new At};break;case"SpotLight":n={shadowIntensity:1,shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new At};break;case"PointLight":n={shadowIntensity:1,shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new At,shadowCameraNear:1,shadowCameraFar:1e3};break}return t[e.id]=n,n}}}let M3=0;function E3(t,e){return(e.castShadow?2:0)-(t.castShadow?2:0)+(e.map?1:0)-(t.map?1:0)}function T3(t){const e=new b3,n=S3(),i={version:0,hash:{directionalLength:-1,pointLength:-1,spotLength:-1,rectAreaLength:-1,hemiLength:-1,numDirectionalShadows:-1,numPointShadows:-1,numSpotShadows:-1,numSpotMaps:-1,numLightProbes:-1},ambient:[0,0,0],probe:[],directional:[],directionalShadow:[],directionalShadowMap:[],directionalShadowMatrix:[],spot:[],spotLightMap:[],spotShadow:[],spotShadowMap:[],spotLightMatrix:[],rectArea:[],rectAreaLTC1:null,rectAreaLTC2:null,point:[],pointShadow:[],pointShadowMap:[],pointShadowMatrix:[],hemi:[],numSpotLightShadowsWithMaps:0,numLightProbes:0};for(let u=0;u<9;u++)i.probe.push(new Y);const a=new Y,s=new Ft,r=new Ft;function o(u){let d=0,m=0,f=0;for(let C=0;C<9;C++)i.probe[C].set(0,0,0);let p=0,x=0,_=0,g=0,h=0,v=0,b=0,y=0,A=0,M=0,T=0;u.sort(E3);for(let C=0,R=u.length;C<R;C++){const L=u[C],O=L.color,W=L.intensity,z=L.distance;let D=null;if(L.shadow&&L.shadow.map&&(L.shadow.map.texture.format===er?D=L.shadow.map.texture:D=L.shadow.map.depthTexture||L.shadow.map.texture),L.isAmbientLight)d+=O.r*W,m+=O.g*W,f+=O.b*W;else if(L.isLightProbe){for(let B=0;B<9;B++)i.probe[B].addScaledVector(L.sh.coefficients[B],W);T++}else if(L.isDirectionalLight){const B=e.get(L);if(B.color.copy(L.color).multiplyScalar(L.intensity),L.castShadow){const k=L.shadow,V=n.get(L);V.shadowIntensity=k.intensity,V.shadowBias=k.bias,V.shadowNormalBias=k.normalBias,V.shadowRadius=k.radius,V.shadowMapSize=k.mapSize,i.directionalShadow[p]=V,i.directionalShadowMap[p]=D,i.directionalShadowMatrix[p]=L.shadow.matrix,v++}i.directional[p]=B,p++}else if(L.isSpotLight){const B=e.get(L);B.position.setFromMatrixPosition(L.matrixWorld),B.color.copy(O).multiplyScalar(W),B.distance=z,B.coneCos=Math.cos(L.angle),B.penumbraCos=Math.cos(L.angle*(1-L.penumbra)),B.decay=L.decay,i.spot[_]=B;const k=L.shadow;if(L.map&&(i.spotLightMap[A]=L.map,A++,k.updateMatrices(L),L.castShadow&&M++),i.spotLightMatrix[_]=k.matrix,L.castShadow){const V=n.get(L);V.shadowIntensity=k.intensity,V.shadowBias=k.bias,V.shadowNormalBias=k.normalBias,V.shadowRadius=k.radius,V.shadowMapSize=k.mapSize,i.spotShadow[_]=V,i.spotShadowMap[_]=D,y++}_++}else if(L.isRectAreaLight){const B=e.get(L);B.color.copy(O).multiplyScalar(W),B.halfWidth.set(L.width*.5,0,0),B.halfHeight.set(0,L.height*.5,0),i.rectArea[g]=B,g++}else if(L.isPointLight){const B=e.get(L);if(B.color.copy(L.color).multiplyScalar(L.intensity),B.distance=L.distance,B.decay=L.decay,L.castShadow){const k=L.shadow,V=n.get(L);V.shadowIntensity=k.intensity,V.shadowBias=k.bias,V.shadowNormalBias=k.normalBias,V.shadowRadius=k.radius,V.shadowMapSize=k.mapSize,V.shadowCameraNear=k.camera.near,V.shadowCameraFar=k.camera.far,i.pointShadow[x]=V,i.pointShadowMap[x]=D,i.pointShadowMatrix[x]=L.shadow.matrix,b++}i.point[x]=B,x++}else if(L.isHemisphereLight){const B=e.get(L);B.skyColor.copy(L.color).multiplyScalar(W),B.groundColor.copy(L.groundColor).multiplyScalar(W),i.hemi[h]=B,h++}}g>0&&(t.has("OES_texture_float_linear")===!0?(i.rectAreaLTC1=De.LTC_FLOAT_1,i.rectAreaLTC2=De.LTC_FLOAT_2):(i.rectAreaLTC1=De.LTC_HALF_1,i.rectAreaLTC2=De.LTC_HALF_2)),i.ambient[0]=d,i.ambient[1]=m,i.ambient[2]=f;const S=i.hash;(S.directionalLength!==p||S.pointLength!==x||S.spotLength!==_||S.rectAreaLength!==g||S.hemiLength!==h||S.numDirectionalShadows!==v||S.numPointShadows!==b||S.numSpotShadows!==y||S.numSpotMaps!==A||S.numLightProbes!==T)&&(i.directional.length=p,i.spot.length=_,i.rectArea.length=g,i.point.length=x,i.hemi.length=h,i.directionalShadow.length=v,i.directionalShadowMap.length=v,i.pointShadow.length=b,i.pointShadowMap.length=b,i.spotShadow.length=y,i.spotShadowMap.length=y,i.directionalShadowMatrix.length=v,i.pointShadowMatrix.length=b,i.spotLightMatrix.length=y+A-M,i.spotLightMap.length=A,i.numSpotLightShadowsWithMaps=M,i.numLightProbes=T,S.directionalLength=p,S.pointLength=x,S.spotLength=_,S.rectAreaLength=g,S.hemiLength=h,S.numDirectionalShadows=v,S.numPointShadows=b,S.numSpotShadows=y,S.numSpotMaps=A,S.numLightProbes=T,i.version=M3++)}function c(u,d){let m=0,f=0,p=0,x=0,_=0;const g=d.matrixWorldInverse;for(let h=0,v=u.length;h<v;h++){const b=u[h];if(b.isDirectionalLight){const y=i.directional[m];y.direction.setFromMatrixPosition(b.matrixWorld),a.setFromMatrixPosition(b.target.matrixWorld),y.direction.sub(a),y.direction.transformDirection(g),m++}else if(b.isSpotLight){const y=i.spot[p];y.position.setFromMatrixPosition(b.matrixWorld),y.position.applyMatrix4(g),y.direction.setFromMatrixPosition(b.matrixWorld),a.setFromMatrixPosition(b.target.matrixWorld),y.direction.sub(a),y.direction.transformDirection(g),p++}else if(b.isRectAreaLight){const y=i.rectArea[x];y.position.setFromMatrixPosition(b.matrixWorld),y.position.applyMatrix4(g),r.identity(),s.copy(b.matrixWorld),s.premultiply(g),r.extractRotation(s),y.halfWidth.set(b.width*.5,0,0),y.halfHeight.set(0,b.height*.5,0),y.halfWidth.applyMatrix4(r),y.halfHeight.applyMatrix4(r),x++}else if(b.isPointLight){const y=i.point[f];y.position.setFromMatrixPosition(b.matrixWorld),y.position.applyMatrix4(g),f++}else if(b.isHemisphereLight){const y=i.hemi[_];y.direction.setFromMatrixPosition(b.matrixWorld),y.direction.transformDirection(g),_++}}}return{setup:o,setupView:c,state:i}}function mv(t){const e=new T3(t),n=[],i=[],a=[];function s(f){m.camera=f,n.length=0,i.length=0,a.length=0}function r(f){n.push(f)}function o(f){i.push(f)}function c(f){a.push(f)}function u(){e.setup(n)}function d(f){e.setupView(n,f)}const m={lightsArray:n,shadowsArray:i,lightProbeGridArray:a,camera:null,lights:e,transmissionRenderTarget:{},textureUnits:0};return{init:s,state:m,setupLights:u,setupLightsView:d,pushLight:r,pushShadow:o,pushLightProbeGrid:c}}function A3(t){let e=new WeakMap;function n(a,s=0){const r=e.get(a);let o;return r===void 0?(o=new mv(t),e.set(a,[o])):s>=r.length?(o=new mv(t),r.push(o)):o=r[s],o}function i(){e=new WeakMap}return{get:n,dispose:i}}const w3=`void main() { - gl_Position = vec4( position, 1.0 ); -}`,C3=`uniform sampler2D shadow_pass; -uniform vec2 resolution; -uniform float radius; -void main() { - const float samples = float( VSM_SAMPLES ); - float mean = 0.0; - float squared_mean = 0.0; - float uvStride = samples <= 1.0 ? 0.0 : 2.0 / ( samples - 1.0 ); - float uvStart = samples <= 1.0 ? 0.0 : - 1.0; - for ( float i = 0.0; i < samples; i ++ ) { - float uvOffset = uvStart + i * uvStride; - #ifdef HORIZONTAL_PASS - vec2 distribution = texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ).rg; - mean += distribution.x; - squared_mean += distribution.y * distribution.y + distribution.x * distribution.x; - #else - float depth = texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ).r; - mean += depth; - squared_mean += depth * depth; - #endif - } - mean = mean / samples; - squared_mean = squared_mean / samples; - float std_dev = sqrt( max( 0.0, squared_mean - mean * mean ) ); - gl_FragColor = vec4( mean, std_dev, 0.0, 1.0 ); -}`,N3=[new Y(1,0,0),new Y(-1,0,0),new Y(0,1,0),new Y(0,-1,0),new Y(0,0,1),new Y(0,0,-1)],R3=[new Y(0,-1,0),new Y(0,-1,0),new Y(0,0,1),new Y(0,0,-1),new Y(0,-1,0),new Y(0,-1,0)],gv=new Ft,$l=new Y,sh=new Y;function D3(t,e,n){let i=new bS;const a=new At,s=new At,r=new Qt,o=new WA,c=new XA,u={},d=n.maxTextureSize,m={[Ts]:Yn,[Yn]:Ts,[ba]:ba},f=new Xn({defines:{VSM_SAMPLES:8},uniforms:{shadow_pass:{value:null},resolution:{value:new At},radius:{value:4}},vertexShader:w3,fragmentShader:C3}),p=f.clone();p.defines.HORIZONTAL_PASS=1;const x=new Zn;x.setAttribute("position",new cn(new Float32Array([-1,-1,.5,3,-1,.5,-1,3,.5]),3));const _=new Ri(x,f),g=this;this.enabled=!1,this.autoUpdate=!0,this.needsUpdate=!1,this.type=bu;let h=this.type;this.render=function(M,T,S){if(g.enabled===!1||g.autoUpdate===!1&&g.needsUpdate===!1||M.length===0)return;this.type===DT&&(Je("WebGLShadowMap: PCFSoftShadowMap has been deprecated. Using PCFShadowMap instead."),this.type=bu);const C=t.getRenderTarget(),R=t.getActiveCubeFace(),L=t.getActiveMipmapLevel(),O=t.state;O.setBlending(Na),O.buffers.depth.getReversed()===!0?O.buffers.color.setClear(0,0,0,0):O.buffers.color.setClear(1,1,1,1),O.buffers.depth.setTest(!0),O.setScissorTest(!1);const W=h!==this.type;W&&T.traverse(function(z){z.material&&(Array.isArray(z.material)?z.material.forEach(D=>D.needsUpdate=!0):z.material.needsUpdate=!0)});for(let z=0,D=M.length;z<D;z++){const B=M[z],k=B.shadow;if(k===void 0){Je("WebGLShadowMap:",B,"has no shadow.");continue}if(k.autoUpdate===!1&&k.needsUpdate===!1)continue;a.copy(k.mapSize);const V=k.getFrameExtents();a.multiply(V),s.copy(k.mapSize),(a.x>d||a.y>d)&&(a.x>d&&(s.x=Math.floor(d/V.x),a.x=s.x*V.x,k.mapSize.x=s.x),a.y>d&&(s.y=Math.floor(d/V.y),a.y=s.y*V.y,k.mapSize.y=s.y));const X=t.state.buffers.depth.getReversed();if(k.camera._reversedDepth=X,k.map===null||W===!0){if(k.map!==null&&(k.map.depthTexture!==null&&(k.map.depthTexture.dispose(),k.map.depthTexture=null),k.map.dispose()),this.type===so){if(B.isPointLight){Je("WebGLShadowMap: VSM shadow maps are not supported for PointLights. Use PCF or BasicShadowMap instead.");continue}k.map=new aa(a.x,a.y,{format:er,type:za,minFilter:Rn,magFilter:Rn,generateMipmaps:!1}),k.map.texture.name=B.name+".shadowMap",k.map.depthTexture=new yl(a.x,a.y,Ii),k.map.depthTexture.name=B.name+".shadowMapDepth",k.map.depthTexture.format=Ia,k.map.depthTexture.compareFunction=null,k.map.depthTexture.minFilter=En,k.map.depthTexture.magFilter=En}else B.isPointLight?(k.map=new NS(a.x),k.map.depthTexture=new kA(a.x,sa)):(k.map=new aa(a.x,a.y),k.map.depthTexture=new yl(a.x,a.y,sa)),k.map.depthTexture.name=B.name+".shadowMap",k.map.depthTexture.format=Ia,this.type===bu?(k.map.depthTexture.compareFunction=X?xg:gg,k.map.depthTexture.minFilter=Rn,k.map.depthTexture.magFilter=Rn):(k.map.depthTexture.compareFunction=null,k.map.depthTexture.minFilter=En,k.map.depthTexture.magFilter=En);k.camera.updateProjectionMatrix()}const ge=k.map.isWebGLCubeRenderTarget?6:1;for(let he=0;he<ge;he++){if(k.map.isWebGLCubeRenderTarget)t.setRenderTarget(k.map,he),t.clear();else{he===0&&(t.setRenderTarget(k.map),t.clear());const Ae=k.getViewport(he);r.set(s.x*Ae.x,s.y*Ae.y,s.x*Ae.z,s.y*Ae.w),O.viewport(r)}if(B.isPointLight){const Ae=k.camera,q=k.matrix,fe=B.distance||Ae.far;fe!==Ae.far&&(Ae.far=fe,Ae.updateProjectionMatrix()),$l.setFromMatrixPosition(B.matrixWorld),Ae.position.copy($l),sh.copy(Ae.position),sh.add(N3[he]),Ae.up.copy(R3[he]),Ae.lookAt(sh),Ae.updateMatrixWorld(),q.makeTranslation(-$l.x,-$l.y,-$l.z),gv.multiplyMatrices(Ae.projectionMatrix,Ae.matrixWorldInverse),k._frustum.setFromProjectionMatrix(gv,Ae.coordinateSystem,Ae.reversedDepth)}else k.updateMatrices(B);i=k.getFrustum(),y(T,S,k.camera,B,this.type)}k.isPointLightShadow!==!0&&this.type===so&&v(k,S),k.needsUpdate=!1}h=this.type,g.needsUpdate=!1,t.setRenderTarget(C,R,L)};function v(M,T){const S=e.update(_);f.defines.VSM_SAMPLES!==M.blurSamples&&(f.defines.VSM_SAMPLES=M.blurSamples,p.defines.VSM_SAMPLES=M.blurSamples,f.needsUpdate=!0,p.needsUpdate=!0),M.mapPass===null&&(M.mapPass=new aa(a.x,a.y,{format:er,type:za})),f.uniforms.shadow_pass.value=M.map.depthTexture,f.uniforms.resolution.value=M.mapSize,f.uniforms.radius.value=M.radius,t.setRenderTarget(M.mapPass),t.clear(),t.renderBufferDirect(T,null,S,f,_,null),p.uniforms.shadow_pass.value=M.mapPass.texture,p.uniforms.resolution.value=M.mapSize,p.uniforms.radius.value=M.radius,t.setRenderTarget(M.map),t.clear(),t.renderBufferDirect(T,null,S,p,_,null)}function b(M,T,S,C){let R=null;const L=S.isPointLight===!0?M.customDistanceMaterial:M.customDepthMaterial;if(L!==void 0)R=L;else if(R=S.isPointLight===!0?c:o,t.localClippingEnabled&&T.clipShadows===!0&&Array.isArray(T.clippingPlanes)&&T.clippingPlanes.length!==0||T.displacementMap&&T.displacementScale!==0||T.alphaMap&&T.alphaTest>0||T.map&&T.alphaTest>0||T.alphaToCoverage===!0){const O=R.uuid,W=T.uuid;let z=u[O];z===void 0&&(z={},u[O]=z);let D=z[W];D===void 0&&(D=R.clone(),z[W]=D,T.addEventListener("dispose",A)),R=D}if(R.visible=T.visible,R.wireframe=T.wireframe,C===so?R.side=T.shadowSide!==null?T.shadowSide:T.side:R.side=T.shadowSide!==null?T.shadowSide:m[T.side],R.alphaMap=T.alphaMap,R.alphaTest=T.alphaToCoverage===!0?.5:T.alphaTest,R.map=T.map,R.clipShadows=T.clipShadows,R.clippingPlanes=T.clippingPlanes,R.clipIntersection=T.clipIntersection,R.displacementMap=T.displacementMap,R.displacementScale=T.displacementScale,R.displacementBias=T.displacementBias,R.wireframeLinewidth=T.wireframeLinewidth,R.linewidth=T.linewidth,S.isPointLight===!0&&R.isMeshDistanceMaterial===!0){const O=t.properties.get(R);O.light=S}return R}function y(M,T,S,C,R){if(M.visible===!1)return;if(M.layers.test(T.layers)&&(M.isMesh||M.isLine||M.isPoints)&&(M.castShadow||M.receiveShadow&&R===so)&&(!M.frustumCulled||i.intersectsObject(M))){M.modelViewMatrix.multiplyMatrices(S.matrixWorldInverse,M.matrixWorld);const W=e.update(M),z=M.material;if(Array.isArray(z)){const D=W.groups;for(let B=0,k=D.length;B<k;B++){const V=D[B],X=z[V.materialIndex];if(X&&X.visible){const ge=b(M,X,C,R);M.onBeforeShadow(t,M,T,S,W,ge,V),t.renderBufferDirect(S,null,W,ge,M,V),M.onAfterShadow(t,M,T,S,W,ge,V)}}}else if(z.visible){const D=b(M,z,C,R);M.onBeforeShadow(t,M,T,S,W,D,null),t.renderBufferDirect(S,null,W,D,M,null),M.onAfterShadow(t,M,T,S,W,D,null)}}const O=M.children;for(let W=0,z=O.length;W<z;W++)y(O[W],T,S,C,R)}function A(M){M.target.removeEventListener("dispose",A);for(const S in u){const C=u[S],R=M.target.uuid;R in C&&(C[R].dispose(),delete C[R])}}}function L3(t,e){function n(){let j=!1;const Me=new Qt;let se=null;const Te=new Qt(0,0,0,0);return{setMask:function(Re){se!==Re&&!j&&(t.colorMask(Re,Re,Re,Re),se=Re)},setLocked:function(Re){j=Re},setClear:function(Re,de,ze,je,_t){_t===!0&&(Re*=je,de*=je,ze*=je),Me.set(Re,de,ze,je),Te.equals(Me)===!1&&(t.clearColor(Re,de,ze,je),Te.copy(Me))},reset:function(){j=!1,se=null,Te.set(-1,0,0,0)}}}function i(){let j=!1,Me=!1,se=null,Te=null,Re=null;return{setReversed:function(de){if(Me!==de){const ze=e.get("EXT_clip_control");de?ze.clipControlEXT(ze.LOWER_LEFT_EXT,ze.ZERO_TO_ONE_EXT):ze.clipControlEXT(ze.LOWER_LEFT_EXT,ze.NEGATIVE_ONE_TO_ONE_EXT),Me=de;const je=Re;Re=null,this.setClear(je)}},getReversed:function(){return Me},setTest:function(de){de?oe(t.DEPTH_TEST):Ee(t.DEPTH_TEST)},setMask:function(de){se!==de&&!j&&(t.depthMask(de),se=de)},setFunc:function(de){if(Me&&(de=uA[de]),Te!==de){switch(de){case pp:t.depthFunc(t.NEVER);break;case mp:t.depthFunc(t.ALWAYS);break;case gp:t.depthFunc(t.LESS);break;case vl:t.depthFunc(t.LEQUAL);break;case xp:t.depthFunc(t.EQUAL);break;case vp:t.depthFunc(t.GEQUAL);break;case _p:t.depthFunc(t.GREATER);break;case yp:t.depthFunc(t.NOTEQUAL);break;default:t.depthFunc(t.LEQUAL)}Te=de}},setLocked:function(de){j=de},setClear:function(de){Re!==de&&(Re=de,Me&&(de=1-de),t.clearDepth(de))},reset:function(){j=!1,se=null,Te=null,Re=null,Me=!1}}}function a(){let j=!1,Me=null,se=null,Te=null,Re=null,de=null,ze=null,je=null,_t=null;return{setTest:function(yt){j||(yt?oe(t.STENCIL_TEST):Ee(t.STENCIL_TEST))},setMask:function(yt){Me!==yt&&!j&&(t.stencilMask(yt),Me=yt)},setFunc:function(yt,Ot,_n){(se!==yt||Te!==Ot||Re!==_n)&&(t.stencilFunc(yt,Ot,_n),se=yt,Te=Ot,Re=_n)},setOp:function(yt,Ot,_n){(de!==yt||ze!==Ot||je!==_n)&&(t.stencilOp(yt,Ot,_n),de=yt,ze=Ot,je=_n)},setLocked:function(yt){j=yt},setClear:function(yt){_t!==yt&&(t.clearStencil(yt),_t=yt)},reset:function(){j=!1,Me=null,se=null,Te=null,Re=null,de=null,ze=null,je=null,_t=null}}}const s=new n,r=new i,o=new a,c=new WeakMap,u=new WeakMap;let d={},m={},f={},p=new WeakMap,x=[],_=null,g=!1,h=null,v=null,b=null,y=null,A=null,M=null,T=null,S=new gt(0,0,0),C=0,R=!1,L=null,O=null,W=null,z=null,D=null;const B=t.getParameter(t.MAX_COMBINED_TEXTURE_IMAGE_UNITS);let k=!1,V=0;const X=t.getParameter(t.VERSION);X.indexOf("WebGL")!==-1?(V=parseFloat(/^WebGL (\d)/.exec(X)[1]),k=V>=1):X.indexOf("OpenGL ES")!==-1&&(V=parseFloat(/^OpenGL ES (\d)/.exec(X)[1]),k=V>=2);let ge=null,he={};const Ae=t.getParameter(t.SCISSOR_BOX),q=t.getParameter(t.VIEWPORT),fe=new Qt().fromArray(Ae),Ne=new Qt().fromArray(q);function ie(j,Me,se,Te){const Re=new Uint8Array(4),de=t.createTexture();t.bindTexture(j,de),t.texParameteri(j,t.TEXTURE_MIN_FILTER,t.NEAREST),t.texParameteri(j,t.TEXTURE_MAG_FILTER,t.NEAREST);for(let ze=0;ze<se;ze++)j===t.TEXTURE_3D||j===t.TEXTURE_2D_ARRAY?t.texImage3D(Me,0,t.RGBA,1,1,Te,0,t.RGBA,t.UNSIGNED_BYTE,Re):t.texImage2D(Me+ze,0,t.RGBA,1,1,0,t.RGBA,t.UNSIGNED_BYTE,Re);return de}const xe={};xe[t.TEXTURE_2D]=ie(t.TEXTURE_2D,t.TEXTURE_2D,1),xe[t.TEXTURE_CUBE_MAP]=ie(t.TEXTURE_CUBE_MAP,t.TEXTURE_CUBE_MAP_POSITIVE_X,6),xe[t.TEXTURE_2D_ARRAY]=ie(t.TEXTURE_2D_ARRAY,t.TEXTURE_2D_ARRAY,1,1),xe[t.TEXTURE_3D]=ie(t.TEXTURE_3D,t.TEXTURE_3D,1,1),s.setClear(0,0,0,1),r.setClear(1),o.setClear(0),oe(t.DEPTH_TEST),r.setFunc(vl),Ve(!1),Ge(u0),oe(t.CULL_FACE),Ce(Na);function oe(j){d[j]!==!0&&(t.enable(j),d[j]=!0)}function Ee(j){d[j]!==!1&&(t.disable(j),d[j]=!1)}function Oe(j,Me){return f[j]!==Me?(t.bindFramebuffer(j,Me),f[j]=Me,j===t.DRAW_FRAMEBUFFER&&(f[t.FRAMEBUFFER]=Me),j===t.FRAMEBUFFER&&(f[t.DRAW_FRAMEBUFFER]=Me),!0):!1}function ke(j,Me){let se=x,Te=!1;if(j){se=p.get(Me),se===void 0&&(se=[],p.set(Me,se));const Re=j.textures;if(se.length!==Re.length||se[0]!==t.COLOR_ATTACHMENT0){for(let de=0,ze=Re.length;de<ze;de++)se[de]=t.COLOR_ATTACHMENT0+de;se.length=Re.length,Te=!0}}else se[0]!==t.BACK&&(se[0]=t.BACK,Te=!0);Te&&t.drawBuffers(se)}function xt(j){return _!==j?(t.useProgram(j),_=j,!0):!1}const re={[Bs]:t.FUNC_ADD,[UT]:t.FUNC_SUBTRACT,[OT]:t.FUNC_REVERSE_SUBTRACT};re[PT]=t.MIN,re[zT]=t.MAX;const Se={[IT]:t.ZERO,[kT]:t.ONE,[BT]:t.SRC_COLOR,[dp]:t.SRC_ALPHA,[WT]:t.SRC_ALPHA_SATURATE,[GT]:t.DST_COLOR,[jT]:t.DST_ALPHA,[FT]:t.ONE_MINUS_SRC_COLOR,[hp]:t.ONE_MINUS_SRC_ALPHA,[VT]:t.ONE_MINUS_DST_COLOR,[HT]:t.ONE_MINUS_DST_ALPHA,[XT]:t.CONSTANT_COLOR,[qT]:t.ONE_MINUS_CONSTANT_COLOR,[YT]:t.CONSTANT_ALPHA,[KT]:t.ONE_MINUS_CONSTANT_ALPHA};function Ce(j,Me,se,Te,Re,de,ze,je,_t,yt){if(j===Na){g===!0&&(Ee(t.BLEND),g=!1);return}if(g===!1&&(oe(t.BLEND),g=!0),j!==LT){if(j!==h||yt!==R){if((v!==Bs||A!==Bs)&&(t.blendEquation(t.FUNC_ADD),v=Bs,A=Bs),yt)switch(j){case nl:t.blendFuncSeparate(t.ONE,t.ONE_MINUS_SRC_ALPHA,t.ONE,t.ONE_MINUS_SRC_ALPHA);break;case f0:t.blendFunc(t.ONE,t.ONE);break;case d0:t.blendFuncSeparate(t.ZERO,t.ONE_MINUS_SRC_COLOR,t.ZERO,t.ONE);break;case h0:t.blendFuncSeparate(t.DST_COLOR,t.ONE_MINUS_SRC_ALPHA,t.ZERO,t.ONE);break;default:St("WebGLState: Invalid blending: ",j);break}else switch(j){case nl:t.blendFuncSeparate(t.SRC_ALPHA,t.ONE_MINUS_SRC_ALPHA,t.ONE,t.ONE_MINUS_SRC_ALPHA);break;case f0:t.blendFuncSeparate(t.SRC_ALPHA,t.ONE,t.ONE,t.ONE);break;case d0:St("WebGLState: SubtractiveBlending requires material.premultipliedAlpha = true");break;case h0:St("WebGLState: MultiplyBlending requires material.premultipliedAlpha = true");break;default:St("WebGLState: Invalid blending: ",j);break}b=null,y=null,M=null,T=null,S.set(0,0,0),C=0,h=j,R=yt}return}Re=Re||Me,de=de||se,ze=ze||Te,(Me!==v||Re!==A)&&(t.blendEquationSeparate(re[Me],re[Re]),v=Me,A=Re),(se!==b||Te!==y||de!==M||ze!==T)&&(t.blendFuncSeparate(Se[se],Se[Te],Se[de],Se[ze]),b=se,y=Te,M=de,T=ze),(je.equals(S)===!1||_t!==C)&&(t.blendColor(je.r,je.g,je.b,_t),S.copy(je),C=_t),h=j,R=!1}function be(j,Me){j.side===ba?Ee(t.CULL_FACE):oe(t.CULL_FACE);let se=j.side===Yn;Me&&(se=!se),Ve(se),j.blending===nl&&j.transparent===!1?Ce(Na):Ce(j.blending,j.blendEquation,j.blendSrc,j.blendDst,j.blendEquationAlpha,j.blendSrcAlpha,j.blendDstAlpha,j.blendColor,j.blendAlpha,j.premultipliedAlpha),r.setFunc(j.depthFunc),r.setTest(j.depthTest),r.setMask(j.depthWrite),s.setMask(j.colorWrite);const Te=j.stencilWrite;o.setTest(Te),Te&&(o.setMask(j.stencilWriteMask),o.setFunc(j.stencilFunc,j.stencilRef,j.stencilFuncMask),o.setOp(j.stencilFail,j.stencilZFail,j.stencilZPass)),we(j.polygonOffset,j.polygonOffsetFactor,j.polygonOffsetUnits),j.alphaToCoverage===!0?oe(t.SAMPLE_ALPHA_TO_COVERAGE):Ee(t.SAMPLE_ALPHA_TO_COVERAGE)}function Ve(j){L!==j&&(j?t.frontFace(t.CW):t.frontFace(t.CCW),L=j)}function Ge(j){j!==NT?(oe(t.CULL_FACE),j!==O&&(j===u0?t.cullFace(t.BACK):j===RT?t.cullFace(t.FRONT):t.cullFace(t.FRONT_AND_BACK))):Ee(t.CULL_FACE),O=j}function Pe(j){j!==W&&(k&&t.lineWidth(j),W=j)}function we(j,Me,se){j?(oe(t.POLYGON_OFFSET_FILL),(z!==Me||D!==se)&&(z=Me,D=se,r.getReversed()&&(Me=-Me),t.polygonOffset(Me,se))):Ee(t.POLYGON_OFFSET_FILL)}function We(j){j?oe(t.SCISSOR_TEST):Ee(t.SCISSOR_TEST)}function ve(j){j===void 0&&(j=t.TEXTURE0+B-1),ge!==j&&(t.activeTexture(j),ge=j)}function I(j,Me,se){se===void 0&&(ge===null?se=t.TEXTURE0+B-1:se=ge);let Te=he[se];Te===void 0&&(Te={type:void 0,texture:void 0},he[se]=Te),(Te.type!==j||Te.texture!==Me)&&(ge!==se&&(t.activeTexture(se),ge=se),t.bindTexture(j,Me||xe[j]),Te.type=j,Te.texture=Me)}function nt(){const j=he[ge];j!==void 0&&j.type!==void 0&&(t.bindTexture(j.type,null),j.type=void 0,j.texture=void 0)}function Ke(){try{t.compressedTexImage2D(...arguments)}catch(j){St("WebGLState:",j)}}function N(){try{t.compressedTexImage3D(...arguments)}catch(j){St("WebGLState:",j)}}function E(){try{t.texSubImage2D(...arguments)}catch(j){St("WebGLState:",j)}}function H(){try{t.texSubImage3D(...arguments)}catch(j){St("WebGLState:",j)}}function K(){try{t.compressedTexSubImage2D(...arguments)}catch(j){St("WebGLState:",j)}}function J(){try{t.compressedTexSubImage3D(...arguments)}catch(j){St("WebGLState:",j)}}function _e(){try{t.texStorage2D(...arguments)}catch(j){St("WebGLState:",j)}}function pe(){try{t.texStorage3D(...arguments)}catch(j){St("WebGLState:",j)}}function te(){try{t.texImage2D(...arguments)}catch(j){St("WebGLState:",j)}}function Z(){try{t.texImage3D(...arguments)}catch(j){St("WebGLState:",j)}}function ae(j){return m[j]!==void 0?m[j]:t.getParameter(j)}function ce(j,Me){m[j]!==Me&&(t.pixelStorei(j,Me),m[j]=Me)}function me(j){fe.equals(j)===!1&&(t.scissor(j.x,j.y,j.z,j.w),fe.copy(j))}function ye(j){Ne.equals(j)===!1&&(t.viewport(j.x,j.y,j.z,j.w),Ne.copy(j))}function Fe(j,Me){let se=u.get(Me);se===void 0&&(se=new WeakMap,u.set(Me,se));let Te=se.get(j);Te===void 0&&(Te=t.getUniformBlockIndex(Me,j.name),se.set(j,Te))}function Xe(j,Me){const Te=u.get(Me).get(j);c.get(Me)!==Te&&(t.uniformBlockBinding(Me,Te,j.__bindingPointIndex),c.set(Me,Te))}function et(){t.disable(t.BLEND),t.disable(t.CULL_FACE),t.disable(t.DEPTH_TEST),t.disable(t.POLYGON_OFFSET_FILL),t.disable(t.SCISSOR_TEST),t.disable(t.STENCIL_TEST),t.disable(t.SAMPLE_ALPHA_TO_COVERAGE),t.blendEquation(t.FUNC_ADD),t.blendFunc(t.ONE,t.ZERO),t.blendFuncSeparate(t.ONE,t.ZERO,t.ONE,t.ZERO),t.blendColor(0,0,0,0),t.colorMask(!0,!0,!0,!0),t.clearColor(0,0,0,0),t.depthMask(!0),t.depthFunc(t.LESS),r.setReversed(!1),t.clearDepth(1),t.stencilMask(4294967295),t.stencilFunc(t.ALWAYS,0,4294967295),t.stencilOp(t.KEEP,t.KEEP,t.KEEP),t.clearStencil(0),t.cullFace(t.BACK),t.frontFace(t.CCW),t.polygonOffset(0,0),t.activeTexture(t.TEXTURE0),t.bindFramebuffer(t.FRAMEBUFFER,null),t.bindFramebuffer(t.DRAW_FRAMEBUFFER,null),t.bindFramebuffer(t.READ_FRAMEBUFFER,null),t.useProgram(null),t.lineWidth(1),t.scissor(0,0,t.canvas.width,t.canvas.height),t.viewport(0,0,t.canvas.width,t.canvas.height),t.pixelStorei(t.PACK_ALIGNMENT,4),t.pixelStorei(t.UNPACK_ALIGNMENT,4),t.pixelStorei(t.UNPACK_FLIP_Y_WEBGL,!1),t.pixelStorei(t.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1),t.pixelStorei(t.UNPACK_COLORSPACE_CONVERSION_WEBGL,t.BROWSER_DEFAULT_WEBGL),t.pixelStorei(t.PACK_ROW_LENGTH,0),t.pixelStorei(t.PACK_SKIP_PIXELS,0),t.pixelStorei(t.PACK_SKIP_ROWS,0),t.pixelStorei(t.UNPACK_ROW_LENGTH,0),t.pixelStorei(t.UNPACK_IMAGE_HEIGHT,0),t.pixelStorei(t.UNPACK_SKIP_PIXELS,0),t.pixelStorei(t.UNPACK_SKIP_ROWS,0),t.pixelStorei(t.UNPACK_SKIP_IMAGES,0),d={},m={},ge=null,he={},f={},p=new WeakMap,x=[],_=null,g=!1,h=null,v=null,b=null,y=null,A=null,M=null,T=null,S=new gt(0,0,0),C=0,R=!1,L=null,O=null,W=null,z=null,D=null,fe.set(0,0,t.canvas.width,t.canvas.height),Ne.set(0,0,t.canvas.width,t.canvas.height),s.reset(),r.reset(),o.reset()}return{buffers:{color:s,depth:r,stencil:o},enable:oe,disable:Ee,bindFramebuffer:Oe,drawBuffers:ke,useProgram:xt,setBlending:Ce,setMaterial:be,setFlipSided:Ve,setCullFace:Ge,setLineWidth:Pe,setPolygonOffset:we,setScissorTest:We,activeTexture:ve,bindTexture:I,unbindTexture:nt,compressedTexImage2D:Ke,compressedTexImage3D:N,texImage2D:te,texImage3D:Z,pixelStorei:ce,getParameter:ae,updateUBOMapping:Fe,uniformBlockBinding:Xe,texStorage2D:_e,texStorage3D:pe,texSubImage2D:E,texSubImage3D:H,compressedTexSubImage2D:K,compressedTexSubImage3D:J,scissor:me,viewport:ye,reset:et}}function U3(t,e,n,i,a,s,r){const o=e.has("WEBGL_multisampled_render_to_texture")?e.get("WEBGL_multisampled_render_to_texture"):null,c=typeof navigator>"u"?!1:/OculusBrowser/g.test(navigator.userAgent),u=new At,d=new WeakMap,m=new Set;let f;const p=new WeakMap;let x=!1;try{x=typeof OffscreenCanvas<"u"&&new OffscreenCanvas(1,1).getContext("2d")!==null}catch{}function _(N,E){return x?new OffscreenCanvas(N,E):uf("canvas")}function g(N,E,H){let K=1;const J=Ke(N);if((J.width>H||J.height>H)&&(K=H/Math.max(J.width,J.height)),K<1)if(typeof HTMLImageElement<"u"&&N instanceof HTMLImageElement||typeof HTMLCanvasElement<"u"&&N instanceof HTMLCanvasElement||typeof ImageBitmap<"u"&&N instanceof ImageBitmap||typeof VideoFrame<"u"&&N instanceof VideoFrame){const _e=Math.floor(K*J.width),pe=Math.floor(K*J.height);f===void 0&&(f=_(_e,pe));const te=E?_(_e,pe):f;return te.width=_e,te.height=pe,te.getContext("2d").drawImage(N,0,0,_e,pe),Je("WebGLRenderer: Texture has been resized from ("+J.width+"x"+J.height+") to ("+_e+"x"+pe+")."),te}else return"data"in N&&Je("WebGLRenderer: Image in DataTexture is too big ("+J.width+"x"+J.height+")."),N;return N}function h(N){return N.generateMipmaps}function v(N){t.generateMipmap(N)}function b(N){return N.isWebGLCubeRenderTarget?t.TEXTURE_CUBE_MAP:N.isWebGL3DRenderTarget?t.TEXTURE_3D:N.isWebGLArrayRenderTarget||N.isCompressedArrayTexture?t.TEXTURE_2D_ARRAY:t.TEXTURE_2D}function y(N,E,H,K,J,_e=!1){if(N!==null){if(t[N]!==void 0)return t[N];Je("WebGLRenderer: Attempt to use non-existing WebGL internal format '"+N+"'")}let pe;K&&(pe=e.get("EXT_texture_norm16"),pe||Je("WebGLRenderer: Unable to use normalized textures without EXT_texture_norm16 extension"));let te=E;if(E===t.RED&&(H===t.FLOAT&&(te=t.R32F),H===t.HALF_FLOAT&&(te=t.R16F),H===t.UNSIGNED_BYTE&&(te=t.R8),H===t.UNSIGNED_SHORT&&pe&&(te=pe.R16_EXT),H===t.SHORT&&pe&&(te=pe.R16_SNORM_EXT)),E===t.RED_INTEGER&&(H===t.UNSIGNED_BYTE&&(te=t.R8UI),H===t.UNSIGNED_SHORT&&(te=t.R16UI),H===t.UNSIGNED_INT&&(te=t.R32UI),H===t.BYTE&&(te=t.R8I),H===t.SHORT&&(te=t.R16I),H===t.INT&&(te=t.R32I)),E===t.RG&&(H===t.FLOAT&&(te=t.RG32F),H===t.HALF_FLOAT&&(te=t.RG16F),H===t.UNSIGNED_BYTE&&(te=t.RG8),H===t.UNSIGNED_SHORT&&pe&&(te=pe.RG16_EXT),H===t.SHORT&&pe&&(te=pe.RG16_SNORM_EXT)),E===t.RG_INTEGER&&(H===t.UNSIGNED_BYTE&&(te=t.RG8UI),H===t.UNSIGNED_SHORT&&(te=t.RG16UI),H===t.UNSIGNED_INT&&(te=t.RG32UI),H===t.BYTE&&(te=t.RG8I),H===t.SHORT&&(te=t.RG16I),H===t.INT&&(te=t.RG32I)),E===t.RGB_INTEGER&&(H===t.UNSIGNED_BYTE&&(te=t.RGB8UI),H===t.UNSIGNED_SHORT&&(te=t.RGB16UI),H===t.UNSIGNED_INT&&(te=t.RGB32UI),H===t.BYTE&&(te=t.RGB8I),H===t.SHORT&&(te=t.RGB16I),H===t.INT&&(te=t.RGB32I)),E===t.RGBA_INTEGER&&(H===t.UNSIGNED_BYTE&&(te=t.RGBA8UI),H===t.UNSIGNED_SHORT&&(te=t.RGBA16UI),H===t.UNSIGNED_INT&&(te=t.RGBA32UI),H===t.BYTE&&(te=t.RGBA8I),H===t.SHORT&&(te=t.RGBA16I),H===t.INT&&(te=t.RGBA32I)),E===t.RGB&&(H===t.UNSIGNED_SHORT&&pe&&(te=pe.RGB16_EXT),H===t.SHORT&&pe&&(te=pe.RGB16_SNORM_EXT),H===t.UNSIGNED_INT_5_9_9_9_REV&&(te=t.RGB9_E5),H===t.UNSIGNED_INT_10F_11F_11F_REV&&(te=t.R11F_G11F_B10F)),E===t.RGBA){const Z=_e?of:ht.getTransfer(J);H===t.FLOAT&&(te=t.RGBA32F),H===t.HALF_FLOAT&&(te=t.RGBA16F),H===t.UNSIGNED_BYTE&&(te=Z===Ct?t.SRGB8_ALPHA8:t.RGBA8),H===t.UNSIGNED_SHORT&&pe&&(te=pe.RGBA16_EXT),H===t.SHORT&&pe&&(te=pe.RGBA16_SNORM_EXT),H===t.UNSIGNED_SHORT_4_4_4_4&&(te=t.RGBA4),H===t.UNSIGNED_SHORT_5_5_5_1&&(te=t.RGB5_A1)}return(te===t.R16F||te===t.R32F||te===t.RG16F||te===t.RG32F||te===t.RGBA16F||te===t.RGBA32F)&&e.get("EXT_color_buffer_float"),te}function A(N,E){let H;return N?E===null||E===sa||E===Vo?H=t.DEPTH24_STENCIL8:E===Ii?H=t.DEPTH32F_STENCIL8:E===Go&&(H=t.DEPTH24_STENCIL8,Je("DepthTexture: 16 bit depth attachment is not supported with stencil. Using 24-bit attachment.")):E===null||E===sa||E===Vo?H=t.DEPTH_COMPONENT24:E===Ii?H=t.DEPTH_COMPONENT32F:E===Go&&(H=t.DEPTH_COMPONENT16),H}function M(N,E){return h(N)===!0||N.isFramebufferTexture&&N.minFilter!==En&&N.minFilter!==Rn?Math.log2(Math.max(E.width,E.height))+1:N.mipmaps!==void 0&&N.mipmaps.length>0?N.mipmaps.length:N.isCompressedTexture&&Array.isArray(N.image)?E.mipmaps.length:1}function T(N){const E=N.target;E.removeEventListener("dispose",T),C(E),E.isVideoTexture&&d.delete(E),E.isHTMLTexture&&m.delete(E)}function S(N){const E=N.target;E.removeEventListener("dispose",S),L(E)}function C(N){const E=i.get(N);if(E.__webglInit===void 0)return;const H=N.source,K=p.get(H);if(K){const J=K[E.__cacheKey];J.usedTimes--,J.usedTimes===0&&R(N),Object.keys(K).length===0&&p.delete(H)}i.remove(N)}function R(N){const E=i.get(N);t.deleteTexture(E.__webglTexture);const H=N.source,K=p.get(H);delete K[E.__cacheKey],r.memory.textures--}function L(N){const E=i.get(N);if(N.depthTexture&&(N.depthTexture.dispose(),i.remove(N.depthTexture)),N.isWebGLCubeRenderTarget)for(let K=0;K<6;K++){if(Array.isArray(E.__webglFramebuffer[K]))for(let J=0;J<E.__webglFramebuffer[K].length;J++)t.deleteFramebuffer(E.__webglFramebuffer[K][J]);else t.deleteFramebuffer(E.__webglFramebuffer[K]);E.__webglDepthbuffer&&t.deleteRenderbuffer(E.__webglDepthbuffer[K])}else{if(Array.isArray(E.__webglFramebuffer))for(let K=0;K<E.__webglFramebuffer.length;K++)t.deleteFramebuffer(E.__webglFramebuffer[K]);else t.deleteFramebuffer(E.__webglFramebuffer);if(E.__webglDepthbuffer&&t.deleteRenderbuffer(E.__webglDepthbuffer),E.__webglMultisampledFramebuffer&&t.deleteFramebuffer(E.__webglMultisampledFramebuffer),E.__webglColorRenderbuffer)for(let K=0;K<E.__webglColorRenderbuffer.length;K++)E.__webglColorRenderbuffer[K]&&t.deleteRenderbuffer(E.__webglColorRenderbuffer[K]);E.__webglDepthRenderbuffer&&t.deleteRenderbuffer(E.__webglDepthRenderbuffer)}const H=N.textures;for(let K=0,J=H.length;K<J;K++){const _e=i.get(H[K]);_e.__webglTexture&&(t.deleteTexture(_e.__webglTexture),r.memory.textures--),i.remove(H[K])}i.remove(N)}let O=0;function W(){O=0}function z(){return O}function D(N){O=N}function B(){const N=O;return N>=a.maxTextures&&Je("WebGLTextures: Trying to use "+N+" texture units while this GPU supports only "+a.maxTextures),O+=1,N}function k(N){const E=[];return E.push(N.wrapS),E.push(N.wrapT),E.push(N.wrapR||0),E.push(N.magFilter),E.push(N.minFilter),E.push(N.anisotropy),E.push(N.internalFormat),E.push(N.format),E.push(N.type),E.push(N.generateMipmaps),E.push(N.premultiplyAlpha),E.push(N.flipY),E.push(N.unpackAlignment),E.push(N.colorSpace),E.join()}function V(N,E){const H=i.get(N);if(N.isVideoTexture&&I(N),N.isRenderTargetTexture===!1&&N.isExternalTexture!==!0&&N.version>0&&H.__version!==N.version){const K=N.image;if(K===null)Je("WebGLRenderer: Texture marked for update but no image data found.");else if(K.complete===!1)Je("WebGLRenderer: Texture marked for update but image is incomplete");else{Ee(H,N,E);return}}else N.isExternalTexture&&(H.__webglTexture=N.sourceTexture?N.sourceTexture:null);n.bindTexture(t.TEXTURE_2D,H.__webglTexture,t.TEXTURE0+E)}function X(N,E){const H=i.get(N);if(N.isRenderTargetTexture===!1&&N.version>0&&H.__version!==N.version){Ee(H,N,E);return}else N.isExternalTexture&&(H.__webglTexture=N.sourceTexture?N.sourceTexture:null);n.bindTexture(t.TEXTURE_2D_ARRAY,H.__webglTexture,t.TEXTURE0+E)}function ge(N,E){const H=i.get(N);if(N.isRenderTargetTexture===!1&&N.version>0&&H.__version!==N.version){Ee(H,N,E);return}n.bindTexture(t.TEXTURE_3D,H.__webglTexture,t.TEXTURE0+E)}function he(N,E){const H=i.get(N);if(N.isCubeDepthTexture!==!0&&N.version>0&&H.__version!==N.version){Oe(H,N,E);return}n.bindTexture(t.TEXTURE_CUBE_MAP,H.__webglTexture,t.TEXTURE0+E)}const Ae={[bp]:t.REPEAT,[Ta]:t.CLAMP_TO_EDGE,[Sp]:t.MIRRORED_REPEAT},q={[En]:t.NEAREST,[QT]:t.NEAREST_MIPMAP_NEAREST,[Mc]:t.NEAREST_MIPMAP_LINEAR,[Rn]:t.LINEAR,[wd]:t.LINEAR_MIPMAP_NEAREST,[js]:t.LINEAR_MIPMAP_LINEAR},fe={[tA]:t.NEVER,[rA]:t.ALWAYS,[nA]:t.LESS,[gg]:t.LEQUAL,[iA]:t.EQUAL,[xg]:t.GEQUAL,[aA]:t.GREATER,[sA]:t.NOTEQUAL};function Ne(N,E){if(E.type===Ii&&e.has("OES_texture_float_linear")===!1&&(E.magFilter===Rn||E.magFilter===wd||E.magFilter===Mc||E.magFilter===js||E.minFilter===Rn||E.minFilter===wd||E.minFilter===Mc||E.minFilter===js)&&Je("WebGLRenderer: Unable to use linear filtering with floating point textures. OES_texture_float_linear not supported on this device."),t.texParameteri(N,t.TEXTURE_WRAP_S,Ae[E.wrapS]),t.texParameteri(N,t.TEXTURE_WRAP_T,Ae[E.wrapT]),(N===t.TEXTURE_3D||N===t.TEXTURE_2D_ARRAY)&&t.texParameteri(N,t.TEXTURE_WRAP_R,Ae[E.wrapR]),t.texParameteri(N,t.TEXTURE_MAG_FILTER,q[E.magFilter]),t.texParameteri(N,t.TEXTURE_MIN_FILTER,q[E.minFilter]),E.compareFunction&&(t.texParameteri(N,t.TEXTURE_COMPARE_MODE,t.COMPARE_REF_TO_TEXTURE),t.texParameteri(N,t.TEXTURE_COMPARE_FUNC,fe[E.compareFunction])),e.has("EXT_texture_filter_anisotropic")===!0){if(E.magFilter===En||E.minFilter!==Mc&&E.minFilter!==js||E.type===Ii&&e.has("OES_texture_float_linear")===!1)return;if(E.anisotropy>1||i.get(E).__currentAnisotropy){const H=e.get("EXT_texture_filter_anisotropic");t.texParameterf(N,H.TEXTURE_MAX_ANISOTROPY_EXT,Math.min(E.anisotropy,a.getMaxAnisotropy())),i.get(E).__currentAnisotropy=E.anisotropy}}}function ie(N,E){let H=!1;N.__webglInit===void 0&&(N.__webglInit=!0,E.addEventListener("dispose",T));const K=E.source;let J=p.get(K);J===void 0&&(J={},p.set(K,J));const _e=k(E);if(_e!==N.__cacheKey){J[_e]===void 0&&(J[_e]={texture:t.createTexture(),usedTimes:0},r.memory.textures++,H=!0),J[_e].usedTimes++;const pe=J[N.__cacheKey];pe!==void 0&&(J[N.__cacheKey].usedTimes--,pe.usedTimes===0&&R(E)),N.__cacheKey=_e,N.__webglTexture=J[_e].texture}return H}function xe(N,E,H){return Math.floor(Math.floor(N/H)/E)}function oe(N,E,H,K){const _e=N.updateRanges;if(_e.length===0)n.texSubImage2D(t.TEXTURE_2D,0,0,0,E.width,E.height,H,K,E.data);else{_e.sort((ce,me)=>ce.start-me.start);let pe=0;for(let ce=1;ce<_e.length;ce++){const me=_e[pe],ye=_e[ce],Fe=me.start+me.count,Xe=xe(ye.start,E.width,4),et=xe(me.start,E.width,4);ye.start<=Fe+1&&Xe===et&&xe(ye.start+ye.count-1,E.width,4)===Xe?me.count=Math.max(me.count,ye.start+ye.count-me.start):(++pe,_e[pe]=ye)}_e.length=pe+1;const te=n.getParameter(t.UNPACK_ROW_LENGTH),Z=n.getParameter(t.UNPACK_SKIP_PIXELS),ae=n.getParameter(t.UNPACK_SKIP_ROWS);n.pixelStorei(t.UNPACK_ROW_LENGTH,E.width);for(let ce=0,me=_e.length;ce<me;ce++){const ye=_e[ce],Fe=Math.floor(ye.start/4),Xe=Math.ceil(ye.count/4),et=Fe%E.width,j=Math.floor(Fe/E.width),Me=Xe,se=1;n.pixelStorei(t.UNPACK_SKIP_PIXELS,et),n.pixelStorei(t.UNPACK_SKIP_ROWS,j),n.texSubImage2D(t.TEXTURE_2D,0,et,j,Me,se,H,K,E.data)}N.clearUpdateRanges(),n.pixelStorei(t.UNPACK_ROW_LENGTH,te),n.pixelStorei(t.UNPACK_SKIP_PIXELS,Z),n.pixelStorei(t.UNPACK_SKIP_ROWS,ae)}}function Ee(N,E,H){let K=t.TEXTURE_2D;(E.isDataArrayTexture||E.isCompressedArrayTexture)&&(K=t.TEXTURE_2D_ARRAY),E.isData3DTexture&&(K=t.TEXTURE_3D);const J=ie(N,E),_e=E.source;n.bindTexture(K,N.__webglTexture,t.TEXTURE0+H);const pe=i.get(_e);if(_e.version!==pe.__version||J===!0){if(n.activeTexture(t.TEXTURE0+H),(typeof ImageBitmap<"u"&&E.image instanceof ImageBitmap)===!1){const se=ht.getPrimaries(ht.workingColorSpace),Te=E.colorSpace===is?null:ht.getPrimaries(E.colorSpace),Re=E.colorSpace===is||se===Te?t.NONE:t.BROWSER_DEFAULT_WEBGL;n.pixelStorei(t.UNPACK_FLIP_Y_WEBGL,E.flipY),n.pixelStorei(t.UNPACK_PREMULTIPLY_ALPHA_WEBGL,E.premultiplyAlpha),n.pixelStorei(t.UNPACK_COLORSPACE_CONVERSION_WEBGL,Re)}n.pixelStorei(t.UNPACK_ALIGNMENT,E.unpackAlignment);let Z=g(E.image,!1,a.maxTextureSize);Z=nt(E,Z);const ae=s.convert(E.format,E.colorSpace),ce=s.convert(E.type);let me=y(E.internalFormat,ae,ce,E.normalized,E.colorSpace,E.isVideoTexture);Ne(K,E);let ye;const Fe=E.mipmaps,Xe=E.isVideoTexture!==!0,et=pe.__version===void 0||J===!0,j=_e.dataReady,Me=M(E,Z);if(E.isDepthTexture)me=A(E.format===Hs,E.type),et&&(Xe?n.texStorage2D(t.TEXTURE_2D,1,me,Z.width,Z.height):n.texImage2D(t.TEXTURE_2D,0,me,Z.width,Z.height,0,ae,ce,null));else if(E.isDataTexture)if(Fe.length>0){Xe&&et&&n.texStorage2D(t.TEXTURE_2D,Me,me,Fe[0].width,Fe[0].height);for(let se=0,Te=Fe.length;se<Te;se++)ye=Fe[se],Xe?j&&n.texSubImage2D(t.TEXTURE_2D,se,0,0,ye.width,ye.height,ae,ce,ye.data):n.texImage2D(t.TEXTURE_2D,se,me,ye.width,ye.height,0,ae,ce,ye.data);E.generateMipmaps=!1}else Xe?(et&&n.texStorage2D(t.TEXTURE_2D,Me,me,Z.width,Z.height),j&&oe(E,Z,ae,ce)):n.texImage2D(t.TEXTURE_2D,0,me,Z.width,Z.height,0,ae,ce,Z.data);else if(E.isCompressedTexture)if(E.isCompressedArrayTexture){Xe&&et&&n.texStorage3D(t.TEXTURE_2D_ARRAY,Me,me,Fe[0].width,Fe[0].height,Z.depth);for(let se=0,Te=Fe.length;se<Te;se++)if(ye=Fe[se],E.format!==ki)if(ae!==null)if(Xe){if(j)if(E.layerUpdates.size>0){const Re=q0(ye.width,ye.height,E.format,E.type);for(const de of E.layerUpdates){const ze=ye.data.subarray(de*Re/ye.data.BYTES_PER_ELEMENT,(de+1)*Re/ye.data.BYTES_PER_ELEMENT);n.compressedTexSubImage3D(t.TEXTURE_2D_ARRAY,se,0,0,de,ye.width,ye.height,1,ae,ze)}E.clearLayerUpdates()}else n.compressedTexSubImage3D(t.TEXTURE_2D_ARRAY,se,0,0,0,ye.width,ye.height,Z.depth,ae,ye.data)}else n.compressedTexImage3D(t.TEXTURE_2D_ARRAY,se,me,ye.width,ye.height,Z.depth,0,ye.data,0,0);else Je("WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()");else Xe?j&&n.texSubImage3D(t.TEXTURE_2D_ARRAY,se,0,0,0,ye.width,ye.height,Z.depth,ae,ce,ye.data):n.texImage3D(t.TEXTURE_2D_ARRAY,se,me,ye.width,ye.height,Z.depth,0,ae,ce,ye.data)}else{Xe&&et&&n.texStorage2D(t.TEXTURE_2D,Me,me,Fe[0].width,Fe[0].height);for(let se=0,Te=Fe.length;se<Te;se++)ye=Fe[se],E.format!==ki?ae!==null?Xe?j&&n.compressedTexSubImage2D(t.TEXTURE_2D,se,0,0,ye.width,ye.height,ae,ye.data):n.compressedTexImage2D(t.TEXTURE_2D,se,me,ye.width,ye.height,0,ye.data):Je("WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()"):Xe?j&&n.texSubImage2D(t.TEXTURE_2D,se,0,0,ye.width,ye.height,ae,ce,ye.data):n.texImage2D(t.TEXTURE_2D,se,me,ye.width,ye.height,0,ae,ce,ye.data)}else if(E.isDataArrayTexture)if(Xe){if(et&&n.texStorage3D(t.TEXTURE_2D_ARRAY,Me,me,Z.width,Z.height,Z.depth),j)if(E.layerUpdates.size>0){const se=q0(Z.width,Z.height,E.format,E.type);for(const Te of E.layerUpdates){const Re=Z.data.subarray(Te*se/Z.data.BYTES_PER_ELEMENT,(Te+1)*se/Z.data.BYTES_PER_ELEMENT);n.texSubImage3D(t.TEXTURE_2D_ARRAY,0,0,0,Te,Z.width,Z.height,1,ae,ce,Re)}E.clearLayerUpdates()}else n.texSubImage3D(t.TEXTURE_2D_ARRAY,0,0,0,0,Z.width,Z.height,Z.depth,ae,ce,Z.data)}else n.texImage3D(t.TEXTURE_2D_ARRAY,0,me,Z.width,Z.height,Z.depth,0,ae,ce,Z.data);else if(E.isData3DTexture)Xe?(et&&n.texStorage3D(t.TEXTURE_3D,Me,me,Z.width,Z.height,Z.depth),j&&n.texSubImage3D(t.TEXTURE_3D,0,0,0,0,Z.width,Z.height,Z.depth,ae,ce,Z.data)):n.texImage3D(t.TEXTURE_3D,0,me,Z.width,Z.height,Z.depth,0,ae,ce,Z.data);else if(E.isFramebufferTexture){if(et)if(Xe)n.texStorage2D(t.TEXTURE_2D,Me,me,Z.width,Z.height);else{let se=Z.width,Te=Z.height;for(let Re=0;Re<Me;Re++)n.texImage2D(t.TEXTURE_2D,Re,me,se,Te,0,ae,ce,null),se>>=1,Te>>=1}}else if(E.isHTMLTexture){if("texElementImage2D"in t){const se=t.canvas;if(se.hasAttribute("layoutsubtree")||se.setAttribute("layoutsubtree","true"),Z.parentNode!==se){se.appendChild(Z),m.add(E),se.onpaint=Te=>{const Re=Te.changedElements;for(const de of m)Re.includes(de.image)&&(de.needsUpdate=!0)},se.requestPaint();return}if(t.texElementImage2D.length===3)t.texElementImage2D(t.TEXTURE_2D,t.RGBA8,Z);else{const Re=t.RGBA,de=t.RGBA,ze=t.UNSIGNED_BYTE;t.texElementImage2D(t.TEXTURE_2D,0,Re,de,ze,Z)}t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,t.LINEAR),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_S,t.CLAMP_TO_EDGE),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_T,t.CLAMP_TO_EDGE)}}else if(Fe.length>0){if(Xe&&et){const se=Ke(Fe[0]);n.texStorage2D(t.TEXTURE_2D,Me,me,se.width,se.height)}for(let se=0,Te=Fe.length;se<Te;se++)ye=Fe[se],Xe?j&&n.texSubImage2D(t.TEXTURE_2D,se,0,0,ae,ce,ye):n.texImage2D(t.TEXTURE_2D,se,me,ae,ce,ye);E.generateMipmaps=!1}else if(Xe){if(et){const se=Ke(Z);n.texStorage2D(t.TEXTURE_2D,Me,me,se.width,se.height)}j&&n.texSubImage2D(t.TEXTURE_2D,0,0,0,ae,ce,Z)}else n.texImage2D(t.TEXTURE_2D,0,me,ae,ce,Z);h(E)&&v(K),pe.__version=_e.version,E.onUpdate&&E.onUpdate(E)}N.__version=E.version}function Oe(N,E,H){if(E.image.length!==6)return;const K=ie(N,E),J=E.source;n.bindTexture(t.TEXTURE_CUBE_MAP,N.__webglTexture,t.TEXTURE0+H);const _e=i.get(J);if(J.version!==_e.__version||K===!0){n.activeTexture(t.TEXTURE0+H);const pe=ht.getPrimaries(ht.workingColorSpace),te=E.colorSpace===is?null:ht.getPrimaries(E.colorSpace),Z=E.colorSpace===is||pe===te?t.NONE:t.BROWSER_DEFAULT_WEBGL;n.pixelStorei(t.UNPACK_FLIP_Y_WEBGL,E.flipY),n.pixelStorei(t.UNPACK_PREMULTIPLY_ALPHA_WEBGL,E.premultiplyAlpha),n.pixelStorei(t.UNPACK_ALIGNMENT,E.unpackAlignment),n.pixelStorei(t.UNPACK_COLORSPACE_CONVERSION_WEBGL,Z);const ae=E.isCompressedTexture||E.image[0].isCompressedTexture,ce=E.image[0]&&E.image[0].isDataTexture,me=[];for(let de=0;de<6;de++)!ae&&!ce?me[de]=g(E.image[de],!0,a.maxCubemapSize):me[de]=ce?E.image[de].image:E.image[de],me[de]=nt(E,me[de]);const ye=me[0],Fe=s.convert(E.format,E.colorSpace),Xe=s.convert(E.type),et=y(E.internalFormat,Fe,Xe,E.normalized,E.colorSpace),j=E.isVideoTexture!==!0,Me=_e.__version===void 0||K===!0,se=J.dataReady;let Te=M(E,ye);Ne(t.TEXTURE_CUBE_MAP,E);let Re;if(ae){j&&Me&&n.texStorage2D(t.TEXTURE_CUBE_MAP,Te,et,ye.width,ye.height);for(let de=0;de<6;de++){Re=me[de].mipmaps;for(let ze=0;ze<Re.length;ze++){const je=Re[ze];E.format!==ki?Fe!==null?j?se&&n.compressedTexSubImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+de,ze,0,0,je.width,je.height,Fe,je.data):n.compressedTexImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+de,ze,et,je.width,je.height,0,je.data):Je("WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()"):j?se&&n.texSubImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+de,ze,0,0,je.width,je.height,Fe,Xe,je.data):n.texImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+de,ze,et,je.width,je.height,0,Fe,Xe,je.data)}}}else{if(Re=E.mipmaps,j&&Me){Re.length>0&&Te++;const de=Ke(me[0]);n.texStorage2D(t.TEXTURE_CUBE_MAP,Te,et,de.width,de.height)}for(let de=0;de<6;de++)if(ce){j?se&&n.texSubImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+de,0,0,0,me[de].width,me[de].height,Fe,Xe,me[de].data):n.texImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+de,0,et,me[de].width,me[de].height,0,Fe,Xe,me[de].data);for(let ze=0;ze<Re.length;ze++){const _t=Re[ze].image[de].image;j?se&&n.texSubImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+de,ze+1,0,0,_t.width,_t.height,Fe,Xe,_t.data):n.texImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+de,ze+1,et,_t.width,_t.height,0,Fe,Xe,_t.data)}}else{j?se&&n.texSubImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+de,0,0,0,Fe,Xe,me[de]):n.texImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+de,0,et,Fe,Xe,me[de]);for(let ze=0;ze<Re.length;ze++){const je=Re[ze];j?se&&n.texSubImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+de,ze+1,0,0,Fe,Xe,je.image[de]):n.texImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+de,ze+1,et,Fe,Xe,je.image[de])}}}h(E)&&v(t.TEXTURE_CUBE_MAP),_e.__version=J.version,E.onUpdate&&E.onUpdate(E)}N.__version=E.version}function ke(N,E,H,K,J,_e){const pe=s.convert(H.format,H.colorSpace),te=s.convert(H.type),Z=y(H.internalFormat,pe,te,H.normalized,H.colorSpace),ae=i.get(E),ce=i.get(H);if(ce.__renderTarget=E,!ae.__hasExternalTextures){const me=Math.max(1,E.width>>_e),ye=Math.max(1,E.height>>_e);J===t.TEXTURE_3D||J===t.TEXTURE_2D_ARRAY?n.texImage3D(J,_e,Z,me,ye,E.depth,0,pe,te,null):n.texImage2D(J,_e,Z,me,ye,0,pe,te,null)}n.bindFramebuffer(t.FRAMEBUFFER,N),ve(E)?o.framebufferTexture2DMultisampleEXT(t.FRAMEBUFFER,K,J,ce.__webglTexture,0,We(E)):(J===t.TEXTURE_2D||J>=t.TEXTURE_CUBE_MAP_POSITIVE_X&&J<=t.TEXTURE_CUBE_MAP_NEGATIVE_Z)&&t.framebufferTexture2D(t.FRAMEBUFFER,K,J,ce.__webglTexture,_e),n.bindFramebuffer(t.FRAMEBUFFER,null)}function xt(N,E,H){if(t.bindRenderbuffer(t.RENDERBUFFER,N),E.depthBuffer){const K=E.depthTexture,J=K&&K.isDepthTexture?K.type:null,_e=A(E.stencilBuffer,J),pe=E.stencilBuffer?t.DEPTH_STENCIL_ATTACHMENT:t.DEPTH_ATTACHMENT;ve(E)?o.renderbufferStorageMultisampleEXT(t.RENDERBUFFER,We(E),_e,E.width,E.height):H?t.renderbufferStorageMultisample(t.RENDERBUFFER,We(E),_e,E.width,E.height):t.renderbufferStorage(t.RENDERBUFFER,_e,E.width,E.height),t.framebufferRenderbuffer(t.FRAMEBUFFER,pe,t.RENDERBUFFER,N)}else{const K=E.textures;for(let J=0;J<K.length;J++){const _e=K[J],pe=s.convert(_e.format,_e.colorSpace),te=s.convert(_e.type),Z=y(_e.internalFormat,pe,te,_e.normalized,_e.colorSpace);ve(E)?o.renderbufferStorageMultisampleEXT(t.RENDERBUFFER,We(E),Z,E.width,E.height):H?t.renderbufferStorageMultisample(t.RENDERBUFFER,We(E),Z,E.width,E.height):t.renderbufferStorage(t.RENDERBUFFER,Z,E.width,E.height)}}t.bindRenderbuffer(t.RENDERBUFFER,null)}function re(N,E,H){const K=E.isWebGLCubeRenderTarget===!0;if(n.bindFramebuffer(t.FRAMEBUFFER,N),!(E.depthTexture&&E.depthTexture.isDepthTexture))throw new Error("THREE.WebGLTextures: renderTarget.depthTexture must be an instance of THREE.DepthTexture.");const J=i.get(E.depthTexture);if(J.__renderTarget=E,(!J.__webglTexture||E.depthTexture.image.width!==E.width||E.depthTexture.image.height!==E.height)&&(E.depthTexture.image.width=E.width,E.depthTexture.image.height=E.height,E.depthTexture.needsUpdate=!0),K){if(J.__webglInit===void 0&&(J.__webglInit=!0,E.depthTexture.addEventListener("dispose",T)),J.__webglTexture===void 0){J.__webglTexture=t.createTexture(),n.bindTexture(t.TEXTURE_CUBE_MAP,J.__webglTexture),Ne(t.TEXTURE_CUBE_MAP,E.depthTexture);const ae=s.convert(E.depthTexture.format),ce=s.convert(E.depthTexture.type);let me;E.depthTexture.format===Ia?me=t.DEPTH_COMPONENT24:E.depthTexture.format===Hs&&(me=t.DEPTH24_STENCIL8);for(let ye=0;ye<6;ye++)t.texImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+ye,0,me,E.width,E.height,0,ae,ce,null)}}else V(E.depthTexture,0);const _e=J.__webglTexture,pe=We(E),te=K?t.TEXTURE_CUBE_MAP_POSITIVE_X+H:t.TEXTURE_2D,Z=E.depthTexture.format===Hs?t.DEPTH_STENCIL_ATTACHMENT:t.DEPTH_ATTACHMENT;if(E.depthTexture.format===Ia)ve(E)?o.framebufferTexture2DMultisampleEXT(t.FRAMEBUFFER,Z,te,_e,0,pe):t.framebufferTexture2D(t.FRAMEBUFFER,Z,te,_e,0);else if(E.depthTexture.format===Hs)ve(E)?o.framebufferTexture2DMultisampleEXT(t.FRAMEBUFFER,Z,te,_e,0,pe):t.framebufferTexture2D(t.FRAMEBUFFER,Z,te,_e,0);else throw new Error("THREE.WebGLTextures: Unknown depthTexture format.")}function Se(N){const E=i.get(N),H=N.isWebGLCubeRenderTarget===!0;if(E.__boundDepthTexture!==N.depthTexture){const K=N.depthTexture;if(E.__depthDisposeCallback&&E.__depthDisposeCallback(),K){const J=()=>{delete E.__boundDepthTexture,delete E.__depthDisposeCallback,K.removeEventListener("dispose",J)};K.addEventListener("dispose",J),E.__depthDisposeCallback=J}E.__boundDepthTexture=K}if(N.depthTexture&&!E.__autoAllocateDepthBuffer)if(H)for(let K=0;K<6;K++)re(E.__webglFramebuffer[K],N,K);else{const K=N.texture.mipmaps;K&&K.length>0?re(E.__webglFramebuffer[0],N,0):re(E.__webglFramebuffer,N,0)}else if(H){E.__webglDepthbuffer=[];for(let K=0;K<6;K++)if(n.bindFramebuffer(t.FRAMEBUFFER,E.__webglFramebuffer[K]),E.__webglDepthbuffer[K]===void 0)E.__webglDepthbuffer[K]=t.createRenderbuffer(),xt(E.__webglDepthbuffer[K],N,!1);else{const J=N.stencilBuffer?t.DEPTH_STENCIL_ATTACHMENT:t.DEPTH_ATTACHMENT,_e=E.__webglDepthbuffer[K];t.bindRenderbuffer(t.RENDERBUFFER,_e),t.framebufferRenderbuffer(t.FRAMEBUFFER,J,t.RENDERBUFFER,_e)}}else{const K=N.texture.mipmaps;if(K&&K.length>0?n.bindFramebuffer(t.FRAMEBUFFER,E.__webglFramebuffer[0]):n.bindFramebuffer(t.FRAMEBUFFER,E.__webglFramebuffer),E.__webglDepthbuffer===void 0)E.__webglDepthbuffer=t.createRenderbuffer(),xt(E.__webglDepthbuffer,N,!1);else{const J=N.stencilBuffer?t.DEPTH_STENCIL_ATTACHMENT:t.DEPTH_ATTACHMENT,_e=E.__webglDepthbuffer;t.bindRenderbuffer(t.RENDERBUFFER,_e),t.framebufferRenderbuffer(t.FRAMEBUFFER,J,t.RENDERBUFFER,_e)}}n.bindFramebuffer(t.FRAMEBUFFER,null)}function Ce(N,E,H){const K=i.get(N);E!==void 0&&ke(K.__webglFramebuffer,N,N.texture,t.COLOR_ATTACHMENT0,t.TEXTURE_2D,0),H!==void 0&&Se(N)}function be(N){const E=N.texture,H=i.get(N),K=i.get(E);N.addEventListener("dispose",S);const J=N.textures,_e=N.isWebGLCubeRenderTarget===!0,pe=J.length>1;if(pe||(K.__webglTexture===void 0&&(K.__webglTexture=t.createTexture()),K.__version=E.version,r.memory.textures++),_e){H.__webglFramebuffer=[];for(let te=0;te<6;te++)if(E.mipmaps&&E.mipmaps.length>0){H.__webglFramebuffer[te]=[];for(let Z=0;Z<E.mipmaps.length;Z++)H.__webglFramebuffer[te][Z]=t.createFramebuffer()}else H.__webglFramebuffer[te]=t.createFramebuffer()}else{if(E.mipmaps&&E.mipmaps.length>0){H.__webglFramebuffer=[];for(let te=0;te<E.mipmaps.length;te++)H.__webglFramebuffer[te]=t.createFramebuffer()}else H.__webglFramebuffer=t.createFramebuffer();if(pe)for(let te=0,Z=J.length;te<Z;te++){const ae=i.get(J[te]);ae.__webglTexture===void 0&&(ae.__webglTexture=t.createTexture(),r.memory.textures++)}if(N.samples>0&&ve(N)===!1){H.__webglMultisampledFramebuffer=t.createFramebuffer(),H.__webglColorRenderbuffer=[],n.bindFramebuffer(t.FRAMEBUFFER,H.__webglMultisampledFramebuffer);for(let te=0;te<J.length;te++){const Z=J[te];H.__webglColorRenderbuffer[te]=t.createRenderbuffer(),t.bindRenderbuffer(t.RENDERBUFFER,H.__webglColorRenderbuffer[te]);const ae=s.convert(Z.format,Z.colorSpace),ce=s.convert(Z.type),me=y(Z.internalFormat,ae,ce,Z.normalized,Z.colorSpace,N.isXRRenderTarget===!0),ye=We(N);t.renderbufferStorageMultisample(t.RENDERBUFFER,ye,me,N.width,N.height),t.framebufferRenderbuffer(t.FRAMEBUFFER,t.COLOR_ATTACHMENT0+te,t.RENDERBUFFER,H.__webglColorRenderbuffer[te])}t.bindRenderbuffer(t.RENDERBUFFER,null),N.depthBuffer&&(H.__webglDepthRenderbuffer=t.createRenderbuffer(),xt(H.__webglDepthRenderbuffer,N,!0)),n.bindFramebuffer(t.FRAMEBUFFER,null)}}if(_e){n.bindTexture(t.TEXTURE_CUBE_MAP,K.__webglTexture),Ne(t.TEXTURE_CUBE_MAP,E);for(let te=0;te<6;te++)if(E.mipmaps&&E.mipmaps.length>0)for(let Z=0;Z<E.mipmaps.length;Z++)ke(H.__webglFramebuffer[te][Z],N,E,t.COLOR_ATTACHMENT0,t.TEXTURE_CUBE_MAP_POSITIVE_X+te,Z);else ke(H.__webglFramebuffer[te],N,E,t.COLOR_ATTACHMENT0,t.TEXTURE_CUBE_MAP_POSITIVE_X+te,0);h(E)&&v(t.TEXTURE_CUBE_MAP),n.unbindTexture()}else if(pe){for(let te=0,Z=J.length;te<Z;te++){const ae=J[te],ce=i.get(ae);let me=t.TEXTURE_2D;(N.isWebGL3DRenderTarget||N.isWebGLArrayRenderTarget)&&(me=N.isWebGL3DRenderTarget?t.TEXTURE_3D:t.TEXTURE_2D_ARRAY),n.bindTexture(me,ce.__webglTexture),Ne(me,ae),ke(H.__webglFramebuffer,N,ae,t.COLOR_ATTACHMENT0+te,me,0),h(ae)&&v(me)}n.unbindTexture()}else{let te=t.TEXTURE_2D;if((N.isWebGL3DRenderTarget||N.isWebGLArrayRenderTarget)&&(te=N.isWebGL3DRenderTarget?t.TEXTURE_3D:t.TEXTURE_2D_ARRAY),n.bindTexture(te,K.__webglTexture),Ne(te,E),E.mipmaps&&E.mipmaps.length>0)for(let Z=0;Z<E.mipmaps.length;Z++)ke(H.__webglFramebuffer[Z],N,E,t.COLOR_ATTACHMENT0,te,Z);else ke(H.__webglFramebuffer,N,E,t.COLOR_ATTACHMENT0,te,0);h(E)&&v(te),n.unbindTexture()}N.depthBuffer&&Se(N)}function Ve(N){const E=N.textures;for(let H=0,K=E.length;H<K;H++){const J=E[H];if(h(J)){const _e=b(N),pe=i.get(J).__webglTexture;n.bindTexture(_e,pe),v(_e),n.unbindTexture()}}}const Ge=[],Pe=[];function we(N){if(N.samples>0){if(ve(N)===!1){const E=N.textures,H=N.width,K=N.height;let J=t.COLOR_BUFFER_BIT;const _e=N.stencilBuffer?t.DEPTH_STENCIL_ATTACHMENT:t.DEPTH_ATTACHMENT,pe=i.get(N),te=E.length>1;if(te)for(let ae=0;ae<E.length;ae++)n.bindFramebuffer(t.FRAMEBUFFER,pe.__webglMultisampledFramebuffer),t.framebufferRenderbuffer(t.FRAMEBUFFER,t.COLOR_ATTACHMENT0+ae,t.RENDERBUFFER,null),n.bindFramebuffer(t.FRAMEBUFFER,pe.__webglFramebuffer),t.framebufferTexture2D(t.DRAW_FRAMEBUFFER,t.COLOR_ATTACHMENT0+ae,t.TEXTURE_2D,null,0);n.bindFramebuffer(t.READ_FRAMEBUFFER,pe.__webglMultisampledFramebuffer);const Z=N.texture.mipmaps;Z&&Z.length>0?n.bindFramebuffer(t.DRAW_FRAMEBUFFER,pe.__webglFramebuffer[0]):n.bindFramebuffer(t.DRAW_FRAMEBUFFER,pe.__webglFramebuffer);for(let ae=0;ae<E.length;ae++){if(N.resolveDepthBuffer&&(N.depthBuffer&&(J|=t.DEPTH_BUFFER_BIT),N.stencilBuffer&&N.resolveStencilBuffer&&(J|=t.STENCIL_BUFFER_BIT)),te){t.framebufferRenderbuffer(t.READ_FRAMEBUFFER,t.COLOR_ATTACHMENT0,t.RENDERBUFFER,pe.__webglColorRenderbuffer[ae]);const ce=i.get(E[ae]).__webglTexture;t.framebufferTexture2D(t.DRAW_FRAMEBUFFER,t.COLOR_ATTACHMENT0,t.TEXTURE_2D,ce,0)}t.blitFramebuffer(0,0,H,K,0,0,H,K,J,t.NEAREST),c===!0&&(Ge.length=0,Pe.length=0,Ge.push(t.COLOR_ATTACHMENT0+ae),N.depthBuffer&&N.resolveDepthBuffer===!1&&(Ge.push(_e),Pe.push(_e),t.invalidateFramebuffer(t.DRAW_FRAMEBUFFER,Pe)),t.invalidateFramebuffer(t.READ_FRAMEBUFFER,Ge))}if(n.bindFramebuffer(t.READ_FRAMEBUFFER,null),n.bindFramebuffer(t.DRAW_FRAMEBUFFER,null),te)for(let ae=0;ae<E.length;ae++){n.bindFramebuffer(t.FRAMEBUFFER,pe.__webglMultisampledFramebuffer),t.framebufferRenderbuffer(t.FRAMEBUFFER,t.COLOR_ATTACHMENT0+ae,t.RENDERBUFFER,pe.__webglColorRenderbuffer[ae]);const ce=i.get(E[ae]).__webglTexture;n.bindFramebuffer(t.FRAMEBUFFER,pe.__webglFramebuffer),t.framebufferTexture2D(t.DRAW_FRAMEBUFFER,t.COLOR_ATTACHMENT0+ae,t.TEXTURE_2D,ce,0)}n.bindFramebuffer(t.DRAW_FRAMEBUFFER,pe.__webglMultisampledFramebuffer)}else if(N.depthBuffer&&N.resolveDepthBuffer===!1&&c){const E=N.stencilBuffer?t.DEPTH_STENCIL_ATTACHMENT:t.DEPTH_ATTACHMENT;t.invalidateFramebuffer(t.DRAW_FRAMEBUFFER,[E])}}}function We(N){return Math.min(a.maxSamples,N.samples)}function ve(N){const E=i.get(N);return N.samples>0&&e.has("WEBGL_multisampled_render_to_texture")===!0&&E.__useRenderToTexture!==!1}function I(N){const E=r.render.frame;d.get(N)!==E&&(d.set(N,E),N.update())}function nt(N,E){const H=N.colorSpace,K=N.format,J=N.type;return N.isCompressedTexture===!0||N.isVideoTexture===!0||H!==lf&&H!==is&&(ht.getTransfer(H)===Ct?(K!==ki||J!==Ei)&&Je("WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType."):St("WebGLTextures: Unsupported texture color space:",H)),E}function Ke(N){return typeof HTMLImageElement<"u"&&N instanceof HTMLImageElement?(u.width=N.naturalWidth||N.width,u.height=N.naturalHeight||N.height):typeof VideoFrame<"u"&&N instanceof VideoFrame?(u.width=N.displayWidth,u.height=N.displayHeight):(u.width=N.width,u.height=N.height),u}this.allocateTextureUnit=B,this.resetTextureUnits=W,this.getTextureUnits=z,this.setTextureUnits=D,this.setTexture2D=V,this.setTexture2DArray=X,this.setTexture3D=ge,this.setTextureCube=he,this.rebindTextures=Ce,this.setupRenderTarget=be,this.updateRenderTargetMipmap=Ve,this.updateMultisampleRenderTarget=we,this.setupDepthRenderbuffer=Se,this.setupFrameBufferTexture=ke,this.useMultisampledRTT=ve,this.isReversedDepthBuffer=function(){return n.buffers.depth.getReversed()}}function O3(t,e){function n(i,a=is){let s;const r=ht.getTransfer(a);if(i===Ei)return t.UNSIGNED_BYTE;if(i===ug)return t.UNSIGNED_SHORT_4_4_4_4;if(i===fg)return t.UNSIGNED_SHORT_5_5_5_1;if(i===cS)return t.UNSIGNED_INT_5_9_9_9_REV;if(i===uS)return t.UNSIGNED_INT_10F_11F_11F_REV;if(i===lS)return t.BYTE;if(i===oS)return t.SHORT;if(i===Go)return t.UNSIGNED_SHORT;if(i===cg)return t.INT;if(i===sa)return t.UNSIGNED_INT;if(i===Ii)return t.FLOAT;if(i===za)return t.HALF_FLOAT;if(i===fS)return t.ALPHA;if(i===dS)return t.RGB;if(i===ki)return t.RGBA;if(i===Ia)return t.DEPTH_COMPONENT;if(i===Hs)return t.DEPTH_STENCIL;if(i===dg)return t.RED;if(i===hg)return t.RED_INTEGER;if(i===er)return t.RG;if(i===pg)return t.RG_INTEGER;if(i===mg)return t.RGBA_INTEGER;if(i===Su||i===Mu||i===Eu||i===Tu)if(r===Ct)if(s=e.get("WEBGL_compressed_texture_s3tc_srgb"),s!==null){if(i===Su)return s.COMPRESSED_SRGB_S3TC_DXT1_EXT;if(i===Mu)return s.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;if(i===Eu)return s.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;if(i===Tu)return s.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}else return null;else if(s=e.get("WEBGL_compressed_texture_s3tc"),s!==null){if(i===Su)return s.COMPRESSED_RGB_S3TC_DXT1_EXT;if(i===Mu)return s.COMPRESSED_RGBA_S3TC_DXT1_EXT;if(i===Eu)return s.COMPRESSED_RGBA_S3TC_DXT3_EXT;if(i===Tu)return s.COMPRESSED_RGBA_S3TC_DXT5_EXT}else return null;if(i===Mp||i===Ep||i===Tp||i===Ap)if(s=e.get("WEBGL_compressed_texture_pvrtc"),s!==null){if(i===Mp)return s.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;if(i===Ep)return s.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;if(i===Tp)return s.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;if(i===Ap)return s.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG}else return null;if(i===wp||i===Cp||i===Np||i===Rp||i===Dp||i===sf||i===Lp)if(s=e.get("WEBGL_compressed_texture_etc"),s!==null){if(i===wp||i===Cp)return r===Ct?s.COMPRESSED_SRGB8_ETC2:s.COMPRESSED_RGB8_ETC2;if(i===Np)return r===Ct?s.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:s.COMPRESSED_RGBA8_ETC2_EAC;if(i===Rp)return s.COMPRESSED_R11_EAC;if(i===Dp)return s.COMPRESSED_SIGNED_R11_EAC;if(i===sf)return s.COMPRESSED_RG11_EAC;if(i===Lp)return s.COMPRESSED_SIGNED_RG11_EAC}else return null;if(i===Up||i===Op||i===Pp||i===zp||i===Ip||i===kp||i===Bp||i===Fp||i===jp||i===Hp||i===Gp||i===Vp||i===Wp||i===Xp)if(s=e.get("WEBGL_compressed_texture_astc"),s!==null){if(i===Up)return r===Ct?s.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:s.COMPRESSED_RGBA_ASTC_4x4_KHR;if(i===Op)return r===Ct?s.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:s.COMPRESSED_RGBA_ASTC_5x4_KHR;if(i===Pp)return r===Ct?s.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:s.COMPRESSED_RGBA_ASTC_5x5_KHR;if(i===zp)return r===Ct?s.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:s.COMPRESSED_RGBA_ASTC_6x5_KHR;if(i===Ip)return r===Ct?s.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:s.COMPRESSED_RGBA_ASTC_6x6_KHR;if(i===kp)return r===Ct?s.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:s.COMPRESSED_RGBA_ASTC_8x5_KHR;if(i===Bp)return r===Ct?s.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:s.COMPRESSED_RGBA_ASTC_8x6_KHR;if(i===Fp)return r===Ct?s.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:s.COMPRESSED_RGBA_ASTC_8x8_KHR;if(i===jp)return r===Ct?s.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:s.COMPRESSED_RGBA_ASTC_10x5_KHR;if(i===Hp)return r===Ct?s.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:s.COMPRESSED_RGBA_ASTC_10x6_KHR;if(i===Gp)return r===Ct?s.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:s.COMPRESSED_RGBA_ASTC_10x8_KHR;if(i===Vp)return r===Ct?s.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:s.COMPRESSED_RGBA_ASTC_10x10_KHR;if(i===Wp)return r===Ct?s.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:s.COMPRESSED_RGBA_ASTC_12x10_KHR;if(i===Xp)return r===Ct?s.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:s.COMPRESSED_RGBA_ASTC_12x12_KHR}else return null;if(i===qp||i===Yp||i===Kp)if(s=e.get("EXT_texture_compression_bptc"),s!==null){if(i===qp)return r===Ct?s.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT:s.COMPRESSED_RGBA_BPTC_UNORM_EXT;if(i===Yp)return s.COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT;if(i===Kp)return s.COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT}else return null;if(i===Zp||i===$p||i===rf||i===Qp)if(s=e.get("EXT_texture_compression_rgtc"),s!==null){if(i===Zp)return s.COMPRESSED_RED_RGTC1_EXT;if(i===$p)return s.COMPRESSED_SIGNED_RED_RGTC1_EXT;if(i===rf)return s.COMPRESSED_RED_GREEN_RGTC2_EXT;if(i===Qp)return s.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT}else return null;return i===Vo?t.UNSIGNED_INT_24_8:t[i]!==void 0?t[i]:null}return{convert:n}}const P3=` -void main() { - - gl_Position = vec4( position, 1.0 ); - -}`,z3=` -uniform sampler2DArray depthColor; -uniform float depthWidth; -uniform float depthHeight; - -void main() { - - vec2 coord = vec2( gl_FragCoord.x / depthWidth, gl_FragCoord.y / depthHeight ); - - if ( coord.x >= 1.0 ) { - - gl_FragDepth = texture( depthColor, vec3( coord.x - 1.0, coord.y, 1 ) ).r; - - } else { - - gl_FragDepth = texture( depthColor, vec3( coord.x, coord.y, 0 ) ).r; - - } - -}`;class I3{constructor(){this.texture=null,this.mesh=null,this.depthNear=0,this.depthFar=0}init(e,n){if(this.texture===null){const i=new ES(e.texture);(e.depthNear!==n.depthNear||e.depthFar!==n.depthFar)&&(this.depthNear=e.depthNear,this.depthFar=e.depthFar),this.texture=i}}getMesh(e){if(this.texture!==null&&this.mesh===null){const n=e.cameras[0].viewport,i=new Xn({vertexShader:P3,fragmentShader:z3,uniforms:{depthColor:{value:this.texture},depthWidth:{value:n.z},depthHeight:{value:n.w}}});this.mesh=new Ri(new kf(20,20),i)}return this.mesh}reset(){this.texture=null,this.mesh=null}getDepthTexture(){return this.texture}}class k3 extends cr{constructor(e,n){super();const i=this;let a=null,s=1,r=null,o="local-floor",c=1,u=null,d=null,m=null,f=null,p=null,x=null;const _=typeof XRWebGLBinding<"u",g=new I3,h={},v=n.getContextAttributes();let b=null,y=null;const A=[],M=[],T=new At;let S=null;const C=new zi;C.viewport=new Qt;const R=new zi;R.viewport=new Qt;const L=[C,R],O=new YA;let W=null,z=null;this.cameraAutoUpdate=!0,this.enabled=!1,this.isPresenting=!1,this.getController=function(ie){let xe=A[ie];return xe===void 0&&(xe=new Od,A[ie]=xe),xe.getTargetRaySpace()},this.getControllerGrip=function(ie){let xe=A[ie];return xe===void 0&&(xe=new Od,A[ie]=xe),xe.getGripSpace()},this.getHand=function(ie){let xe=A[ie];return xe===void 0&&(xe=new Od,A[ie]=xe),xe.getHandSpace()};function D(ie){const xe=M.indexOf(ie.inputSource);if(xe===-1)return;const oe=A[xe];oe!==void 0&&(oe.update(ie.inputSource,ie.frame,u||r),oe.dispatchEvent({type:ie.type,data:ie.inputSource}))}function B(){a.removeEventListener("select",D),a.removeEventListener("selectstart",D),a.removeEventListener("selectend",D),a.removeEventListener("squeeze",D),a.removeEventListener("squeezestart",D),a.removeEventListener("squeezeend",D),a.removeEventListener("end",B),a.removeEventListener("inputsourceschange",k);for(let ie=0;ie<A.length;ie++){const xe=M[ie];xe!==null&&(M[ie]=null,A[ie].disconnect(xe))}W=null,z=null,g.reset();for(const ie in h)delete h[ie];e.setRenderTarget(b),p=null,f=null,m=null,a=null,y=null,Ne.stop(),i.isPresenting=!1,e.setPixelRatio(S),e.setSize(T.width,T.height,!1),i.dispatchEvent({type:"sessionend"})}this.setFramebufferScaleFactor=function(ie){s=ie,i.isPresenting===!0&&Je("WebXRManager: Cannot change framebuffer scale while presenting.")},this.setReferenceSpaceType=function(ie){o=ie,i.isPresenting===!0&&Je("WebXRManager: Cannot change reference space type while presenting.")},this.getReferenceSpace=function(){return u||r},this.setReferenceSpace=function(ie){u=ie},this.getBaseLayer=function(){return f!==null?f:p},this.getBinding=function(){return m===null&&_&&(m=new XRWebGLBinding(a,n)),m},this.getFrame=function(){return x},this.getSession=function(){return a},this.setSession=async function(ie){if(a=ie,a!==null){if(b=e.getRenderTarget(),a.addEventListener("select",D),a.addEventListener("selectstart",D),a.addEventListener("selectend",D),a.addEventListener("squeeze",D),a.addEventListener("squeezestart",D),a.addEventListener("squeezeend",D),a.addEventListener("end",B),a.addEventListener("inputsourceschange",k),v.xrCompatible!==!0&&await n.makeXRCompatible(),S=e.getPixelRatio(),e.getSize(T),_&&"createProjectionLayer"in XRWebGLBinding.prototype){let oe=null,Ee=null,Oe=null;v.depth&&(Oe=v.stencil?n.DEPTH24_STENCIL8:n.DEPTH_COMPONENT24,oe=v.stencil?Hs:Ia,Ee=v.stencil?Vo:sa);const ke={colorFormat:n.RGBA8,depthFormat:Oe,scaleFactor:s};m=this.getBinding(),f=m.createProjectionLayer(ke),a.updateRenderState({layers:[f]}),e.setPixelRatio(1),e.setSize(f.textureWidth,f.textureHeight,!1),y=new aa(f.textureWidth,f.textureHeight,{format:ki,type:Ei,depthTexture:new yl(f.textureWidth,f.textureHeight,Ee,void 0,void 0,void 0,void 0,void 0,void 0,oe),stencilBuffer:v.stencil,colorSpace:e.outputColorSpace,samples:v.antialias?4:0,resolveDepthBuffer:f.ignoreDepthValues===!1,resolveStencilBuffer:f.ignoreDepthValues===!1})}else{const oe={antialias:v.antialias,alpha:!0,depth:v.depth,stencil:v.stencil,framebufferScaleFactor:s};p=new XRWebGLLayer(a,n,oe),a.updateRenderState({baseLayer:p}),e.setPixelRatio(1),e.setSize(p.framebufferWidth,p.framebufferHeight,!1),y=new aa(p.framebufferWidth,p.framebufferHeight,{format:ki,type:Ei,colorSpace:e.outputColorSpace,stencilBuffer:v.stencil,resolveDepthBuffer:p.ignoreDepthValues===!1,resolveStencilBuffer:p.ignoreDepthValues===!1})}y.isXRRenderTarget=!0,this.setFoveation(c),u=null,r=await a.requestReferenceSpace(o),Ne.setContext(a),Ne.start(),i.isPresenting=!0,i.dispatchEvent({type:"sessionstart"})}},this.getEnvironmentBlendMode=function(){if(a!==null)return a.environmentBlendMode},this.getDepthTexture=function(){return g.getDepthTexture()};function k(ie){for(let xe=0;xe<ie.removed.length;xe++){const oe=ie.removed[xe],Ee=M.indexOf(oe);Ee>=0&&(M[Ee]=null,A[Ee].disconnect(oe))}for(let xe=0;xe<ie.added.length;xe++){const oe=ie.added[xe];let Ee=M.indexOf(oe);if(Ee===-1){for(let ke=0;ke<A.length;ke++)if(ke>=M.length){M.push(oe),Ee=ke;break}else if(M[ke]===null){M[ke]=oe,Ee=ke;break}if(Ee===-1)break}const Oe=A[Ee];Oe&&Oe.connect(oe)}}const V=new Y,X=new Y;function ge(ie,xe,oe){V.setFromMatrixPosition(xe.matrixWorld),X.setFromMatrixPosition(oe.matrixWorld);const Ee=V.distanceTo(X),Oe=xe.projectionMatrix.elements,ke=oe.projectionMatrix.elements,xt=Oe[14]/(Oe[10]-1),re=Oe[14]/(Oe[10]+1),Se=(Oe[9]+1)/Oe[5],Ce=(Oe[9]-1)/Oe[5],be=(Oe[8]-1)/Oe[0],Ve=(ke[8]+1)/ke[0],Ge=xt*be,Pe=xt*Ve,we=Ee/(-be+Ve),We=we*-be;if(xe.matrixWorld.decompose(ie.position,ie.quaternion,ie.scale),ie.translateX(We),ie.translateZ(we),ie.matrixWorld.compose(ie.position,ie.quaternion,ie.scale),ie.matrixWorldInverse.copy(ie.matrixWorld).invert(),Oe[10]===-1)ie.projectionMatrix.copy(xe.projectionMatrix),ie.projectionMatrixInverse.copy(xe.projectionMatrixInverse);else{const ve=xt+we,I=re+we,nt=Ge-We,Ke=Pe+(Ee-We),N=Se*re/I*ve,E=Ce*re/I*ve;ie.projectionMatrix.makePerspective(nt,Ke,N,E,ve,I),ie.projectionMatrixInverse.copy(ie.projectionMatrix).invert()}}function he(ie,xe){xe===null?ie.matrixWorld.copy(ie.matrix):ie.matrixWorld.multiplyMatrices(xe.matrixWorld,ie.matrix),ie.matrixWorldInverse.copy(ie.matrixWorld).invert()}this.updateCamera=function(ie){if(a===null)return;let xe=ie.near,oe=ie.far;g.texture!==null&&(g.depthNear>0&&(xe=g.depthNear),g.depthFar>0&&(oe=g.depthFar)),O.near=R.near=C.near=xe,O.far=R.far=C.far=oe,(W!==O.near||z!==O.far)&&(a.updateRenderState({depthNear:O.near,depthFar:O.far}),W=O.near,z=O.far),O.layers.mask=ie.layers.mask|6,C.layers.mask=O.layers.mask&-5,R.layers.mask=O.layers.mask&-3;const Ee=ie.parent,Oe=O.cameras;he(O,Ee);for(let ke=0;ke<Oe.length;ke++)he(Oe[ke],Ee);Oe.length===2?ge(O,C,R):O.projectionMatrix.copy(C.projectionMatrix),Ae(ie,O,Ee)};function Ae(ie,xe,oe){oe===null?ie.matrix.copy(xe.matrixWorld):(ie.matrix.copy(oe.matrixWorld),ie.matrix.invert(),ie.matrix.multiply(xe.matrixWorld)),ie.matrix.decompose(ie.position,ie.quaternion,ie.scale),ie.updateMatrixWorld(!0),ie.projectionMatrix.copy(xe.projectionMatrix),ie.projectionMatrixInverse.copy(xe.projectionMatrixInverse),ie.isPerspectiveCamera&&(ie.fov=Jp*2*Math.atan(1/ie.projectionMatrix.elements[5]),ie.zoom=1)}this.getCamera=function(){return O},this.getFoveation=function(){if(!(f===null&&p===null))return c},this.setFoveation=function(ie){c=ie,f!==null&&(f.fixedFoveation=ie),p!==null&&p.fixedFoveation!==void 0&&(p.fixedFoveation=ie)},this.hasDepthSensing=function(){return g.texture!==null},this.getDepthSensingMesh=function(){return g.getMesh(O)},this.getCameraTexture=function(ie){return h[ie]};let q=null;function fe(ie,xe){if(d=xe.getViewerPose(u||r),x=xe,d!==null){const oe=d.views;p!==null&&(e.setRenderTargetFramebuffer(y,p.framebuffer),e.setRenderTarget(y));let Ee=!1;oe.length!==O.cameras.length&&(O.cameras.length=0,Ee=!0);for(let re=0;re<oe.length;re++){const Se=oe[re];let Ce=null;if(p!==null)Ce=p.getViewport(Se);else{const Ve=m.getViewSubImage(f,Se);Ce=Ve.viewport,re===0&&(e.setRenderTargetTextures(y,Ve.colorTexture,Ve.depthStencilTexture),e.setRenderTarget(y))}let be=L[re];be===void 0&&(be=new zi,be.layers.enable(re),be.viewport=new Qt,L[re]=be),be.matrix.fromArray(Se.transform.matrix),be.matrix.decompose(be.position,be.quaternion,be.scale),be.projectionMatrix.fromArray(Se.projectionMatrix),be.projectionMatrixInverse.copy(be.projectionMatrix).invert(),be.viewport.set(Ce.x,Ce.y,Ce.width,Ce.height),re===0&&(O.matrix.copy(be.matrix),O.matrix.decompose(O.position,O.quaternion,O.scale)),Ee===!0&&O.cameras.push(be)}const Oe=a.enabledFeatures;if(Oe&&Oe.includes("depth-sensing")&&a.depthUsage=="gpu-optimized"&&_){m=i.getBinding();const re=m.getDepthInformation(oe[0]);re&&re.isValid&&re.texture&&g.init(re,a.renderState)}if(Oe&&Oe.includes("camera-access")&&_){e.state.unbindTexture(),m=i.getBinding();for(let re=0;re<oe.length;re++){const Se=oe[re].camera;if(Se){let Ce=h[Se];Ce||(Ce=new ES,h[Se]=Ce);const be=m.getCameraImage(Se);Ce.sourceTexture=be}}}}for(let oe=0;oe<A.length;oe++){const Ee=M[oe],Oe=A[oe];Ee!==null&&Oe!==void 0&&Oe.update(Ee,xe,u||r)}q&&q(ie,xe),xe.detectedPlanes&&i.dispatchEvent({type:"planesdetected",data:xe}),x=null}const Ne=new wS;Ne.setAnimationLoop(fe),this.setAnimationLoop=function(ie){q=ie},this.dispose=function(){}}}const B3=new Ft,OS=new it;OS.set(-1,0,0,0,1,0,0,0,1);function F3(t,e){function n(g,h){g.matrixAutoUpdate===!0&&g.updateMatrix(),h.value.copy(g.matrix)}function i(g,h){h.color.getRGB(g.fogColor.value,TS(t)),h.isFog?(g.fogNear.value=h.near,g.fogFar.value=h.far):h.isFogExp2&&(g.fogDensity.value=h.density)}function a(g,h,v,b,y){h.isNodeMaterial?h.uniformsNeedUpdate=!1:h.isMeshBasicMaterial?s(g,h):h.isMeshLambertMaterial?(s(g,h),h.envMap&&(g.envMapIntensity.value=h.envMapIntensity)):h.isMeshToonMaterial?(s(g,h),m(g,h)):h.isMeshPhongMaterial?(s(g,h),d(g,h),h.envMap&&(g.envMapIntensity.value=h.envMapIntensity)):h.isMeshStandardMaterial?(s(g,h),f(g,h),h.isMeshPhysicalMaterial&&p(g,h,y)):h.isMeshMatcapMaterial?(s(g,h),x(g,h)):h.isMeshDepthMaterial?s(g,h):h.isMeshDistanceMaterial?(s(g,h),_(g,h)):h.isMeshNormalMaterial?s(g,h):h.isLineBasicMaterial?(r(g,h),h.isLineDashedMaterial&&o(g,h)):h.isPointsMaterial?c(g,h,v,b):h.isSpriteMaterial?u(g,h):h.isShadowMaterial?(g.color.value.copy(h.color),g.opacity.value=h.opacity):h.isShaderMaterial&&(h.uniformsNeedUpdate=!1)}function s(g,h){g.opacity.value=h.opacity,h.color&&g.diffuse.value.copy(h.color),h.emissive&&g.emissive.value.copy(h.emissive).multiplyScalar(h.emissiveIntensity),h.map&&(g.map.value=h.map,n(h.map,g.mapTransform)),h.alphaMap&&(g.alphaMap.value=h.alphaMap,n(h.alphaMap,g.alphaMapTransform)),h.bumpMap&&(g.bumpMap.value=h.bumpMap,n(h.bumpMap,g.bumpMapTransform),g.bumpScale.value=h.bumpScale,h.side===Yn&&(g.bumpScale.value*=-1)),h.normalMap&&(g.normalMap.value=h.normalMap,n(h.normalMap,g.normalMapTransform),g.normalScale.value.copy(h.normalScale),h.side===Yn&&g.normalScale.value.negate()),h.displacementMap&&(g.displacementMap.value=h.displacementMap,n(h.displacementMap,g.displacementMapTransform),g.displacementScale.value=h.displacementScale,g.displacementBias.value=h.displacementBias),h.emissiveMap&&(g.emissiveMap.value=h.emissiveMap,n(h.emissiveMap,g.emissiveMapTransform)),h.specularMap&&(g.specularMap.value=h.specularMap,n(h.specularMap,g.specularMapTransform)),h.alphaTest>0&&(g.alphaTest.value=h.alphaTest);const v=e.get(h),b=v.envMap,y=v.envMapRotation;b&&(g.envMap.value=b,g.envMapRotation.value.setFromMatrix4(B3.makeRotationFromEuler(y)).transpose(),b.isCubeTexture&&b.isRenderTargetTexture===!1&&g.envMapRotation.value.premultiply(OS),g.reflectivity.value=h.reflectivity,g.ior.value=h.ior,g.refractionRatio.value=h.refractionRatio),h.lightMap&&(g.lightMap.value=h.lightMap,g.lightMapIntensity.value=h.lightMapIntensity,n(h.lightMap,g.lightMapTransform)),h.aoMap&&(g.aoMap.value=h.aoMap,g.aoMapIntensity.value=h.aoMapIntensity,n(h.aoMap,g.aoMapTransform))}function r(g,h){g.diffuse.value.copy(h.color),g.opacity.value=h.opacity,h.map&&(g.map.value=h.map,n(h.map,g.mapTransform))}function o(g,h){g.dashSize.value=h.dashSize,g.totalSize.value=h.dashSize+h.gapSize,g.scale.value=h.scale}function c(g,h,v,b){g.diffuse.value.copy(h.color),g.opacity.value=h.opacity,g.size.value=h.size*v,g.scale.value=b*.5,h.map&&(g.map.value=h.map,n(h.map,g.uvTransform)),h.alphaMap&&(g.alphaMap.value=h.alphaMap,n(h.alphaMap,g.alphaMapTransform)),h.alphaTest>0&&(g.alphaTest.value=h.alphaTest)}function u(g,h){g.diffuse.value.copy(h.color),g.opacity.value=h.opacity,g.rotation.value=h.rotation,h.map&&(g.map.value=h.map,n(h.map,g.mapTransform)),h.alphaMap&&(g.alphaMap.value=h.alphaMap,n(h.alphaMap,g.alphaMapTransform)),h.alphaTest>0&&(g.alphaTest.value=h.alphaTest)}function d(g,h){g.specular.value.copy(h.specular),g.shininess.value=Math.max(h.shininess,1e-4)}function m(g,h){h.gradientMap&&(g.gradientMap.value=h.gradientMap)}function f(g,h){g.metalness.value=h.metalness,h.metalnessMap&&(g.metalnessMap.value=h.metalnessMap,n(h.metalnessMap,g.metalnessMapTransform)),g.roughness.value=h.roughness,h.roughnessMap&&(g.roughnessMap.value=h.roughnessMap,n(h.roughnessMap,g.roughnessMapTransform)),h.envMap&&(g.envMapIntensity.value=h.envMapIntensity)}function p(g,h,v){g.ior.value=h.ior,h.sheen>0&&(g.sheenColor.value.copy(h.sheenColor).multiplyScalar(h.sheen),g.sheenRoughness.value=h.sheenRoughness,h.sheenColorMap&&(g.sheenColorMap.value=h.sheenColorMap,n(h.sheenColorMap,g.sheenColorMapTransform)),h.sheenRoughnessMap&&(g.sheenRoughnessMap.value=h.sheenRoughnessMap,n(h.sheenRoughnessMap,g.sheenRoughnessMapTransform))),h.clearcoat>0&&(g.clearcoat.value=h.clearcoat,g.clearcoatRoughness.value=h.clearcoatRoughness,h.clearcoatMap&&(g.clearcoatMap.value=h.clearcoatMap,n(h.clearcoatMap,g.clearcoatMapTransform)),h.clearcoatRoughnessMap&&(g.clearcoatRoughnessMap.value=h.clearcoatRoughnessMap,n(h.clearcoatRoughnessMap,g.clearcoatRoughnessMapTransform)),h.clearcoatNormalMap&&(g.clearcoatNormalMap.value=h.clearcoatNormalMap,n(h.clearcoatNormalMap,g.clearcoatNormalMapTransform),g.clearcoatNormalScale.value.copy(h.clearcoatNormalScale),h.side===Yn&&g.clearcoatNormalScale.value.negate())),h.dispersion>0&&(g.dispersion.value=h.dispersion),h.iridescence>0&&(g.iridescence.value=h.iridescence,g.iridescenceIOR.value=h.iridescenceIOR,g.iridescenceThicknessMinimum.value=h.iridescenceThicknessRange[0],g.iridescenceThicknessMaximum.value=h.iridescenceThicknessRange[1],h.iridescenceMap&&(g.iridescenceMap.value=h.iridescenceMap,n(h.iridescenceMap,g.iridescenceMapTransform)),h.iridescenceThicknessMap&&(g.iridescenceThicknessMap.value=h.iridescenceThicknessMap,n(h.iridescenceThicknessMap,g.iridescenceThicknessMapTransform))),h.transmission>0&&(g.transmission.value=h.transmission,g.transmissionSamplerMap.value=v.texture,g.transmissionSamplerSize.value.set(v.width,v.height),h.transmissionMap&&(g.transmissionMap.value=h.transmissionMap,n(h.transmissionMap,g.transmissionMapTransform)),g.thickness.value=h.thickness,h.thicknessMap&&(g.thicknessMap.value=h.thicknessMap,n(h.thicknessMap,g.thicknessMapTransform)),g.attenuationDistance.value=h.attenuationDistance,g.attenuationColor.value.copy(h.attenuationColor)),h.anisotropy>0&&(g.anisotropyVector.value.set(h.anisotropy*Math.cos(h.anisotropyRotation),h.anisotropy*Math.sin(h.anisotropyRotation)),h.anisotropyMap&&(g.anisotropyMap.value=h.anisotropyMap,n(h.anisotropyMap,g.anisotropyMapTransform))),g.specularIntensity.value=h.specularIntensity,g.specularColor.value.copy(h.specularColor),h.specularColorMap&&(g.specularColorMap.value=h.specularColorMap,n(h.specularColorMap,g.specularColorMapTransform)),h.specularIntensityMap&&(g.specularIntensityMap.value=h.specularIntensityMap,n(h.specularIntensityMap,g.specularIntensityMapTransform))}function x(g,h){h.matcap&&(g.matcap.value=h.matcap)}function _(g,h){const v=e.get(h).light;g.referencePosition.value.setFromMatrixPosition(v.matrixWorld),g.nearDistance.value=v.shadow.camera.near,g.farDistance.value=v.shadow.camera.far}return{refreshFogUniforms:i,refreshMaterialUniforms:a}}function j3(t,e,n,i){let a={},s={},r=[];const o=t.getParameter(t.MAX_UNIFORM_BUFFER_BINDINGS);function c(y,A){const M=A.program;i.uniformBlockBinding(y,M)}function u(y,A){let M=a[y.id];M===void 0&&(g(y),M=d(y),a[y.id]=M,y.addEventListener("dispose",v));const T=A.program;i.updateUBOMapping(y,T);const S=e.render.frame;s[y.id]!==S&&(f(y),s[y.id]=S)}function d(y){const A=m();y.__bindingPointIndex=A;const M=t.createBuffer(),T=y.__size,S=y.usage;return t.bindBuffer(t.UNIFORM_BUFFER,M),t.bufferData(t.UNIFORM_BUFFER,T,S),t.bindBuffer(t.UNIFORM_BUFFER,null),t.bindBufferBase(t.UNIFORM_BUFFER,A,M),M}function m(){for(let y=0;y<o;y++)if(r.indexOf(y)===-1)return r.push(y),y;return St("WebGLRenderer: Maximum number of simultaneously usable uniforms groups reached."),0}function f(y){const A=a[y.id],M=y.uniforms,T=y.__cache;t.bindBuffer(t.UNIFORM_BUFFER,A);for(let S=0,C=M.length;S<C;S++){const R=M[S];if(Array.isArray(R))for(let L=0,O=R.length;L<O;L++)p(R[L],S,L,T);else p(R,S,0,T)}t.bindBuffer(t.UNIFORM_BUFFER,null)}function p(y,A,M,T){if(_(y,A,M,T)===!0){const S=y.__offset,C=y.value;if(Array.isArray(C)){let R=0;for(let L=0;L<C.length;L++){const O=C[L],W=h(O);x(O,y.__data,R),typeof O!="number"&&typeof O!="boolean"&&!O.isMatrix3&&!ArrayBuffer.isView(O)&&(R+=W.storage/Float32Array.BYTES_PER_ELEMENT)}}else x(C,y.__data,0);t.bufferSubData(t.UNIFORM_BUFFER,S,y.__data)}}function x(y,A,M){typeof y=="number"||typeof y=="boolean"?A[0]=y:y.isMatrix3?(A[0]=y.elements[0],A[1]=y.elements[1],A[2]=y.elements[2],A[3]=0,A[4]=y.elements[3],A[5]=y.elements[4],A[6]=y.elements[5],A[7]=0,A[8]=y.elements[6],A[9]=y.elements[7],A[10]=y.elements[8],A[11]=0):ArrayBuffer.isView(y)?A.set(new y.constructor(y.buffer,y.byteOffset,A.length)):y.toArray(A,M)}function _(y,A,M,T){const S=y.value,C=A+"_"+M;if(T[C]===void 0)return typeof S=="number"||typeof S=="boolean"?T[C]=S:ArrayBuffer.isView(S)?T[C]=S.slice():T[C]=S.clone(),!0;{const R=T[C];if(typeof S=="number"||typeof S=="boolean"){if(R!==S)return T[C]=S,!0}else{if(ArrayBuffer.isView(S))return!0;if(R.equals(S)===!1)return R.copy(S),!0}}return!1}function g(y){const A=y.uniforms;let M=0;const T=16;for(let C=0,R=A.length;C<R;C++){const L=Array.isArray(A[C])?A[C]:[A[C]];for(let O=0,W=L.length;O<W;O++){const z=L[O],D=Array.isArray(z.value)?z.value:[z.value];for(let B=0,k=D.length;B<k;B++){const V=D[B],X=h(V),ge=M%T,he=ge%X.boundary,Ae=ge+he;M+=he,Ae!==0&&T-Ae<X.storage&&(M+=T-Ae),z.__data=new Float32Array(X.storage/Float32Array.BYTES_PER_ELEMENT),z.__offset=M,M+=X.storage}}}const S=M%T;return S>0&&(M+=T-S),y.__size=M,y.__cache={},this}function h(y){const A={boundary:0,storage:0};return typeof y=="number"||typeof y=="boolean"?(A.boundary=4,A.storage=4):y.isVector2?(A.boundary=8,A.storage=8):y.isVector3||y.isColor?(A.boundary=16,A.storage=12):y.isVector4?(A.boundary=16,A.storage=16):y.isMatrix3?(A.boundary=48,A.storage=48):y.isMatrix4?(A.boundary=64,A.storage=64):y.isTexture?Je("WebGLRenderer: Texture samplers can not be part of an uniforms group."):ArrayBuffer.isView(y)?(A.boundary=16,A.storage=y.byteLength):Je("WebGLRenderer: Unsupported uniform value type.",y),A}function v(y){const A=y.target;A.removeEventListener("dispose",v);const M=r.indexOf(A.__bindingPointIndex);r.splice(M,1),t.deleteBuffer(a[A.id]),delete a[A.id],delete s[A.id]}function b(){for(const y in a)t.deleteBuffer(a[y]);r=[],a={},s={}}return{bind:c,update:u,dispose:b}}const H3=new Uint16Array([12469,15057,12620,14925,13266,14620,13807,14376,14323,13990,14545,13625,14713,13328,14840,12882,14931,12528,14996,12233,15039,11829,15066,11525,15080,11295,15085,10976,15082,10705,15073,10495,13880,14564,13898,14542,13977,14430,14158,14124,14393,13732,14556,13410,14702,12996,14814,12596,14891,12291,14937,11834,14957,11489,14958,11194,14943,10803,14921,10506,14893,10278,14858,9960,14484,14039,14487,14025,14499,13941,14524,13740,14574,13468,14654,13106,14743,12678,14818,12344,14867,11893,14889,11509,14893,11180,14881,10751,14852,10428,14812,10128,14765,9754,14712,9466,14764,13480,14764,13475,14766,13440,14766,13347,14769,13070,14786,12713,14816,12387,14844,11957,14860,11549,14868,11215,14855,10751,14825,10403,14782,10044,14729,9651,14666,9352,14599,9029,14967,12835,14966,12831,14963,12804,14954,12723,14936,12564,14917,12347,14900,11958,14886,11569,14878,11247,14859,10765,14828,10401,14784,10011,14727,9600,14660,9289,14586,8893,14508,8533,15111,12234,15110,12234,15104,12216,15092,12156,15067,12010,15028,11776,14981,11500,14942,11205,14902,10752,14861,10393,14812,9991,14752,9570,14682,9252,14603,8808,14519,8445,14431,8145,15209,11449,15208,11451,15202,11451,15190,11438,15163,11384,15117,11274,15055,10979,14994,10648,14932,10343,14871,9936,14803,9532,14729,9218,14645,8742,14556,8381,14461,8020,14365,7603,15273,10603,15272,10607,15267,10619,15256,10631,15231,10614,15182,10535,15118,10389,15042,10167,14963,9787,14883,9447,14800,9115,14710,8665,14615,8318,14514,7911,14411,7507,14279,7198,15314,9675,15313,9683,15309,9712,15298,9759,15277,9797,15229,9773,15166,9668,15084,9487,14995,9274,14898,8910,14800,8539,14697,8234,14590,7790,14479,7409,14367,7067,14178,6621,15337,8619,15337,8631,15333,8677,15325,8769,15305,8871,15264,8940,15202,8909,15119,8775,15022,8565,14916,8328,14804,8009,14688,7614,14569,7287,14448,6888,14321,6483,14088,6171,15350,7402,15350,7419,15347,7480,15340,7613,15322,7804,15287,7973,15229,8057,15148,8012,15046,7846,14933,7611,14810,7357,14682,7069,14552,6656,14421,6316,14251,5948,14007,5528,15356,5942,15356,5977,15353,6119,15348,6294,15332,6551,15302,6824,15249,7044,15171,7122,15070,7050,14949,6861,14818,6611,14679,6349,14538,6067,14398,5651,14189,5311,13935,4958,15359,4123,15359,4153,15356,4296,15353,4646,15338,5160,15311,5508,15263,5829,15188,6042,15088,6094,14966,6001,14826,5796,14678,5543,14527,5287,14377,4985,14133,4586,13869,4257,15360,1563,15360,1642,15358,2076,15354,2636,15341,3350,15317,4019,15273,4429,15203,4732,15105,4911,14981,4932,14836,4818,14679,4621,14517,4386,14359,4156,14083,3795,13808,3437,15360,122,15360,137,15358,285,15355,636,15344,1274,15322,2177,15281,2765,15215,3223,15120,3451,14995,3569,14846,3567,14681,3466,14511,3305,14344,3121,14037,2800,13753,2467,15360,0,15360,1,15359,21,15355,89,15346,253,15325,479,15287,796,15225,1148,15133,1492,15008,1749,14856,1882,14685,1886,14506,1783,14324,1608,13996,1398,13702,1183]);let qi=null;function G3(){return qi===null&&(qi=new yS(H3,16,16,er,za),qi.name="DFG_LUT",qi.minFilter=Rn,qi.magFilter=Rn,qi.wrapS=Ta,qi.wrapT=Ta,qi.generateMipmaps=!1,qi.needsUpdate=!0),qi}class V3{constructor(e={}){const{canvas:n=oA(),context:i=null,depth:a=!0,stencil:s=!1,alpha:r=!1,antialias:o=!1,premultipliedAlpha:c=!0,preserveDrawingBuffer:u=!1,powerPreference:d="default",failIfMajorPerformanceCaveat:m=!1,reversedDepthBuffer:f=!1,outputBufferType:p=Ei}=e;this.isWebGLRenderer=!0;let x;if(i!==null){if(typeof WebGLRenderingContext<"u"&&i instanceof WebGLRenderingContext)throw new Error("THREE.WebGLRenderer: WebGL 1 is not supported since r163.");x=i.getContextAttributes().alpha}else x=r;const _=p,g=new Set([mg,pg,hg]),h=new Set([Ei,sa,Go,Vo,ug,fg]),v=new Uint32Array(4),b=new Int32Array(4),y=new Y;let A=null,M=null;const T=[],S=[];let C=null;this.domElement=n,this.debug={checkShaderErrors:!0,onShaderError:null},this.autoClear=!0,this.autoClearColor=!0,this.autoClearDepth=!0,this.autoClearStencil=!0,this.sortObjects=!0,this.clippingPlanes=[],this.localClippingEnabled=!1,this.toneMapping=ia,this.toneMappingExposure=1,this.transmissionResolutionScale=1;const R=this;let L=!1,O=null,W=null,z=null,D=null;this._outputColorSpace=xi;let B=0,k=0,V=null,X=-1,ge=null;const he=new Qt,Ae=new Qt;let q=null;const fe=new gt(0);let Ne=0,ie=n.width,xe=n.height,oe=1,Ee=null,Oe=null;const ke=new Qt(0,0,ie,xe),xt=new Qt(0,0,ie,xe);let re=!1;const Se=new bS;let Ce=!1,be=!1;const Ve=new Ft,Ge=new Y,Pe=new Qt,we={background:null,fog:null,environment:null,overrideMaterial:null,isScene:!0};let We=!1;function ve(){return V===null?oe:1}let I=i;function nt(w,G){return n.getContext(w,G)}try{const w={alpha:!0,depth:a,stencil:s,antialias:o,premultipliedAlpha:c,preserveDrawingBuffer:u,powerPreference:d,failIfMajorPerformanceCaveat:m};if("setAttribute"in n&&n.setAttribute("data-engine",`three.js r${og}`),n.addEventListener("webglcontextlost",_t,!1),n.addEventListener("webglcontextrestored",yt,!1),n.addEventListener("webglcontextcreationerror",Ot,!1),I===null){const G="webgl2";if(I=nt(G,w),I===null)throw nt(G)?new Error("THREE.WebGLRenderer: Error creating WebGL context with your selected attributes."):new Error("THREE.WebGLRenderer: Error creating WebGL context.")}}catch(w){throw St("WebGLRenderer: "+w.message),w}let Ke,N,E,H,K,J,_e,pe,te,Z,ae,ce,me,ye,Fe,Xe,et,j,Me,se,Te,Re,de;function ze(){Ke=new GN(I),Ke.init(),Te=new O3(I,Ke),N=new PN(I,Ke,e,Te),E=new L3(I,Ke),N.reversedDepthBuffer&&f&&E.buffers.depth.setReversed(!0),W=I.createFramebuffer(),z=I.createFramebuffer(),D=I.createFramebuffer(),H=new XN(I),K=new v3,J=new U3(I,Ke,E,K,N,Te,H),_e=new HN(R),pe=new ZA(I),Re=new UN(I,pe),te=new VN(I,pe,H,Re),Z=new YN(I,te,pe,Re,H),j=new qN(I,N,J),Fe=new zN(K),ae=new x3(R,_e,Ke,N,Re,Fe),ce=new F3(R,K),me=new y3,ye=new A3(Ke),et=new LN(R,_e,E,Z,x,c),Xe=new D3(R,Z,N),de=new j3(I,H,N,E),Me=new ON(I,Ke,H),se=new WN(I,Ke,H),H.programs=ae.programs,R.capabilities=N,R.extensions=Ke,R.properties=K,R.renderLists=me,R.shadowMap=Xe,R.state=E,R.info=H}ze(),_!==Ei&&(C=new ZN(_,n.width,n.height,o,a,s));const je=new k3(R,I);this.xr=je,this.getContext=function(){return I},this.getContextAttributes=function(){return I.getContextAttributes()},this.forceContextLoss=function(){const w=Ke.get("WEBGL_lose_context");w&&w.loseContext()},this.forceContextRestore=function(){const w=Ke.get("WEBGL_lose_context");w&&w.restoreContext()},this.getPixelRatio=function(){return oe},this.setPixelRatio=function(w){w!==void 0&&(oe=w,this.setSize(ie,xe,!1))},this.getSize=function(w){return w.set(ie,xe)},this.setSize=function(w,G,ee=!0){if(je.isPresenting){Je("WebGLRenderer: Can't change size while VR device is presenting.");return}ie=w,xe=G,n.width=Math.floor(w*oe),n.height=Math.floor(G*oe),ee===!0&&(n.style.width=w+"px",n.style.height=G+"px"),C!==null&&C.setSize(n.width,n.height),this.setViewport(0,0,w,G)},this.getDrawingBufferSize=function(w){return w.set(ie*oe,xe*oe).floor()},this.setDrawingBufferSize=function(w,G,ee){ie=w,xe=G,oe=ee,n.width=Math.floor(w*ee),n.height=Math.floor(G*ee),this.setViewport(0,0,w,G)},this.setEffects=function(w){if(_===Ei){St("WebGLRenderer: setEffects() requires outputBufferType set to HalfFloatType or FloatType.");return}if(w){for(let G=0;G<w.length;G++)if(w[G].isOutputPass===!0){Je("WebGLRenderer: OutputPass is not needed in setEffects(). Tone mapping and color space conversion are applied automatically.");break}}C.setEffects(w||[])},this.getCurrentViewport=function(w){return w.copy(he)},this.getViewport=function(w){return w.copy(ke)},this.setViewport=function(w,G,ee,$){w.isVector4?ke.set(w.x,w.y,w.z,w.w):ke.set(w,G,ee,$),E.viewport(he.copy(ke).multiplyScalar(oe).round())},this.getScissor=function(w){return w.copy(xt)},this.setScissor=function(w,G,ee,$){w.isVector4?xt.set(w.x,w.y,w.z,w.w):xt.set(w,G,ee,$),E.scissor(Ae.copy(xt).multiplyScalar(oe).round())},this.getScissorTest=function(){return re},this.setScissorTest=function(w){E.setScissorTest(re=w)},this.setOpaqueSort=function(w){Ee=w},this.setTransparentSort=function(w){Oe=w},this.getClearColor=function(w){return w.copy(et.getClearColor())},this.setClearColor=function(){et.setClearColor(...arguments)},this.getClearAlpha=function(){return et.getClearAlpha()},this.setClearAlpha=function(){et.setClearAlpha(...arguments)},this.clear=function(w=!0,G=!0,ee=!0){let $=0;if(w){let Q=!1;if(V!==null){const Ue=V.texture.format;Q=g.has(Ue)}if(Q){const Ue=V.texture.type,He=h.has(Ue),Le=et.getClearColor(),qe=et.getClearAlpha(),Ze=Le.r,st=Le.g,ot=Le.b;He?(v[0]=Ze,v[1]=st,v[2]=ot,v[3]=qe,I.clearBufferuiv(I.COLOR,0,v)):(b[0]=Ze,b[1]=st,b[2]=ot,b[3]=qe,I.clearBufferiv(I.COLOR,0,b))}else $|=I.COLOR_BUFFER_BIT}G&&($|=I.DEPTH_BUFFER_BIT,this.state.buffers.depth.setMask(!0)),ee&&($|=I.STENCIL_BUFFER_BIT,this.state.buffers.stencil.setMask(4294967295)),$!==0&&I.clear($)},this.clearColor=function(){this.clear(!0,!1,!1)},this.clearDepth=function(){this.clear(!1,!0,!1)},this.clearStencil=function(){this.clear(!1,!1,!0)},this.setNodesHandler=function(w){w.setRenderer(this),O=w},this.dispose=function(){n.removeEventListener("webglcontextlost",_t,!1),n.removeEventListener("webglcontextrestored",yt,!1),n.removeEventListener("webglcontextcreationerror",Ot,!1),et.dispose(),me.dispose(),ye.dispose(),K.dispose(),_e.dispose(),Z.dispose(),Re.dispose(),de.dispose(),ae.dispose(),je.dispose(),je.removeEventListener("sessionstart",U),je.removeEventListener("sessionend",le),Ie.stop()};function _t(w){w.preventDefault(),_0("WebGLRenderer: Context Lost."),L=!0}function yt(){_0("WebGLRenderer: Context Restored."),L=!1;const w=H.autoReset,G=Xe.enabled,ee=Xe.autoUpdate,$=Xe.needsUpdate,Q=Xe.type;ze(),H.autoReset=w,Xe.enabled=G,Xe.autoUpdate=ee,Xe.needsUpdate=$,Xe.type=Q}function Ot(w){St("WebGLRenderer: A WebGL context could not be created. Reason: ",w.statusMessage)}function _n(w){const G=w.target;G.removeEventListener("dispose",_n),ji(G)}function ji(w){wt(w),K.remove(w)}function wt(w){const G=K.get(w).programs;G!==void 0&&(G.forEach(function(ee){ae.releaseProgram(ee)}),w.isShaderMaterial&&ae.releaseShaderCache(w))}this.renderBufferDirect=function(w,G,ee,$,Q,Ue){G===null&&(G=we);const He=Q.isMesh&&Q.matrixWorld.determinantAffine()<0,Le=Cs(w,G,ee,$,Q);E.setMaterial($,He);let qe=ee.index,Ze=1;if($.wireframe===!0){if(qe=te.getWireframeAttribute(ee),qe===void 0)return;Ze=2}const st=ee.drawRange,ot=ee.attributes.position;let $e=st.start*Ze,Dt=(st.start+st.count)*Ze;Ue!==null&&($e=Math.max($e,Ue.start*Ze),Dt=Math.min(Dt,(Ue.start+Ue.count)*Ze)),qe!==null?($e=Math.max($e,0),Dt=Math.min(Dt,qe.count)):ot!=null&&($e=Math.max($e,0),Dt=Math.min(Dt,ot.count));const Jt=Dt-$e;if(Jt<0||Jt===1/0)return;Re.setup(Q,$,Le,ee,qe);let Kt,Pt=Me;if(qe!==null&&(Kt=pe.get(qe),Pt=se,Pt.setIndex(Kt)),Q.isMesh)$.wireframe===!0?(E.setLineWidth($.wireframeLinewidth*ve()),Pt.setMode(I.LINES)):Pt.setMode(I.TRIANGLES);else if(Q.isLine){let wn=$.linewidth;wn===void 0&&(wn=1),E.setLineWidth(wn*ve()),Q.isLineSegments?Pt.setMode(I.LINES):Q.isLineLoop?Pt.setMode(I.LINE_LOOP):Pt.setMode(I.LINE_STRIP)}else Q.isPoints?Pt.setMode(I.POINTS):Q.isSprite&&Pt.setMode(I.TRIANGLES);if(Q.isBatchedMesh)if(Ke.get("WEBGL_multi_draw"))Pt.renderMultiDraw(Q._multiDrawStarts,Q._multiDrawCounts,Q._multiDrawCount);else{const wn=Q._multiDrawStarts,Be=Q._multiDrawCounts,ei=Q._multiDrawCount,bt=qe?pe.get(qe).bytesPerElement:1,pi=K.get($).currentProgram.getUniforms();for(let Wi=0;Wi<ei;Wi++)pi.setValue(I,"_gl_DrawID",Wi),Pt.render(wn[Wi]/bt,Be[Wi])}else if(Q.isInstancedMesh)Pt.renderInstances($e,Jt,Q.count);else if(ee.isInstancedBufferGeometry){const wn=ee._maxInstanceCount!==void 0?ee._maxInstanceCount:1/0,Be=Math.min(ee.instanceCount,wn);Pt.renderInstances($e,Jt,Be)}else Pt.render($e,Jt)};function Hi(w,G,ee){w.transparent===!0&&w.side===ba&&w.forceSinglePass===!1?(w.side=Yn,w.needsUpdate=!0,Ye(w,G,ee),w.side=Ts,w.needsUpdate=!0,Ye(w,G,ee),w.side=ba):Ye(w,G,ee)}this.compile=function(w,G,ee=null){ee===null&&(ee=w),M=ye.get(ee),M.init(G),S.push(M),ee.traverseVisible(function(Q){Q.isLight&&Q.layers.test(G.layers)&&(M.pushLight(Q),Q.castShadow&&M.pushShadow(Q))}),w!==ee&&w.traverseVisible(function(Q){Q.isLight&&Q.layers.test(G.layers)&&(M.pushLight(Q),Q.castShadow&&M.pushShadow(Q))}),M.setupLights();const $=new Set;return w.traverse(function(Q){if(!(Q.isMesh||Q.isPoints||Q.isLine||Q.isSprite))return;const Ue=Q.material;if(Ue)if(Array.isArray(Ue))for(let He=0;He<Ue.length;He++){const Le=Ue[He];Hi(Le,ee,Q),$.add(Le)}else Hi(Ue,ee,Q),$.add(Ue)}),M=S.pop(),$},this.compileAsync=function(w,G,ee=null){const $=this.compile(w,G,ee);return new Promise(Q=>{function Ue(){if($.forEach(function(He){K.get(He).currentProgram.isReady()&&$.delete(He)}),$.size===0){Q(w);return}setTimeout(Ue,10)}Ke.get("KHR_parallel_shader_compile")!==null?Ue():setTimeout(Ue,10)})};let Gi=null;function Ba(w){Gi&&Gi(w)}function U(){Ie.stop()}function le(){Ie.start()}const Ie=new wS;Ie.setAnimationLoop(Ba),typeof self<"u"&&Ie.setContext(self),this.setAnimationLoop=function(w){Gi=w,je.setAnimationLoop(w),w===null?Ie.stop():Ie.start()},je.addEventListener("sessionstart",U),je.addEventListener("sessionend",le),this.render=function(w,G){if(G!==void 0&&G.isCamera!==!0){St("WebGLRenderer.render: camera is not an instance of THREE.Camera.");return}if(L===!0)return;O!==null&&O.renderStart(w,G);const ee=je.enabled===!0&&je.isPresenting===!0,$=C!==null&&(V===null||ee)&&C.begin(R,V);if(w.matrixWorldAutoUpdate===!0&&w.updateMatrixWorld(),G.parent===null&&G.matrixWorldAutoUpdate===!0&&G.updateMatrixWorld(),je.enabled===!0&&je.isPresenting===!0&&(C===null||C.isCompositing()===!1)&&(je.cameraAutoUpdate===!0&&je.updateCamera(G),G=je.getCamera()),w.isScene===!0&&w.onBeforeRender(R,w,G,V),M=ye.get(w,S.length),M.init(G),M.state.textureUnits=J.getTextureUnits(),S.push(M),Ve.multiplyMatrices(G.projectionMatrix,G.matrixWorldInverse),Se.setFromProjectionMatrix(Ve,ta,G.reversedDepth),be=this.localClippingEnabled,Ce=Fe.init(this.clippingPlanes,be),A=me.get(w,T.length),A.init(),T.push(A),je.enabled===!0&&je.isPresenting===!0){const He=R.xr.getDepthSensingMesh();He!==null&&ft(He,G,-1/0,R.sortObjects)}ft(w,G,0,R.sortObjects),A.finish(),R.sortObjects===!0&&A.sort(Ee,Oe,G.reversedDepth),We=je.enabled===!1||je.isPresenting===!1||je.hasDepthSensing()===!1,We&&et.addToRenderList(A,w),this.info.render.frame++,this.info.autoReset===!0&&this.info.reset(),Ce===!0&&Fe.beginShadows();const Q=M.state.shadowsArray;if(Xe.render(Q,w,G),Ce===!0&&Fe.endShadows(),($&&C.hasRenderPass())===!1){const He=A.opaque,Le=A.transmissive;if(M.setupLights(),G.isArrayCamera){const qe=G.cameras;if(Le.length>0)for(let Ze=0,st=qe.length;Ze<st;Ze++){const ot=qe[Ze];F(He,Le,w,ot)}We&&et.render(w);for(let Ze=0,st=qe.length;Ze<st;Ze++){const ot=qe[Ze];Yt(A,w,ot,ot.viewport)}}else Le.length>0&&F(He,Le,w,G),We&&et.render(w),Yt(A,w,G)}V!==null&&k===0&&(J.updateMultisampleRenderTarget(V),J.updateRenderTargetMipmap(V)),$&&C.end(R),w.isScene===!0&&w.onAfterRender(R,w,G),Re.resetDefaultState(),X=-1,ge=null,S.pop(),S.length>0?(M=S[S.length-1],J.setTextureUnits(M.state.textureUnits),Ce===!0&&Fe.setGlobalState(R.clippingPlanes,M.state.camera)):M=null,T.pop(),T.length>0?A=T[T.length-1]:A=null,O!==null&&O.renderEnd()};function ft(w,G,ee,$){if(w.visible===!1)return;if(w.layers.test(G.layers)){if(w.isGroup)ee=w.renderOrder;else if(w.isLOD)w.autoUpdate===!0&&w.update(G);else if(w.isLightProbeGrid)M.pushLightProbeGrid(w);else if(w.isLight)M.pushLight(w),w.castShadow&&M.pushShadow(w);else if(w.isSprite){if(!w.frustumCulled||Se.intersectsSprite(w)){$&&Pe.setFromMatrixPosition(w.matrixWorld).applyMatrix4(Ve);const He=Z.update(w),Le=w.material;Le.visible&&A.push(w,He,Le,ee,Pe.z,null)}}else if((w.isMesh||w.isLine||w.isPoints)&&(!w.frustumCulled||Se.intersectsObject(w))){const He=Z.update(w),Le=w.material;if($&&(w.boundingSphere!==void 0?(w.boundingSphere===null&&w.computeBoundingSphere(),Pe.copy(w.boundingSphere.center)):(He.boundingSphere===null&&He.computeBoundingSphere(),Pe.copy(He.boundingSphere.center)),Pe.applyMatrix4(w.matrixWorld).applyMatrix4(Ve)),Array.isArray(Le)){const qe=He.groups;for(let Ze=0,st=qe.length;Ze<st;Ze++){const ot=qe[Ze],$e=Le[ot.materialIndex];$e&&$e.visible&&A.push(w,He,$e,ee,Pe.z,ot)}}else Le.visible&&A.push(w,He,Le,ee,Pe.z,null)}}const Ue=w.children;for(let He=0,Le=Ue.length;He<Le;He++)ft(Ue[He],G,ee,$)}function Yt(w,G,ee,$){const{opaque:Q,transmissive:Ue,transparent:He}=w;M.setupLightsView(ee),Ce===!0&&Fe.setGlobalState(R.clippingPlanes,ee),$&&E.viewport(he.copy($)),Q.length>0&&ne(Q,G,ee),Ue.length>0&&ne(Ue,G,ee),He.length>0&&ne(He,G,ee),E.buffers.depth.setTest(!0),E.buffers.depth.setMask(!0),E.buffers.color.setMask(!0),E.setPolygonOffset(!1)}function F(w,G,ee,$){if((ee.isScene===!0?ee.overrideMaterial:null)!==null)return;if(M.state.transmissionRenderTarget[$.id]===void 0){const $e=Ke.has("EXT_color_buffer_half_float")||Ke.has("EXT_color_buffer_float");M.state.transmissionRenderTarget[$.id]=new aa(1,1,{generateMipmaps:!0,type:$e?za:Ei,minFilter:js,samples:Math.max(4,N.samples),stencilBuffer:s,resolveDepthBuffer:!1,resolveStencilBuffer:!1,colorSpace:ht.workingColorSpace})}const Ue=M.state.transmissionRenderTarget[$.id],He=$.viewport||he;Ue.setSize(He.z*R.transmissionResolutionScale,He.w*R.transmissionResolutionScale);const Le=R.getRenderTarget(),qe=R.getActiveCubeFace(),Ze=R.getActiveMipmapLevel();R.setRenderTarget(Ue),R.getClearColor(fe),Ne=R.getClearAlpha(),Ne<1&&R.setClearColor(16777215,.5),R.clear(),We&&et.render(ee);const st=R.toneMapping;R.toneMapping=ia;const ot=$.viewport;if($.viewport!==void 0&&($.viewport=void 0),M.setupLightsView($),Ce===!0&&Fe.setGlobalState(R.clippingPlanes,$),ne(w,ee,$),J.updateMultisampleRenderTarget(Ue),J.updateRenderTargetMipmap(Ue),Ke.has("WEBGL_multisampled_render_to_texture")===!1){let $e=!1;for(let Dt=0,Jt=G.length;Dt<Jt;Dt++){const Kt=G[Dt],{object:Pt,geometry:wn,material:Be,group:ei}=Kt;if(Be.side===ba&&Pt.layers.test($.layers)){const bt=Be.side;Be.side=Yn,Be.needsUpdate=!0,Qe(Pt,ee,$,wn,Be,ei),Be.side=bt,Be.needsUpdate=!0,$e=!0}}$e===!0&&(J.updateMultisampleRenderTarget(Ue),J.updateRenderTargetMipmap(Ue))}R.setRenderTarget(Le,qe,Ze),R.setClearColor(fe,Ne),ot!==void 0&&($.viewport=ot),R.toneMapping=st}function ne(w,G,ee){const $=G.isScene===!0?G.overrideMaterial:null;for(let Q=0,Ue=w.length;Q<Ue;Q++){const He=w[Q],{object:Le,geometry:qe,group:Ze}=He;let st=He.material;st.allowOverride===!0&&$!==null&&(st=$),Le.layers.test(ee.layers)&&Qe(Le,G,ee,qe,st,Ze)}}function Qe(w,G,ee,$,Q,Ue){w.onBeforeRender(R,G,ee,$,Q,Ue),w.modelViewMatrix.multiplyMatrices(ee.matrixWorldInverse,w.matrixWorld),w.normalMatrix.getNormalMatrix(w.modelViewMatrix),Q.onBeforeRender(R,G,ee,$,w,Ue),Q.transparent===!0&&Q.side===ba&&Q.forceSinglePass===!1?(Q.side=Yn,Q.needsUpdate=!0,R.renderBufferDirect(ee,G,$,Q,w,Ue),Q.side=Ts,Q.needsUpdate=!0,R.renderBufferDirect(ee,G,$,Q,w,Ue),Q.side=ba):R.renderBufferDirect(ee,G,$,Q,w,Ue),w.onAfterRender(R,G,ee,$,Q,Ue)}function Ye(w,G,ee){G.isScene!==!0&&(G=we);const $=K.get(w),Q=M.state.lights,Ue=M.state.shadowsArray,He=Q.state.version,Le=ae.getParameters(w,Q.state,Ue,G,ee,M.state.lightProbeGridArray),qe=ae.getProgramCacheKey(Le);let Ze=$.programs;$.environment=w.isMeshStandardMaterial||w.isMeshLambertMaterial||w.isMeshPhongMaterial?G.environment:null,$.fog=G.fog;const st=w.isMeshStandardMaterial||w.isMeshLambertMaterial&&!w.envMap||w.isMeshPhongMaterial&&!w.envMap;$.envMap=_e.get(w.envMap||$.environment,st),$.envMapRotation=$.environment!==null&&w.envMap===null?G.environmentRotation:w.envMapRotation,Ze===void 0&&(w.addEventListener("dispose",_n),Ze=new Map,$.programs=Ze);let ot=Ze.get(qe);if(ot!==void 0){if($.currentProgram===ot&&$.lightsStateVersion===He)return Jn(w,Le),ot}else Le.uniforms=ae.getUniforms(w),O!==null&&w.isNodeMaterial&&O.build(w,ee,Le),w.onBeforeCompile(Le,R),ot=ae.acquireProgram(Le,qe),Ze.set(qe,ot),$.uniforms=Le.uniforms;const $e=$.uniforms;return(!w.isShaderMaterial&&!w.isRawShaderMaterial||w.clipping===!0)&&($e.clippingPlanes=Fe.uniform),Jn(w,Le),$.needsLights=hi(w),$.lightsStateVersion=He,$.needsLights&&($e.ambientLightColor.value=Q.state.ambient,$e.lightProbe.value=Q.state.probe,$e.directionalLights.value=Q.state.directional,$e.directionalLightShadows.value=Q.state.directionalShadow,$e.spotLights.value=Q.state.spot,$e.spotLightShadows.value=Q.state.spotShadow,$e.rectAreaLights.value=Q.state.rectArea,$e.ltc_1.value=Q.state.rectAreaLTC1,$e.ltc_2.value=Q.state.rectAreaLTC2,$e.pointLights.value=Q.state.point,$e.pointLightShadows.value=Q.state.pointShadow,$e.hemisphereLights.value=Q.state.hemi,$e.directionalShadowMatrix.value=Q.state.directionalShadowMatrix,$e.spotLightMatrix.value=Q.state.spotLightMatrix,$e.spotLightMap.value=Q.state.spotLightMap,$e.pointShadowMatrix.value=Q.state.pointShadowMatrix),$.lightProbeGrid=M.state.lightProbeGridArray.length>0,$.currentProgram=ot,$.uniformsList=null,ot}function Wt(w){if(w.uniformsList===null){const G=w.currentProgram.getUniforms();w.uniformsList=wu.seqWithValue(G.seq,w.uniforms)}return w.uniformsList}function Jn(w,G){const ee=K.get(w);ee.outputColorSpace=G.outputColorSpace,ee.batching=G.batching,ee.batchingColor=G.batchingColor,ee.instancing=G.instancing,ee.instancingColor=G.instancingColor,ee.instancingMorph=G.instancingMorph,ee.skinning=G.skinning,ee.morphTargets=G.morphTargets,ee.morphNormals=G.morphNormals,ee.morphColors=G.morphColors,ee.morphTargetsCount=G.morphTargetsCount,ee.numClippingPlanes=G.numClippingPlanes,ee.numIntersection=G.numClipIntersection,ee.vertexAlphas=G.vertexAlphas,ee.vertexTangents=G.vertexTangents,ee.toneMapping=G.toneMapping}function Vi(w,G){if(w.length===0)return null;if(w.length===1)return w[0].texture!==null?w[0]:null;y.setFromMatrixPosition(G.matrixWorld);for(let ee=0,$=w.length;ee<$;ee++){const Q=w[ee];if(Q.texture!==null&&Q.boundingBox.containsPoint(y))return Q}return null}function Cs(w,G,ee,$,Q){G.isScene!==!0&&(G=we),J.resetTextureUnits();const Ue=G.fog,He=$.isMeshStandardMaterial||$.isMeshLambertMaterial||$.isMeshPhongMaterial?G.environment:null,Le=V===null?R.outputColorSpace:V.isXRRenderTarget===!0?V.texture.colorSpace:ht.workingColorSpace,qe=$.isMeshStandardMaterial||$.isMeshLambertMaterial&&!$.envMap||$.isMeshPhongMaterial&&!$.envMap,Ze=_e.get($.envMap||He,qe),st=$.vertexColors===!0&&!!ee.attributes.color&&ee.attributes.color.itemSize===4,ot=!!ee.attributes.tangent&&(!!$.normalMap||$.anisotropy>0),$e=!!ee.morphAttributes.position,Dt=!!ee.morphAttributes.normal,Jt=!!ee.morphAttributes.color;let Kt=ia;$.toneMapped&&(V===null||V.isXRRenderTarget===!0)&&(Kt=R.toneMapping);const Pt=ee.morphAttributes.position||ee.morphAttributes.normal||ee.morphAttributes.color,wn=Pt!==void 0?Pt.length:0,Be=K.get($),ei=M.state.lights;if(Ce===!0&&(be===!0||w!==ge)){const kt=w===ge&&$.id===X;Fe.setState($,w,kt)}let bt=!1;$.version===Be.__version?(Be.needsLights&&Be.lightsStateVersion!==ei.state.version||Be.outputColorSpace!==Le||Q.isBatchedMesh&&Be.batching===!1||!Q.isBatchedMesh&&Be.batching===!0||Q.isBatchedMesh&&Be.batchingColor===!0&&Q.colorTexture===null||Q.isBatchedMesh&&Be.batchingColor===!1&&Q.colorTexture!==null||Q.isInstancedMesh&&Be.instancing===!1||!Q.isInstancedMesh&&Be.instancing===!0||Q.isSkinnedMesh&&Be.skinning===!1||!Q.isSkinnedMesh&&Be.skinning===!0||Q.isInstancedMesh&&Be.instancingColor===!0&&Q.instanceColor===null||Q.isInstancedMesh&&Be.instancingColor===!1&&Q.instanceColor!==null||Q.isInstancedMesh&&Be.instancingMorph===!0&&Q.morphTexture===null||Q.isInstancedMesh&&Be.instancingMorph===!1&&Q.morphTexture!==null||Be.envMap!==Ze||$.fog===!0&&Be.fog!==Ue||Be.numClippingPlanes!==void 0&&(Be.numClippingPlanes!==Fe.numPlanes||Be.numIntersection!==Fe.numIntersection)||Be.vertexAlphas!==st||Be.vertexTangents!==ot||Be.morphTargets!==$e||Be.morphNormals!==Dt||Be.morphColors!==Jt||Be.toneMapping!==Kt||Be.morphTargetsCount!==wn||!!Be.lightProbeGrid!=M.state.lightProbeGridArray.length>0)&&(bt=!0):(bt=!0,Be.__version=$.version);let pi=Be.currentProgram;bt===!0&&(pi=Ye($,G,Q),O&&$.isNodeMaterial&&O.onUpdateProgram($,pi,Be));let Wi=!1,Fa=!1,dr=!1;const zt=pi.getUniforms(),en=Be.uniforms;if(E.useProgram(pi.program)&&(Wi=!0,Fa=!0,dr=!0),$.id!==X&&(X=$.id,Fa=!0),Be.needsLights){const kt=Vi(M.state.lightProbeGridArray,Q);Be.lightProbeGrid!==kt&&(Be.lightProbeGrid=kt,Fa=!0)}if(Wi||ge!==w){E.buffers.depth.getReversed()&&w.reversedDepth!==!0&&(w._reversedDepth=!0,w.updateProjectionMatrix()),zt.setValue(I,"projectionMatrix",w.projectionMatrix),zt.setValue(I,"viewMatrix",w.matrixWorldInverse);const Ha=zt.map.cameraPosition;Ha!==void 0&&Ha.setValue(I,Ge.setFromMatrixPosition(w.matrixWorld)),N.logarithmicDepthBuffer&&zt.setValue(I,"logDepthBufFC",2/(Math.log(w.far+1)/Math.LN2)),($.isMeshPhongMaterial||$.isMeshToonMaterial||$.isMeshLambertMaterial||$.isMeshBasicMaterial||$.isMeshStandardMaterial||$.isShaderMaterial)&&zt.setValue(I,"isOrthographic",w.isOrthographicCamera===!0),ge!==w&&(ge=w,Fa=!0,dr=!0)}if(Be.needsLights&&(ei.state.directionalShadowMap.length>0&&zt.setValue(I,"directionalShadowMap",ei.state.directionalShadowMap,J),ei.state.spotShadowMap.length>0&&zt.setValue(I,"spotShadowMap",ei.state.spotShadowMap,J),ei.state.pointShadowMap.length>0&&zt.setValue(I,"pointShadowMap",ei.state.pointShadowMap,J)),Q.isSkinnedMesh){zt.setOptional(I,Q,"bindMatrix"),zt.setOptional(I,Q,"bindMatrixInverse");const kt=Q.skeleton;kt&&(kt.boneTexture===null&&kt.computeBoneTexture(),zt.setValue(I,"boneTexture",kt.boneTexture,J))}Q.isBatchedMesh&&(zt.setOptional(I,Q,"batchingTexture"),zt.setValue(I,"batchingTexture",Q._matricesTexture,J),zt.setOptional(I,Q,"batchingIdTexture"),zt.setValue(I,"batchingIdTexture",Q._indirectTexture,J),zt.setOptional(I,Q,"batchingColorTexture"),Q._colorsTexture!==null&&zt.setValue(I,"batchingColorTexture",Q._colorsTexture,J));const ja=ee.morphAttributes;if((ja.position!==void 0||ja.normal!==void 0||ja.color!==void 0)&&j.update(Q,ee,pi),(Fa||Be.receiveShadow!==Q.receiveShadow)&&(Be.receiveShadow=Q.receiveShadow,zt.setValue(I,"receiveShadow",Q.receiveShadow)),($.isMeshStandardMaterial||$.isMeshLambertMaterial||$.isMeshPhongMaterial)&&$.envMap===null&&G.environment!==null&&(en.envMapIntensity.value=G.environmentIntensity),en.dfgLUT!==void 0&&(en.dfgLUT.value=G3()),Fa){if(zt.setValue(I,"toneMappingExposure",R.toneMappingExposure),Be.needsLights&&An(en,dr),Ue&&$.fog===!0&&ce.refreshFogUniforms(en,Ue),ce.refreshMaterialUniforms(en,$,oe,xe,M.state.transmissionRenderTarget[w.id]),Be.needsLights&&Be.lightProbeGrid){const kt=Be.lightProbeGrid;en.probesSH.value=kt.texture,en.probesMin.value.copy(kt.boundingBox.min),en.probesMax.value.copy(kt.boundingBox.max),en.probesResolution.value.copy(kt.resolution)}wu.upload(I,Wt(Be),en,J)}if($.isShaderMaterial&&$.uniformsNeedUpdate===!0&&(wu.upload(I,Wt(Be),en,J),$.uniformsNeedUpdate=!1),$.isSpriteMaterial&&zt.setValue(I,"center",Q.center),zt.setValue(I,"modelViewMatrix",Q.modelViewMatrix),zt.setValue(I,"normalMatrix",Q.normalMatrix),zt.setValue(I,"modelMatrix",Q.matrixWorld),$.uniformsGroups!==void 0){const kt=$.uniformsGroups;for(let Ha=0,hr=kt.length;Ha<hr;Ha++){const Ng=kt[Ha];de.update(Ng,pi),de.bind(Ng,pi)}}return pi}function An(w,G){w.ambientLightColor.needsUpdate=G,w.lightProbe.needsUpdate=G,w.directionalLights.needsUpdate=G,w.directionalLightShadows.needsUpdate=G,w.pointLights.needsUpdate=G,w.pointLightShadows.needsUpdate=G,w.spotLights.needsUpdate=G,w.spotLightShadows.needsUpdate=G,w.rectAreaLights.needsUpdate=G,w.hemisphereLights.needsUpdate=G}function hi(w){return w.isMeshLambertMaterial||w.isMeshToonMaterial||w.isMeshPhongMaterial||w.isMeshStandardMaterial||w.isShadowMaterial||w.isShaderMaterial&&w.lights===!0}this.getActiveCubeFace=function(){return B},this.getActiveMipmapLevel=function(){return k},this.getRenderTarget=function(){return V},this.setRenderTargetTextures=function(w,G,ee){const $=K.get(w);$.__autoAllocateDepthBuffer=w.resolveDepthBuffer===!1,$.__autoAllocateDepthBuffer===!1&&($.__useRenderToTexture=!1),K.get(w.texture).__webglTexture=G,K.get(w.depthTexture).__webglTexture=$.__autoAllocateDepthBuffer?void 0:ee,$.__hasExternalTextures=!0},this.setRenderTargetFramebuffer=function(w,G){const ee=K.get(w);ee.__webglFramebuffer=G,ee.__useDefaultFramebuffer=G===void 0},this.setRenderTarget=function(w,G=0,ee=0){V=w,B=G,k=ee;let $=null,Q=!1,Ue=!1;if(w){const Le=K.get(w);if(Le.__useDefaultFramebuffer!==void 0){E.bindFramebuffer(I.FRAMEBUFFER,Le.__webglFramebuffer),he.copy(w.viewport),Ae.copy(w.scissor),q=w.scissorTest,E.viewport(he),E.scissor(Ae),E.setScissorTest(q),X=-1;return}else if(Le.__webglFramebuffer===void 0)J.setupRenderTarget(w);else if(Le.__hasExternalTextures)J.rebindTextures(w,K.get(w.texture).__webglTexture,K.get(w.depthTexture).__webglTexture);else if(w.depthBuffer){const st=w.depthTexture;if(Le.__boundDepthTexture!==st){if(st!==null&&K.has(st)&&(w.width!==st.image.width||w.height!==st.image.height))throw new Error("THREE.WebGLRenderer: Attached DepthTexture is initialized to the incorrect size.");J.setupDepthRenderbuffer(w)}}const qe=w.texture;(qe.isData3DTexture||qe.isDataArrayTexture||qe.isCompressedArrayTexture)&&(Ue=!0);const Ze=K.get(w).__webglFramebuffer;w.isWebGLCubeRenderTarget?(Array.isArray(Ze[G])?$=Ze[G][ee]:$=Ze[G],Q=!0):w.samples>0&&J.useMultisampledRTT(w)===!1?$=K.get(w).__webglMultisampledFramebuffer:Array.isArray(Ze)?$=Ze[ee]:$=Ze,he.copy(w.viewport),Ae.copy(w.scissor),q=w.scissorTest}else he.copy(ke).multiplyScalar(oe).floor(),Ae.copy(xt).multiplyScalar(oe).floor(),q=re;if(ee!==0&&($=W),E.bindFramebuffer(I.FRAMEBUFFER,$)&&E.drawBuffers(w,$),E.viewport(he),E.scissor(Ae),E.setScissorTest(q),Q){const Le=K.get(w.texture);I.framebufferTexture2D(I.FRAMEBUFFER,I.COLOR_ATTACHMENT0,I.TEXTURE_CUBE_MAP_POSITIVE_X+G,Le.__webglTexture,ee)}else if(Ue){const Le=G;for(let qe=0;qe<w.textures.length;qe++){const Ze=K.get(w.textures[qe]);I.framebufferTextureLayer(I.FRAMEBUFFER,I.COLOR_ATTACHMENT0+qe,Ze.__webglTexture,ee,Le)}}else if(w!==null&&ee!==0){const Le=K.get(w.texture);I.framebufferTexture2D(I.FRAMEBUFFER,I.COLOR_ATTACHMENT0,I.TEXTURE_2D,Le.__webglTexture,ee)}X=-1},this.readRenderTargetPixels=function(w,G,ee,$,Q,Ue,He,Le=0){if(!(w&&w.isWebGLRenderTarget)){St("WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.");return}let qe=K.get(w).__webglFramebuffer;if(w.isWebGLCubeRenderTarget&&He!==void 0&&(qe=qe[He]),qe){E.bindFramebuffer(I.FRAMEBUFFER,qe);try{const Ze=w.textures[Le],st=Ze.format,ot=Ze.type;if(w.textures.length>1&&I.readBuffer(I.COLOR_ATTACHMENT0+Le),!N.textureFormatReadable(st)){St("WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.");return}if(!N.textureTypeReadable(ot)){St("WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.");return}G>=0&&G<=w.width-$&&ee>=0&&ee<=w.height-Q&&I.readPixels(G,ee,$,Q,Te.convert(st),Te.convert(ot),Ue)}finally{const Ze=V!==null?K.get(V).__webglFramebuffer:null;E.bindFramebuffer(I.FRAMEBUFFER,Ze)}}},this.readRenderTargetPixelsAsync=async function(w,G,ee,$,Q,Ue,He,Le=0){if(!(w&&w.isWebGLRenderTarget))throw new Error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.");let qe=K.get(w).__webglFramebuffer;if(w.isWebGLCubeRenderTarget&&He!==void 0&&(qe=qe[He]),qe)if(G>=0&&G<=w.width-$&&ee>=0&&ee<=w.height-Q){E.bindFramebuffer(I.FRAMEBUFFER,qe);const Ze=w.textures[Le],st=Ze.format,ot=Ze.type;if(w.textures.length>1&&I.readBuffer(I.COLOR_ATTACHMENT0+Le),!N.textureFormatReadable(st))throw new Error("THREE.WebGLRenderer.readRenderTargetPixelsAsync: renderTarget is not in RGBA or implementation defined format.");if(!N.textureTypeReadable(ot))throw new Error("THREE.WebGLRenderer.readRenderTargetPixelsAsync: renderTarget is not in UnsignedByteType or implementation defined type.");const $e=I.createBuffer();I.bindBuffer(I.PIXEL_PACK_BUFFER,$e),I.bufferData(I.PIXEL_PACK_BUFFER,Ue.byteLength,I.STREAM_READ),I.readPixels(G,ee,$,Q,Te.convert(st),Te.convert(ot),0);const Dt=V!==null?K.get(V).__webglFramebuffer:null;E.bindFramebuffer(I.FRAMEBUFFER,Dt);const Jt=I.fenceSync(I.SYNC_GPU_COMMANDS_COMPLETE,0);return I.flush(),await cA(I,Jt,4),I.bindBuffer(I.PIXEL_PACK_BUFFER,$e),I.getBufferSubData(I.PIXEL_PACK_BUFFER,0,Ue),I.deleteBuffer($e),I.deleteSync(Jt),Ue}else throw new Error("THREE.WebGLRenderer.readRenderTargetPixelsAsync: requested read bounds are out of range.")},this.copyFramebufferToTexture=function(w,G=null,ee=0){const $=Math.pow(2,-ee),Q=Math.floor(w.image.width*$),Ue=Math.floor(w.image.height*$),He=G!==null?G.x:0,Le=G!==null?G.y:0;J.setTexture2D(w,0),I.copyTexSubImage2D(I.TEXTURE_2D,ee,0,0,He,Le,Q,Ue),E.unbindTexture()},this.copyTextureToTexture=function(w,G,ee=null,$=null,Q=0,Ue=0){let He,Le,qe,Ze,st,ot,$e,Dt,Jt;const Kt=w.isCompressedTexture?w.mipmaps[Ue]:w.image;if(ee!==null)He=ee.max.x-ee.min.x,Le=ee.max.y-ee.min.y,qe=ee.isBox3?ee.max.z-ee.min.z:1,Ze=ee.min.x,st=ee.min.y,ot=ee.isBox3?ee.min.z:0;else{const en=Math.pow(2,-Q);He=Math.floor(Kt.width*en),Le=Math.floor(Kt.height*en),w.isDataArrayTexture?qe=Kt.depth:w.isData3DTexture?qe=Math.floor(Kt.depth*en):qe=1,Ze=0,st=0,ot=0}$!==null?($e=$.x,Dt=$.y,Jt=$.z):($e=0,Dt=0,Jt=0);const Pt=Te.convert(G.format),wn=Te.convert(G.type);let Be;G.isData3DTexture?(J.setTexture3D(G,0),Be=I.TEXTURE_3D):G.isDataArrayTexture||G.isCompressedArrayTexture?(J.setTexture2DArray(G,0),Be=I.TEXTURE_2D_ARRAY):(J.setTexture2D(G,0),Be=I.TEXTURE_2D),E.activeTexture(I.TEXTURE0),E.pixelStorei(I.UNPACK_FLIP_Y_WEBGL,G.flipY),E.pixelStorei(I.UNPACK_PREMULTIPLY_ALPHA_WEBGL,G.premultiplyAlpha),E.pixelStorei(I.UNPACK_ALIGNMENT,G.unpackAlignment);const ei=E.getParameter(I.UNPACK_ROW_LENGTH),bt=E.getParameter(I.UNPACK_IMAGE_HEIGHT),pi=E.getParameter(I.UNPACK_SKIP_PIXELS),Wi=E.getParameter(I.UNPACK_SKIP_ROWS),Fa=E.getParameter(I.UNPACK_SKIP_IMAGES);E.pixelStorei(I.UNPACK_ROW_LENGTH,Kt.width),E.pixelStorei(I.UNPACK_IMAGE_HEIGHT,Kt.height),E.pixelStorei(I.UNPACK_SKIP_PIXELS,Ze),E.pixelStorei(I.UNPACK_SKIP_ROWS,st),E.pixelStorei(I.UNPACK_SKIP_IMAGES,ot);const dr=w.isDataArrayTexture||w.isData3DTexture,zt=G.isDataArrayTexture||G.isData3DTexture;if(w.isDepthTexture){const en=K.get(w),ja=K.get(G),kt=K.get(en.__renderTarget),Ha=K.get(ja.__renderTarget);E.bindFramebuffer(I.READ_FRAMEBUFFER,kt.__webglFramebuffer),E.bindFramebuffer(I.DRAW_FRAMEBUFFER,Ha.__webglFramebuffer);for(let hr=0;hr<qe;hr++)dr&&(I.framebufferTextureLayer(I.READ_FRAMEBUFFER,I.COLOR_ATTACHMENT0,K.get(w).__webglTexture,Q,ot+hr),I.framebufferTextureLayer(I.DRAW_FRAMEBUFFER,I.COLOR_ATTACHMENT0,K.get(G).__webglTexture,Ue,Jt+hr)),I.blitFramebuffer(Ze,st,He,Le,$e,Dt,He,Le,I.DEPTH_BUFFER_BIT,I.NEAREST);E.bindFramebuffer(I.READ_FRAMEBUFFER,null),E.bindFramebuffer(I.DRAW_FRAMEBUFFER,null)}else if(Q!==0||w.isRenderTargetTexture||K.has(w)){const en=K.get(w),ja=K.get(G);E.bindFramebuffer(I.READ_FRAMEBUFFER,z),E.bindFramebuffer(I.DRAW_FRAMEBUFFER,D);for(let kt=0;kt<qe;kt++)dr?I.framebufferTextureLayer(I.READ_FRAMEBUFFER,I.COLOR_ATTACHMENT0,en.__webglTexture,Q,ot+kt):I.framebufferTexture2D(I.READ_FRAMEBUFFER,I.COLOR_ATTACHMENT0,I.TEXTURE_2D,en.__webglTexture,Q),zt?I.framebufferTextureLayer(I.DRAW_FRAMEBUFFER,I.COLOR_ATTACHMENT0,ja.__webglTexture,Ue,Jt+kt):I.framebufferTexture2D(I.DRAW_FRAMEBUFFER,I.COLOR_ATTACHMENT0,I.TEXTURE_2D,ja.__webglTexture,Ue),Q!==0?I.blitFramebuffer(Ze,st,He,Le,$e,Dt,He,Le,I.COLOR_BUFFER_BIT,I.NEAREST):zt?I.copyTexSubImage3D(Be,Ue,$e,Dt,Jt+kt,Ze,st,He,Le):I.copyTexSubImage2D(Be,Ue,$e,Dt,Ze,st,He,Le);E.bindFramebuffer(I.READ_FRAMEBUFFER,null),E.bindFramebuffer(I.DRAW_FRAMEBUFFER,null)}else zt?w.isDataTexture||w.isData3DTexture?I.texSubImage3D(Be,Ue,$e,Dt,Jt,He,Le,qe,Pt,wn,Kt.data):G.isCompressedArrayTexture?I.compressedTexSubImage3D(Be,Ue,$e,Dt,Jt,He,Le,qe,Pt,Kt.data):I.texSubImage3D(Be,Ue,$e,Dt,Jt,He,Le,qe,Pt,wn,Kt):w.isDataTexture?I.texSubImage2D(I.TEXTURE_2D,Ue,$e,Dt,He,Le,Pt,wn,Kt.data):w.isCompressedTexture?I.compressedTexSubImage2D(I.TEXTURE_2D,Ue,$e,Dt,Kt.width,Kt.height,Pt,Kt.data):I.texSubImage2D(I.TEXTURE_2D,Ue,$e,Dt,He,Le,Pt,wn,Kt);E.pixelStorei(I.UNPACK_ROW_LENGTH,ei),E.pixelStorei(I.UNPACK_IMAGE_HEIGHT,bt),E.pixelStorei(I.UNPACK_SKIP_PIXELS,pi),E.pixelStorei(I.UNPACK_SKIP_ROWS,Wi),E.pixelStorei(I.UNPACK_SKIP_IMAGES,Fa),Ue===0&&G.generateMipmaps&&I.generateMipmap(Be),E.unbindTexture()},this.initRenderTarget=function(w){K.get(w).__webglFramebuffer===void 0&&J.setupRenderTarget(w)},this.initTexture=function(w){w.isCubeTexture?J.setTextureCube(w,0):w.isData3DTexture?J.setTexture3D(w,0):w.isDataArrayTexture||w.isCompressedArrayTexture?J.setTexture2DArray(w,0):J.setTexture2D(w,0),E.unbindTexture()},this.resetState=function(){B=0,k=0,V=null,E.reset(),Re.reset()},typeof __THREE_DEVTOOLS__<"u"&&__THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe",{detail:this}))}get coordinateSystem(){return ta}get outputColorSpace(){return this._outputColorSpace}set outputColorSpace(e){this._outputColorSpace=e;const n=this.getContext();n.drawingBufferColorSpace=ht._getDrawingBufferColorSpace(e),n.unpackColorSpace=ht._getUnpackColorSpace()}}const W3=1024,X3=2048,xv=96,vv=new Map;function q3(t){if(!t.agentColor)return null;const e=t.agentColor+(t.access||"");let n=vv.get(e);if(!n){n=new gt;const i=/hsl\(\s*([\d.]+)[\s,]+([\d.]+)%[\s,]+([\d.]+)%/.exec(t.agentColor);if(i){const a=t.access==="read"?Math.max(.18,+i[3]/100*.55):+i[3]/100;n.setHSL(+i[1]/360,+i[2]/100,a)}else try{n.set(t.agentColor)}catch{n.setHSL(0,0,.5)}vv.set(e,n)}return n}function Y3(t,e,n,{onSelect:i,onZoom:a}){let s;try{s=new V3({antialias:!0,alpha:!0,canvas:t})}catch{}if(!s)return null;s.setPixelRatio(Math.min(devicePixelRatio,2)),s.setClearColor(0,0);const r=new TA,o=new yg(-1,1,1,-1,-400,600),c=` - attribute vec3 aColor; attribute vec2 aHi; // x = highlight, y = fade - varying vec3 vN, vW, vColor; varying vec2 vHi; - void main(){ - vColor = aColor; vHi = aHi; - vec3 tp = position; - #ifdef USE_INSTANCING - tp = (instanceMatrix * vec4(position, 1.0)).xyz; - vN = normalize(mat3(instanceMatrix) * normal); - #else - vN = normal; - #endif - vW = tp; - gl_Position = projectionMatrix * modelViewMatrix * vec4(tp, 1.0); - }`,u=` - precision highp float; - varying vec3 vN, vW, vColor; varying vec2 vHi; - uniform float uHatch, uTime; - void main(){ - vec3 n = normalize(vN); - // One fixed brightness per axis: an isometric drawing doesn't need real - // lighting, it needs the three faces instantly distinguishable. - float f = n.y > 0.5 ? 1.0 : (abs(n.x) > 0.5 ? 0.74 : 0.56); - vec3 paper = vec3(0.867, 0.827, 0.706); - vec3 c = mix(paper, vColor, 0.62) * f; - - // Hatching: drawn only on upward faces, in world space, so the lines - // continue across neighbouring buildings. - if (n.y > 0.5 && uHatch > 0.5) { - float s = sin((vW.x + vW.z) * 3.2 + vW.y * 0.4); - c -= vec3(0.055) * smoothstep(0.72, 1.0, abs(s)); - } - c = mix(c, vec3(0.98, 0.94, 0.86), vHi.x * 0.42); - c = mix(paper * 0.94, c, mix(0.30, 1.0, 1.0 - vHi.y)); - gl_FragColor = vec4(c, 1.0); - }`,d=new nr(1,1,1),m=new Xn({uniforms:{uHatch:{value:1},uTime:{value:0}},vertexShader:c,fragmentShader:u});let f=W3,p=new z0(d,m,f);p.frustumCulled=!1;let x=new Yr(new Float32Array(f*3),3),_=new Yr(new Float32Array(f*2),2);d.setAttribute("aColor",x),d.setAttribute("aHi",_),r.add(p);const g=new BA(d),h=new SS({color:3814695,transparent:!0,opacity:.3});let v=[];for(let ve=0;ve<f;ve++){const I=new Qd(g,h);I.visible=!1,v.push(I),r.add(I)}const b=(ve,I)=>{let nt=Math.max(1,ve);for(;nt<I;)nt*=2;return nt};function y(ve){if(ve<=f)return;const I=b(f,ve),nt=p,Ke=v,N=new z0(d,m,I);N.frustumCulled=!1,N.count=0;const E=new Yr(new Float32Array(I*3),3),H=new Yr(new Float32Array(I*2),2);d.setAttribute("aColor",E),d.setAttribute("aHi",H);const K=[];for(let J=0;J<I;J++){const _e=new Qd(g,h);_e.visible=!1,K.push(_e),r.add(_e)}r.remove(nt);for(const J of Ke)r.remove(J);p=N,x=E,_=H,v=K,f=I}const A=()=>new Xn({uniforms:{},vertexShader:`varying vec3 vN; varying vec3 vW; - void main(){ vN = normal; vW = position; - gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); }`,fragmentShader:`precision mediump float; varying vec3 vN; varying vec3 vW; - void main(){ - float f = vN.y > 0.5 ? 1.0 : 0.78; - vec3 c = vec3(0.816, 0.776, 0.651) * f; - if (vN.y > 0.5) { - // A fine grid on the plot's top face, so it reads as surveyed land. - vec2 g = abs(fract(vW.xz * 0.5) - 0.5); - c -= vec3(0.03) * smoothstep(0.46, 0.5, max(g.x, g.y)); - } - gl_FragColor = vec4(c, 1.0); - }`}),M=[],T=new nr(1,1,1);for(let ve=0;ve<xv;ve++){const I=new Ri(T,A());I.visible=!1,M.push(I),r.add(I)}const S=3;let C=X3,R=new Float32Array(C*S*3),L=new Float32Array(C*S);const O=new Zn;O.setAttribute("position",new cn(R,3)),O.setAttribute("aA",new cn(L,1));const W=new IA(O,new Xn({uniforms:{uPx:{value:4}},vertexShader:`attribute float aA; varying float vA; uniform float uPx; - void main(){ vA = aA; - gl_PointSize = uPx; - gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); }`,fragmentShader:`precision mediump float; varying float vA; - void main(){ - float d = length(gl_PointCoord - 0.5) * 2.0; - if (d > 1.0 || vA <= 0.0) discard; - gl_FragColor = vec4(0.35, 0.22, 0.12, (1.0 - d) * vA); - }`,transparent:!0,depthWrite:!1}));W.frustumCulled=!1,r.add(W);let z=new Float32Array(C*6),D=new Float32Array(C*2);const B=new Zn;B.setAttribute("position",new cn(z,3)),B.setAttribute("aA",new cn(D,1));const k=new Qd(B,new Xn({vertexShader:`attribute float aA; varying float vA; - void main(){ vA = aA; gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); }`,fragmentShader:`precision mediump float; varying float vA; - void main(){ gl_FragColor = vec4(0.30, 0.19, 0.10, vA); }`,transparent:!0,depthWrite:!1}));k.frustumCulled=!1,r.add(k);function V(ve){ve<=C||(C=b(C,ve),R=new Float32Array(C*S*3),L=new Float32Array(C*S),z=new Float32Array(C*6),D=new Float32Array(C*2),O.setAttribute("position",new cn(R,3)),O.setAttribute("aA",new cn(L,1)),B.setAttribute("position",new cn(z,3)),B.setAttribute("aA",new cn(D,1)))}const X={yaw:Math.PI*.25,tYaw:Math.PI*.25,zoom:16,tZoom:16},ge=Math.atan(1/Math.SQRT2);let he=!1,Ae=0,q=0,fe=!0;const Ne={x:-1,y:-1,live:!1};t.addEventListener("pointerdown",ve=>{he=!0,q=0,Ae=ve.clientX,t.setPointerCapture(ve.pointerId),t.classList.add("drag")}),t.addEventListener("pointerup",ve=>{he=!1,t.classList.remove("drag"),t.releasePointerCapture(ve.pointerId)}),t.addEventListener("pointermove",ve=>{const I=t.getBoundingClientRect();Ne.x=ve.clientX-I.left,Ne.y=ve.clientY-I.top,Ne.live=!0,he&&(q+=Math.abs(ve.clientX-Ae),X.tYaw-=(ve.clientX-Ae)*.006,Ae=ve.clientX,fe=!1,Se(!1))}),t.addEventListener("pointerleave",()=>{Ne.live=!1});const ie=X.tZoom,xe=()=>a(Math.round(ie/X.tZoom*100)),oe=ve=>{X.tZoom=Math.max(4,Math.min(60,X.tZoom*ve)),xe()};t.addEventListener("wheel",ve=>{ve.preventDefault(),oe(1+Math.sign(ve.deltaY)*.11)},{passive:!1}),xe();let Ee=null,Oe=null,ke=null,xt="",re=null,Se=()=>{};t.addEventListener("click",()=>{q>5||i(Ee&&Oe!==Ee?Ee:null)});const Ce=Ho.map(ve=>new gt(lg(ve)||"#8a4b2a")),be=new Y,Ve=new Ft,Ge=new gt;let Pe=!0,we=performance.now();function We(ve){requestAnimationFrame(We);const I=Math.min(.05,(ve-we)/1e3);we=ve;const nt=e.clientWidth,Ke=e.clientHeight;if(!nt||!Ke)return;t.width!==Math.round(nt*s.getPixelRatio())&&s.setSize(nt,Ke,!1);const N=1-Math.pow(.002,I);CT(),y(qt.length),V(cs.length),fe&&(X.tYaw+=I*.12),X.yaw+=(X.tYaw-X.yaw)*N,X.zoom+=(X.tZoom-X.zoom)*N;const E=X.zoom*4,H=E*(nt/Ke);o.left=-H,o.right=H,o.top=E,o.bottom=-E,o.updateProjectionMatrix();const K=180;o.position.set(Math.cos(X.yaw)*Math.cos(ge)*K,Math.sin(ge)*K,Math.sin(X.yaw)*Math.cos(ge)*K),o.lookAt(0,6,0);for(let Z=0;Z<xv;Z++){const ae=M[Z],ce=va[Z];if(!ce||Z>=va.length){ae.visible=!1;continue}ce.x=ce.x===void 0?ce.tx+ce.tw/2:ce.x,ce.z=ce.z===void 0?ce.tz+ce.th/2:ce.z;const me=ce.tx+ce.tw/2,ye=ce.tz+ce.th/2;ce.x+=(me-ce.x)*N,ce.z+=(ye-ce.z)*N,ce.w+=(ce.tw-ce.w)*N,ce.h+=(ce.th-ce.h)*N,ae.visible=!0,ae.position.set(ce.x,-.25,ce.z),ae.scale.set(Math.max(.01,ce.w-qr*.45),.5,Math.max(.01,ce.h-qr*.45))}const J=Oe?new Set([Oe.path,...Oe.deps,...Oe.usedBy]):null,_e=Oe||Ee||ke,pe=qt.length;p.count=pe;for(let Z=0;Z<pe;Z++){const ae=qt[Z];ae.x!==ae.tx&&(ae.x+=(ae.tx-ae.x)*N*.7),ae.z!==ae.tz&&(ae.z+=(ae.tz-ae.z)*N*.7);const ce=ET(ae);ae.h=ae.h===void 0?ce:ae.h+(ce-ae.h)*(ae.dying?N*1.4:N*.6),ae.pulse=Math.max(0,(ae.pulse||0)-I*1.6),Ve.makeScale(Zi*.68,Math.max(.01,ae.h),Zi*.68),Ve.setPosition(ae.x,ae.h/2,ae.z),p.setMatrixAt(Z,Ve);const me=v[Z];me.visible=!0,me.scale.set(Zi*.68,Math.max(.01,ae.h),Zi*.68),me.position.set(ae.x,ae.h/2,ae.z);const ye=q3(ae);ye?Ge.copy(ye):Ge.copy(Ce[(ae.ki??0)%Ce.length]).offsetHSL(0,0,(Zb.get(ae.dir)-.5)*.17),x.array[Z*3]=Ge.r,x.array[Z*3+1]=Ge.g,x.array[Z*3+2]=Ge.b;const Fe=ae===_e?1:Math.min(.85,ae.pulse||0);let Xe=J?J.has(ae.path)?0:1:xt&&!ae.path.toLowerCase().includes(xt)?1:0;!J&&!xt&&re&&(Xe=ae.top===re?0:1),_.array[Z*2]+=(Fe-_.array[Z*2])*N,_.array[Z*2+1]+=(Xe-_.array[Z*2+1])*N}for(let Z=pe;Z<f;Z++)v[Z].visible=!1;p.instanceMatrix.needsUpdate=!0,x.needsUpdate=_.needsUpdate=!0;const te=cs.length;B.setDrawRange(0,te*2),O.setDrawRange(0,te*S);for(let Z=0;Z<te;Z++){const ae=cs[Z],ce=ae.from,me=ae.to,ye=Z*6;z[ye]=ce.x,z[ye+1]=ce.h,z[ye+2]=ce.z,z[ye+3]=me.x,z[ye+4]=me.h,z[ye+5]=me.z;const Fe=!J||J.has(ce.path)&&J.has(me.path),Xe=Oe&&(ce===Oe||me===Oe),et=Xe?.55:Fe?.1:.02;D[Z*2]+=(et-D[Z*2])*N,D[Z*2+1]=D[Z*2];for(let j=0;j<S;j++){const Me=Z*S+j,se=(ve/2600+(Z*.37+j/S))%1,Te=Math.sin(se*Math.PI)*Math.hypot(me.x-ce.x,me.z-ce.z)*.22;R[Me*3]=ce.x+(me.x-ce.x)*se,R[Me*3+1]=ce.h+(me.h-ce.h)*se+Te+1.2,R[Me*3+2]=ce.z+(me.z-ce.z)*se,L[Me]=(Pe?1:0)*(Xe?1:Fe?.45:.06)*Math.sin(se*Math.PI)}}if(B.getAttribute("position").needsUpdate=!0,B.getAttribute("aA").needsUpdate=!0,O.getAttribute("position").needsUpdate=!0,O.getAttribute("aA").needsUpdate=!0,W.material.uniforms.uPx.value=3.4*s.getPixelRatio(),Ne.live&&!he){let Z=null,ae=26*26;for(let ce=0;ce<pe;ce++){const me=qt[ce];if(me.dying||me.h<1)continue;be.set(me.x,me.h*.6,me.z).project(o);const ye=(be.x*.5+.5)*nt,Fe=(-be.y*.5+.5)*Ke,Xe=(ye-Ne.x)**2+(Fe-Ne.y)**2;Xe<ae&&(ae=Xe,Z=me,me.sx=ye,me.sy=Fe)}Ee=Z,t.style.cursor=he?"grabbing":Z?"pointer":"grab"}else Ne.live||(Ee=null);Ee?(n.style.display="block",n.style.left=Ee.sx+"px",n.style.top=Ee.sy+"px",n.innerHTML=`<b>${Ee.name}</b> · ${Ee.loc} lines<br>${Ee.dir} · referenced by ${Ee.usedBy.length}`):n.style.display="none",s.render(r,o)}return requestAnimationFrame(We),{setFlow:ve=>{Pe=ve},setHatch:ve=>{m.uniforms.uHatch.value=ve?1:0},setSpin:ve=>{fe=ve},spinning:()=>fe,onSpinChange:ve=>{Se=ve},dolly:oe,reset:()=>{X.tYaw=Math.PI*.25,X.tZoom=ie,xe()},setSel:ve=>{Oe=ve},setRailHover:ve=>{ke=ve},setQuery:ve=>{xt=ve},setFocusTop:ve=>{re=ve}}}const Dr=new Map;function _v(t){var n,i;const e=((n=t==null?void 0:t.properties)==null?void 0:n.path)||((i=t==null?void 0:t.properties)==null?void 0:i.filePath)||(t==null?void 0:t.uri);return typeof e=="string"&&e.length>0?e:null}function K3(t){var i,a,s;const e=((i=t==null?void 0:t.properties)==null?void 0:i.loc)??((a=t==null?void 0:t.properties)==null?void 0:a.lines)??((s=t==null?void 0:t.properties)==null?void 0:s.size),n=Number(e);return Number.isFinite(n)&&n>0?Math.min(4e3,Math.round(n)):40}function Z3(t,e){const i=String(t).replace(/\\/g,"/").split("/");return i[0]==="Code"&&i[1]?i[1].split("--")[0]:["Master","Roadmap","Auftrag","Planung","Codebasis","PLUG-Ordner","Aufräumen"].includes(i[0])?i[0]:e}function $3(t){var d;const e=t==null?void 0:t.workspace,n=(d=t==null?void 0:t.graph)==null?void 0:d.nodes;if(!(e!=null&&e.id)||!Array.isArray(n))return{workspaces:Dr.size,buildings:0,added:0};const i=Hb(e.name||e.canonicalPath||e.id)||e.id;for(const m of xn.slice())m.sim&&ao.unregister(m.id);const a=Array.isArray(t.graph.edges)?t.graph.edges:[],s=new Map(n.filter(m=>m&&typeof m.id=="string").map(m=>[m.id,m])),r=new Map;for(const m of a){const f=s.get(m==null?void 0:m.sourceId),p=s.get(m==null?void 0:m.targetId);if(!f||!p)continue;const x=_v(p);x&&(r.has(f.id)||r.set(f.id,[]),r.get(f.id).push(x))}const o=[];for(const m of n){const f=_v(m);f&&o.push({node:m,path:f})}o.sort((m,f)=>m.path.localeCompare(f.path));let c=0,u=0;for(const{node:m,path:f}of o){const p=Z3(f,i);Dr.has(p)||(ao.register({id:p,name:p}),Dr.set(p,new Set));const x=Dr.get(p);if(x.has(f))continue;x.add(f),u+=x.size;const _=m.properties||{};ao.grow(p,{path:f,loc:K3(m),deps:r.get(m.id)||[],note:m.type||"",agentColor:_.agentColor||_.readerColor||null,agentName:_.agentName||_.readerName||null,access:_.agentColor?"write":_.readerColor?"read":null}),c+=1}return c>0&&ao.event(String(e.id),`${c} indexed object${c===1?"":"s"} added across ${Dr.size} districts`),{workspaces:Dr.size,buildings:u,added:c,total:o.length,truncated:!1}}function Q3({snapshot:t,onSelectFile:e}){P.useEffect(()=>{t&&$3(t)},[t]);const[n,i]=P.useState(!0),[a,s]=P.useState("loc"),[r,o]=P.useState(!0),[c,u]=P.useState(!0),[d,m]=P.useState(!0),[f,p]=P.useState(100),[x,_]=P.useState(null),[g,h]=P.useState(""),[v,b]=P.useState(null),[y,A]=P.useState(!0),[,M]=P.useReducer(z=>z+1,0),T=P.useRef(null),S=P.useRef(null),C=P.useRef(null),R=P.useRef(null);P.useEffect(()=>{const z=Y3(T.current,S.current,C.current,{onSelect:D=>_(D),onZoom:D=>p(D)});if(!z){i(!1);return}R.current=z,z.onSpinChange(D=>m(D))},[]),P.useEffect(()=>{const z=TT(()=>M());return()=>{z()}},[]),P.useEffect(()=>{!x&&qt.length>0&&_(qt[0])},[qt.length,x]),P.useEffect(()=>{var z;(z=R.current)==null||z.setSel(x)},[x]),P.useEffect(()=>{var z;(z=R.current)==null||z.setQuery(g)},[g]),P.useEffect(()=>{var z;(z=R.current)==null||z.setFocusTop(v)},[v]),P.useEffect(()=>{const z=D=>{var k,V;const B=D.target;if(/^(INPUT|TEXTAREA)$/.test(B.tagName)){D.key==="Escape"&&B.blur();return}D.key==="Escape"?(_(null),b(null)):D.key==="="||D.key==="+"?(k=R.current)==null||k.dolly(.8474576271186441):D.key==="-"||D.key==="_"?(V=R.current)==null||V.dolly(1.18):(D.key==="e"||D.key==="E")&&A(X=>!X)};return addEventListener("keydown",z),()=>removeEventListener("keydown",z)},[]);const L=qt.reduce((z,D)=>z+D.loc,0),O=xn.reduce((z,D)=>z+D.events,0),W=(z,D)=>D.length?l.jsxs(l.Fragment,{children:[l.jsxs("h3",{children:[z+" ",l.jsx("span",{style:{color:"var(--faint)"},children:D.length})]}),D.map(B=>{const k=Eo[B];return k&&l.jsxs("div",{className:"dep","data-p":B,onClick:()=>_(k),children:[l.jsx("span",{className:"sw",style:{background:ST(k)}}),l.jsx("span",{children:B})]},B)})]}):null;return l.jsxs("div",{id:"app",className:x?void 0:"closed",children:[l.jsxs("aside",{children:[l.jsxs("div",{className:"hd",children:[l.jsx("h1",{children:"Code-Stadt"}),l.jsx("div",{className:"repo",id:"repo",children:"3D-Topologie · Repositories als Distrikte, Module als Gebäude"}),l.jsxs("div",{className:"kpis",children:[l.jsxs("div",{children:[l.jsx("b",{id:"k-ws",children:xn.length}),l.jsx("i",{children:"Workspaces"})]}),l.jsxs("div",{children:[l.jsx("b",{id:"k-bld",children:qt.length}),l.jsx("i",{children:"Dateien"})]}),l.jsxs("div",{children:[l.jsx("b",{id:"k-ev",children:O}),l.jsx("i",{children:"Ereignisse"})]})]})]}),l.jsx("div",{className:"q",children:l.jsx("input",{id:"q",type:"search",placeholder:"Modul oder Datei suchen …",spellCheck:!1,onChange:z=>h(z.target.value.trim().toLowerCase())})}),l.jsx("div",{className:"tree",id:"tree",children:xn.length?xn.map(z=>{const D=qt.filter(k=>k.dir===z.id),B=D.reduce((k,V)=>k+V.loc,0);return l.jsxs("div",{className:"ws"+(v===z.id?" on":"")+(z.dying?" dying":""),onClick:()=>b(k=>k===z.id?null:z.id),children:[l.jsxs("div",{className:"wsrow",children:[l.jsx("span",{className:"sw",style:{background:c0(z)}}),l.jsx("span",{className:"nm",children:z.name}),z.sim?l.jsx("span",{className:"tag",children:"sim"}):null,l.jsxs("span",{className:"lc",children:[D.length," Dat. · ",B," Z."]})]}),l.jsx("div",{className:"loadbar",children:l.jsx("i",{style:{width:Math.round(z.load*100)+"%",background:c0(z)}})})]},z.id)}):l.jsxs("div",{className:"empty",children:["Keine Workspaces für die 3D-Stadtansicht indiziert.",l.jsx("br",{}),l.jsx("br",{}),"Sobald Repositories indiziert sind, wachsen sie hier als Stadt."]})})]}),l.jsxs("div",{id:"stage",ref:S,children:[l.jsx("canvas",{id:"cv",ref:T}),l.jsx("div",{id:"tip",ref:C}),l.jsxs("div",{id:"crumb",children:["PLUGBRAIN / ",l.jsx("b",{id:"crumb-t",children:x?x.path.toUpperCase():v?v.toUpperCase():"STADT-ÜBERSICHT"})]}),y&&l.jsx("div",{id:"feed",children:yu.slice(0,9).map(z=>l.jsxs("div",{className:"fe",children:[l.jsx("span",{className:"ft",children:z.t.toLocaleTimeString("de-DE",{hour12:!1})}),l.jsx("span",{className:"fw",style:{color:"var(--accent)"},children:z.ws}),l.jsx("span",{className:"fm",children:z.msg})]},z.id))}),l.jsx("div",{id:"legend",children:l.jsx("div",{style:{color:"var(--faint)"},children:`Distrikt = Workspace · Gebäude = Datei · Höhe = ${a==="loc"?"Zeilenanzahl":"Abhängigkeiten"} · Licht = Trace-Aktivität`})}),l.jsxs("div",{id:"bar",children:[[["loc","Höhe = Zeilen"],["dep","Höhe = Abhängigkeiten"]].map(([z,D])=>l.jsx("button",{className:"tb"+(a===z?" on":""),"data-h":z,type:"button",onClick:()=>{MT(z),s(z)},children:D},z)),l.jsx("div",{className:"vsep"}),l.jsx("button",{className:"tb"+(r?" on":""),id:"t-flow",type:"button",onClick:()=>{o(z=>{var D;return(D=R.current)==null||D.setFlow(!z),!z})},children:"Fluss"}),l.jsx("button",{className:"tb"+(c?" on":""),id:"t-hatch",type:"button",onClick:()=>{u(z=>{var D;return(D=R.current)==null||D.setHatch(!z),!z})},children:"Schraffur"}),l.jsx("button",{className:"tb"+(d?" on":""),id:"t-spin",type:"button",onClick:()=>{m(z=>{var D;return(D=R.current)==null||D.setSpin(!z),!z})},children:"Orbit"}),l.jsx("button",{className:"tb"+(y?" on":""),id:"t-feed",type:"button",title:"Feed ein-/ausblenden (E)",onClick:()=>A(z=>!z),children:"Feed"}),l.jsx("div",{className:"vsep"}),l.jsx("button",{className:"tb",id:"zout",type:"button",title:"Herauszoomen",onClick:()=>{var z;return(z=R.current)==null?void 0:z.dolly(1.18)},children:"−"}),l.jsx("button",{className:"tb",id:"zlvl",type:"button",title:"Zoom zurücksetzen",onClick:()=>{var z;return(z=R.current)==null?void 0:z.reset()},children:f+"%"}),l.jsx("button",{className:"tb",id:"zin",type:"button",title:"Hineinzoomen",onClick:()=>{var z;return(z=R.current)==null?void 0:z.dolly(1/1.18)},children:"+"}),l.jsx("div",{className:"vsep"}),l.jsx("button",{className:"tb",id:"t-reset",type:"button",onClick:()=>{var z;(z=R.current)==null||z.reset(),_(null),b(null)},children:"Zurücksetzen"})]}),l.jsxs("div",{id:"gate",style:n?void 0:{display:"grid"},children:["WebGL ist auf diesem Gerät nicht verfügbar.",l.jsx("br",{}),"Die Workspace-Verwaltung bleibt vollständig nutzbar."]})]}),l.jsx("div",{id:"side",children:l.jsx("div",{id:"dt",children:x&&l.jsxs("div",{className:"dt",children:[l.jsx("div",{className:"kind",children:x.dir+"/"}),l.jsx("h2",{children:x.name}),x.note?l.jsx("div",{className:"note",children:x.note}):null,e&&l.jsx("button",{type:"button",className:"btn primary",style:{marginTop:"10px",marginBottom:"14px",width:"100%",padding:"8px 12px"},onClick:()=>e(x.path),children:"Datei in Quellansicht öffnen"}),l.jsxs("dl",{children:[l.jsx("dt",{children:"Size"}),l.jsx("dd",{children:x.loc}),l.jsx("dt",{children:"References"}),l.jsx("dd",{children:x.deps.length}),l.jsx("dt",{children:"Referenced by"}),l.jsx("dd",{children:x.usedBy.length}),l.jsx("dt",{children:"Share of total"}),l.jsx("dd",{children:L?(x.loc/L*100).toFixed(1)+"%":"—"})]}),W("References",x.deps),W("Referenced by",x.usedBy)]})})})]})}const yv={agent:"Agent",task:"Aufgabe",worker:"Worker",worktree:"Worktree",file:"Datei",artifact:"Artefakt",route:"Route"},bv={live:"Live-Ereignis",recovered:"wiederhergestelltes Ereignis","historical-import":"historischer Import"};function rh(t){const e=new Date(t);return Number.isNaN(e.getTime())?t:e.toLocaleString()}function lh(t){if(t.kind!=="worker")return null;switch(t.proof){case"process-started":return"Start im Trace beobachtet — keine Aussage über den aktuellen Prozesszustand.";case"finished":return"Abschluss im Trace beobachtet.";case"proof-unavailable":return"Kein beobachteter Prozessstart; dieser Worker wird nicht als laufend dargestellt.";default:return"Kein Prozessbeweis vorhanden."}}function J3(t){const e=t.id.slice(t.id.indexOf(":")+1);return t.kind==="agent"?{agentId:e}:t.kind==="task"?{taskId:e}:t.kind==="worker"?{workerId:e}:null}function Sv(t,e){var n;return((n=t.find(i=>i.id===e))==null?void 0:n.label)??e}function e2({mesh:t,workspaceId:e,onSelectFile:n,focusAgentId:i}){const[a,s]=P.useState(null),[r,o]=P.useState([]),[c,u]=P.useState(!1),[d,m]=P.useState(""),f=P.useMemo(()=>(t==null?void 0:t.nodes.find(x=>x.id===a))??null,[t,a]);return P.useEffect(()=>{if(!i)return;const x=`agent:${i}`;t!=null&&t.nodes.some(_=>_.id===x)&&s(x)},[i,t]),P.useEffect(()=>{a!==null&&f===null&&s(null)},[f,a]),P.useEffect(()=>{const x=f?J3(f):null;if(!e||x===null){o([]),m(""),u(!1);return}let _=!0;return u(!0),m(""),aT(e,{...x,limit:12}).then(g=>{_&&o(g)}).catch(()=>{_&&(o([]),m("Die Trace-Zeitleiste ist derzeit nicht verfügbar."))}).finally(()=>{_&&u(!1)}),()=>{_=!1}},[f,e]),e?t===null||t.workspaceId!==e?l.jsxs("section",{className:"mesh-trace mesh-trace--empty","aria-live":"polite",children:[l.jsx("h2",{children:"Agent Mesh"}),l.jsx("p",{children:"Die Core-Trace-Projektion ist für diesen Workspace noch nicht verfügbar."}),l.jsx("p",{className:"mesh-trace__muted",children:"Es werden weder Registry-Einträge noch historische Aktivitätszähler als Ersatz angezeigt."})]}):t.nodes.length===0&&t.edges.length===0?l.jsxs("section",{className:"mesh-trace mesh-trace--empty","aria-live":"polite",children:[l.jsx("h2",{children:"Agent Mesh"}),l.jsx("p",{children:"Für diesen Workspace wurde noch keine trace-gestützte Arbeit beobachtet."}),l.jsx("p",{className:"mesh-trace__muted",children:"Keine simulierten Agenten, keine Roster-Fallbacks und kein daraus abgeleiteter Prozessstatus."})]}):l.jsxs("section",{className:"mesh-trace","aria-label":"Trace-backed Agent Mesh",children:[l.jsxs("header",{className:"mesh-trace__header",children:[l.jsxs("div",{children:[l.jsxs("h2",{children:["Agent Mesh ",l.jsx("span",{children:"Trace-backed"})]}),l.jsx("p",{children:"Jeder Knoten und jede Kante stammt aus einem autoritätsbestätigten Trace-Ereignis."})]}),l.jsxs("dl",{className:"mesh-trace__totals",children:[l.jsxs("div",{children:[l.jsx("dt",{children:"Knoten"}),l.jsx("dd",{children:t.totals.nodes??t.nodes.length})]}),l.jsxs("div",{children:[l.jsx("dt",{children:"Kanten"}),l.jsx("dd",{children:t.totals.edges??t.edges.length})]}),l.jsxs("div",{children:[l.jsx("dt",{children:"ohne Startbeweis"}),l.jsx("dd",{children:t.unprovenWorkers.length})]})]})]}),t.unprovenWorkers.length>0&&l.jsxs("aside",{className:"mesh-trace__notice","aria-label":"Unproven workers",children:[l.jsx("strong",{children:"Unbelegte Worker werden nicht als laufend angezeigt."}),l.jsx("ul",{children:t.unprovenWorkers.map(x=>l.jsxs("li",{children:[l.jsx("code",{children:x.workerId}),x.taskId?l.jsxs(l.Fragment,{children:[" · Aufgabe ",l.jsx("code",{children:x.taskId})]}):""," — ",x.reason]},x.workerId))})]}),l.jsxs("div",{className:"mesh-trace__grid",children:[l.jsxs("section",{className:"mesh-trace__panel","aria-label":"Trace nodes",children:[l.jsxs("h3",{children:["Knoten ",l.jsx("span",{children:t.nodes.length})]}),l.jsx("ol",{className:"mesh-trace__nodes",children:t.nodes.map(x=>{const _=lh(x),g=x.id===a;return l.jsx("li",{children:l.jsxs("button",{type:"button",className:g?"mesh-trace__node is-selected":"mesh-trace__node",onClick:()=>s(x.id),"aria-pressed":g,children:[l.jsx("span",{className:"mesh-trace__kind",children:yv[x.kind]}),l.jsx("span",{className:"mesh-trace__label",title:x.label,children:x.label}),l.jsxs("span",{className:"mesh-trace__events",children:[x.eventCount," Ereignis",x.eventCount===1?"":"se"]}),l.jsx("span",{className:"mesh-trace__provenance",title:"Ereignis-Provenienz, nicht aktueller Prozessstatus",children:bv[x.provenance]}),_&&l.jsx("span",{className:"mesh-trace__proof",children:_})]})},x.id)})})]}),l.jsxs("section",{className:"mesh-trace__panel","aria-label":"Trace edges",children:[l.jsxs("h3",{children:["Kanten ",l.jsx("span",{children:t.edges.length})]}),l.jsx("ol",{className:"mesh-trace__edges",children:t.edges.map(x=>l.jsxs("li",{children:[l.jsx("span",{className:"mesh-trace__edge-kind",children:x.kind}),l.jsx("span",{title:x.from,children:Sv(t.nodes,x.from)}),l.jsx("span",{"aria-hidden":"true",children:"→"}),l.jsx("span",{title:x.to,children:Sv(t.nodes,x.to)}),l.jsxs("small",{children:[x.count," Ereignis",x.count===1?"":"se"," · Belege: ",x.evidence.join(", ")]})]},x.id))})]})]}),f&&l.jsxs("aside",{className:"mesh-trace__detail","aria-label":"Details for "+f.label,children:[l.jsxs("div",{className:"mesh-trace__detail-head",children:[l.jsxs("div",{children:[l.jsx("span",{className:"mesh-trace__kind",children:yv[f.kind]}),l.jsx("h3",{children:f.label})]}),l.jsx("button",{type:"button",onClick:()=>s(null),"aria-label":"Detailansicht schließen",children:"×"})]}),l.jsxs("dl",{children:[l.jsxs("div",{children:[l.jsx("dt",{children:"Erstmals"}),l.jsx("dd",{children:rh(f.firstSeen)})]}),l.jsxs("div",{children:[l.jsx("dt",{children:"Zuletzt"}),l.jsx("dd",{children:rh(f.lastSeen)})]}),l.jsxs("div",{children:[l.jsx("dt",{children:"Provenienz"}),l.jsx("dd",{children:bv[f.provenance]})]}),lh(f)&&l.jsxs("div",{children:[l.jsx("dt",{children:"Worker-Beweis"}),l.jsx("dd",{children:lh(f)})]}),Object.entries(f.detail).map(([x,_])=>l.jsxs("div",{children:[l.jsx("dt",{children:x}),l.jsx("dd",{children:_??"—"})]},x))]}),f.kind==="file"&&n&&l.jsx("button",{type:"button",className:"mesh-trace__source",onClick:()=>n(f.label),children:"Datei im Source-View öffnen"}),l.jsxs("section",{className:"mesh-trace__timeline","aria-label":"Trace timeline",children:[l.jsx("h4",{children:"Beobachtete Ereignisse"}),c&&l.jsx("p",{children:"Lade Trace-Ereignisse …"}),d&&l.jsx("p",{role:"status",children:d}),!c&&!d&&r.length===0&&l.jsx("p",{children:"Für diesen Knotentyp gibt es keine gefilterte Zeitleiste."}),l.jsx("ol",{children:r.map(x=>l.jsxs("li",{children:[l.jsx("code",{children:x.type})," ",l.jsx("time",{dateTime:x.occurredAt,children:rh(x.occurredAt)}),l.jsx("span",{children:x.summary})]},x.eventId))})]})]})]}):l.jsxs("section",{className:"mesh-trace mesh-trace--empty","aria-live":"polite",children:[l.jsx("h2",{children:"Agent Mesh"}),l.jsx("p",{children:"Wähle einen registrierten Workspace. Ohne Workspace kann keine Trace-Projektion behauptet werden."})]})}function t2({tasks:t,depth:e}){return l.jsxs("div",{className:"queue",children:[l.jsx("div",{className:"queue__intro",style:{padding:"12px 16px",background:"var(--panel)",borderBottom:"1px solid var(--line)",fontSize:"13px",color:"var(--muted)"},children:"Warteschlange für Workspace-Aufgaben — freie Agenten beanspruchen Aufgaben selbstständig (First-In-First-Out)."}),t.length===0?l.jsxs("div",{className:"brain-empty",style:{padding:"48px 24px",textAlign:"center",color:"var(--muted)"},children:[l.jsx("p",{style:{margin:0,fontSize:"14px"},children:"Keine Aufgaben in der Warteschlange."}),l.jsx("small",{style:{color:"var(--faint)",display:"block",marginTop:"6px"},children:"Nichts wartet und nichts wird erfunden. Neue Aufgaben erscheinen hier automatisch."})]}):l.jsxs(l.Fragment,{children:[l.jsxs("div",{className:"queue__figures",children:[l.jsx(eu,{value:e,label:"WARTEND",tone:e>8?"hot":void 0}),l.jsx(eu,{value:t.filter(n=>n.state==="claimed").length,label:"IN ARBEIT"}),l.jsx(eu,{value:t.filter(n=>n.state==="delivered").length,label:"GELIEFERT"}),l.jsx(eu,{value:t.filter(n=>n.state==="claimed"&&n.stale).length,label:"STILL",tone:t.some(n=>n.stale)?"hot":void 0})]}),l.jsx("ol",{className:"queue__list",children:t.map(n=>l.jsxs("li",{className:`queue__row queue__row--${n.state}`,children:[l.jsx("span",{className:"queue__state",children:n2[n.state]??n.state}),l.jsx("span",{className:"queue__title",title:n.title,children:n.title}),l.jsx("span",{className:"queue__holder",children:n.claimed_by?`Wird bearbeitet von Agent: ${n.claimed_by}`:n.addressed_to?`Zugewiesen an: Agent ${n.addressed_to}`:"Frei für den nächsten verfügbaren Agenten"}),n.stale&&l.jsx("span",{className:"queue__stale",title:"Keine Regung seit dem Claim. PlugBrain meldet das nur — es beendet keinen Claim.",children:"still"}),n.delivered_path&&l.jsxs("span",{className:"queue__path",title:`Geliefert nach: ${n.delivered_path}`,children:["Ergebnis: ",n.delivered_path]})]},n.id))})]})]})}const n2={pending:"WARTET",claimed:"IN ARBEIT",delivered:"GELIEFERT",cancelled:"ABGEBROCHEN"};function eu({value:t,label:e,tone:n}){return l.jsxs("div",{className:`queue__figure${n==="hot"?" queue__figure--hot":""}`,children:[l.jsx("strong",{children:t}),l.jsx("span",{children:e})]})}const i2=8e3;function a2(t,e=i2){return new Promise((n,i)=>{const a=globalThis.setTimeout(()=>{i(new Error(`Brain-Dateiabruf hat nach ${e/1e3} Sekunden nicht geantwortet. Bitte nach dem Indexlauf erneut versuchen.`))},e);t.then(s=>{globalThis.clearTimeout(a),n(s)},s=>{globalThis.clearTimeout(a),i(s)})})}function Mv({workspaceId:t,path:e,highlightLine:n,onClose:i,onNavigateFile:a}){const[s,r]=P.useState(!0),[o,c]=P.useState(null),[u,d]=P.useState(null),[m,f]=P.useState(null),[p,x]=P.useState([]),_=P.useRef(null);if(P.useEffect(()=>{let b=!0;return r(!0),c(null),d(null),f(null),x([]),a2(cT(t,e)).then(y=>{b&&(c(y),r(!1))}).catch(y=>{b&&(c({ok:!1,path:e,content:"",bytes:0,lang:null,error:String((y==null?void 0:y.message)??y)}),r(!1))}),Kb(t).then(y=>{b&&d(y)}).catch(()=>{}),sT(t,e).then(y=>{b&&f(y)}).catch(()=>{}),dT(t,e).then(y=>{b&&x(y)}).catch(()=>{}),()=>{b=!1}},[t,e]),P.useEffect(()=>{!s&&_.current&&_.current.scrollIntoView({behavior:"smooth",block:"center"})},[s,n]),s)return l.jsxs("div",{className:"source-container source-container--loading",children:[l.jsx("div",{className:"source-spinner"}),l.jsxs("p",{children:["Lade Dateiinhalt aus dem Brain (",e,") …"]})]});if(!o||!o.ok)return l.jsxs("div",{className:"source-container source-container--error",role:"alert",children:[l.jsxs("div",{className:"source-header",children:[l.jsx("span",{className:"source-header__path mono",children:e}),i&&l.jsx("button",{type:"button",className:"source-close-btn",onClick:i,title:"Schließen",children:"✕"})]}),l.jsxs("div",{className:"source-error-box",children:[l.jsx("div",{className:"source-error-icon","aria-hidden":"true",children:"!"}),l.jsx("h3",{children:"Fehler beim Laden der Datei"}),l.jsx("p",{className:"source-error-msg",children:(o==null?void 0:o.error)||"Die Datei existiert nicht im Workspace oder der Pfad ist ungültig."}),l.jsxs("div",{className:"source-error-details mono",children:["Workspace: ",t,l.jsx("br",{}),"Pfad: ",e]})]})]});const g=o.content.split(/\r?\n/),h=g.length,v=u!=null&&u.head?u.head.slice(0,8):null;return l.jsxs("div",{className:"source-container",children:[l.jsxs("div",{className:"source-header",children:[l.jsxs("div",{className:"source-header__meta",children:[l.jsx("span",{className:"source-header__path mono",title:o.path,children:o.path}),o.lang&&l.jsx("span",{className:"source-badge source-badge--lang",children:o.lang}),l.jsxs("span",{className:"source-badge source-badge--info",children:[h," Zeilen · ",o.bytes," B"]}),v&&l.jsxs("span",{className:"source-badge source-badge--git",title:`Git Revision: ${u==null?void 0:u.head}`,children:["git: ",v," (",(u==null?void 0:u.branch)??"detached",")"]}),(m==null?void 0:m.owner)&&l.jsxs("span",{className:"source-badge source-badge--agent",style:{borderColor:m.owner.color},title:`Zuletzt geändert durch ${m.owner.name} (${m.owner.at})`,children:[l.jsx("i",{style:{background:m.owner.color}}),m.owner.name]})]}),l.jsxs("div",{className:"source-header__actions",children:[n&&l.jsxs("span",{className:"source-badge source-badge--highlight",children:["Fokus: Zeile ",n]}),i&&l.jsx("button",{type:"button",className:"source-close-btn",onClick:i,title:"Quellansicht schließen",children:"✕"})]})]}),l.jsxs("div",{className:"source-body",children:[l.jsx("div",{className:"source-code-view",children:l.jsx("table",{className:"source-table",children:l.jsx("tbody",{children:g.map((b,y)=>{const A=y+1,M=n===A;return l.jsxs("tr",{ref:M?_:void 0,className:`source-line-row ${M?"source-line-row--highlight":""}`,children:[l.jsx("td",{className:"source-line-num mono","data-line":A,children:A}),l.jsx("td",{className:"source-line-code mono",children:l.jsx("pre",{children:b||" "})})]},A)})})})}),p.length>0&&l.jsxs("div",{className:"source-backlinks",style:{padding:"12px 16px",borderTop:"1px solid var(--line)",background:"rgba(255,255,255,0.02)"},children:[l.jsxs("div",{style:{fontSize:"12px",fontWeight:600,color:"var(--accent)",marginBottom:"6px"},children:["← Rückverweise / Backlinks (",p.length,")"]}),l.jsx("div",{style:{display:"flex",flexWrap:"wrap",gap:"8px"},children:p.map((b,y)=>l.jsxs("div",{className:"search-hit-card",style:{padding:"6px 10px",fontSize:"11px",cursor:"pointer"},onClick:()=>a==null?void 0:a(b.path,b.line),title:`Zeile ${b.line} in ${b.path}`,children:[l.jsx("span",{className:"mono",style:{color:"var(--accent)"},children:b.path}),l.jsxs("span",{style:{color:"var(--faint)",marginLeft:"6px"},children:[":",b.line]}),b.alias&&l.jsxs("span",{style:{marginLeft:"4px",fontStyle:"italic"},children:["(",b.alias,")"]})]},y))})]})]})]})}function Mt({path:t,label:e}){return l.jsxs("svg",{className:"pb-icon",viewBox:"0 0 16 16",fill:"none",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":e?void 0:!0,role:e?"img":void 0,children:[e&&l.jsx("title",{children:e}),l.jsx("path",{d:t})]})}const vt={search:"M7 11.5a4.5 4.5 0 1 0 0-9 4.5 4.5 0 0 0 0 9ZM10.5 10.5 14 14",fit:"M2.5 6V2.5H6M10 2.5h3.5V6M13.5 10v3.5H10M6 13.5H2.5V10",minus:"M3.5 8h9",plus:"M8 3.5v9M3.5 8h9",play:"M5 3.5v9l7-4.5-7-4.5Z",pause:"M5.5 3.5v9M10.5 3.5v9",close:"M4 4l8 8M12 4l-8 8",open:"M9 3h4v4M13 3 7.5 8.5M11.5 9.5V13H3V4.5h3.5",centre:"M8 1.5v3M8 11.5v3M1.5 8h3M11.5 8h3M8 10a2 2 0 1 0 0-4 2 2 0 0 0 0 4Z",chevron:"M4.5 6.5 8 10l3.5-3.5",keyboard:"M2 4.5h12v7H2zM4.5 7h.01M7 7h.01M9.5 7h.01M12 7h.01M5 9.5h6",folder:"M2 4.5h4l1.5 1.5H14v6.5H2z",refresh:"M13 8a5 5 0 1 1-1.5-3.5M13 2.5v3h-3",burger:"M2 4h12M2 8h12M2 12h12",pencil:"M11 2.5 13.5 5 5 13.5H2.5V11L11 2.5Z",notes:"M3 2.5h10v11H3zM5 5.5h6M5 8.5h6M5 11.5h4",gear:"M6.7 1.5h2.6l.4 1.8a5.6 5.6 0 0 1 1.4.8l1.7-.8 1.8 1.8-.8 1.7c.3.4.6.9.8 1.4l1.8.4v2.6l-1.8.4a5.6 5.6 0 0 1-.8 1.4l.8 1.7-1.8 1.8-1.7-.8a5.6 5.6 0 0 1-1.4.8l-.4 1.8H6.7l-.4-1.8a5.6 5.6 0 0 1-1.4-.8l-1.7.8-1.8-1.8.8-1.7a5.6 5.6 0 0 1-.8-1.4l-1.8-.4V6.7l1.8-.4a5.6 5.6 0 0 1 .8-1.4l-.8-1.7 1.8-1.8 1.7.8a5.6 5.6 0 0 1 1.4-.8l.4-1.8ZM8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5Z",info:"M8 2a6 6 0 1 0 0 12 6 6 0 0 0 0-12ZM8 7.5v3M8 5v.01",file:"M3.5 1.5h6L13 5v9.5H3.5zM9.5 1.5V5H13",code:"M5.5 4.5 2 8l3.5 3.5M10.5 4.5 14 8l-3.5 3.5"};function s2(t){return!t||typeof t!="string"?"":t.replace(/\\/g,"/")}function r2(t){const e={name:"",path:"",isDir:!0,children:new Map};if(!Array.isArray(t))return e;for(const n of t){if(!n||typeof n.path!="string")continue;const i=n.path.split(/[\\/]/).filter(Boolean);if(i.length===0)continue;let a=e;for(let s=0;s<i.length;s++){const r=i[s],o=s===i.length-1,c=i.slice(0,s+1).join("/");if(o)a.children.set(r,{name:r,path:c,isDir:!1,children:new Map,file:{...n,path:c}});else{let u=a.children.get(r);u||(u={name:r,path:c,isDir:!0,children:new Map},a.children.set(r,u)),a=u}}}return e}function l2({workspaceName:t,files:e,activePath:n,onSelectFile:i}){const[a,s]=P.useState(""),[r,o]=P.useState(new Set),c=P.useMemo(()=>r2(e),[e]),u=P.useMemo(()=>{const g=[];function h(v){v.isDir&&v.path&&g.push(v.path);for(const b of v.children.values())b.isDir&&h(b)}return h(c),g},[c]),d=g=>{o(h=>{const v=new Set(h);return v.has(g)?v.delete(g):v.add(g),v})},m=()=>o(new Set),f=()=>o(new Set(u)),p=s2(n);function x(g,h){if(!h)return!0;const v=h.toLowerCase();if(!g.isDir)return g.name.toLowerCase().includes(v)||g.path.toLowerCase().includes(v);if(g.name&&g.name.toLowerCase().includes(v))return!0;for(const b of g.children.values())if(x(b,h))return!0;return!1}const _=(g,h=0)=>{var b,y,A;if(g.isDir){const M=r.has(g.path),T=Array.from(g.children.values()).sort((C,R)=>C.isDir!==R.isDir?C.isDir?-1:1:C.name.localeCompare(R.name)),S=a?T.filter(C=>x(C,a)):T;return a&&S.length===0&&(!g.name||!g.name.toLowerCase().includes(a.toLowerCase()))?null:l.jsxs("div",{className:"tree-dir-group",children:[g.name&&l.jsxs("div",{className:`tree-item tree-item--dir ${h===0?"tree-item--root":""}`,style:{paddingLeft:`${h*14+10}px`},onClick:()=>d(g.path),role:"treeitem","aria-expanded":!M,children:[l.jsx("span",{className:"tree-chevron",children:M?"▶":"▼"}),l.jsx("span",{className:"tree-icon",children:l.jsx(Mt,{path:vt.folder})}),l.jsx("span",{className:"tree-label",children:g.name}),l.jsx("span",{className:"tree-badge tree-badge--count",children:g.children.size})]}),(!M||a)&&l.jsx("div",{className:"tree-dir-children",children:S.map(C=>_(C,g.name?h+1:h))})]},g.path||"root")}const v=p!==""&&p===g.path;return a&&!g.path.toLowerCase().includes(a.toLowerCase())?null:l.jsxs("div",{className:`tree-item tree-item--file ${v?"tree-item--active":""}`,style:{paddingLeft:`${h*14+22}px`},onClick:()=>i(g.path),title:g.path,role:"treeitem",children:[l.jsx("span",{className:"tree-dot","aria-hidden":"true"}),l.jsx("span",{className:"tree-label mono",children:g.name}),((b=g.file)==null?void 0:b.lang)&&l.jsx("span",{className:"tree-badge tree-badge--lang",children:g.file.lang}),((y=g.file)==null?void 0:y.loc)!==void 0&&l.jsxs("span",{className:"tree-badge tree-badge--loc",children:[g.file.loc," L"]}),((A=g.file)==null?void 0:A.agent)&&l.jsx("span",{className:"tree-agent-dot",style:{background:g.file.agent.color},title:`Owner: ${g.file.agent.name}`})]},g.path)};return l.jsxs("div",{className:"explorer-view",children:[l.jsxs("div",{className:"explorer-header",children:[l.jsxs("div",{className:"explorer-title",children:[l.jsx(Mt,{path:vt.folder}),l.jsxs("strong",{children:[t||"Workspace"," Explorer"]})]}),l.jsx("div",{className:"explorer-stats",children:l.jsxs("span",{children:[e.length," Dateien aus Brain"]})})]}),l.jsxs("div",{className:"explorer-search",children:[l.jsx("input",{type:"text",placeholder:"Dateibaum filtern …",value:a,onChange:g=>s(g.target.value),className:"explorer-search__input"}),a&&l.jsx("button",{type:"button",className:"explorer-search__clear",onClick:()=>s(""),children:"✕"})]}),u.length>0&&l.jsxs("div",{className:"tree-actions",children:[l.jsx("button",{type:"button",className:"tree-action-btn",onClick:m,title:"Alle Ordner aufklappen",children:"Alle aufklappen"}),l.jsx("button",{type:"button",className:"tree-action-btn",onClick:f,title:"Alle Ordner einklappen",children:"Alle einklappen"})]}),l.jsx("div",{className:"explorer-tree",children:e.length===0?l.jsx("div",{className:"explorer-empty",children:"Keine Dateien im Snapshot vorhanden."}):_(c)})]})}function o2({workspaceId:t,onSelectHit:e}){const[n,i]=P.useState(()=>{const M=new URLSearchParams(window.location.search).get("mode");return M==="code"||M==="notes"||M==="prose"?M:"all"}),[a,s]=P.useState(()=>new URLSearchParams(window.location.search).get("q")||""),[r,o]=P.useState([]),[c,u]=P.useState([]),[d,m]=P.useState(null),[f,p]=P.useState(!1),[x,_]=P.useState(""),[g,h]=P.useState(""),[v,b]=P.useState(""),y=()=>{let M=`# Suchtreffer-Kontext: "${g}" -Workspace: ${t} - -`;if(n==="notes"){M+=`Gefundene Notizen (${c.length}): -`;for(const T of c)M+=`- **${T.title}** (\`${T.path}\`)${T.stand?` [${T.stand}]`:""} -`}else{M+=`Gefundene Treffer (${r.length}): -`;for(const T of r)M+=`- **${T.name}** (${T.kind}) — \`${T.path}${T.line?`:${T.line}`:""}\` -`,T.snippet&&(M+=` > ${T.snippet.replace(/\n/g," ")} -`)}navigator.clipboard.writeText(M).then(()=>{b("Suchergebnisse als Kontext kopiert (bereit für ChatGPT, Claude & Co.)"),setTimeout(()=>b(""),3e3)}).catch(()=>{b("Fehler beim Kopieren in die Zwischenablage")})},A=async(M,T,S)=>{M&&M.preventDefault();const C=S??n,R=(T??a).trim();if(!R)return;p(!0),_(""),m(null);const L=performance.now();try{if(C==="all"){const[O,W]=await Promise.allSettled([op(t,R),cp(t,R)]),z=[];if(O.status==="fulfilled")for(const D of O.value)z.push({id:`code-${D.path}-${D.name}-${D.line??0}`,type:"code",name:D.name,kind:D.kind,path:D.path,line:D.line});if(W.status==="fulfilled")for(const D of W.value.hits||[])z.push({id:`note-${D.path}-${D.line??0}`,type:"note",name:D.title,kind:"Notiz",path:D.path,line:D.line,snippet:D.snippet});o(z),u([])}else if(C==="code"){const O=await op(t,R);o(O.map((W,z)=>({id:`code-${W.path}-${W.name}-${W.line??z}`,type:"code",name:W.name,kind:W.kind,path:W.path,line:W.line}))),u([])}else if(C==="prose"){const O=await cp(t,R);o((O.hits||[]).map((W,z)=>({id:`note-${W.path}-${W.line??z}`,type:"note",name:W.title,kind:"Notiz",path:W.path,line:W.line,snippet:W.snippet}))),u([])}else{const O=await fT(t,R);u(O.notes||[]),o([])}m(Math.round(performance.now()-L)),h(R)}catch(O){_((O==null?void 0:O.message)||`Fehler bei der Suche (${C})`),o([]),u([])}finally{p(!1)}};return P.useEffect(()=>{const M=new URLSearchParams(window.location.search).get("q");M&&t&&A(void 0,M,n)},[t]),l.jsxs("div",{className:"search-view",children:[l.jsxs("div",{className:"search-view__header",children:[l.jsxs("div",{className:"search-view__title",children:[l.jsx(Mt,{path:vt.search}),l.jsx("strong",{children:n==="all"?"Suche über Notizen & Code":n==="code"?"Code- & Symbolsuche":n==="prose"?"Notiz-Volltextsuche":"Notizen- & Property-Filter"}),l.jsx("span",{className:"search-view__endpoint mono",children:n==="all"?"Notizen + Code":n==="code"?"/api/agent/search":n==="prose"?"/api/notes/search":"/api/notes/query"})]}),l.jsxs("div",{style:{display:"flex",gap:"6px",marginTop:"8px"},children:[l.jsx("button",{type:"button",className:`tb ${n==="all"?"on":""}`,onClick:()=>{i("all"),a.trim()&&A(void 0,a,"all")},children:"Alle"}),l.jsx("button",{type:"button",className:`tb ${n==="code"?"on":""}`,onClick:()=>{i("code"),a.trim()&&A(void 0,a,"code")},children:"Code & Symbole"}),l.jsx("button",{type:"button",className:`tb ${n==="prose"?"on":""}`,onClick:()=>{i("prose"),a.trim()&&A(void 0,a,"prose")},children:"Notizen-Volltext"}),l.jsx("button",{type:"button",className:`tb ${n==="notes"?"on":""}`,onClick:()=>{i("notes");const M=a.trim()||"typ=gate UND stand=offen";s(M),A(void 0,M,"notes")},children:"Properties (Bases)"})]})]}),l.jsx("form",{className:"search-form",onSubmit:A,style:{marginTop:"10px"},children:l.jsxs("div",{className:"search-input-group",children:[l.jsx("input",{type:"search",className:"search-input",placeholder:n==="all"?"Symbol, Datei oder Notiztext (z. B. workspaceIdFor, Brain) …":n==="code"?"Symbol, Variable, Klasse, Datei (z. B. authKey) …":n==="prose"?"Satz oder Stichwörter aus dem Notiztext …":"Bases-Filter: typ=gate UND stand=offen …",value:a,onChange:M=>s(M.target.value),autoFocus:!0}),l.jsx("button",{type:"submit",className:"search-submit-btn pb-button pb-button--primary",disabled:f||!a.trim(),children:f?"Suche …":"Suchen"})]})}),x&&l.jsx("div",{className:"search-error-alert",role:"alert",children:x}),l.jsxs("div",{className:"search-results",children:[g&&l.jsxs("div",{className:"search-results-summary",style:{display:"flex",alignItems:"center",justifyContent:"space-between",flexWrap:"wrap",gap:"8px"},children:[l.jsxs("div",{children:[n==="notes"?c.length===0?`Keine Notizen entsprechen dem Filter "${g}"`:`${c.length} Notiz(en) gefunden für "${g}":`:r.length===0?`Keine Treffer für "${g}" im Brain-Index`:`${r.length} Treffer für "${g}":`,d!==null&&l.jsxs("span",{className:"search-results-time mono",style:{marginLeft:"8px",opacity:.7},children:[d," ms"]})]}),(n==="notes"&&c.length>0||n!=="notes"&&r.length>0)&&l.jsx("button",{type:"button",className:"pb-button pb-button--secondary",onClick:()=>y(),title:"Alle Suchtreffer als Markdown-Kontext kopieren",style:{fontSize:"11px",padding:"4px 10px"},children:"Als Kontext kopieren"})]}),v&&l.jsx("div",{style:{margin:"8px 0",padding:"6px 12px",background:"var(--accent-soft)",border:"1px solid var(--pos)",borderRadius:"var(--r)",fontSize:"12px",color:"var(--ink)",fontFamily:"var(--mono)"},children:v}),n==="notes"?l.jsx("div",{className:"search-hits-list",children:c.map(M=>l.jsxs("div",{className:"search-hit-card",onClick:()=>e(M.path),children:[l.jsxs("div",{className:"search-hit-card__head",children:[l.jsx("span",{className:"search-hit-name",children:M.title}),M.typ&&l.jsx("span",{className:"search-hit-kind search-hit-kind--class",children:M.typ}),M.stand&&l.jsx("span",{className:"source-badge",style:{fontSize:"11px",marginLeft:"6px"},children:M.stand})]}),l.jsx("div",{className:"search-hit-path mono",title:M.path,style:{marginTop:"4px"},children:M.path}),(M.inLinks!==void 0||M.outLinks!==void 0)&&l.jsxs("div",{style:{fontSize:"11px",color:"var(--faint)",marginTop:"4px"},children:["Verlinkungen: → ",M.outLinks??0," ausgehend · ← ",M.inLinks??0," Rückverweise"]})]},M.path))}):l.jsx("div",{className:"search-hits-list",children:r.map(M=>l.jsxs("div",{className:"search-hit-card",onClick:()=>e(M.path,M.line),children:[l.jsxs("div",{className:"search-hit-card__head",children:[l.jsx("span",{className:"search-hit-name mono",children:M.name}),l.jsx("span",{className:`search-hit-kind search-hit-kind--${M.kind.toLowerCase()}`,children:M.kind}),M.line!==null&&l.jsxs("span",{className:"search-hit-line mono",children:["Zeile ",M.line]})]}),M.snippet&&l.jsx("div",{className:"search-hit-snippet",children:M.snippet}),l.jsx("div",{className:"search-hit-path mono",title:M.path,style:{marginTop:"4px"},children:M.path})]},M.id))})]})]})}function c2({workspaceId:t,focus:e,onOpenNote:n}){const[i,a]=P.useState(1),[s,r]=P.useState(120),[o,c]=P.useState(null),[u,d]=P.useState(0),[m,f]=P.useState("");P.useEffect(()=>{let v=!0;return f(""),pT(t,{focus:e,depth:i,limit:s}).then(b=>{v&&(c(b),d(0))}).catch(b=>{v&&(c(null),f(b instanceof Error?b.message:String(b)))}),()=>{v=!1}},[t,e,i,s]);const p=P.useMemo(()=>((o==null?void 0:o.nodes)??[]).slice().sort((v,b)=>Number(b.focus)-Number(v.focus)||b.inLinks+b.outLinks-(v.inLinks+v.outLinks)||v.path.localeCompare(b.path)),[o]),x=p[u]??null,_=P.useMemo(()=>!x||!o?[]:o.edges.filter(v=>v.source===x.id||v.target===x.id).map(v=>p.find(b=>b.id===(v.source===x.id?v.target:v.source))).filter(v=>v!==void 0),[x,o,p]),g=v=>d(b=>Math.max(0,Math.min(p.length-1,b+v))),h=v=>{p.length&&(v.key==="ArrowDown"||v.key==="ArrowRight"?(v.preventDefault(),g(1)):v.key==="ArrowUp"||v.key==="ArrowLeft"?(v.preventDefault(),g(-1)):v.key==="Home"?(v.preventDefault(),d(0)):v.key==="End"?(v.preventDefault(),d(p.length-1)):v.key==="Enter"&&x&&(v.preventDefault(),n(x.path)))};return l.jsxs("section",{className:"knowledge-graph","aria-label":"Wissensgraph",onKeyDown:h,tabIndex:0,children:[l.jsxs("header",{className:"knowledge-graph__head",children:[l.jsxs("div",{children:[l.jsx("h2",{children:"Wissensgraph"}),l.jsx("p",{children:"Aus den indizierten Wiki-Links; keine berechneten Ersatzknoten."})]}),l.jsxs("label",{children:["Tiefe ",l.jsxs("select",{value:i,onChange:v=>a(Number(v.target.value)),children:[l.jsx("option",{value:0,children:"Nur Fokus"}),l.jsx("option",{value:1,children:"1 Hop"}),l.jsx("option",{value:2,children:"2 Hops"}),l.jsx("option",{value:3,children:"3 Hops"})]})]}),l.jsxs("label",{children:["LOD ",l.jsxs("select",{value:s,onChange:v=>r(Number(v.target.value)),children:[l.jsx("option",{value:60,children:"Kompakt (60)"}),l.jsx("option",{value:120,children:"Standard (120)"}),l.jsx("option",{value:300,children:"Detail (300)"})]})]})]}),m&&l.jsx("p",{className:"notes-conflict",role:"alert",children:m}),!m&&o&&l.jsxs("p",{className:"knowledge-graph__coverage",role:"status",children:[o.coverage.selected," von ",o.coverage.notesInScope," Notizen · ",o.edges.length," Verweise",o.coverage.truncated?" · für diese LOD begrenzt":""]}),l.jsxs("div",{className:"knowledge-graph__body",children:[l.jsxs("ol",{className:"knowledge-graph__list","aria-label":"Graphknoten, Pfeiltasten wählen, Eingabe öffnen",children:[p.map((v,b)=>{var y;return l.jsx("li",{children:l.jsxs("button",{type:"button",className:b===u?"on":"",onFocus:()=>d(b),onClick:()=>n(v.path),children:[l.jsx("i",{style:{background:(y=o==null?void 0:o.groups.find(A=>A.name===v.group))==null?void 0:y.color}}),l.jsxs("span",{children:[l.jsx("strong",{children:v.title}),l.jsx("small",{children:v.path})]}),l.jsx("b",{children:v.inLinks+v.outLinks})]})},v.id)}),!p.length&&l.jsx("li",{className:"knowledge-graph__empty",children:"Keine Knoten für diese Auswahl."})]}),l.jsx("aside",{className:"knowledge-graph__inspector","aria-live":"polite",children:x?l.jsxs(l.Fragment,{children:[l.jsx("h3",{children:"Inspector"}),l.jsx("strong",{children:x.title}),l.jsx("code",{children:x.path}),l.jsxs("dl",{children:[l.jsxs("div",{children:[l.jsx("dt",{children:"Typ"}),l.jsx("dd",{children:x.properties.typ??"—"})]}),l.jsxs("div",{children:[l.jsx("dt",{children:"Status"}),l.jsx("dd",{children:x.properties.stand??"—"})]}),l.jsxs("div",{children:[l.jsx("dt",{children:"Tags"}),l.jsx("dd",{children:x.properties.tags.map(v=>`#${v}`).join(" ")||"—"})]}),l.jsxs("div",{children:[l.jsx("dt",{children:"Verweise"}),l.jsxs("dd",{children:[x.outLinks," hinaus · ",x.inLinks," herein"]})]})]}),l.jsx("button",{type:"button",className:"primary",onClick:()=>n(x.path),children:"Notiz öffnen"}),_.length>0&&l.jsxs("section",{children:[l.jsx("h4",{children:"Verknüpft"}),_.map(v=>l.jsx("button",{type:"button",onClick:()=>n(v.path),children:v.title},v.id))]})]}):l.jsx("p",{children:"Wähle einen Knoten aus der Liste."})})]})]})}const Ev={note:{type:"note",title:"HINWEIS",color:"var(--info)",border:"var(--info)",bg:"color-mix(in srgb, var(--info) 8%, transparent)",glyph:"[i]"},important:{type:"important",title:"WICHTIG",color:"var(--accent)",border:"var(--accent)",bg:"var(--accent-soft)",glyph:"[!]"},warning:{type:"warning",title:"WARNUNG",color:"var(--warn)",border:"var(--warn)",bg:"color-mix(in srgb, var(--warn) 8%, transparent)",glyph:"[!]"},tip:{type:"tip",title:"TIPP",color:"var(--pos)",border:"var(--pos)",bg:"var(--accent-soft)",glyph:"[*]"},caution:{type:"caution",title:"ACHTUNG",color:"var(--neg)",border:"var(--neg)",bg:"color-mix(in srgb, var(--neg) 8%, transparent)",glyph:"[!]"}};function u2(t,e){const n=[];let i=0;const a=new Set(["import","export","from","default","const","let","var","function","return","if","else","for","while","do","switch","case","break","continue","try","catch","finally","throw","new","typeof","instanceof","async","await","yield","class","extends","interface","type","enum","implements","public","private","protected","readonly","static","as","is","keyof","in","of","void","any","never","unknown","null","undefined","true","false","super","this","debugger"]),s=new Set(["string","number","boolean","symbol","bigint","object","Array","Record","Promise","Map","Set","Date","RegExp","Error","Function","React","Node","HTMLElement","Partial","Required","Readonly"]);for(;i<t.length;){if(t.slice(i,i+2)==="//"||t[i]==="#"&&(i===0||/\s/.test(t[i-1]))){n.push({type:"comment",text:t.slice(i)});break}if(t[i]==='"'||t[i]==="'"||t[i]==="`"){const r=t[i];let o=i+1;for(;o<t.length;)if(t[o]==="\\")o+=2;else if(t[o]===r){o++;break}else o++;n.push({type:"string",text:t.slice(i,o)}),i=o;continue}if(/\d/.test(t[i])&&(i===0||/[^\w$]/.test(t[i-1]))){let r=i;for(;r<t.length&&/[\d.xXbBoOa-fA-F_]/.test(t[r]);)r++;n.push({type:"number",text:t.slice(i,r)}),i=r;continue}if(/[a-zA-Z_$]/.test(t[i])){let r=i;for(;r<t.length&&/[a-zA-Z0-9_$]/.test(t[r]);)r++;const o=t.slice(i,r);a.has(o)?n.push({type:"keyword",text:o}):s.has(o)||/^[A-Z][a-zA-Z0-9_$]*$/.test(o)?n.push({type:"type",text:o}):r<t.length&&t[r]==="("?n.push({type:"fn",text:o}):n.push({type:"text",text:o}),i=r;continue}n.push({type:"text",text:t[i]}),i++}return n}function f2({code:t,language:e}){const[n,i]=P.useState(!1),a=()=>{var r;(r=navigator.clipboard)==null||r.writeText(t).then(()=>{i(!0),setTimeout(()=>i(!1),2e3)}).catch(()=>{})},s=t.split(` -`);return l.jsxs("div",{className:"md-code-block",children:[l.jsxs("div",{className:"md-code-header",children:[l.jsx("span",{className:"md-code-lang",children:e||"text"}),l.jsx("button",{type:"button",className:"md-code-copy",onClick:a,children:n?"Kopiert":"Kopieren"})]}),l.jsx("pre",{className:"md-code-pre",children:l.jsx("code",{children:s.map((r,o)=>{const c=u2(r);return l.jsxs("div",{className:"md-code-line",children:[l.jsx("span",{className:"md-line-num",children:o+1}),l.jsxs("span",{className:"md-line-code",children:[c.map((u,d)=>l.jsx("span",{className:`md-tok md-tok--${u.type}`,children:u.text},d)),c.length===0&&" "]})]},o)})})})]})}function d2(t){return/^[a-zA-Z0-9_.-]+(?:\/[a-zA-Z0-9_.-]+)+\.[a-zA-Z0-9]+(?::\d+)?$/.test(t)||/^[a-zA-Z0-9_.-]+\.(?:ts|tsx|js|mjs|cjs|json|css|html|rs|go|py|sh|nsi|nsh)(?::\d+)?$/.test(t)}function Wn(t,e,n,i="inline"){var u;const a=[];let s=0,r=0;const o=/(\[\[([^\]|#]+)(?:#[^\]|]+)?(?:\|([^\]]+))?\]\])|(\[([^\]]+)\]\(([^)]+)\))|(`([^`]+)`)|(\*\*([^*]+)\*\*)|(\*([^*]+)\*)|(__([^_]+)__)|(_([^_]+)_)|(~~([^~]+)~~)/g;let c;for(;(c=o.exec(t))!==null;){c.index>s&&a.push(t.slice(s,c.index));const d=`${i}-${r++}`;if(c[1]){const m=c[2].trim(),f=((u=c[3])==null?void 0:u.trim())||m;a.push(l.jsxs("button",{type:"button",className:"md-wikilink",onClick:()=>e==null?void 0:e(m),title:`Notiz öffnen: ${m}`,children:[l.jsx("span",{className:"md-wikilink__brackets",children:"[["}),l.jsx("span",{className:"md-wikilink__text",children:f}),l.jsx("span",{className:"md-wikilink__brackets",children:"]]"}),l.jsx("span",{className:"md-wikilink__arrow","aria-hidden":"true",children:"↗"})]},d))}else if(c[4]){const m=c[5],f=c[6],p=f.startsWith("http://")||f.startsWith("https://")||f.startsWith("mailto:")||f.startsWith("#")||f.startsWith("/")||f.startsWith("./")||f.startsWith("../")||!f.includes(":"),x=f.startsWith("http://")||f.startsWith("https://");p?a.push(l.jsx("a",{href:f,className:"md-link",target:x?"_blank":void 0,rel:x?"noopener noreferrer":void 0,children:m},d)):a.push(l.jsx("span",{className:"md-link-blocked",children:m},d))}else if(c[7]){const m=c[8];d2(m)&&n?a.push(l.jsxs("button",{type:"button",className:"md-code-link",onClick:()=>n(m),title:`Quelle öffnen: ${m}`,children:[l.jsx("code",{children:m}),l.jsx("span",{className:"md-code-link__glyph","aria-hidden":"true",children:"↗"})]},d)):a.push(l.jsx("code",{className:"md-inline-code",children:m},d))}else c[9]?a.push(l.jsx("strong",{children:Wn(c[10],e,n,`${d}-b`)},d)):c[11]?a.push(l.jsx("em",{children:Wn(c[12],e,n,`${d}-i`)},d)):c[13]?a.push(l.jsx("strong",{children:Wn(c[14],e,n,`${d}-b2`)},d)):c[15]?a.push(l.jsx("em",{children:Wn(c[16],e,n,`${d}-i2`)},d)):c[17]&&a.push(l.jsx("del",{children:Wn(c[18],e,n,`${d}-s`)},d));s=c.index+c[0].length}return s<t.length&&a.push(t.slice(s)),a}function h2({content:t,onNavigateNote:e,onOpenSource:n,className:i=""}){const a=P.useMemo(()=>t?t.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/,""):"",[t]),s=P.useMemo(()=>{if(!a.trim())return[l.jsx("p",{className:"md-empty",children:"Leere Notiz"},"empty")];const r=a.split(/\r?\n/),o=[];let c=0;for(;c<r.length;){const u=r[c];if(!u.trim()){c++;continue}if(u.trim().startsWith("```")){const f=u.trim().slice(3).trim(),p=[];for(c++;c<r.length&&!r[c].trim().startsWith("```");)p.push(r[c]),c++;c<r.length&&c++,o.push(l.jsx(f2,{code:p.join(` -`),language:f},`code-${o.length}`));continue}if(u.trim().startsWith(">")){const f=[];for(;c<r.length&&r[c].trim().startsWith(">");)f.push(r[c].trim().replace(/^>\s?/,"")),c++;const x=(f[0]||"").match(/^\[!(note|important|warning|tip|caution)\]/i);if(x){const _=x[1].toLowerCase(),g=Ev[_]||Ev.note,h=f.slice(1).join(` -`);o.push(l.jsxs("div",{className:`md-callout md-callout--${g.type}`,style:{borderLeft:`4px solid ${g.border}`,backgroundColor:g.bg},children:[l.jsxs("div",{className:"md-callout-header",style:{color:g.color},children:[l.jsx("span",{className:"md-callout-glyph",children:g.glyph}),l.jsx("span",{className:"md-callout-title",children:g.title})]}),h&&l.jsx("div",{className:"md-callout-content",children:h.split(` -`).map((v,b)=>l.jsx("p",{children:Wn(v,e,n,`callout-${o.length}-${b}`)},b))})]},`callout-${o.length}`))}else o.push(l.jsx("blockquote",{className:"md-blockquote",children:f.map((_,g)=>l.jsx("p",{children:Wn(_,e,n,`bq-${o.length}-${g}`)},g))},`bq-${o.length}`));continue}if(u.includes("|")&&c+1<r.length&&r[c+1].includes("|")&&/[-:]+/.test(r[c+1])){const f=[];for(;c<r.length&&r[c].includes("|");)f.push(r[c]),c++;const p=v=>{let b=v.trim();return b.startsWith("|")&&(b=b.slice(1)),b.endsWith("|")&&(b=b.slice(0,-1)),b.split("|").map(y=>y.trim())},x=p(f[0]),g=p(f[1]).map(v=>{const b=v.startsWith(":"),y=v.endsWith(":");return b&&y?"center":y?"right":"left"}),h=f.slice(2).map(p);o.push(l.jsx("div",{className:"md-table-wrap",children:l.jsxs("table",{className:"md-table",children:[l.jsx("thead",{children:l.jsx("tr",{children:x.map((v,b)=>l.jsx("th",{style:{textAlign:g[b]||"left"},children:Wn(v,e,n,`th-${o.length}-${b}`)},b))})}),l.jsx("tbody",{children:h.map((v,b)=>l.jsx("tr",{children:v.map((y,A)=>l.jsx("td",{style:{textAlign:g[A]||"left"},children:Wn(y,e,n,`td-${o.length}-${b}-${A}`)},A))},b))})]})},`table-${o.length}`));continue}const d=u.match(/^(#{1,6})\s+(.*)$/);if(d){const f=d[1].length,p=d[2],x=Wn(p,e,n,`h-${o.length}`),_=`h-${o.length}`;switch(f){case 1:o.push(l.jsx("h1",{className:"md-heading md-heading--1",children:x},_));break;case 2:o.push(l.jsx("h2",{className:"md-heading md-heading--2",children:x},_));break;case 3:o.push(l.jsx("h3",{className:"md-heading md-heading--3",children:x},_));break;case 4:o.push(l.jsx("h4",{className:"md-heading md-heading--4",children:x},_));break;case 5:o.push(l.jsx("h5",{className:"md-heading md-heading--5",children:x},_));break;default:o.push(l.jsx("h6",{className:"md-heading md-heading--6",children:x},_));break}c++;continue}if(/^(\*{3,}|-{3,}|_{3,})$/.test(u.trim())){o.push(l.jsx("hr",{className:"md-hr"},`hr-${o.length}`)),c++;continue}if(/^\s*[-*]\s+\[([ xX])\]\s+(.*)$/.test(u)){const f=[];for(;c<r.length&&/^\s*[-*]\s+\[([ xX])\]\s+(.*)$/.test(r[c]);){const p=r[c].match(/^\s*[-*]\s+\[([ xX])\]\s+(.*)$/);f.push({checked:p[1].toLowerCase()==="x",text:p[2]}),c++}o.push(l.jsx("ul",{className:"md-checklist",children:f.map((p,x)=>l.jsxs("li",{className:`md-checklist-item ${p.checked?"is-checked":""}`,children:[l.jsx("input",{type:"checkbox",checked:p.checked,readOnly:!0,className:"md-checkbox"}),l.jsx("span",{className:"md-checklist-label",children:Wn(p.text,e,n,`check-${o.length}-${x}`)})]},x))},`checklist-${o.length}`));continue}if(/^\s*[-*+]\s+(.*)$/.test(u)){const f=[];for(;c<r.length&&/^\s*[-*+]\s+(.*)$/.test(r[c])&&!/^\s*[-*+]\s+\[[ xX]\]/.test(r[c]);){const p=r[c].match(/^\s*[-*+]\s+(.*)$/);f.push(p[1]),c++}o.push(l.jsx("ul",{className:"md-ul",children:f.map((p,x)=>l.jsx("li",{children:Wn(p,e,n,`ul-${o.length}-${x}`)},x))},`ul-${o.length}`));continue}if(/^\s*\d+\.\s+(.*)$/.test(u)){const f=[];for(;c<r.length&&/^\s*\d+\.\s+(.*)$/.test(r[c]);){const p=r[c].match(/^\s*\d+\.\s+(.*)$/);f.push(p[1]),c++}o.push(l.jsx("ol",{className:"md-ol",children:f.map((p,x)=>l.jsx("li",{children:Wn(p,e,n,`ol-${o.length}-${x}`)},x))},`ol-${o.length}`));continue}const m=[];for(;c<r.length&&r[c].trim()&&!r[c].trim().startsWith("#")&&!r[c].trim().startsWith("```")&&!r[c].trim().startsWith(">")&&!/^(\*{3,}|-{3,}|_{3,})$/.test(r[c].trim())&&!/^\s*[-*+]\s+/.test(r[c])&&!/^\s*\d+\.\s+/.test(r[c])&&!(r[c].includes("|")&&c+1<r.length&&/[-:]+/.test(r[c+1]));)m.push(r[c]),c++;m.length>0&&o.push(l.jsx("p",{className:"md-paragraph",children:m.map((f,p)=>l.jsxs(uM.Fragment,{children:[Wn(f,e,n,`p-${o.length}-${p}`),p<m.length-1&&l.jsx("br",{})]},p))},`p-${o.length}`))}return o},[a,e,n]);return l.jsx("article",{className:`markdown-preview ${i}`,children:s})}function p2(t){const e=t.trim(),n=e.match(/^([^:#]+):(\d+)(?::\d+)?$/);if(n)return{path:n[1],line:parseInt(n[2],10)};const i=e.match(/^([^:#]+)#(\d+)$/);return i?{path:i[1],line:parseInt(i[2],10)}:{path:e,line:null}}function m2({workspaceId:t,onOpenSource:e,onOpenRevision:n,onOpenAgentRun:i,onNavigateTab:a}){const[s,r]=P.useState([]),[o,c]=P.useState(null),[u,d]=P.useState(""),[m,f]=P.useState(""),[p,x]=P.useState(!1),[_,g]=P.useState(""),[h,v]=P.useState(""),[b,y]=P.useState(""),[A,M]=P.useState([]),[T,S]=P.useState([]),[C,R]=P.useState([]),[L,O]=P.useState(!1),[W,z]=P.useState(""),[D,B]=P.useState(""),[k,V]=P.useState("saved"),[X,ge]=P.useState(!0),[he,Ae]=P.useState(!0),[q,fe]=P.useState(!1),[Ne,ie]=P.useState(null),[xe,oe]=P.useState("editor"),[Ee,Oe]=P.useState("edit"),[ke,xt]=P.useState("notiz"),[re,Se]=P.useState("entwurf"),[Ce,be]=P.useState(()=>{try{return localStorage.getItem("plugbrain.notes_meta_collapsed")==="1"}catch{return!1}}),[Ve,Ge]=P.useState(!1),[Pe,we]=P.useState(!1),[We,ve]=P.useState(""),[I,nt]=P.useState(!1),[Ke,N]=P.useState(""),[E,H]=P.useState(0),K=()=>{be(U=>{const le=!U;try{localStorage.setItem("plugbrain.notes_meta_collapsed",le?"1":"0")}catch{}return le})},J=P.useRef(null),_e=async()=>{const U=await hT(t);return r(U),U},pe=async U=>{O(!0),B(""),z(""),fe(!1);try{const le=await Sc(t,U);c(le),d(le.content),V("saved"),R(await l0(t,le.path))}catch(le){z(le instanceof Error?le.message:String(le))}finally{O(!1)}};P.useEffect(()=>{_e().then(U=>{if(U[0])return pe(U[0].path)}).catch(U=>z(String(U)))},[t]);const te=P.useMemo(()=>[...new Set(s.flatMap(U=>U.tags||[]))].sort(),[s]),Z=P.useMemo(()=>{const U={};for(const le of s)if(le.tags)for(const Ie of le.tags)U[Ie]=(U[Ie]||0)+1;return U},[s]),ae=P.useMemo(()=>{if(!_.trim())return te;const U=_.toLowerCase();return te.filter(le=>le.toLowerCase().includes(U))},[te,_]),ce=P.useMemo(()=>s.filter(U=>{var le;return!m||((le=U.tags)==null?void 0:le.includes(m))}),[s,m]);P.useEffect(()=>{o&&(u!==o.content?k!=="conflict"&&k!=="saving"&&V("dirty"):k==="dirty"&&V("saved"))},[u,o==null?void 0:o.content,k]);const me=async(U=!1)=>{if(!(!o||L)){O(!0),B(""),z(""),V("saving");try{const le=o.content,Ie=await jl(t,o.path,u,o.hash||void 0,void 0,o.hash==="");ie({path:o.path,content:le,savedHash:Ie.hash});const ft=await Sc(t,o.path);c(ft),d(ft.content),V("saved"),await _e(),z(U?"Automatisch gespeichert und im Brain indiziert.":Ie.created?"Notiz angelegt und im Brain indiziert.":"Gespeichert und im Brain indiziert.")}catch(le){le!=null&&le.conflict||(le==null?void 0:le.status)===409?(B(le!=null&&le.conflict?`${le.message} Bitte neu laden und die Änderungen zusammenführen.`:"Konflikt beim Speichern: Die Notiz wurde extern geändert."),V("conflict")):(z(le instanceof Error?le.message:String(le)),V("dirty"))}finally{O(!1)}}},ye=async()=>{if(!(!o||L)){O(!0),B(""),z(""),V("saving");try{const U=o.content,le=await jl(t,o.path,u,void 0,void 0,!1);ie({path:o.path,content:U,savedHash:le.hash});const Ie=await Sc(t,o.path);c(Ie),d(Ie.content),V("saved"),fe(!1),await _e(),z("Änderungen erzwungen und im Brain indiziert.")}catch(U){z(U instanceof Error?U.message:String(U)),V("conflict")}finally{O(!1)}}};P.useEffect(()=>{if(!X||!o||o.hash===""||L||D||u===o.content)return;const U=setTimeout(()=>{!L&&!D&&u!==o.content&&o&&o.hash!==""&&me(!0)},600);return()=>clearTimeout(U)},[u,o,X,L,D]);const Fe=async()=>{if(!(!Ne||!o||Ne.path!==o.path||L)){O(!0),B(""),z("");try{const U=await jl(t,Ne.path,Ne.content,Ne.savedHash),le=await Sc(t,Ne.path);c(le),d(le.content),ie(null),V("saved"),await _e(),z("Wiederhergestellt.")}catch(U){z(U instanceof Error?U.message:String(U))}finally{O(!1)}}},Xe=()=>{const U=document.querySelector(".notes-editor textarea");if(!U)return;const le=U.selectionStart,Ie=U.selectionEnd,ft=u.substring(le,Ie),Yt=`[[${ft||"Notizname"}]]`,F=u.substring(0,le)+Yt+u.substring(Ie);d(F),setTimeout(()=>{U.focus();const ne=le+(ft?Yt.length:2);U.setSelectionRange(ne,ne+(ft?0:9))},10)},et=async U=>{if(!o||!U.trim())return;let le=U.trim();if(le.endsWith(".md")||(le+=".md"),le===o.path){we(!1);return}O(!0);try{await jl(t,le,u,void 0,void 0,!0),await _e(),await pe(le),we(!1),z(`Notiz umbenannt nach "${le}".`)}catch(Ie){z(Ie instanceof Error?Ie.message:String(Ie))}finally{O(!1)}};P.useEffect(()=>{const U=le=>{(le.ctrlKey||le.metaKey)&&le.key.toLowerCase()==="o"?(le.preventDefault(),nt(!0),N(""),H(0)):le.key==="Escape"&&(I&&nt(!1),Pe&&we(!1),Ve&&Ge(!1))};return window.addEventListener("keydown",U),()=>window.removeEventListener("keydown",U)},[I,Pe,Ve]);const j=P.useMemo(()=>{if(!Ke.trim())return s.slice(0,15);const U=Ke.toLowerCase();return s.filter(le=>le.title.toLowerCase().includes(U)||le.path.toLowerCase().includes(U)||(le.tags||[]).some(Ie=>Ie.toLowerCase().includes(U))).slice(0,20)},[s,Ke]),Me=async()=>{const U=prompt("Dateiname der neuen Notiz (z.B. Architektur.md):");if(!U)return;const le=U.endsWith(".md")?U:`${U}.md`,Ie=le.replace(/\.md$/,"").split("/").pop()||le,ft=ke==="notiz"?"":ke,Yt=ft?ft==="entscheidung"?`--- -typ: ${ft} -stand: ${re} -bezug: -code: -revision: ---- - -# ${Ie} - -## Kontext - -## Entscheidung - -## Konsequenzen -`:`--- -typ: ${ft} -stand: ${re} -bezug: -code: ---- - -# ${Ie} - -## Befund - -## Ursache - -## Lösungsvorschlag -`:`# ${Ie} - -`;O(!0);try{await jl(t,le,Yt,void 0,void 0,!0),await _e(),await pe(le)}catch(F){z(F instanceof Error?F.message:String(F))}finally{O(!1)}},se=U=>{const le=(o==null?void 0:o.title)||"Notiz";let Ie="";U==="entscheidung"?Ie=`--- -typ: entscheidung -stand: entwurf -bezug: -code: -revision: ---- - -# ${le} - -## Kontext - -## Entscheidung - -## Konsequenzen - -`:Ie=`--- -typ: widerspruch -stand: offen -bezug: -code: ---- - -# ${le} - -## Befund - -## Ursache - -## Lösungsvorschlag - -`,d(ft=>Ie+ft)},Te=U=>{const{path:le,line:Ie}=p2(U);e==null||e(le,Ie)},Re=U=>{const le=U.trim(),Ie=s.find(ft=>ft.path===le||ft.path===`Notizen/${le}.md`||ft.path.replace(/\.md$/,"").endsWith(le)||ft.title.toLowerCase()===le.toLowerCase());pe(Ie?Ie.path:`Notizen/${le}.md`)},de=async U=>{if(U.preventDefault(),!b.trim()){M([]),S([]);return}O(!0);try{const le=await cp(t,b);M(le.hits||[]);try{const Ie=await op(t,b);S(Ie)}catch{S([])}}catch(le){z(le instanceof Error?le.message:String(le))}finally{O(!1)}},ze=async U=>{if(!(!U||!o)){O(!0);try{await mT(t,o.path,U),R(await l0(t,o.path)),z(`Anhang ${U.name} hochgeladen.`)}catch(le){z(le instanceof Error?le.message:String(le))}finally{O(!1)}}},je=U=>{(U.ctrlKey||U.metaKey)&&U.key==="s"&&(U.preventDefault(),me(!1))},_t=U=>{var le;return((le=o==null?void 0:o.properties.find(Ie=>Ie.key.toLowerCase()===U))==null?void 0:le.value.trim())||null},yt=_t("bezug"),Ot=_t("code"),_n=_t("revision"),ji=_t("agentenlauf"),wt=P.useMemo(()=>{var ft;if(!u)return null;const U=u.match(/^---\r?\n([\s\S]*?)\r?\n---/);if(!U){if(!((ft=o==null?void 0:o.properties)!=null&&ft.length))return null;const Yt=F=>{var ne;return((ne=o.properties.find(Qe=>Qe.key.toLowerCase()===F))==null?void 0:ne.value.trim())||null};return{typ:o.typ||Yt("typ"),stand:o.stand||Yt("stand"),bezug:Yt("bezug"),code:Yt("code"),revision:Yt("revision"),agentenlauf:Yt("agentenlauf")}}const le=U[1].split(/\r?\n/),Ie={};for(const Yt of le){const F=Yt.match(/^([a-zA-Z0-9_-]+)\s*:\s*(.*)$/);F&&(Ie[F[1].toLowerCase()]=F[2].trim().replace(/^['"]|['"]$/g,""))}return{typ:Ie.typ||(o==null?void 0:o.typ)||null,stand:Ie.stand||(o==null?void 0:o.stand)||null,bezug:Ie.bezug||_t("bezug"),code:Ie.code||_t("code"),revision:Ie.revision||_t("revision"),agentenlauf:Ie.agentenlauf||_t("agentenlauf")}},[u,o]),Hi=P.useMemo(()=>u?[...u.matchAll(/\[\[([^\]|#]+)(?:#[^\]|]+)?(?:\|([^\]]+))?\]\]/g)].map(le=>{var F;const Ie=le[1].trim(),ft=((F=le[2])==null?void 0:F.trim())||null,Yt=s.find(ne=>ne.path===Ie||ne.path===`Notizen/${Ie}.md`||ne.path.replace(/\.md$/,"").endsWith(Ie)||ne.title.toLowerCase()===Ie.toLowerCase());return{target:Ie,alias:ft,resolvedPath:Yt?Yt.path:null}}):[],[u,s]),Gi=P.useMemo(()=>{if(!(o!=null&&o.backlinks))return[];const U=new Set;for(const le of o.backlinks){const Ie=s.find(ft=>ft.path===le.path);if(Ie!=null&&Ie.tags)for(const ft of Ie.tags)U.add(ft)}return[...U].sort()},[o==null?void 0:o.backlinks,s]),Ba=P.useMemo(()=>o!=null&&o.backlinks?h?o.backlinks.filter(U=>{var Ie;const le=s.find(ft=>ft.path===U.path);return(Ie=le==null?void 0:le.tags)==null?void 0:Ie.includes(h)}):o.backlinks:[],[o==null?void 0:o.backlinks,s,h]);return l.jsxs("main",{className:"notes-workbench",children:[l.jsxs("aside",{className:"notes-sidebar",children:[l.jsxs("div",{className:"notes-sidebar__head",children:[l.jsx("strong",{children:"Wissen"}),l.jsxs("span",{children:[l.jsx("button",{type:"button",className:xe==="editor"?"on":"",onClick:()=>oe("editor"),children:"Editor"}),l.jsx("button",{type:"button",className:xe==="graph"?"on":"",onClick:()=>oe("graph"),"aria-label":"Graph-Ansicht",children:"Graph-Ansicht"})]})]}),l.jsxs("div",{className:"notes-create",children:[l.jsxs("select",{"aria-label":"Typ der neuen Wissensnotiz",value:ke,onChange:U=>xt(U.target.value),children:[l.jsx("option",{value:"notiz",children:"Notiz"}),l.jsx("option",{value:"entscheidung",children:"Entscheidung"}),l.jsx("option",{value:"widerspruch",children:"Widerspruch"})]}),ke!=="notiz"&&l.jsxs("select",{"aria-label":"Status der neuen Wissensnotiz",value:re,onChange:U=>Se(U.target.value),children:[l.jsx("option",{value:"entwurf",children:"Entwurf"}),l.jsx("option",{value:"offen",children:"Offen"}),l.jsx("option",{value:"entschieden",children:"Entschieden"}),l.jsx("option",{value:"geklärt",children:"Geklärt"})]}),l.jsx("button",{type:"button",onClick:Me,children:"Neu"})]}),l.jsxs("form",{className:"notes-search",onSubmit:de,children:[l.jsx("input",{value:b,onChange:U=>y(U.target.value),placeholder:"Notizen & Code suchen …"}),l.jsx("button",{disabled:L,type:"submit",children:"Suchen"})]}),l.jsxs("div",{className:"notes-tag-drawer",children:[l.jsxs("div",{className:"notes-tag-drawer__head",children:[l.jsx("button",{type:"button",className:"notes-tag-drawer__toggle",onClick:()=>x(U=>!U),children:l.jsxs("span",{children:[p?"▾":"▸"," Tags (",te.length,")"]})}),m&&l.jsxs("div",{className:"notes-tag-drawer__active",children:[l.jsxs("span",{className:"notes-tag-chip",children:["#",m]}),l.jsx("button",{type:"button",className:"notes-tag-clear",onClick:()=>f(""),title:"Tag-Filter aufheben",children:"✕"})]})]}),p&&l.jsxs("div",{className:"notes-tag-drawer__body",children:[l.jsx("input",{type:"search",className:"notes-tag-search",placeholder:"Tags filtern...",value:_,onChange:U=>g(U.target.value)}),l.jsxs("div",{className:"notes-tags notes-tag-drawer__list",children:[l.jsxs("button",{type:"button",className:m?"":"on",onClick:()=>f(""),children:["Alle (",s.length,")"]}),ae.map(U=>l.jsxs("button",{type:"button",className:m===U?"on":"",onClick:()=>f(m===U?"":U),children:["#",U," (",Z[U]||0,")"]},U))]})]}),!p&&l.jsxs("div",{className:"notes-tags notes-tags--compact",children:[l.jsx("button",{type:"button",className:m?"":"on",onClick:()=>f(""),children:"Alle"}),te.slice(0,8).map(U=>l.jsxs("button",{type:"button",className:m===U?"on":"",onClick:()=>f(U),children:["#",U]},U)),te.length>8&&l.jsxs("button",{type:"button",className:"notes-tags-more",onClick:()=>x(!0),children:["+",te.length-8," weitere …"]})]})]}),(A.length>0||T.length>0)&&l.jsxs("div",{className:"notes-results",children:[A.length>0&&l.jsxs("div",{children:[l.jsxs("div",{className:"notes-results__section-head",children:["Notizen (",A.length,")"]}),A.map(U=>l.jsxs("button",{type:"button",onClick:()=>void pe(U.path),children:[l.jsx("strong",{children:U.title}),l.jsxs("span",{children:[U.path,U.line?`:${U.line}`:""]}),U.snippet&&l.jsx("small",{children:U.snippet})]},`${U.path}:${U.line}`))]}),T.length>0&&l.jsxs("div",{children:[l.jsxs("div",{className:"notes-results__section-head",children:["Code-Symbole (",T.length,")"]}),T.map((U,le)=>l.jsxs("button",{type:"button",onClick:()=>Te(`${U.path}${U.line?`:${U.line}`:""}`),children:[l.jsxs("div",{className:"notes-results__sym-row",children:[l.jsx("strong",{children:U.name}),l.jsx("span",{className:"notes-results__sym-kind",children:U.kind})]}),l.jsxs("span",{children:[U.path,U.line?`:${U.line}`:""]})]},`${U.path}:${U.line}:${le}`))]})]}),l.jsx("div",{className:"notes-list",children:s.length===0?l.jsxs("div",{className:"notes-empty-list",children:[l.jsx("p",{children:"Noch keine Notizen"}),l.jsx("button",{type:"button",className:"pb-button pb-button--primary",onClick:Me,children:"Erste Notiz anlegen"})]}):ce.map(U=>l.jsxs("button",{type:"button",className:(o==null?void 0:o.path)===U.path?"on":"",onClick:()=>void pe(U.path),children:[l.jsx("strong",{children:U.title}),l.jsx("span",{children:U.path}),l.jsxs("small",{children:[(U.tags||[]).map(le=>`#${le}`).join(" ")," ",U.inLinks?`←${U.inLinks}`:""]})]},U.path))})]}),l.jsxs("section",{className:"notes-editor",children:[xe==="graph"&&l.jsx(c2,{workspaceId:t,focus:o==null?void 0:o.path,onOpenNote:U=>{oe("editor"),pe(U)}}),xe==="editor"&&(o?l.jsxs(l.Fragment,{children:[l.jsxs("header",{className:"notes-editor__head",children:[l.jsxs("div",{className:"notes-editor__title",children:[l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:"8px"},children:[l.jsx("strong",{title:o.title,children:o.title}),l.jsx("button",{type:"button",className:"pb-tool pb-tool--icon",style:{width:"24px",height:"24px",padding:0},onClick:()=>{ve(o.path),we(!0)},title:"Notiz umbenennen",children:l.jsx(Mt,{path:vt.pencil})})]}),l.jsx("span",{children:o.path}),o.typ&&l.jsxs("small",{children:[o.typ,o.stand?` · ${o.stand}`:""]})]}),l.jsxs("div",{className:"notes-editor__actions",children:[l.jsxs("div",{className:"notes-view-mode-toggle",role:"group","aria-label":"Editor-Ansichtsmodus",children:[l.jsx("button",{type:"button",className:`notes-toggle-btn ${Ee==="edit"?"on":""}`,onClick:()=>Oe("edit"),title:"Reiner Texteditor",children:"Edit"}),l.jsx("button",{type:"button",className:`notes-toggle-btn ${Ee==="split"?"on":""}`,onClick:()=>Oe("split"),title:"Editor und Live-Vorschau nebeneinander",children:"Split"}),l.jsx("button",{type:"button",className:`notes-toggle-btn ${Ee==="lesen"?"on":""}`,onClick:()=>Oe("lesen"),title:"Reine Leseansicht (Markdown)",children:"Lesen"})]}),l.jsx("button",{type:"button",className:"pb-tool",onClick:Xe,title:"Wiki-Link [[...]] an Cursorposition einfügen",children:"[[ Link ]]"}),l.jsxs("span",{className:"pb-status","data-tone":k==="saved"?"ok":k==="saving"?"idle":k==="conflict"?"bad":"warn",title:k==="conflict"?"Konflikt beim Speichern":k==="dirty"?"Ungespeicherte Änderungen":"Gespeichert",children:[l.jsx("i",{"aria-hidden":"true"}),l.jsx("span",{children:k==="saving"?"SPEICHERT":k==="dirty"?"GEÄNDERT":k==="conflict"?"KONFLIKT":"GESPEICHERT"})]}),l.jsx("button",{type:"button",className:"pb-button pb-button--primary",disabled:L||k==="saved",onClick:()=>void me(!1),title:"Notiz speichern (Strg+S)",children:"Speichern"}),l.jsxs("button",{type:"button",className:`pb-tool ${he?"is-active":""}`,onClick:()=>Ae(U=>!U),title:"Rückverweise und Eigenschaften anzeigen/verbergen",children:["Rückverweise (",o.backlinks.length,")"]}),l.jsxs("div",{className:"notes-overflow-wrap",style:{position:"relative"},children:[l.jsx("button",{type:"button",className:"pb-tool pb-tool--icon",onClick:()=>Ge(U=>!U),title:"Weitere Aktionen","aria-expanded":Ve,children:"⋯"}),Ve&&l.jsxs("div",{className:"notes-overflow-dropdown",onClick:()=>Ge(!1),children:[l.jsxs("button",{type:"button",onClick:()=>ge(U=>!U),children:["Autosave: ",X?"Aktiv":"Aus"]}),l.jsx("button",{type:"button",disabled:L||!Ne||Ne.path!==o.path,onClick:()=>void Fe(),children:"Rückgängig"}),l.jsx("button",{type:"button",onClick:()=>{ve(o.path),we(!0)},children:"Notiz umbenennen …"}),l.jsx("a",{href:o0(t,o.path),download:!0,children:"Notiz exportieren"}),l.jsx("a",{href:o0(t),download:!0,children:"Vault exportieren"})]})]})]})]}),wt&&(wt.typ||wt.stand||wt.bezug||wt.code||wt.revision)&&l.jsxs("div",{className:"notes-frontmatter-bar",children:[wt.typ&&l.jsxs("span",{className:`notes-fm-pill notes-fm-pill--${wt.typ.toLowerCase()}`,children:["TYP: ",wt.typ.toUpperCase()]}),wt.stand&&l.jsxs("span",{className:"notes-fm-pill notes-fm-pill--stand",children:["STATUS: ",wt.stand.toUpperCase()]}),wt.bezug&&l.jsxs("button",{type:"button",className:"notes-fm-btn notes-fm-btn--link",onClick:()=>Re(wt.bezug.replace(/^\[\[|\]\]$/g,"")),title:`Notiz öffnen: ${wt.bezug}`,children:["BEZUG: ",wt.bezug," ↗"]}),wt.code&&l.jsxs("button",{type:"button",className:"notes-fm-btn notes-fm-btn--code",onClick:()=>Te(wt.code),title:`Datei im Editor öffnen: ${wt.code}`,children:["CODE: ",wt.code," ↗"]}),wt.revision&&l.jsxs("button",{type:"button",className:"notes-fm-btn notes-fm-btn--rev",onClick:()=>n==null?void 0:n(wt.revision),title:`Revision prüfen: ${wt.revision}`,children:["GIT: ",wt.revision.slice(0,8)," ↗"]})]}),D&&l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:"8px",background:"rgba(239, 68, 68, 0.15)",padding:"6px 12px",borderRadius:"4px",margin:"4px 0"},children:[l.jsx("p",{className:"notes-conflict",role:"alert",style:{margin:0,flex:1},children:D}),l.jsx("button",{type:"button",style:{fontSize:"11px",padding:"2px 8px"},onClick:()=>o&&void pe(o.path),children:"Neu laden"}),l.jsx("button",{type:"button",style:{fontSize:"11px",padding:"2px 8px"},onClick:()=>void ye(),children:"Trotzdem überschreiben"}),l.jsx("button",{type:"button",style:{fontSize:"11px",padding:"2px 8px"},onClick:()=>fe(U=>!U),children:q?"Diff schließen":"Diff anzeigen"})]}),q&&l.jsxs("div",{style:{display:"grid",gridTemplateColumns:"1fr 1fr",gap:"8px",padding:"8px 12px",background:"var(--panel)",borderBottom:"1px solid var(--border)",maxHeight:"180px",overflowY:"auto",fontSize:"11px",fontFamily:"var(--mono)"},children:[l.jsxs("div",{children:[l.jsxs("strong",{style:{display:"block",color:"var(--faint)",marginBottom:"4px"},children:["Server-Version (",o.path,"):"]}),l.jsx("pre",{style:{margin:0,whiteSpace:"pre-wrap",color:"var(--neg)"},children:o.content})]}),l.jsxs("div",{children:[l.jsx("strong",{style:{display:"block",color:"var(--faint)",marginBottom:"4px"},children:"Lokale Version (Entwurf):"}),l.jsx("pre",{style:{margin:0,whiteSpace:"pre-wrap",color:"var(--pos)"},children:u})]})]}),W&&l.jsx("p",{className:"notes-notice",role:"status",children:W}),!u.startsWith("---")&&l.jsxs("div",{style:{display:"flex",gap:"6px",margin:"4px 0",flex:"none"},children:[l.jsx("button",{type:"button",style:{fontSize:"10.5px",padding:"2px 7px"},onClick:()=>se("entscheidung"),children:"+ Vorlage: Entscheidung"}),l.jsx("button",{type:"button",style:{fontSize:"10.5px",padding:"2px 7px"},onClick:()=>se("widerspruch"),children:"+ Vorlage: Widerspruch"})]}),l.jsxs("div",{className:"notes-content-grid",style:{display:"grid",gridTemplateColumns:he?Ee==="split"?"1fr 1fr 280px":"1fr 280px":Ee==="split"?"1fr 1fr":"1fr",height:"100%",minHeight:0,overflow:"hidden"},children:[l.jsx("div",{className:"notes-editor-pane",style:{display:Ee==="lesen"?"none":"flex",flexDirection:"column",height:"100%",minHeight:0,overflow:"hidden",borderRight:Ee==="split"||he?"1px solid var(--border)":"none"},children:l.jsx("textarea",{"aria-label":"Notizinhalt",value:u,onChange:U=>d(U.target.value),onKeyDown:je,spellCheck:!1,style:{height:"100%",width:"100%",boxSizing:"border-box",resize:"none",border:"none",outline:"none"}})}),Ee==="lesen"&&l.jsx("textarea",{"aria-label":"Notizinhalt",value:u,onChange:U=>d(U.target.value),onKeyDown:je,spellCheck:!1,style:{position:"absolute",opacity:0,pointerEvents:"none",width:0,height:0}}),(Ee==="split"||Ee==="lesen")&&l.jsx("div",{className:"notes-preview-pane",style:{height:"100%",minHeight:0,overflowY:"auto",padding:"16px 20px",background:"var(--bg)",borderRight:he?"1px solid var(--border)":"none"},children:l.jsx(h2,{content:u,onNavigateNote:Re,onOpenSource:Te})}),he&&l.jsxs("aside",{className:"notes-backlinks-pane","aria-label":"Wissensnetz & Backlinks",style:{overflowY:"auto",padding:"12px",background:"var(--panel)",display:"flex",flexDirection:"column",gap:"12px",fontSize:"12px"},children:[l.jsxs("div",{children:[l.jsxs("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:"6px"},children:[l.jsxs("strong",{style:{fontSize:"11px",textTransform:"uppercase",letterSpacing:"0.08em",color:"var(--faint)"},children:["Rückverweise (",Ba.length,")"]}),Gi.length>0&&l.jsxs("select",{value:h,onChange:U=>v(U.target.value),style:{fontSize:"11px",padding:"1px 4px",background:"transparent",color:"var(--fg)",border:"1px solid var(--border)",borderRadius:"3px"},"aria-label":"Backlinks nach Tag filtern",children:[l.jsx("option",{value:"",children:"Alle Tags"}),Gi.map(U=>l.jsxs("option",{value:U,children:["#",U]},U))]})]}),Ba.length>0?l.jsx("div",{style:{display:"flex",flexDirection:"column",gap:"4px"},children:Ba.map(U=>l.jsxs("button",{type:"button",onClick:()=>void pe(U.path),style:{textAlign:"left",background:"rgba(255,255,255,0.03)",border:"1px solid var(--border)",borderRadius:"4px",padding:"6px 8px",color:"var(--fg)",cursor:"pointer"},children:[l.jsxs("div",{style:{fontWeight:600,color:"var(--info)"},children:["← ",U.title]}),l.jsxs("div",{style:{fontSize:"11px",color:"var(--faint)",display:"flex",justifyContent:"space-between",marginTop:"2px"},children:[l.jsx("span",{children:U.path}),l.jsxs("span",{children:["Z. ",U.line]})]})]},`${U.path}:${U.line}`))}):l.jsx("div",{style:{color:"var(--faint)",fontStyle:"italic",fontSize:"11px"},children:"Keine Rückverweise für aktuelle Auswahl."})]}),l.jsxs("div",{children:[l.jsxs("strong",{style:{display:"block",fontSize:"11px",textTransform:"uppercase",letterSpacing:"0.08em",color:"var(--faint)",marginBottom:"6px"},children:["Ausgehende Wiki-Links (",Hi.length,")"]}),Hi.length>0?l.jsx("div",{style:{display:"flex",flexDirection:"column",gap:"4px"},children:Hi.map((U,le)=>l.jsxs("button",{type:"button",disabled:!U.resolvedPath,onClick:()=>U.resolvedPath&&void pe(U.resolvedPath),style:{textAlign:"left",background:"rgba(255,255,255,0.03)",border:"1px solid var(--border)",borderRadius:"4px",padding:"6px 8px",color:U.resolvedPath?"var(--fg)":"var(--faint)",cursor:U.resolvedPath?"pointer":"default"},children:[l.jsxs("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between"},children:[l.jsx("span",{style:{fontWeight:600},children:U.alias||U.target}),l.jsx("span",{style:{fontSize:"10px",color:U.resolvedPath?"var(--pos)":"var(--faint)"},children:U.resolvedPath?"im Vault":"unaufgelöst"})]}),U.resolvedPath&&l.jsx("div",{style:{fontSize:"11px",color:"var(--faint)",marginTop:"2px"},children:U.resolvedPath})]},`${U.target}:${le}`))}):l.jsx("div",{style:{color:"var(--faint)",fontStyle:"italic",fontSize:"11px"},children:"Keine [[Wiki-Links]] im Text."})]}),Ot&&l.jsxs("div",{style:{borderTop:"1px solid var(--border)",paddingTop:"8px"},children:[l.jsx("strong",{style:{display:"block",fontSize:"11px",textTransform:"uppercase",letterSpacing:"0.08em",color:"var(--faint)",marginBottom:"6px"},children:"Code-Bindung"}),l.jsxs("button",{type:"button",disabled:!e,onClick:()=>Te(Ot),style:{width:"100%",textAlign:"left",background:"color-mix(in srgb, var(--info) 8%, transparent)",border:"1px solid color-mix(in srgb, var(--info) 30%, transparent)",borderRadius:"4px",padding:"6px 8px",color:"var(--info)",cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"space-between"},children:[l.jsx("span",{style:{fontFamily:"var(--mono)",fontSize:"11px"},children:Ot}),l.jsx("span",{style:{fontSize:"11px"},children:"Quelle öffnen →"})]})]})]})]}),l.jsx("div",{className:"notes-meta-bar",children:l.jsxs("button",{type:"button",className:"notes-meta-toggle",onClick:K,"aria-expanded":!Ce,title:Ce?"Metadaten und Verknüpfungen aufklappen":"Metadaten und Verknüpfungen einklappen",children:[l.jsxs("span",{children:[Ce?"▸":"▾"," Verknüpfungen & Anhänge"]}),l.jsxs("span",{className:"notes-meta-counts",children:[o.links.length," Links · ",o.backlinks.length," Backlinks",C.length>0?` · ${C.length} Anhänge`:""]})]})}),!Ce&&l.jsxs("footer",{className:"notes-editor__meta",children:[l.jsxs("section",{children:[l.jsxs("h3",{children:["Links (",o.links.length,")"]}),o.links.length?o.links.map(U=>l.jsxs("button",{type:"button",disabled:!U.path,onClick:()=>U.path&&void pe(U.path),children:[U.alias||U.target,U.path?"":" (nicht aufgelöst)"]},`${U.target}:${U.line}`)):l.jsx("span",{children:"Keine Wiki-Links."})]}),l.jsxs("section",{children:[l.jsxs("h3",{children:["Backlinks (",o.backlinks.length,")"]}),o.backlinks.length?o.backlinks.map(U=>l.jsxs("button",{type:"button",onClick:()=>void pe(U.path),children:["← ",U.title," · Zeile ",U.line]},`${U.path}:${U.line}`)):l.jsx("span",{children:"Keine Rückverweise."})]}),(yt||Ot||_n||ji)&&l.jsxs("section",{children:[l.jsx("h3",{children:"Verknüpfungen"}),yt&&l.jsxs("button",{type:"button",onClick:()=>void pe(yt.replace(/^\[\[|\]\]$/g,"")),children:["Entscheidung/Widerspruch: ",yt]}),Ot&&l.jsxs("button",{type:"button",disabled:!e,onClick:()=>Te(Ot),children:["Datei: ",Ot]}),_n&&l.jsxs("button",{type:"button",disabled:!n,onClick:()=>n==null?void 0:n(_n),children:["Revision: ",_n]}),ji&&l.jsxs("button",{type:"button",disabled:!i,onClick:()=>i==null?void 0:i(ji),children:["Agentenlauf: ",ji]})]}),l.jsxs("section",{children:[l.jsxs("h3",{children:["Anhänge (",C.length,")"]}),l.jsx("input",{ref:J,type:"file",hidden:!0,onChange:U=>{var le;return void ze((le=U.target.files)==null?void 0:le[0])}}),l.jsx("button",{type:"button",disabled:L||o.hash==="",title:o.hash===""?"Die Notiz zuerst speichern":void 0,onClick:()=>{var U;return(U=J.current)==null?void 0:U.click()},children:"Datei anhängen"}),o.hash===""&&l.jsx("span",{children:"Notiz zuerst speichern."}),C.map(U=>l.jsxs("a",{href:gT(t,o.path,U.name),children:[U.name," · ",U.bytes," B"]},U.path))]})]})]}):s.length===0?l.jsxs("div",{className:"notes-empty--initial",children:[l.jsx("div",{className:"notes-empty__icon",children:l.jsx(Mt,{path:vt.notes})}),l.jsx("h3",{children:"Dieser Vault ist noch leer"}),l.jsx("p",{children:"Erstelle deine erste Notiz mit Wiki-Links, Tags und Eigenschaften — genau wie in Obsidian."}),l.jsx("button",{type:"button",className:"pb-button pb-button--primary",onClick:Me,children:"Erste Notiz anlegen"})]}):l.jsxs("div",{className:"notes-empty--initial",children:[l.jsx("div",{className:"notes-empty__icon",children:l.jsx(Mt,{path:vt.notes})}),l.jsx("h3",{children:"Keine Notiz ausgewählt"}),l.jsx("p",{children:"Wähle eine Notiz in der linken Seitenleiste aus oder erstelle eine neue."}),l.jsx("button",{type:"button",className:"pb-button pb-button--primary",onClick:Me,children:"Neue Notiz anlegen"})]}))]}),I&&l.jsx("div",{className:"brain-modal-backdrop",onClick:()=>nt(!1),children:l.jsxs("div",{className:"brain-modal brain-quick-switcher",onClick:U=>U.stopPropagation(),children:[l.jsxs("div",{className:"brain-modal__header",children:[l.jsx("h3",{children:"Schnellwechsler (Notiz öffnen)"}),l.jsx("button",{type:"button",className:"brain-modal__close",onClick:()=>nt(!1),children:"✕"})]}),l.jsx("div",{className:"quick-switcher-input-wrap",children:l.jsx("input",{type:"text",className:"brain-modal__input",autoFocus:!0,placeholder:"Notiz nach Titel, Pfad oder #Tag suchen …",value:Ke,onChange:U=>{N(U.target.value),H(0)},onKeyDown:U=>{U.key==="ArrowDown"?(U.preventDefault(),H(le=>Math.min(j.length-1,le+1))):U.key==="ArrowUp"?(U.preventDefault(),H(le=>Math.max(0,le-1))):U.key==="Enter"?(U.preventDefault(),j[E]&&(pe(j[E].path),nt(!1))):U.key==="Escape"&&nt(!1)}})}),l.jsx("div",{className:"quick-switcher-list",role:"listbox",children:j.length===0?l.jsx("p",{className:"quick-switcher-empty",children:"Keine passende Notiz gefunden."}):j.map((U,le)=>l.jsxs("button",{type:"button",className:`quick-switcher-item ${le===E?"is-selected":""}`,onClick:()=>{pe(U.path),nt(!1)},onMouseEnter:()=>H(le),children:[l.jsx("div",{className:"quick-switcher-item__title",children:U.title}),l.jsx("div",{className:"quick-switcher-item__path",children:U.path}),U.tags&&U.tags.length>0&&l.jsx("div",{className:"quick-switcher-item__tags",children:U.tags.map(Ie=>`#${Ie}`).join(" ")})]},U.path))}),l.jsxs("div",{className:"quick-switcher-hint",children:[l.jsxs("span",{children:[l.jsx("kbd",{children:"↑"}),l.jsx("kbd",{children:"↓"})," Auswählen"]}),l.jsxs("span",{children:[l.jsx("kbd",{children:"Enter"})," Öffnen"]}),l.jsxs("span",{children:[l.jsx("kbd",{children:"Esc"})," Schließen"]})]})]})}),Pe&&l.jsx("div",{className:"brain-modal-backdrop",onClick:()=>we(!1),children:l.jsxs("div",{className:"brain-modal",onClick:U=>U.stopPropagation(),children:[l.jsxs("div",{className:"brain-modal__header",children:[l.jsx("h3",{children:"Notiz umbenennen"}),l.jsx("button",{type:"button",className:"brain-modal__close",onClick:()=>we(!1),children:"✕"})]}),l.jsxs("form",{onSubmit:U=>{U.preventDefault(),et(We)},children:[l.jsxs("div",{className:"brain-modal__field",children:[l.jsx("label",{children:"Neuer Pfad / Dateiname (im Vault):"}),l.jsx("input",{type:"text",className:"brain-modal__input",autoFocus:!0,value:We,onChange:U=>ve(U.target.value),placeholder:"Ordner/NeueNotiz.md"})]}),l.jsx("p",{style:{fontSize:"11px",color:"var(--muted)",margin:"0 0 16px"},children:"Die Notiz wird unter dem neuen Pfad gespeichert und im Wissensgraphen indiziert."}),l.jsxs("div",{className:"brain-modal__actions",children:[l.jsx("button",{type:"button",onClick:()=>we(!1),children:"Abbrechen"}),l.jsx("button",{type:"submit",className:"pb-button pb-button--primary",disabled:L||!We.trim(),children:L?"Speichert …":"Umbenennen"})]})]})]})})]})}function g2(t){const e=[],n=t.split(` -`);for(const i of n){const a=i.match(/^-\s*`([^`]+)`\s*—\s*(.*)$/);a&&e.push({path:a[1],reasons:a[2]})}return e}function x2({workspaceId:t,onSelectSource:e}){const[n,i]=P.useState(()=>new URLSearchParams(window.location.search).get("goal")||"authKey security tests"),[a,s]=P.useState(!1),[r,o]=P.useState(""),[c,u]=P.useState(null),[d,m]=P.useState(null),[f,p]=P.useState(!1),[x,_]=P.useState(!1);P.useEffect(()=>{const M=new URLSearchParams(window.location.search).get("goal");M&&t&&(s(!0),r0(t,M).then(T=>{u(T),T.id&&h(T.id)}).catch(T=>o((T==null?void 0:T.message)||"Fehler beim Erzeugen")).finally(()=>s(!1)))},[t]);const g=async M=>{M&&M.preventDefault();const T=n.trim();if(T){s(!0),o(""),u(null),m(null);try{const S=await r0(t,T);u(S),S.id&&h(S.id)}catch(S){o((S==null?void 0:S.message)||"Fehler beim Erzeugen des Context Packs")}finally{s(!1)}}},h=async M=>{p(!0);try{const T=await uT(M);m(T)}catch(T){console.error("Staleness check error:",T)}finally{p(!1)}},[v,b]=P.useState(""),y=(M="chatgpt")=>{if(!(c!=null&&c.body))return;let T=c.body;M==="chatgpt"&&(T=`# Kontext-Paket für Agenten / LLM -Ziel: ${n} -Workspace-ID: ${t} -Version: ${c.version} -Quellen: ${c.sources} - -${c.body}`),navigator.clipboard.writeText(T).then(()=>{b("Als Kontext kopiert (bereit für ChatGPT, Claude & Co.)"),setTimeout(()=>b(""),3e3)}).catch(()=>{b("Fehler beim Kopieren in die Zwischenablage")})},A=c!=null&&c.body?g2(c.body):[];return l.jsxs("div",{className:"pack-view",children:[l.jsxs("div",{className:"pack-view__header",children:[l.jsxs("div",{className:"pack-view__title",children:[l.jsx(Mt,{path:vt.open}),l.jsx("strong",{children:"Context-Pack-Inspector"}),l.jsx("span",{className:"pack-view__endpoint mono",children:"/api/context/pack"})]}),l.jsx("p",{className:"pack-view__lead",style:{fontSize:"12px",color:"var(--muted)",margin:"4px 0 0"},children:"Stellt einen deterministischen Kontext-Ausschnitt aus Code und Notizen für eine Agenten-Aufgabe zusammen."})]}),l.jsx("form",{className:"pack-form",onSubmit:g,children:l.jsxs("div",{className:"pack-form__field",children:[l.jsx("label",{htmlFor:"pack-goal-input",children:"Aufgabe / Ziel für den Agenten:"}),l.jsxs("div",{className:"pack-input-row",children:[l.jsx("input",{id:"pack-goal-input",type:"text",className:"pack-input",value:n,onChange:M=>i(M.target.value),placeholder:"z. B. authKey security tests"}),l.jsx("button",{type:"submit",className:"pack-create-btn pb-button pb-button--primary",disabled:a||!n.trim(),children:a?"Erzeuge …":"Pack erzeugen"})]})]})}),r&&l.jsx("div",{className:"pack-error-alert",role:"alert",children:r}),c&&l.jsx("div",{className:"pack-details",children:l.jsxs("div",{className:"pack-card",children:[l.jsxs("div",{className:"pack-card__header",children:[l.jsxs("div",{className:"pack-card__meta",children:[l.jsx("span",{className:"pack-id mono",children:c.id}),l.jsxs("span",{className:"pack-badge pack-badge--version",children:["v",c.version]}),l.jsxs("span",{className:"pack-badge pack-badge--sources",children:[c.sources," Quellen"]})]}),l.jsxs("div",{className:"pack-card__actions",style:{display:"flex",alignItems:"center",gap:"8px",flexWrap:"wrap"},children:[l.jsx("button",{type:"button",className:"pb-button pb-button--primary",onClick:()=>y("chatgpt"),title:"Markdown-Kontext für ChatGPT oder Claude in die Zwischenablage kopieren","aria-label":"Als Kontext kopieren",style:{fontSize:"12px",padding:"6px 14px"},children:"Als Kontext kopieren"}),l.jsxs("div",{className:"pack-card__staleness",children:[f?l.jsx("span",{className:"pack-staleness-badge pack-staleness-badge--loading",children:"Prüfe …"}):d?l.jsxs("span",{className:"pb-status","data-tone":d.stale?"bad":"ok",style:{marginRight:"8px"},children:[l.jsx("i",{"aria-hidden":"true"}),l.jsx("span",{children:d.stale?"VERALTET":"AKTUELL"})]}):null,l.jsx("button",{type:"button",className:"pack-staleness-btn",onClick:()=>h(c.id),disabled:f,title:"Staleness gegen aktuellen Brain-Index prüfen",children:"Neu prüfen"})]})]})]}),v&&l.jsx("div",{className:"pack-copy-feedback",style:{margin:"10px 0",padding:"8px 12px",background:"var(--accent-soft)",border:"1px solid var(--pos)",borderRadius:"var(--r)",fontSize:"12px",color:"var(--ink)",fontFamily:"var(--mono)"},children:v}),d&&d.stale&&l.jsxs("div",{className:"pack-stale-warning",children:[l.jsx("strong",{children:"Quellen haben sich geändert:"}),d.changed.length>0&&l.jsxs("div",{children:["Geändert: ",d.changed.join(", ")]}),d.missing.length>0&&l.jsxs("div",{children:["Fehlt: ",d.missing.join(", ")]})]}),l.jsxs("div",{className:"pack-sources-section",children:[l.jsx("h4",{children:"Extrahierte Quellen aus dem Index:"}),A.length===0?l.jsx("div",{className:"pack-sources-empty",children:"Keine spezifischen Quelltreffer für dieses Ziel gefunden."}):l.jsx("div",{className:"pack-sources-list",children:A.map(M=>l.jsxs("div",{className:"pack-source-item",onClick:()=>e(M.path),title:`Klicken, um ${M.path} in Quellansicht zu öffnen`,children:[l.jsx("div",{className:"pack-source-path mono",children:M.path}),l.jsx("div",{className:"pack-source-why",children:M.reasons})]},M.path))})]}),l.jsx("div",{className:"pack-body-toggle",children:l.jsx("button",{type:"button",className:"pack-toggle-raw-btn",onClick:()=>_(M=>!M),children:x?"Markdown-Text verbergen":"Vollständigen Pack-Markdown anzeigen"})}),x&&l.jsx("div",{className:"pack-raw-markdown mono",children:l.jsx("pre",{children:c.body})})]})})]})}function v2(t){const e=+this._x.call(null,t),n=+this._y.call(null,t);return PS(this.cover(e,n),e,n,t)}function PS(t,e,n,i){if(isNaN(e)||isNaN(n))return t;var a,s=t._root,r={data:i},o=t._x0,c=t._y0,u=t._x1,d=t._y1,m,f,p,x,_,g,h,v;if(!s)return t._root=r,t;for(;s.length;)if((_=e>=(m=(o+u)/2))?o=m:u=m,(g=n>=(f=(c+d)/2))?c=f:d=f,a=s,!(s=s[h=g<<1|_]))return a[h]=r,t;if(p=+t._x.call(null,s.data),x=+t._y.call(null,s.data),e===p&&n===x)return r.next=s,a?a[h]=r:t._root=r,t;do a=a?a[h]=new Array(4):t._root=new Array(4),(_=e>=(m=(o+u)/2))?o=m:u=m,(g=n>=(f=(c+d)/2))?c=f:d=f;while((h=g<<1|_)===(v=(x>=f)<<1|p>=m));return a[v]=s,a[h]=r,t}function _2(t){var e,n,i=t.length,a,s,r=new Array(i),o=new Array(i),c=1/0,u=1/0,d=-1/0,m=-1/0;for(n=0;n<i;++n)isNaN(a=+this._x.call(null,e=t[n]))||isNaN(s=+this._y.call(null,e))||(r[n]=a,o[n]=s,a<c&&(c=a),a>d&&(d=a),s<u&&(u=s),s>m&&(m=s));if(c>d||u>m)return this;for(this.cover(c,u).cover(d,m),n=0;n<i;++n)PS(this,r[n],o[n],t[n]);return this}function y2(t,e){if(isNaN(t=+t)||isNaN(e=+e))return this;var n=this._x0,i=this._y0,a=this._x1,s=this._y1;if(isNaN(n))a=(n=Math.floor(t))+1,s=(i=Math.floor(e))+1;else{for(var r=a-n||1,o=this._root,c,u;n>t||t>=a||i>e||e>=s;)switch(u=(e<i)<<1|t<n,c=new Array(4),c[u]=o,o=c,r*=2,u){case 0:a=n+r,s=i+r;break;case 1:n=a-r,s=i+r;break;case 2:a=n+r,i=s-r;break;case 3:n=a-r,i=s-r;break}this._root&&this._root.length&&(this._root=o)}return this._x0=n,this._y0=i,this._x1=a,this._y1=s,this}function b2(){var t=[];return this.visit(function(e){if(!e.length)do t.push(e.data);while(e=e.next)}),t}function S2(t){return arguments.length?this.cover(+t[0][0],+t[0][1]).cover(+t[1][0],+t[1][1]):isNaN(this._x0)?void 0:[[this._x0,this._y0],[this._x1,this._y1]]}function Un(t,e,n,i,a){this.node=t,this.x0=e,this.y0=n,this.x1=i,this.y1=a}function M2(t,e,n){var i,a=this._x0,s=this._y0,r,o,c,u,d=this._x1,m=this._y1,f=[],p=this._root,x,_;for(p&&f.push(new Un(p,a,s,d,m)),n==null?n=1/0:(a=t-n,s=e-n,d=t+n,m=e+n,n*=n);x=f.pop();)if(!(!(p=x.node)||(r=x.x0)>d||(o=x.y0)>m||(c=x.x1)<a||(u=x.y1)<s))if(p.length){var g=(r+c)/2,h=(o+u)/2;f.push(new Un(p[3],g,h,c,u),new Un(p[2],r,h,g,u),new Un(p[1],g,o,c,h),new Un(p[0],r,o,g,h)),(_=(e>=h)<<1|t>=g)&&(x=f[f.length-1],f[f.length-1]=f[f.length-1-_],f[f.length-1-_]=x)}else{var v=t-+this._x.call(null,p.data),b=e-+this._y.call(null,p.data),y=v*v+b*b;if(y<n){var A=Math.sqrt(n=y);a=t-A,s=e-A,d=t+A,m=e+A,i=p.data}}return i}function E2(t){if(isNaN(d=+this._x.call(null,t))||isNaN(m=+this._y.call(null,t)))return this;var e,n=this._root,i,a,s,r=this._x0,o=this._y0,c=this._x1,u=this._y1,d,m,f,p,x,_,g,h;if(!n)return this;if(n.length)for(;;){if((x=d>=(f=(r+c)/2))?r=f:c=f,(_=m>=(p=(o+u)/2))?o=p:u=p,e=n,!(n=n[g=_<<1|x]))return this;if(!n.length)break;(e[g+1&3]||e[g+2&3]||e[g+3&3])&&(i=e,h=g)}for(;n.data!==t;)if(a=n,!(n=n.next))return this;return(s=n.next)&&delete n.next,a?(s?a.next=s:delete a.next,this):e?(s?e[g]=s:delete e[g],(n=e[0]||e[1]||e[2]||e[3])&&n===(e[3]||e[2]||e[1]||e[0])&&!n.length&&(i?i[h]=n:this._root=n),this):(this._root=s,this)}function T2(t){for(var e=0,n=t.length;e<n;++e)this.remove(t[e]);return this}function A2(){return this._root}function w2(){var t=0;return this.visit(function(e){if(!e.length)do++t;while(e=e.next)}),t}function C2(t){var e=[],n,i=this._root,a,s,r,o,c;for(i&&e.push(new Un(i,this._x0,this._y0,this._x1,this._y1));n=e.pop();)if(!t(i=n.node,s=n.x0,r=n.y0,o=n.x1,c=n.y1)&&i.length){var u=(s+o)/2,d=(r+c)/2;(a=i[3])&&e.push(new Un(a,u,d,o,c)),(a=i[2])&&e.push(new Un(a,s,d,u,c)),(a=i[1])&&e.push(new Un(a,u,r,o,d)),(a=i[0])&&e.push(new Un(a,s,r,u,d))}return this}function N2(t){var e=[],n=[],i;for(this._root&&e.push(new Un(this._root,this._x0,this._y0,this._x1,this._y1));i=e.pop();){var a=i.node;if(a.length){var s,r=i.x0,o=i.y0,c=i.x1,u=i.y1,d=(r+c)/2,m=(o+u)/2;(s=a[0])&&e.push(new Un(s,r,o,d,m)),(s=a[1])&&e.push(new Un(s,d,o,c,m)),(s=a[2])&&e.push(new Un(s,r,m,d,u)),(s=a[3])&&e.push(new Un(s,d,m,c,u))}n.push(i)}for(;i=n.pop();)t(i.node,i.x0,i.y0,i.x1,i.y1);return this}function R2(t){return t[0]}function D2(t){return arguments.length?(this._x=t,this):this._x}function L2(t){return t[1]}function U2(t){return arguments.length?(this._y=t,this):this._y}function bg(t,e,n){var i=new Sg(e??R2,n??L2,NaN,NaN,NaN,NaN);return t==null?i:i.addAll(t)}function Sg(t,e,n,i,a,s){this._x=t,this._y=e,this._x0=n,this._y0=i,this._x1=a,this._y1=s,this._root=void 0}function Tv(t){for(var e={data:t.data},n=e;t=t.next;)n=n.next={data:t.data};return e}var Bn=bg.prototype=Sg.prototype;Bn.copy=function(){var t=new Sg(this._x,this._y,this._x0,this._y0,this._x1,this._y1),e=this._root,n,i;if(!e)return t;if(!e.length)return t._root=Tv(e),t;for(n=[{source:e,target:t._root=new Array(4)}];e=n.pop();)for(var a=0;a<4;++a)(i=e.source[a])&&(i.length?n.push({source:i,target:e.target[a]=new Array(4)}):e.target[a]=Tv(i));return t};Bn.add=v2;Bn.addAll=_2;Bn.cover=y2;Bn.data=b2;Bn.extent=S2;Bn.find=M2;Bn.remove=E2;Bn.removeAll=T2;Bn.root=A2;Bn.size=w2;Bn.visit=C2;Bn.visitAfter=N2;Bn.x=D2;Bn.y=U2;function Pn(t){return function(){return t}}function fs(t){return(t()-.5)*1e-6}function O2(t){return t.x+t.vx}function P2(t){return t.y+t.vy}function z2(t){var e,n,i,a=1,s=1;typeof t!="function"&&(t=Pn(t==null?1:+t));function r(){for(var u,d=e.length,m,f,p,x,_,g,h=0;h<s;++h)for(m=bg(e,O2,P2).visitAfter(o),u=0;u<d;++u)f=e[u],_=n[f.index],g=_*_,p=f.x+f.vx,x=f.y+f.vy,m.visit(v);function v(b,y,A,M,T){var S=b.data,C=b.r,R=_+C;if(S){if(S.index>f.index){var L=p-S.x-S.vx,O=x-S.y-S.vy,W=L*L+O*O;W<R*R&&(L===0&&(L=fs(i),W+=L*L),O===0&&(O=fs(i),W+=O*O),W=(R-(W=Math.sqrt(W)))/W*a,f.vx+=(L*=W)*(R=(C*=C)/(g+C)),f.vy+=(O*=W)*R,S.vx-=L*(R=1-R),S.vy-=O*R)}return}return y>p+R||M<p-R||A>x+R||T<x-R}}function o(u){if(u.data)return u.r=n[u.data.index];for(var d=u.r=0;d<4;++d)u[d]&&u[d].r>u.r&&(u.r=u[d].r)}function c(){if(e){var u,d=e.length,m;for(n=new Array(d),u=0;u<d;++u)m=e[u],n[m.index]=+t(m,u,e)}}return r.initialize=function(u,d){e=u,i=d,c()},r.iterations=function(u){return arguments.length?(s=+u,r):s},r.strength=function(u){return arguments.length?(a=+u,r):a},r.radius=function(u){return arguments.length?(t=typeof u=="function"?u:Pn(+u),c(),r):t},r}function I2(t){return t.index}function Av(t,e){var n=t.get(e);if(!n)throw new Error("node not found: "+e);return n}function k2(t){var e=I2,n=m,i,a=Pn(30),s,r,o,c,u,d=1;t==null&&(t=[]);function m(g){return 1/Math.min(o[g.source.index],o[g.target.index])}function f(g){for(var h=0,v=t.length;h<d;++h)for(var b=0,y,A,M,T,S,C,R;b<v;++b)y=t[b],A=y.source,M=y.target,T=M.x+M.vx-A.x-A.vx||fs(u),S=M.y+M.vy-A.y-A.vy||fs(u),C=Math.sqrt(T*T+S*S),C=(C-s[b])/C*g*i[b],T*=C,S*=C,M.vx-=T*(R=c[b]),M.vy-=S*R,A.vx+=T*(R=1-R),A.vy+=S*R}function p(){if(r){var g,h=r.length,v=t.length,b=new Map(r.map((A,M)=>[e(A,M,r),A])),y;for(g=0,o=new Array(h);g<v;++g)y=t[g],y.index=g,typeof y.source!="object"&&(y.source=Av(b,y.source)),typeof y.target!="object"&&(y.target=Av(b,y.target)),o[y.source.index]=(o[y.source.index]||0)+1,o[y.target.index]=(o[y.target.index]||0)+1;for(g=0,c=new Array(v);g<v;++g)y=t[g],c[g]=o[y.source.index]/(o[y.source.index]+o[y.target.index]);i=new Array(v),x(),s=new Array(v),_()}}function x(){if(r)for(var g=0,h=t.length;g<h;++g)i[g]=+n(t[g],g,t)}function _(){if(r)for(var g=0,h=t.length;g<h;++g)s[g]=+a(t[g],g,t)}return f.initialize=function(g,h){r=g,u=h,p()},f.links=function(g){return arguments.length?(t=g,p(),f):t},f.id=function(g){return arguments.length?(e=g,f):e},f.iterations=function(g){return arguments.length?(d=+g,f):d},f.strength=function(g){return arguments.length?(n=typeof g=="function"?g:Pn(+g),x(),f):n},f.distance=function(g){return arguments.length?(a=typeof g=="function"?g:Pn(+g),_(),f):a},f}var B2={value:()=>{}};function zS(){for(var t=0,e=arguments.length,n={},i;t<e;++t){if(!(i=arguments[t]+"")||i in n||/[\s.]/.test(i))throw new Error("illegal type: "+i);n[i]=[]}return new Cu(n)}function Cu(t){this._=t}function F2(t,e){return t.trim().split(/^|\s+/).map(function(n){var i="",a=n.indexOf(".");if(a>=0&&(i=n.slice(a+1),n=n.slice(0,a)),n&&!e.hasOwnProperty(n))throw new Error("unknown type: "+n);return{type:n,name:i}})}Cu.prototype=zS.prototype={constructor:Cu,on:function(t,e){var n=this._,i=F2(t+"",n),a,s=-1,r=i.length;if(arguments.length<2){for(;++s<r;)if((a=(t=i[s]).type)&&(a=j2(n[a],t.name)))return a;return}if(e!=null&&typeof e!="function")throw new Error("invalid callback: "+e);for(;++s<r;)if(a=(t=i[s]).type)n[a]=wv(n[a],t.name,e);else if(e==null)for(a in n)n[a]=wv(n[a],t.name,null);return this},copy:function(){var t={},e=this._;for(var n in e)t[n]=e[n].slice();return new Cu(t)},call:function(t,e){if((a=arguments.length-2)>0)for(var n=new Array(a),i=0,a,s;i<a;++i)n[i]=arguments[i+2];if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(s=this._[t],i=0,a=s.length;i<a;++i)s[i].value.apply(e,n)},apply:function(t,e,n){if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(var i=this._[t],a=0,s=i.length;a<s;++a)i[a].value.apply(e,n)}};function j2(t,e){for(var n=0,i=t.length,a;n<i;++n)if((a=t[n]).name===e)return a.value}function wv(t,e,n){for(var i=0,a=t.length;i<a;++i)if(t[i].name===e){t[i]=B2,t=t.slice(0,i).concat(t.slice(i+1));break}return n!=null&&t.push({name:e,value:n}),t}var Sl=0,lo=0,Ql=0,IS=1e3,hf,oo,pf=0,ir=0,jf=0,Wo=typeof performance=="object"&&performance.now?performance:Date,kS=typeof window=="object"&&window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(t){setTimeout(t,17)};function BS(){return ir||(kS(H2),ir=Wo.now()+jf)}function H2(){ir=0}function im(){this._call=this._time=this._next=null}im.prototype=FS.prototype={constructor:im,restart:function(t,e,n){if(typeof t!="function")throw new TypeError("callback is not a function");n=(n==null?BS():+n)+(e==null?0:+e),!this._next&&oo!==this&&(oo?oo._next=this:hf=this,oo=this),this._call=t,this._time=n,am()},stop:function(){this._call&&(this._call=null,this._time=1/0,am())}};function FS(t,e,n){var i=new im;return i.restart(t,e,n),i}function G2(){BS(),++Sl;for(var t=hf,e;t;)(e=ir-t._time)>=0&&t._call.call(void 0,e),t=t._next;--Sl}function Cv(){ir=(pf=Wo.now())+jf,Sl=lo=0;try{G2()}finally{Sl=0,W2(),ir=0}}function V2(){var t=Wo.now(),e=t-pf;e>IS&&(jf-=e,pf=t)}function W2(){for(var t,e=hf,n,i=1/0;e;)e._call?(i>e._time&&(i=e._time),t=e,e=e._next):(n=e._next,e._next=null,e=t?t._next=n:hf=n);oo=t,am(i)}function am(t){if(!Sl){lo&&(lo=clearTimeout(lo));var e=t-ir;e>24?(t<1/0&&(lo=setTimeout(Cv,t-Wo.now()-jf)),Ql&&(Ql=clearInterval(Ql))):(Ql||(pf=Wo.now(),Ql=setInterval(V2,IS)),Sl=1,kS(Cv))}}const X2=1664525,q2=1013904223,Nv=4294967296;function Y2(){let t=1;return()=>(t=(X2*t+q2)%Nv)/Nv}function K2(t){return t.x}function Z2(t){return t.y}var $2=10,Q2=Math.PI*(3-Math.sqrt(5));function J2(t){var e,n=1,i=.001,a=1-Math.pow(i,1/300),s=0,r=.6,o=new Map,c=FS(m),u=zS("tick","end"),d=Y2();t==null&&(t=[]);function m(){f(),u.call("tick",e),n<i&&(c.stop(),u.call("end",e))}function f(_){var g,h=t.length,v;_===void 0&&(_=1);for(var b=0;b<_;++b)for(n+=(s-n)*a,o.forEach(function(y){y(n)}),g=0;g<h;++g)v=t[g],v.fx==null?v.x+=v.vx*=r:(v.x=v.fx,v.vx=0),v.fy==null?v.y+=v.vy*=r:(v.y=v.fy,v.vy=0);return e}function p(){for(var _=0,g=t.length,h;_<g;++_){if(h=t[_],h.index=_,h.fx!=null&&(h.x=h.fx),h.fy!=null&&(h.y=h.fy),isNaN(h.x)||isNaN(h.y)){var v=$2*Math.sqrt(.5+_),b=_*Q2;h.x=v*Math.cos(b),h.y=v*Math.sin(b)}(isNaN(h.vx)||isNaN(h.vy))&&(h.vx=h.vy=0)}}function x(_){return _.initialize&&_.initialize(t,d),_}return p(),e={tick:f,restart:function(){return c.restart(m),e},stop:function(){return c.stop(),e},nodes:function(_){return arguments.length?(t=_,p(),o.forEach(x),e):t},alpha:function(_){return arguments.length?(n=+_,e):n},alphaMin:function(_){return arguments.length?(i=+_,e):i},alphaDecay:function(_){return arguments.length?(a=+_,e):+a},alphaTarget:function(_){return arguments.length?(s=+_,e):s},velocityDecay:function(_){return arguments.length?(r=1-_,e):1-r},randomSource:function(_){return arguments.length?(d=_,o.forEach(x),e):d},force:function(_,g){return arguments.length>1?(g==null?o.delete(_):o.set(_,x(g)),e):o.get(_)},find:function(_,g,h){var v=0,b=t.length,y,A,M,T,S;for(h==null?h=1/0:h*=h,v=0;v<b;++v)T=t[v],y=_-T.x,A=g-T.y,M=y*y+A*A,M<h&&(S=T,h=M);return S},on:function(_,g){return arguments.length>1?(u.on(_,g),e):u.on(_)}}}function eD(){var t,e,n,i,a=Pn(-30),s,r=1,o=1/0,c=.81;function u(p){var x,_=t.length,g=bg(t,K2,Z2).visitAfter(m);for(i=p,x=0;x<_;++x)e=t[x],g.visit(f)}function d(){if(t){var p,x=t.length,_;for(s=new Array(x),p=0;p<x;++p)_=t[p],s[_.index]=+a(_,p,t)}}function m(p){var x=0,_,g,h=0,v,b,y;if(p.length){for(v=b=y=0;y<4;++y)(_=p[y])&&(g=Math.abs(_.value))&&(x+=_.value,h+=g,v+=g*_.x,b+=g*_.y);p.x=v/h,p.y=b/h}else{_=p,_.x=_.data.x,_.y=_.data.y;do x+=s[_.data.index];while(_=_.next)}p.value=x}function f(p,x,_,g){if(!p.value)return!0;var h=p.x-e.x,v=p.y-e.y,b=g-x,y=h*h+v*v;if(b*b/c<y)return y<o&&(h===0&&(h=fs(n),y+=h*h),v===0&&(v=fs(n),y+=v*v),y<r&&(y=Math.sqrt(r*y)),e.vx+=h*p.value*i/y,e.vy+=v*p.value*i/y),!0;if(p.length||y>=o)return;(p.data!==e||p.next)&&(h===0&&(h=fs(n),y+=h*h),v===0&&(v=fs(n),y+=v*v),y<r&&(y=Math.sqrt(r*y)));do p.data!==e&&(b=s[p.data.index]*i/y,e.vx+=h*b,e.vy+=v*b);while(p=p.next)}return u.initialize=function(p,x){t=p,n=x,d()},u.strength=function(p){return arguments.length?(a=typeof p=="function"?p:Pn(+p),d(),u):a},u.distanceMin=function(p){return arguments.length?(r=p*p,u):Math.sqrt(r)},u.distanceMax=function(p){return arguments.length?(o=p*p,u):Math.sqrt(o)},u.theta=function(p){return arguments.length?(c=p*p,u):Math.sqrt(c)},u}function tD(t){var e=Pn(.1),n,i,a;typeof t!="function"&&(t=Pn(t==null?0:+t));function s(o){for(var c=0,u=n.length,d;c<u;++c)d=n[c],d.vx+=(a[c]-d.x)*i[c]*o}function r(){if(n){var o,c=n.length;for(i=new Array(c),a=new Array(c),o=0;o<c;++o)i[o]=isNaN(a[o]=+t(n[o],o,n))?0:+e(n[o],o,n)}}return s.initialize=function(o){n=o,r()},s.strength=function(o){return arguments.length?(e=typeof o=="function"?o:Pn(+o),r(),s):e},s.x=function(o){return arguments.length?(t=typeof o=="function"?o:Pn(+o),r(),s):t},s}function nD(t){var e=Pn(.1),n,i,a;typeof t!="function"&&(t=Pn(t==null?0:+t));function s(o){for(var c=0,u=n.length,d;c<u;++c)d=n[c],d.vy+=(a[c]-d.y)*i[c]*o}function r(){if(n){var o,c=n.length;for(i=new Array(c),a=new Array(c),o=0;o<c;++o)i[o]=isNaN(a[o]=+t(n[o],o,n))?0:+e(n[o],o,n)}}return s.initialize=function(o){n=o,r()},s.strength=function(o){return arguments.length?(e=typeof o=="function"?o:Pn(+o),r(),s):e},s.y=function(o){return arguments.length?(t=typeof o=="function"?o:Pn(+o),r(),s):t},s}const Rv=.04,Dv=6,iD=.0016,aD=4,sD=4,rD=2.6,lD=1.35,oD=13,oh=1.4,cD=.13,uD=.035,fD=.8,dD=.13,hD=.7,pD=1.3,Lv=12,Uv=.75,mD=1.25,gD=.06,xD=280,tu=3,vD=4,_D=40,Ov=56,yD=1.6,bD=260;var Bv;const Pv=typeof window<"u"&&((Bv=window.matchMedia)==null?void 0:Bv.call(window,"(prefers-reduced-motion: reduce)").matches)===!0;function SD(t){return Math.min(oD,rD+lD*Math.sqrt(t))}function MD(t){return 1-Math.pow(1-t,3)}function zv(t){const e=getComputedStyle(t),n=(a,s)=>e.getPropertyValue(a).trim()||s,i=[];for(let a=0;a<8;a+=1)i.push(n(`--graph-g${a}`,n("--graph-other","#6b6e76")));return{bg:n("--graph-bg","#0b0c0e"),edge:n("--graph-edge","#8b8e96"),accent:n("--graph-accent","#9dc4ff"),label:n("--graph-label","#d6d8dd"),labelHalo:n("--graph-label-halo","#0b0c0e"),other:n("--graph-other","#6b6e76"),groups:i,font:e.fontFamily||"ui-sans-serif, system-ui, sans-serif"}}function ED(t,e){const n=t.getContext("2d");if(n===null)throw new Error("Canvas 2D ist in diesem Browser nicht verfügbar.");const i=n;let a=zv(t.parentElement??t),s=null,r=[],o=new Map,c=[],u=new Map,d=null,m=()=>!1,f=new Set,p="auto",x=null,_=null;const g={x:0,y:0,k:1};let h=0,v=0,b=1,y=0,A=!0,M=!1,T=!1,S=null,C=-1;const R=(re,Se)=>[(re-g.x)/g.k,(Se-g.y)/g.k],L=re=>{const Se=u.get(re.group)??-1;return Se>=0?a.groups[Se]??a.other:a.other},O=re=>!m(re.node);function W(re,Se){const[Ce,be]=R(re,Se);let Ve=null,Ge=1/0;for(const Pe of r){if(!O(Pe)||Pe.x===void 0||Pe.y===void 0)continue;const we=Math.max(Pe.r,oh/g.k)+sD/g.k,We=Pe.x-Ce,ve=Pe.y-be,I=We*We+ve*ve;I<=we*we&&I<Ge&&(Ve=Pe,Ge=I)}return Ve}function z(){const re=_??x;if(re===null&&f.size>0)return new Set(f);if(re===null||s===null)return null;const Se=new Set([re]);for(const Ce of s.neighbours.get(re)??[])Se.add(Ce);return Se}function D(){if(i.setTransform(1,0,0,1,0,0),i.fillStyle=a.bg,i.fillRect(0,0,t.width,t.height),r.length===0)return;i.setTransform(b*g.k,0,0,b*g.k,b*g.x,b*g.y);const re=z();i.lineCap="round",i.strokeStyle=a.edge,i.globalAlpha=re?uD:cD,i.lineWidth=hD/g.k,i.beginPath();const Se=[];for(const be of c){const Ve=be.source,Ge=be.target;if(!(!O(Ve)||!O(Ge))){if(re&&(Ve.id===(_??x)||Ge.id===(_??x))){Se.push(be);continue}i.moveTo(Ve.x??0,Ve.y??0),i.lineTo(Ge.x??0,Ge.y??0)}}if(i.stroke(),Se.length>0){i.strokeStyle=a.accent,i.globalAlpha=fD,i.lineWidth=pD/g.k,i.beginPath();for(const be of Se){const Ve=be.source,Ge=be.target;i.moveTo(Ve.x??0,Ve.y??0),i.lineTo(Ge.x??0,Ge.y??0)}i.stroke()}const Ce=oh/g.k;for(const be of r){if(!O(be))continue;const Ve=Math.max(be.r,Ce);i.globalAlpha=re&&!re.has(be.id)?dD:1,i.fillStyle=L(be),i.beginPath(),i.arc(be.x??0,be.y??0,Ve,0,Math.PI*2),i.fill()}i.globalAlpha=1,i.strokeStyle=a.accent;for(const be of r){if(!O(be))continue;const Ve=be.id===x?2.2:be.id===_?1.6:f.has(be.id)?1.4:0;Ve!==0&&(i.lineWidth=Ve/g.k,i.beginPath(),i.arc(be.x??0,be.y??0,Math.max(be.r,Ce)+3/g.k,0,Math.PI*2),i.stroke())}B(re)}function B(re){const Se=_??x,Ce=Pe=>Pe.id===x||Pe.id===_?4:re!=null&&re.has(Pe.id)?3:f.has(Pe.id)?2:0,be=[];for(const Pe of r){if(!O(Pe))continue;const we=Ce(Pe);let We=0;if(we>0)We=1;else if(re)We=0;else if(p==="always")We=1;else if(p==="auto"){const nt=g.k+Pe.r*gD;We=Math.max(0,Math.min(1,(nt-Uv)/(mD-Uv)))}if(We<=.02)continue;const ve=(Pe.x??0)*g.k+g.x,I=(Pe.y??0)*g.k+g.y;ve<-200||ve>h+200||I<-40||I>v+40||be.push({node:Pe,alpha:We,rank:we*1e5+Pe.node.degree})}be.sort((Pe,we)=>we.rank-Pe.rank),i.setTransform(b,0,0,b,0,0),i.font=`500 ${Lv}px ${a.font}`,i.textAlign="center",i.textBaseline="top",i.lineJoin="round";const Ve=[];let Ge=0;for(const{node:Pe,alpha:we}of be){if(Ge>=xD)break;const We=Pe.node.label,ve=i.measureText(We).width,I=Math.max(Pe.r,oh/g.k)*g.k,nt=(Pe.x??0)*g.k+g.x,Ke=(Pe.y??0)*g.k+g.y+I+vD,N={x0:nt-ve/2-tu,y0:Ke-tu,x1:nt+ve/2+tu,y1:Ke+Lv+tu};!(Pe.id===Se)&&Ve.some(H=>N.x0<H.x1&&N.x1>H.x0&&N.y0<H.y1&&N.y1>H.y0)||(Ve.push(N),Ge+=1,i.globalAlpha=we,i.lineWidth=3,i.strokeStyle=a.labelHalo,i.strokeText(We,nt,Ke),i.fillStyle=Pe.id===x||Pe.id===_?a.accent:a.label,i.fillText(We,nt,Ke))}i.globalAlpha=1}function k(re){y=0;let Se=!1;if(S!==null){const Ce=Math.min(1,(re-S.start)/bD),be=MD(Ce);g.x=S.from.x+(S.to.x-S.from.x)*be,g.y=S.from.y+(S.to.y-S.from.y)*be,g.k=S.from.k+(S.to.k-S.from.k)*be,Ce>=1&&(S=null),A=!0,Se=!0}d!==null&&d.alpha()>d.alphaMin()?(d.tick(),A=!0,Se=!0):d!==null&&!T&&(T=!0,M||he(),Se=!0),A&&(D(),A=!1,g.k!==C&&(C=g.k,e.onZoom(g.k))),Se&&V()}function V(){y===0&&(y=requestAnimationFrame(k))}function X(){A=!0,V()}function ge(re){if(Pv){g.x=re.x,g.y=re.y,g.k=re.k,S=null,X();return}S={from:{...g},to:re,start:performance.now()},V()}function he(re=!1){let Se=1/0,Ce=1/0,be=-1/0,Ve=-1/0;for(const we of r)!O(we)||we.x===void 0||we.y===void 0||(Se=Math.min(Se,we.x-we.r),Ce=Math.min(Ce,we.y-we.r),be=Math.max(be,we.x+we.r),Ve=Math.max(Ve,we.y+we.r));if(!Number.isFinite(Se)||h===0||v===0)return;const Ge=Math.max(Rv,Math.min(Dv,Math.min((h-Ov*2)/Math.max(1,be-Se),(v-Ov*2)/Math.max(1,Ve-Ce)))),Pe={k:Ge,x:h/2-(Se+be)/2*Ge,y:v/2-(Ce+Ve)/2*Ge};if(re){g.x=Pe.x,g.y=Pe.y,g.k=Pe.k,S=null,X();return}ge(Pe)}function Ae(re,Se,Ce){const be=Math.max(Rv,Math.min(Dv,g.k*Ce)),[Ve,Ge]=R(re,Se);g.x=re-Ve*be,g.y=Se-Ge*be,g.k=be,S=null,M=!0,X()}let q=null;const fe=re=>{const Se=t.getBoundingClientRect();return[re.clientX-Se.left,re.clientY-Se.top]};function Ne(re){if(re.button!==0)return;const[Se,Ce]=fe(re);q={x:Se,y:Ce,node:W(Se,Ce),moved:!1,pointerId:re.pointerId},t.setPointerCapture(re.pointerId),S=null}function ie(re){const[Se,Ce]=fe(re);if(q===null){const Ge=W(Se,Ce),Pe=(Ge==null?void 0:Ge.id)??null;t.style.cursor=Ge?"pointer":"grab",Pe!==_&&(_=Pe,X());return}const be=Se-q.x,Ve=Ce-q.y;if(!(!q.moved&&Math.hypot(be,Ve)<aD))if(q.moved=!0,M=!0,t.style.cursor="grabbing",q.node!==null&&d!==null){const[Ge,Pe]=R(Se,Ce);q.node.fx=Ge,q.node.fy=Pe,d.alphaTarget(.25),d.alpha()<.25&&d.alpha(.25),X()}else g.x+=re.movementX,g.y+=re.movementY,X()}function xe(re){if(q===null)return;const Se=q.moved,Ce=q.node;if(t.hasPointerCapture(q.pointerId)&&t.releasePointerCapture(q.pointerId),q=null,t.style.cursor=Ce?"pointer":"grab",Ce!==null&&d!==null&&(Ce.fx=null,Ce.fy=null,d.alphaTarget(0)),!Se){const[be,Ve]=fe(re),Ge=W(be,Ve);x=(Ge==null?void 0:Ge.id)??null,e.onSelect(x)}X()}function oe(re){const[Se,Ce]=fe(re),be=W(Se,Ce);be!==null&&e.onOpen(be.id)}function Ee(re){re.preventDefault();const[Se,Ce]=fe(re);Ae(Se,Ce,Math.exp(-re.deltaY*iD))}function Oe(){q===null&&_!==null&&(_=null,X())}t.addEventListener("pointerdown",Ne),t.addEventListener("pointermove",ie),t.addEventListener("pointerup",xe),t.addEventListener("pointercancel",xe),t.addEventListener("pointerleave",Oe),t.addEventListener("dblclick",oe),t.addEventListener("wheel",Ee,{passive:!1}),t.style.cursor="grab",t.style.touchAction="none";function ke(re){s=re,u=new Map(re.groups.map(we=>[we.name,we.slot]));const Se=o,Ce=Se.size===0,be=re.groups.map(we=>we.name),Ve=18*Math.sqrt(re.nodes.length),Ge=new Map;be.forEach((we,We)=>{const ve=We/Math.max(1,be.length)*Math.PI*2,I=We===0?0:Ve;Ge.set(we,[Math.cos(ve)*I,Math.sin(ve)*I])});const Pe=re.nodes.map(we=>{const We=Se.get(we.id),ve={id:we.id,r:SD(we.degree),group:we.group,node:we};if((We==null?void 0:We.x)!==void 0&&We.y!==void 0)ve.x=We.x,ve.y=We.y,ve.vx=We.vx??0,ve.vy=We.vy??0;else{const[I,nt]=Ge.get(we.group)??[0,0],Ke=6*Math.sqrt(re.nodes.length/Math.max(1,be.length));ve.x=I+(Math.random()-.5)*Ke,ve.y=nt+(Math.random()-.5)*Ke}return ve});if(r=Pe,o=new Map(Pe.map(we=>[we.id,we])),c=re.edges.filter(we=>o.has(we.source)&&o.has(we.target)).map(we=>({source:we.source,target:we.target})),d==null||d.stop(),d=J2(Pe).force("link",k2(c).id(we=>we.id).distance(30)).force("charge",eD().strength(-42).distanceMax(420).theta(.9)).force("x",tD(0).strength(.04)).force("y",nD(0).strength(.04)).force("collide",z2(we=>we.r+1.5).strength(.7).iterations(1)).stop(),Pv){const we=Math.ceil(Math.log(d.alphaMin())/Math.log(1-d.alphaDecay()));d.tick(we)}else Ce?d.tick(_D):d.alpha(.3);x!==null&&!o.has(x)&&(x=null,e.onSelect(null)),Ce&&(M=!1,T=!1,he(!0)),X()}function xt(){const re=t.getBoundingClientRect();b=window.devicePixelRatio||1;const Se=h>0;h=re.width,v=re.height,t.width=Math.max(1,Math.round(h*b)),t.height=Math.max(1,Math.round(v*b)),!Se&&r.length>0&&he(!0),X()}return{setModel:ke,setHidden(re){m=re,X()},setQuery(re){f=re,X()},setLabelMode(re){p=re,X()},setSelected(re){x=re,X()},setHovered(re){_=re,X()},focusNode(re){const Se=o.get(re);if((Se==null?void 0:Se.x)===void 0||Se.y===void 0)return;M=!0;const Ce=Math.max(g.k,yD);ge({k:Ce,x:h/2-Se.x*Ce,y:v/2-Se.y*Ce})},fit(){M=!0,he()},zoomBy(re){Ae(h/2,v/2,re)},resize:xt,refreshColours(){a=zv(t.parentElement??t),X()},dispose(){y!==0&&cancelAnimationFrame(y),d==null||d.stop(),t.removeEventListener("pointerdown",Ne),t.removeEventListener("pointermove",ie),t.removeEventListener("pointerup",xe),t.removeEventListener("pointercancel",xe),t.removeEventListener("pointerleave",Oe),t.removeEventListener("dblclick",oe),t.removeEventListener("wheel",Ee)}}}const TD=8,sl="Weitere",AD=new Set(["file","class","interface","note"]);function wD(t){return typeof t=="string"&&AD.has(t)?t:"other"}function Yi(t){return typeof t=="string"&&t!==""?t:null}function CD(t){return typeof t=="number"&&Number.isFinite(t)?t:null}function ND(t){if(t===null)return sl;const e=t.split("/").filter(Boolean);return e.length>=2&&e[0]==="Code"?e[1]:e.length>=2?e[0]:"Workspace"}function RD(t){var u,d,m;const e=new Map,n={file:0,class:0,interface:0,note:0,other:0};for(const f of t.nodes){const p=Yi(f==null?void 0:f.id);if(p===null||e.has(p))continue;const x=Yi((u=f.properties)==null?void 0:u.path),_=wD(f.type),g=Yi(f.label)??Yi(f.name)??(x==null?void 0:x.split("/").pop())??p;e.set(p,{id:p,label:g,kind:_,path:x,group:ND(x),lines:CD((d=f.properties)==null?void 0:d.lines),lang:Yi((m=f.properties)==null?void 0:m.lang),degree:0}),n[_]+=1}const i=[],a=new Map,s=new Set;for(const f of t.edges){const p=Yi(f==null?void 0:f.sourceId)??Yi(f==null?void 0:f.source),x=Yi(f==null?void 0:f.targetId)??Yi(f==null?void 0:f.target);if(p===null||x===null||p===x)continue;const _=e.get(p),g=e.get(x);if(_===void 0||g===void 0)continue;const h=Yi(f.type)??"link",v=`${p}\0${x}\0${h}`;s.has(v)||(s.add(v),i.push({source:p,target:x,kind:h}),_.degree+=1,g.degree+=1,a.has(p)||a.set(p,new Set),a.has(x)||a.set(x,new Set),a.get(p).add(x),a.get(x).add(p))}const r=new Map;for(const f of e.values())r.set(f.group,(r.get(f.group)??0)+1);const c=[...r.entries()].sort((f,p)=>p[1]-f[1]||f[0].localeCompare(p[0])).map(([f,p],x)=>({name:f,count:p,slot:x<TD&&f!==sl?x:-1}));return{nodes:[...e.values()],edges:i,groups:c,byId:e,neighbours:a,kindCounts:n}}function DD(t,e){var i;const n=e.trim().toLowerCase();return n===""?!1:t.label.toLowerCase().includes(n)||(((i=t.path)==null?void 0:i.toLowerCase().includes(n))??!1)}function jS(t){return[...t].sort((e,n)=>n.degree-e.degree||e.label.localeCompare(n.label))}const Za=60;function HS({timeline:t}){return l.jsxs("div",{className:"pb-timeline",children:[l.jsx("button",{type:"button",className:"pb-tool pb-tool--icon",onClick:t.onTogglePlay,"aria-label":t.playing?"Wachstum anhalten":"Wachstum abspielen",title:t.playing?"Anhalten":"Wachstum abspielen",children:l.jsx(Mt,{path:t.playing?vt.pause:vt.play})}),l.jsx("input",{type:"range",min:0,max:Za,step:1,value:t.step,"aria-label":"Zeitpunkt des Index","aria-valuetext":t.label,onChange:e=>t.onScrub(Number(e.target.value))}),l.jsx("span",{className:"pb-timeline__label pb-num",children:t.label})]})}const ch=120,nu=1.25,Iv={file:"Dateien",class:"Klassen",interface:"Interfaces",note:"Notizen",other:"Sonstige"},LD={file:"Datei",class:"Klasse",interface:"Interface",note:"Notiz",other:"Objekt"},UD={imports:["Importiert","Wird importiert von"],defines:["Definiert","Definiert in"],links:["Verweist auf","Verwiesen von"]},Ki=new Intl.NumberFormat("de-DE");function OD({graph:t,coverage:e,error:n,onRetry:i,onOpenSource:a,timeline:s}){const r=P.useMemo(()=>t?RD(t):null,[t]),o=P.useRef(null),c=P.useRef(null),u=P.useRef(null),d=P.useRef(null),m=P.useRef(null),[f,p]=P.useState(""),[x,_]=P.useState(""),[g,h]=P.useState(new Set),[v,b]=P.useState(new Set),[y,A]=P.useState("auto"),[M,T]=P.useState(null),[S,C]=P.useState(!0),R=P.useCallback(q=>{const fe=r==null?void 0:r.byId.get(q);fe!=null&&fe.path&&a(fe.path)},[r,a]),L=P.useRef(R);L.current=R,P.useEffect(()=>{const q=o.current,fe=c.current;if(q===null||fe===null)return;let Ne;try{Ne=ED(q,{onHover:()=>{},onSelect:oe=>T(oe),onOpen:oe=>L.current(oe),onZoom:oe=>{d.current&&(d.current.textContent=`${Math.round(oe*100)} %`)}})}catch(oe){p(oe instanceof Error?oe.message:String(oe));return}m.current=Ne;const ie=new ResizeObserver(()=>Ne.resize());ie.observe(fe),Ne.resize();const xe=new MutationObserver(()=>Ne.refreshColours());return xe.observe(document.documentElement,{attributes:!0,attributeFilter:["data-theme"]}),()=>{xe.disconnect(),ie.disconnect(),Ne.dispose(),m.current=null}},[]),P.useEffect(()=>{var q;r!==null&&((q=m.current)==null||q.setModel(r))},[r]),P.useEffect(()=>{var q;(q=m.current)==null||q.setHidden(fe=>g.has(fe.kind)||v.has(kv(r,fe)))},[g,v,r]);const O=P.useMemo(()=>r===null||x.trim()===""?new Set:new Set(r.nodes.filter(q=>DD(q,x)).map(q=>q.id)),[r,x]);P.useEffect(()=>{var q;(q=m.current)==null||q.setQuery(O)},[O]),P.useEffect(()=>{var q;(q=m.current)==null||q.setLabelMode(y)},[y]),P.useEffect(()=>{var q;(q=m.current)==null||q.setSelected(M)},[M]);const W=P.useCallback((q,fe=!0)=>{var Ne;T(q),fe&&((Ne=m.current)==null||Ne.focusNode(q))},[]),z=P.useMemo(()=>{if(r===null)return[];const q=x.trim()===""?r.nodes:r.nodes.filter(fe=>O.has(fe.id));return jS(q.filter(fe=>!g.has(fe.kind)&&!v.has(kv(r,fe))))},[r,x,O,g,v]),D=M!==null?(r==null?void 0:r.byId.get(M))??null:null;P.useEffect(()=>{const q=fe=>{var xe,oe,Ee,Oe;const Ne=fe.target,ie=Ne!==null&&(Ne.tagName==="INPUT"||Ne.tagName==="TEXTAREA"||Ne.isContentEditable);if(fe.key==="Escape"){if(ie&&Ne===u.current&&x!==""){_("");return}if(M!==null){T(null);return}ie&&Ne.blur();return}ie||fe.metaKey||fe.ctrlKey||fe.altKey||(fe.key==="/"?(fe.preventDefault(),(xe=u.current)==null||xe.focus()):fe.key==="f"||fe.key==="F"?(oe=m.current)==null||oe.fit():fe.key==="+"||fe.key==="="?(Ee=m.current)==null||Ee.zoomBy(nu):fe.key==="-"&&((Oe=m.current)==null||Oe.zoomBy(1/nu)))};return window.addEventListener("keydown",q),()=>window.removeEventListener("keydown",q)},[x,M]);const B=(q,fe)=>{const Ne=new Set(q);return Ne.has(fe)?Ne.delete(fe):Ne.add(fe),Ne},k=q=>{if(q.key!=="ArrowDown"&&q.key!=="ArrowUp")return;q.preventDefault();const fe=[...q.currentTarget.querySelectorAll("button")],Ne=fe.indexOf(document.activeElement),ie=fe[Math.max(0,Math.min(fe.length-1,Ne+(q.key==="ArrowDown"?1:-1)))];ie==null||ie.focus()},V=q=>{var fe;q.key==="Enter"&&z[0]&&W(z[0].id),q.key==="ArrowDown"&&(q.preventDefault(),(fe=document.querySelector(".pb-graph__list button"))==null||fe.focus())},X=n?"offline":r===null?"loading":r.nodes.length===0?"empty":"ready",ge=(r==null?void 0:r.groups.filter(q=>q.slot>=0))??[],he=(r==null?void 0:r.groups.filter(q=>q.slot<0))??[],Ae=he.reduce((q,fe)=>q+fe.count,0);return l.jsxs("section",{className:"pb-graph","data-inspector":D?"open":"closed","aria-label":"Wissensgraph",children:[l.jsxs("aside",{className:"pb-graph__side","aria-label":"Objekte im Graph",children:[l.jsxs("label",{className:"pb-search",children:[l.jsx(Mt,{path:vt.search}),l.jsx("input",{ref:u,type:"search",value:x,placeholder:"Datei oder Symbol suchen","aria-label":"Datei oder Symbol im Graph suchen",spellCheck:!1,autoComplete:"off",onChange:q=>_(q.target.value),onKeyDown:V}),l.jsx("kbd",{"aria-hidden":"true",children:"/"})]}),r!==null&&l.jsxs(l.Fragment,{children:[l.jsx("div",{className:"pb-chips",role:"group","aria-label":"Objektarten anzeigen",children:Object.keys(Iv).filter(q=>r.kindCounts[q]>0).map(q=>l.jsxs("button",{type:"button",className:"pb-chip","aria-pressed":!g.has(q),onClick:()=>h(fe=>B(fe,q)),children:[Iv[q]," ",l.jsx("span",{className:"pb-num",children:Ki.format(r.kindCounts[q])})]},q))}),l.jsx("h2",{className:"pb-heading",children:"Bereiche"}),l.jsxs("ul",{className:"pb-groups","aria-label":"Bereiche anzeigen oder ausblenden",children:[ge.map(q=>l.jsx("li",{children:l.jsxs("button",{type:"button","aria-pressed":!v.has(q.name),onClick:()=>b(fe=>B(fe,q.name)),children:[l.jsx("i",{className:"pb-dot","data-slot":q.slot,"aria-hidden":"true"}),l.jsx("span",{className:"pb-groups__name",title:q.name,children:q.name}),l.jsx("span",{className:"pb-num",children:Ki.format(q.count)})]})},q.name)),he.length>0&&l.jsx("li",{children:l.jsxs("button",{type:"button","aria-pressed":!v.has(sl),onClick:()=>b(q=>B(q,sl)),children:[l.jsx("i",{className:"pb-dot","data-slot":"-1","aria-hidden":"true"}),l.jsxs("span",{className:"pb-groups__name",children:[sl," · ",he.length," Bereiche"]}),l.jsx("span",{className:"pb-num",children:Ki.format(Ae)})]})})]}),l.jsx("h2",{className:"pb-heading",children:x.trim()===""?"Am stärksten vernetzt":`${Ki.format(z.length)} Treffer`}),z.length===0?l.jsx("p",{className:"pb-quiet",children:x.trim()===""?"Alle Objekte sind ausgeblendet.":"Kein Objekt passt zur Suche."}):l.jsxs("ol",{className:"pb-graph__list",onKeyDown:k,children:[z.slice(0,ch).map(q=>l.jsx("li",{children:l.jsxs("button",{type:"button","aria-current":q.id===M?"true":void 0,onClick:()=>W(q.id),onDoubleClick:()=>R(q.id),onMouseEnter:()=>{var fe;return(fe=m.current)==null?void 0:fe.setHovered(q.id)},onMouseLeave:()=>{var fe;return(fe=m.current)==null?void 0:fe.setHovered(null)},onFocus:()=>{var fe;return(fe=m.current)==null?void 0:fe.setHovered(q.id)},onBlur:()=>{var fe;return(fe=m.current)==null?void 0:fe.setHovered(null)},children:[l.jsx("i",{className:"pb-dot","data-slot":Mg(r,q),"aria-hidden":"true"}),l.jsxs("span",{className:"pb-row__text",children:[l.jsx("span",{className:"pb-row__label",children:q.label}),q.path&&l.jsx("span",{className:"pb-row__path",children:zD(q.path)})]}),l.jsx("span",{className:"pb-num",title:`${q.degree} Verbindungen`,children:q.degree})]})},q.id)),z.length>ch&&l.jsxs("li",{className:"pb-quiet",children:[Ki.format(z.length-ch)," weitere — Suche verfeinern"]})]})]})]}),l.jsxs("div",{className:"pb-graph__stage",ref:c,children:[l.jsx("canvas",{ref:o,className:"pb-graph__canvas",role:"img","aria-label":r?`Graph mit ${r.nodes.length} Objekten und ${r.edges.length} Kanten. Die Liste links ist die Tastaturansicht.`:"Graph","data-state":X}),X==="ready"&&l.jsxs("div",{className:"pb-toolbar",role:"toolbar","aria-label":"Graph-Ansicht",children:[l.jsxs("button",{type:"button",className:"pb-tool",onClick:()=>{var q;return(q=m.current)==null?void 0:q.fit()},title:"Alles einpassen (F)",children:[l.jsx(Mt,{path:vt.fit}),l.jsx("span",{children:"Einpassen"})]}),l.jsx("span",{className:"pb-toolbar__sep","aria-hidden":"true"}),l.jsx("button",{type:"button",className:"pb-tool pb-tool--icon",onClick:()=>{var q;return(q=m.current)==null?void 0:q.zoomBy(1/nu)},"aria-label":"Herauszoomen",title:"Herauszoomen (−)",children:l.jsx(Mt,{path:vt.minus})}),l.jsx("span",{className:"pb-toolbar__zoom pb-num",ref:d,"aria-live":"off",children:"100 %"}),l.jsx("button",{type:"button",className:"pb-tool pb-tool--icon",onClick:()=>{var q;return(q=m.current)==null?void 0:q.zoomBy(nu)},"aria-label":"Hineinzoomen",title:"Hineinzoomen (+)",children:l.jsx(Mt,{path:vt.plus})}),l.jsx("span",{className:"pb-toolbar__sep","aria-hidden":"true"}),l.jsx("div",{className:"pb-segmented",role:"radiogroup","aria-label":"Beschriftungen",children:[["auto","Auto"],["always","Alle"],["off","Aus"]].map(([q,fe])=>l.jsx("button",{type:"button",role:"radio","aria-checked":y===q,onClick:()=>A(q),children:fe},q))}),l.jsx("span",{className:"pb-toolbar__sep","aria-hidden":"true"}),l.jsx("button",{type:"button",className:`pb-tool ${S?"pb-tool--active":""}`,onClick:()=>C(q=>!q),title:"Graph-Legende ein-/ausblenden",children:l.jsx("span",{children:"Legende"})}),s&&l.jsxs(l.Fragment,{children:[l.jsx("span",{className:"pb-toolbar__sep","aria-hidden":"true"}),l.jsx(HS,{timeline:s})]})]}),X==="ready"&&S&&l.jsxs("aside",{className:"pb-graph-legend","aria-label":"Graph-Legende",children:[l.jsxs("header",{className:"pb-graph-legend__head",children:[l.jsx("strong",{children:"Graph-Legende"}),l.jsx("button",{type:"button",className:"pb-tool pb-tool--icon",style:{width:"20px",height:"20px",padding:0},onClick:()=>C(!1),"aria-label":"Legende ausblenden",title:"Ausblenden",children:"✕"})]}),l.jsxs("div",{className:"pb-graph-legend__body",children:[l.jsxs("div",{className:"pb-graph-legend__row",children:[l.jsx("span",{className:"pb-legend-dot-sample"}),l.jsxs("div",{children:[l.jsx("strong",{children:"Knotengröße:"})," Vernetzungsgrad (mehr Verbindungen = größerer Knoten)"]})]}),l.jsxs("div",{className:"pb-graph-legend__row",children:[l.jsx("span",{className:"pb-legend-line-sample"}),l.jsxs("div",{children:[l.jsx("strong",{children:"Kanten:"})," Beziehungen (Code-Importe, Symbol-Definitionen, Wiki-Links)"]})]}),l.jsxs("div",{className:"pb-graph-legend__row",children:[l.jsx("span",{className:"pb-legend-color-sample"}),l.jsxs("div",{children:[l.jsx("strong",{children:"Farben:"})," Repositories & Wissensbereiche (Farben links zugeordnet)"]})]})]})]}),X==="ready"&&r&&l.jsxs("p",{className:"pb-graph__status",role:"status",children:[l.jsx("span",{className:"pb-num",children:Ki.format(r.nodes.length)})," Objekte ·"," ",l.jsx("span",{className:"pb-num",children:Ki.format(r.edges.length)})," Kanten",e&&e.totalFiles>e.shownFiles&&l.jsxs("span",{title:"Der Graph zeigt die am stärksten vernetzten Dateien; Suche und Explorer umfassen den ganzen Index.",children:[" ","· Ausschnitt aus ",l.jsx("span",{className:"pb-num",children:Ki.format(e.totalFiles)})," Dateien"]}),e&&!e.indexComplete&&l.jsxs("span",{className:"pb-graph__stale",children:[" · ",Ki.format(e.staleFiles??0)," Dateien warten auf den Index"]})]}),X!=="ready"&&l.jsxs("div",{className:"pb-state",role:X==="offline"?"alert":"status",children:[X==="loading"&&l.jsxs(l.Fragment,{children:[l.jsx("div",{className:"pb-state__bar","aria-hidden":"true",children:l.jsx("span",{})}),l.jsx("h2",{children:"Graph wird geladen"}),l.jsx("p",{children:"Der aktuelle Stand wird aus dem Brain gelesen."})]}),X==="empty"&&l.jsxs(l.Fragment,{children:[l.jsx("h2",{children:"Noch keine indizierten Objekte"}),l.jsx("p",{children:"Sobald der Index Dateien enthält, erscheinen sie hier."})]}),X==="offline"&&l.jsxs(l.Fragment,{children:[l.jsx("h2",{children:"PlugBrain ist nicht erreichbar"}),l.jsx("p",{children:n}),l.jsx("button",{type:"button",className:"pb-button pb-button--primary",onClick:i,children:"Erneut verbinden"})]})]}),f&&l.jsxs("div",{className:"pb-state",role:"alert",children:[l.jsx("h2",{children:"Der Graph kann hier nicht gezeichnet werden"}),l.jsxs("p",{children:[f," Die Liste links bleibt vollständig nutzbar."]})]})]}),D&&r&&l.jsx(PD,{node:D,model:r,onClose:()=>T(null),onSelect:q=>W(q),onOpen:()=>R(D.id),onCentre:()=>{var q;return(q=m.current)==null?void 0:q.focusNode(D.id)}})]})}function PD({node:t,model:e,onClose:n,onSelect:i,onOpen:a,onCentre:s}){const r=P.useMemo(()=>{const c=new Map,u=(d,m)=>{if(m===void 0)return;const f=c.get(d)??[];f.push(m),c.set(d,f)};for(const d of e.edges){const[m,f]=UD[d.kind]??["Verbunden mit","Verbunden mit"];d.source===t.id?u(m,e.byId.get(d.target)):d.target===t.id&&u(f,e.byId.get(d.source))}return[...c.entries()].map(([d,m])=>[d,jS(m)])},[t,e]),o=e.groups.find(c=>c.name===t.group);return l.jsxs("aside",{className:"pb-inspector","aria-label":`Details zu ${t.label}`,children:[l.jsxs("header",{className:"pb-inspector__head",children:[l.jsxs("div",{className:"pb-inspector__kicker",children:[l.jsx("i",{className:"pb-dot","data-slot":(o==null?void 0:o.slot)??-1,"aria-hidden":"true"}),l.jsx("span",{children:LD[t.kind]}),l.jsx("span",{"aria-hidden":"true",children:"·"}),l.jsx("span",{className:"pb-inspector__group",title:t.group,children:t.group})]}),l.jsx("button",{type:"button",className:"pb-tool pb-tool--icon",onClick:n,"aria-label":"Details schließen",title:"Schließen (Esc)",children:l.jsx(Mt,{path:vt.close})})]}),l.jsx("h2",{className:"pb-inspector__title",children:t.label}),t.path&&l.jsx("p",{className:"pb-inspector__path",children:t.path}),l.jsxs("dl",{className:"pb-facts",children:[l.jsxs("div",{children:[l.jsx("dt",{children:"Verbindungen"}),l.jsx("dd",{className:"pb-num",children:t.degree})]}),t.lines!==null&&l.jsxs("div",{children:[l.jsx("dt",{children:"Zeilen"}),l.jsx("dd",{className:"pb-num",children:Ki.format(t.lines)})]}),t.lang&&l.jsxs("div",{children:[l.jsx("dt",{children:"Sprache"}),l.jsx("dd",{children:t.lang})]})]}),l.jsxs("div",{className:"pb-inspector__actions",children:[t.path&&l.jsxs("button",{type:"button",className:"pb-button pb-button--primary",onClick:a,children:[l.jsx(Mt,{path:vt.open})," ",t.kind==="note"?"Notiz öffnen":"Quelle öffnen"]}),l.jsxs("button",{type:"button",className:"pb-button",onClick:s,children:[l.jsx(Mt,{path:vt.centre})," Zentrieren"]})]}),r.length===0?l.jsx("p",{className:"pb-quiet",children:"Dieses Objekt hat im gezeigten Ausschnitt keine Verbindungen."}):r.map(([c,u])=>l.jsxs("section",{className:"pb-inspector__section",children:[l.jsxs("h3",{className:"pb-heading",children:[c," ",l.jsx("span",{className:"pb-num",children:u.length})]}),l.jsxs("ul",{children:[u.slice(0,40).map(d=>l.jsx("li",{children:l.jsxs("button",{type:"button",onClick:()=>i(d.id),children:[l.jsx("i",{className:"pb-dot","data-slot":Mg(e,d),"aria-hidden":"true"}),l.jsx("span",{className:"pb-row__label",children:d.label})]})},d.id)),u.length>40&&l.jsxs("li",{className:"pb-quiet",children:[u.length-40," weitere"]})]})]},c))]})}function Mg(t,e){var n;return((n=t.groups.find(i=>i.name===e.group))==null?void 0:n.slot)??-1}function kv(t,e){return t===null||Mg(t,e)>=0?e.group:sl}function zD(t){const e=t.split("/");return e.pop(),e.slice(-3).join("/")}function ID({workspaceId:t,workspaceName:e,onOpenFile:n,onOpenNotes:i,initialData:a}){var g,h,v,b,y;const[s,r]=P.useState(a??null),[o,c]=P.useState(!a),[u,d]=P.useState(!1),[m,f]=P.useState(!1),[p,x]=P.useState(null),_=()=>{if(a)return;c(!0),d(!1),f(!1),x(null);let A=!0;return xT(t).then(M=>{A&&(c(!1),"routeMissing"in M?d(!0):"unreachable"in M?f(!0):"serverError"in M?x(`Fehler ${M.status}: ${M.error}`):r(M))}).catch(M=>{A&&(c(!1),x(M instanceof Error?M.message:String(M)))}),()=>{A=!1}};return P.useEffect(()=>{if(a){r(a),c(!1),d(!1),f(!1);return}return _()},[t,a]),l.jsxs("div",{className:"briefing-view",children:[l.jsxs("div",{className:"pb-view-header",children:[l.jsx("h2",{children:"Projekt-Briefing"}),l.jsx("p",{children:"Projektüberblick, Kennzahlen, Einstiegspunkte und Hotspots"})]}),l.jsxs("div",{className:"briefing-body",children:[o&&l.jsx("div",{className:"briefing-card briefing-card--loading",role:"status",children:l.jsx("p",{children:"Briefing wird geladen …"})}),u&&!o&&l.jsxs("div",{className:"briefing-card briefing-card--unavailable",role:"status",children:[l.jsx("div",{className:"briefing-unavailable-icon",children:l.jsx(Mt,{path:vt.info})}),l.jsx("h3",{children:"Briefing in dieser Version noch nicht verfügbar"}),l.jsxs("p",{children:["Die Route ",l.jsx("code",{children:"GET /api/briefing"})," ist im angebundenen Kern noch nicht aktiv. Sobald Lane ASK-01 bereitgestellt ist, erscheinen hier automatisch Projekt-Zusammenfassung, Kennzahlen, Einstiegspunkte und Code-Hotspots."]})]}),m&&!o&&l.jsxs("div",{className:"briefing-card briefing-card--error",role:"alert",children:[l.jsx("h3",{children:"Brain nicht erreichbar"}),l.jsxs("p",{children:["Keine Verbindung zum Brain-Server — läuft ",l.jsx("code",{children:"plugbrain serve"}),"?"]}),l.jsx("button",{type:"button",className:"btn btn-secondary",onClick:_,children:"Erneut versuchen"})]}),p&&!o&&l.jsxs("div",{className:"briefing-card briefing-card--error",role:"alert",children:[l.jsx("h3",{children:"Fehler beim Laden des Briefings"}),l.jsx("p",{children:p}),l.jsx("button",{type:"button",className:"btn btn-secondary",onClick:_,children:"Erneut versuchen"})]}),s&&s.unavailable&&s.unavailable.length>0&&l.jsx("div",{className:"briefing-card briefing-card--notice",role:"status",children:l.jsxs("p",{children:["Hinweis: Folgende Bereiche sind nicht verfügbar: ",s.unavailable.join(", ")]})}),s&&!o&&l.jsxs("div",{className:"briefing-grid",children:[l.jsxs("section",{className:"briefing-card briefing-card--summary",children:[l.jsx("header",{className:"briefing-card__head",children:l.jsx("h3",{children:s.name||e||"Workspace"})}),l.jsx("p",{className:"briefing-summary-text",children:s.summary||"Keine Beschreibung hinterlegt."})]}),l.jsxs("section",{className:"briefing-card briefing-card--stats",children:[l.jsx("header",{className:"briefing-card__head",children:l.jsx("h3",{children:"Kennzahlen"})}),l.jsxs("div",{className:"briefing-stats-row",children:[l.jsxs("div",{className:"briefing-stat",children:[l.jsx("span",{className:"briefing-stat__num",children:((g=s.stats)==null?void 0:g.repos)??0}),l.jsx("span",{className:"briefing-stat__label",children:"Repos"})]}),l.jsxs("div",{className:"briefing-stat",children:[l.jsx("span",{className:"briefing-stat__num",children:((h=s.stats)==null?void 0:h.files)??0}),l.jsx("span",{className:"briefing-stat__label",children:"Dateien"})]}),l.jsxs("div",{className:"briefing-stat",children:[l.jsx("span",{className:"briefing-stat__num",children:((v=s.stats)==null?void 0:v.symbols)??0}),l.jsx("span",{className:"briefing-stat__label",children:"Symbole"})]}),l.jsxs("div",{className:"briefing-stat",children:[l.jsx("span",{className:"briefing-stat__num",children:((b=s.stats)==null?void 0:b.notes)??0}),l.jsx("span",{className:"briefing-stat__label",children:"Notizen"})]})]}),((y=s.stats)==null?void 0:y.languages)&&s.stats.languages.length>0&&l.jsxs("div",{className:"briefing-languages",children:[l.jsx("strong",{className:"briefing-subhead",children:"Sprachen:"}),l.jsx("div",{className:"briefing-lang-chips",children:s.stats.languages.map(A=>l.jsxs("span",{className:"briefing-lang-chip",children:[A.name," (",A.files,")"]},A.name))})]})]}),l.jsxs("section",{className:"briefing-card briefing-card--entrypoints",children:[l.jsx("header",{className:"briefing-card__head",children:l.jsx("h3",{children:"Einstiegspunkte"})}),!s.entrypoints||s.entrypoints.length===0?l.jsx("p",{className:"briefing-empty",children:"Keine Einstiegspunkte erkannt."}):l.jsx("ul",{className:"briefing-list",children:s.entrypoints.map(A=>l.jsxs("li",{className:"briefing-list-item",children:[l.jsxs("button",{type:"button",className:"briefing-link-btn",onClick:()=>n(A.path),children:[l.jsx(Mt,{path:vt.file}),l.jsx("span",{className:"briefing-path",children:A.path})]}),A.why&&l.jsx("span",{className:"briefing-desc",children:A.why})]},A.path))})]}),l.jsxs("section",{className:"briefing-card briefing-card--changes",children:[l.jsx("header",{className:"briefing-card__head",children:l.jsx("h3",{children:"Zuletzt geändert"})}),!s.recentChanges||s.recentChanges.length===0?l.jsx("p",{className:"briefing-empty",children:"Keine aktuellen Änderungen verzeichnet."}):l.jsx("ul",{className:"briefing-list",children:s.recentChanges.map(A=>l.jsxs("li",{className:"briefing-list-item",children:[l.jsx("button",{type:"button",className:"briefing-link-btn",onClick:()=>n(A.path),children:l.jsx("span",{className:"briefing-path",children:A.path})}),l.jsxs("div",{className:"briefing-change-meta",children:[l.jsx("span",{className:"briefing-kind-badge",children:A.kind}),l.jsx("time",{className:"briefing-time",children:new Date(A.when).toLocaleString()})]})]},A.path+A.when))})]}),l.jsxs("section",{className:"briefing-card briefing-card--hotspots",children:[l.jsx("header",{className:"briefing-card__head",children:l.jsx("h3",{children:"Code-Hotspots (hohe Vernetzung)"})}),!s.hotspots||s.hotspots.length===0?l.jsx("p",{className:"briefing-empty",children:"Keine Hotspots identifiziert."}):l.jsx("ul",{className:"briefing-list",children:s.hotspots.map(A=>l.jsxs("li",{className:"briefing-list-item",children:[l.jsxs("button",{type:"button",className:"briefing-link-btn",onClick:()=>n(A.path),children:[l.jsx(Mt,{path:vt.code}),l.jsx("span",{className:"briefing-path",children:A.path})]}),l.jsxs("span",{className:"briefing-degree-badge",children:["Grad ",A.degree]})]},A.path))})]})]})]})]})}function To(t){return t==="risk"?"hygiene-risk":t==="attention"?"hygiene-attention":"hygiene-ok"}function Ao(t){return t==="risk"?"● Risiko":t==="attention"?"◌ Achtung":"● Ok"}function mf({cmd:t,label:e}){const[n,i]=P.useState(!1),a=P.useCallback(()=>{navigator.clipboard.writeText(t).then(()=>{i(!0),setTimeout(()=>i(!1),2e3)}).catch(()=>{})},[t]);return l.jsxs("div",{className:"hygiene-cmd-row",children:[l.jsx("code",{className:"hygiene-cmd",children:t}),l.jsx("button",{type:"button",className:"hygiene-copy-btn",onClick:a,title:e??"Befehl in Zwischenablage kopieren","aria-label":e??"Befehl kopieren",children:n?"Kopiert":"Kopieren"})]})}function kD({co:t}){const[e,n]=P.useState(!1),i=t.dirtyFiles>0||t.untrackedFiles>0||t.stashes>0||t.unpushed.length>0||t.orphan,a=t.orphan||t.dirtyFiles>5?"risk":i?"attention":"ok";return l.jsxs("div",{className:`hygiene-checkout-card ${To(a)}`,children:[l.jsxs("button",{type:"button",className:"hygiene-checkout-header",onClick:()=>n(s=>!s),"aria-expanded":e,children:[l.jsx("span",{className:`hygiene-badge hygiene-badge-${a}`,children:Ao(a)}),l.jsx("span",{className:"hygiene-checkout-path",children:t.path}),l.jsxs("span",{className:"hygiene-checkout-meta",children:[t.repo," · ",t.branch]}),l.jsx("span",{className:"hygiene-toggle",children:e?"▲":"▼"})]}),e&&l.jsxs("div",{className:"hygiene-checkout-detail",children:[l.jsxs("div",{className:"hygiene-stat-grid",children:[l.jsxs("div",{className:"hygiene-stat",children:[l.jsx("span",{className:"hygiene-stat-label",children:"Geänderte Dateien"}),l.jsx("span",{className:`hygiene-stat-value ${t.dirtyFiles>0?"hygiene-stat-warn":""}`,children:t.dirtyFiles})]}),l.jsxs("div",{className:"hygiene-stat",children:[l.jsx("span",{className:"hygiene-stat-label",children:"Unverfolgte Dateien"}),l.jsx("span",{className:`hygiene-stat-value ${t.untrackedFiles>0?"hygiene-stat-warn":""}`,children:t.untrackedFiles})]}),l.jsxs("div",{className:"hygiene-stat",children:[l.jsx("span",{className:"hygiene-stat-label",children:"Stashes"}),l.jsx("span",{className:`hygiene-stat-value ${t.stashes>0?"hygiene-stat-warn":""}`,children:t.stashes})]}),l.jsxs("div",{className:"hygiene-stat",children:[l.jsx("span",{className:"hygiene-stat-label",children:"Größe"}),l.jsxs("span",{className:"hygiene-stat-value",children:[t.sizeMb.toFixed(0)," MB"]})]}),l.jsxs("div",{className:"hygiene-stat",children:[l.jsx("span",{className:"hygiene-stat-label",children:"Zuletzt aktiv"}),l.jsxs("span",{className:`hygiene-stat-value ${t.staleDays>30?"hygiene-stat-warn":""}`,children:[t.staleDays," Tage"]})]}),t.orphan&&l.jsx("div",{className:"hygiene-stat hygiene-stat-full",children:l.jsx("span",{className:"hygiene-badge hygiene-badge-risk",children:"Verwaister Checkout / Worktree"})})]}),t.unpushed.length>0&&l.jsxs("div",{className:"hygiene-section",children:[l.jsx("div",{className:"hygiene-section-title",children:"Ungepushte Branches"}),t.unpushed.map((s,r)=>l.jsxs("div",{className:"hygiene-unpushed-row",children:[l.jsx("span",{className:"hygiene-branch",children:s.branch}),l.jsxs("span",{className:"hygiene-ahead",children:[s.ahead," Commits voraus"]}),s.upstream?l.jsxs("span",{className:"hygiene-upstream",children:["→ ",s.upstream]}):l.jsx("span",{className:"hygiene-no-upstream",children:"Kein Upstream konfiguriert"})]},r))]}),t.claims&&t.claims.length>0&&l.jsxs("div",{className:"hygiene-section",children:[l.jsx("div",{className:"hygiene-section-title",children:"Aktive Sperren (Claims)"}),t.claims.map((s,r)=>l.jsxs("div",{className:"hygiene-claim-row",children:[l.jsx("span",{className:"hygiene-agent",children:s.agent}),l.jsx("span",{className:"hygiene-claim-paths",children:s.paths.join(", ")}),s.conflictsWith.length>0&&l.jsxs("span",{className:"hygiene-conflicts",children:["Konflikt mit: ",s.conflictsWith.join(", ")]})]},r))]}),(t.dirtyFiles>0||t.stashes>0)&&l.jsxs("div",{className:"hygiene-section",children:[l.jsx("div",{className:"hygiene-section-title",children:"WIP sichern — Befehl zum Kopieren"}),l.jsx(mf,{cmd:`cd "${t.path}" && git stash push -m "wip-$(date +%Y%m%d-%H%M%S)"`})]})]})]})}function BD({f:t}){return l.jsxs("div",{className:`hygiene-finding-row ${To(t.level)}`,children:[l.jsx("span",{className:`hygiene-badge hygiene-badge-${t.level}`,children:Ao(t.level)}),l.jsxs("div",{className:"hygiene-finding-body",children:[l.jsx("div",{className:"hygiene-finding-text",children:t.text}),t.paths.length>0&&l.jsx("div",{className:"hygiene-finding-paths",children:t.paths.join(", ")}),t.fix&&l.jsx(mf,{cmd:t.fix})]})]})}function FD({workspaceId:t}){const[e,n]=P.useState("workspace"),[i,a]=P.useState(null),[s,r]=P.useState("loading"),[o,c]=P.useState(""),[u,d]=P.useState(null),[m,f]=P.useState("loading"),[p,x]=P.useState(null),[_,g]=P.useState("loading"),[h,v]=P.useState(!1),b=P.useCallback(()=>{if(!t){r("unavailable");return}r("loading"),_T(t).then(M=>{"routeMissing"in M?r("unavailable"):"unreachable"in M?(c("Brain nicht erreichbar — läuft plugbrain serve?"),r("unreachable")):"serverError"in M?(c(`Fehler ${M.status}: ${M.error}`),r("error")):(a(M),r("ready"))}).catch(M=>{c((M==null?void 0:M.message)??"Unbekannter Fehler"),r("error")})},[t]),y=P.useCallback(()=>{f("loading"),yT().then(M=>{"routeMissing"in M?f("unavailable"):"unreachable"in M?f("unreachable"):"serverError"in M?f("error"):(d(M),f("ready"))}).catch(()=>{f("unreachable")})},[]),A=P.useCallback(()=>{g("loading"),bT().then(M=>{"routeMissing"in M?g("unavailable"):"unreachable"in M?g("unreachable"):"serverError"in M?g("error"):(x(M),g("ready"))}).catch(()=>{g("unreachable")})},[]);return P.useEffect(()=>{b(),y(),A()},[b,y,A]),l.jsxs("div",{className:"hygiene-view",children:[l.jsxs("div",{className:"view-header",children:[l.jsxs("div",{className:"view-header-text",children:[l.jsx("h2",{className:"view-title",children:"Aufräumen"}),l.jsx("p",{className:"view-subtitle",children:"Checkouts, ungepushte Branches und ungesicherte Arbeit auf einen Blick"})]}),l.jsxs("div",{className:"view-header-actions",children:[l.jsx("button",{type:"button",className:"btn btn-secondary",onClick:()=>v(!0),title:"Befehl zum Sichern ungesicherter Arbeit anzeigen",children:"WIP sichern"}),l.jsx("button",{type:"button",className:"btn btn-secondary",onClick:()=>{b(),y(),A()},title:"Hygiene-Status neu laden",children:"↻ Aktualisieren"})]})]}),l.jsxs("div",{className:"hygiene-subnav",role:"tablist","aria-label":"Hygiene-Bereiche",children:[l.jsx("button",{type:"button",role:"tab",className:`hygiene-subnav-btn ${e==="workspace"?"active":""}`,"aria-selected":e==="workspace",onClick:()=>n("workspace"),children:"Workspace-Checkouts"}),l.jsx("button",{type:"button",role:"tab",className:`hygiene-subnav-btn ${e==="machine"?"active":""}`,"aria-selected":e==="machine",onClick:()=>n("machine"),children:"System & Festplatten"}),l.jsx("button",{type:"button",role:"tab",className:`hygiene-subnav-btn ${e==="repos"?"active":""}`,"aria-selected":e==="repos",onClick:()=>n("repos"),children:"Globale Git-Inventur"})]}),h&&l.jsx("div",{className:"hygiene-modal-backdrop",onClick:()=>v(!1),children:l.jsxs("div",{className:"hygiene-modal-content",onClick:M=>M.stopPropagation(),children:[l.jsxs("div",{className:"hygiene-modal-header",children:[l.jsx("h3",{className:"hygiene-modal-title",children:"WIP sichern (Ungesicherte Arbeit)"}),l.jsx("button",{type:"button",className:"hygiene-modal-close",onClick:()=>v(!1),"aria-label":"Schließen",children:"✕"})]}),l.jsxs("div",{className:"hygiene-modal-body",children:[l.jsxs("p",{children:["Der folgende Befehl legt automatisch WIP-Snapshots über einen temporären Index an.",l.jsx("strong",{children:" Dein Arbeitsbaum und Index bleiben unverändert"}),":"]}),l.jsx(mf,{cmd:"plugbrain hygiene --wip-snapshot",label:"CLI-Befehl kopieren"}),l.jsxs("div",{className:"hygiene-modal-note",children:[l.jsx("p",{className:"hygiene-note-title",children:"Was bewirkt dieser Befehl?"}),l.jsxs("p",{children:["Er erstellt WIP-Commits unter ",l.jsx("code",{children:"refs/wip/<datum>/<name>"})," für alle schmutzigen Checkouts. Deine lokalen Dateien und uncommitted Changes werden dabei nicht berührt."]})]})]}),l.jsx("div",{className:"hygiene-modal-footer",children:l.jsx("button",{type:"button",className:"btn btn-secondary",onClick:()=>v(!1),children:"Schließen"})})]})}),e==="workspace"&&l.jsxs("div",{className:"hygiene-tab-content",children:[s==="loading"&&l.jsxs("div",{className:"hygiene-status-box",children:[l.jsx("div",{className:"hygiene-spinner","aria-label":"Wird geladen…"}),l.jsx("p",{className:"hygiene-status-text",children:"Hygiene-Daten werden abgerufen…"})]}),s==="unavailable"&&l.jsxs("div",{className:"hygiene-status-box hygiene-offline",children:[l.jsx("p",{className:"hygiene-status-title",children:"Hygiene-Daten nicht verfügbar"}),l.jsx("p",{className:"hygiene-status-text",children:t?"Die Route /api/hygiene ist auf diesem Kern noch nicht aktiv (Lane HYG-01).":"Kein Workspace ausgewählt. Öffne zunächst einen Vault."}),t&&l.jsx("button",{type:"button",className:"btn btn-secondary",onClick:b,children:"Erneut versuchen"})]}),s==="unreachable"&&l.jsxs("div",{className:"hygiene-status-box hygiene-error",children:[l.jsx("p",{className:"hygiene-status-title",children:"Brain nicht erreichbar"}),l.jsxs("p",{className:"hygiene-status-text",children:["Keine Verbindung zum Brain-Server — läuft ",l.jsx("code",{children:"plugbrain serve"}),"?"]}),l.jsx("button",{type:"button",className:"btn btn-secondary",onClick:b,children:"Erneut versuchen"})]}),s==="error"&&l.jsxs("div",{className:"hygiene-status-box hygiene-error",children:[l.jsx("p",{className:"hygiene-status-title",children:"Fehler beim Laden"}),l.jsx("p",{className:"hygiene-status-text",children:o}),l.jsx("button",{type:"button",className:"btn btn-secondary",onClick:b,children:"Erneut versuchen"})]}),s==="ready"&&i&&l.jsxs(l.Fragment,{children:[l.jsxs("div",{className:`hygiene-summary-banner ${To(i.summary.level)}`,children:[l.jsx("span",{className:`hygiene-badge hygiene-badge-${i.summary.level}`,children:Ao(i.summary.level)}),l.jsx("span",{className:"hygiene-summary-text",children:i.summary.text}),l.jsxs("span",{className:"hygiene-summary-meta",children:["Freier Speicher: ",i.diskFreeGb.toFixed(1)," GB · Geprüft: ",new Date(i.checkedAt).toLocaleTimeString("de-DE",{hour:"2-digit",minute:"2-digit"})]})]}),i.findings.length>0&&l.jsxs("section",{className:"hygiene-section-container",children:[l.jsxs("h3",{className:"hygiene-section-heading",children:["Befunde (",i.findings.length,")"]}),l.jsx("div",{className:"hygiene-findings-list",children:i.findings.map((M,T)=>l.jsx(BD,{f:M},T))})]}),l.jsxs("section",{className:"hygiene-section-container",children:[l.jsxs("h3",{className:"hygiene-section-heading",children:["Checkouts (",i.checkouts.length,")"]}),i.checkouts.length===0?l.jsx("div",{className:"hygiene-empty",children:l.jsx("p",{children:"Keine Checkouts im aktuellen Workspace gefunden."})}):l.jsx("div",{className:"hygiene-checkouts-list",children:i.checkouts.map((M,T)=>l.jsx(kD,{co:M},T))})]}),i.unavailable&&i.unavailable.length>0&&l.jsx("div",{className:"hygiene-unavailable-note",children:l.jsxs("span",{children:["Teile der Daten nicht verfügbar: ",i.unavailable.join(", ")]})})]})]}),e==="machine"&&l.jsxs("div",{className:"hygiene-tab-content",children:[m==="loading"&&l.jsxs("div",{className:"hygiene-status-box",children:[l.jsx("div",{className:"hygiene-spinner","aria-label":"Wird geladen…"}),l.jsx("p",{className:"hygiene-status-text",children:"System-Ressourcen werden ermittelt…"})]}),m==="unavailable"&&l.jsxs("div",{className:"hygiene-status-box hygiene-offline",children:[l.jsx("p",{className:"hygiene-status-title",children:"System-Status nicht verfügbar"}),l.jsxs("p",{className:"hygiene-status-text",children:["Die Schnittstelle ",l.jsx("code",{children:"/api/machine"})," ist in dieser Version des Kerns noch nicht aktiv (Lane HYG-02)."]})]}),m==="unreachable"&&l.jsxs("div",{className:"hygiene-status-box hygiene-error",children:[l.jsx("p",{className:"hygiene-status-title",children:"Brain nicht erreichbar"}),l.jsxs("p",{className:"hygiene-status-text",children:["Keine Verbindung zum Brain-Server — läuft ",l.jsx("code",{children:"plugbrain serve"}),"?"]}),l.jsx("button",{type:"button",className:"btn btn-secondary",onClick:y,children:"Erneut versuchen"})]}),m==="error"&&l.jsxs("div",{className:"hygiene-status-box hygiene-error",children:[l.jsx("p",{className:"hygiene-status-title",children:"Fehler beim Laden"}),l.jsx("p",{className:"hygiene-status-text",children:"System-Daten konnten nicht geladen werden."}),l.jsx("button",{type:"button",className:"btn btn-secondary",onClick:y,children:"Erneut versuchen"})]}),m==="ready"&&u&&l.jsxs("div",{className:"hygiene-machine-container",children:[u.drives&&u.drives.length>0&&l.jsxs("section",{className:"hygiene-section-container",children:[l.jsx("h3",{className:"hygiene-section-heading",children:"Festplatten & Partitionen"}),l.jsx("div",{className:"hygiene-drives-grid",children:u.drives.map((M,T)=>l.jsxs("div",{className:`hygiene-drive-card ${To(M.level)}`,children:[l.jsxs("div",{className:"hygiene-drive-header",children:[l.jsx("span",{className:"hygiene-drive-mount",children:M.mount}),l.jsx("span",{className:`hygiene-badge hygiene-badge-${M.level}`,children:Ao(M.level)})]}),l.jsxs("div",{className:"hygiene-drive-numbers",children:[l.jsxs("span",{className:"hygiene-drive-free",children:[M.freeGb.toFixed(1)," GB frei"]}),l.jsxs("span",{className:"hygiene-drive-total",children:["von ",M.totalGb.toFixed(0)," GB"]})]}),l.jsx("div",{className:"hygiene-progress-bar",children:l.jsx("div",{className:"hygiene-progress-fill",style:{width:`${Math.min(100,Math.max(0,(M.totalGb-M.freeGb)/M.totalGb*100))}%`}})})]},T))})]}),u.forecast&&u.forecast.length>0&&l.jsxs("section",{className:"hygiene-section-container",children:[l.jsx("h3",{className:"hygiene-section-heading",children:"Speicher-Prognose"}),l.jsx("div",{className:"hygiene-forecast-list",children:u.forecast.map((M,T)=>l.jsxs("div",{className:"hygiene-forecast-card",children:[l.jsx("span",{className:"hygiene-forecast-mount",children:M.mount}),l.jsxs("span",{className:"hygiene-forecast-text",children:["Voll in ca. ",l.jsxs("strong",{children:[M.fullInHours.toFixed(1)," Stunden"]})," (",M.trendGbPerHour>0?"+":"",M.trendGbPerHour.toFixed(1)," GB/h, Basis: ",M.basis,")"]})]},T))})]}),u.findings&&u.findings.length>0&&l.jsxs("section",{className:"hygiene-section-container",children:[l.jsx("h3",{className:"hygiene-section-heading",children:"System-Befunde"}),l.jsx("div",{className:"hygiene-findings-list",children:u.findings.map((M,T)=>l.jsxs("div",{className:`hygiene-finding-row ${To(M.level)}`,children:[l.jsx("span",{className:`hygiene-badge hygiene-badge-${M.level}`,children:Ao(M.level)}),l.jsxs("div",{className:"hygiene-finding-body",children:[l.jsx("div",{className:"hygiene-finding-text",children:M.text}),M.fix&&l.jsx(mf,{cmd:M.fix})]})]},T))})]})]})]}),e==="repos"&&l.jsxs("div",{className:"hygiene-tab-content",children:[_==="loading"&&l.jsxs("div",{className:"hygiene-status-box",children:[l.jsx("div",{className:"hygiene-spinner","aria-label":"Wird geladen…"}),l.jsx("p",{className:"hygiene-status-text",children:"Globale Git-Inventur wird abgerufen…"})]}),_==="unavailable"&&l.jsxs("div",{className:"hygiene-status-box hygiene-offline",children:[l.jsx("p",{className:"hygiene-status-title",children:"Globale Git-Inventur nicht verfügbar"}),l.jsxs("p",{className:"hygiene-status-text",children:["Die Schnittstelle ",l.jsx("code",{children:"/api/repos"})," ist in dieser Version des Kerns noch nicht aktiv (Lane HYG-02)."]})]}),_==="unreachable"&&l.jsxs("div",{className:"hygiene-status-box hygiene-error",children:[l.jsx("p",{className:"hygiene-status-title",children:"Brain nicht erreichbar"}),l.jsxs("p",{className:"hygiene-status-text",children:["Keine Verbindung zum Brain-Server — läuft ",l.jsx("code",{children:"plugbrain serve"}),"?"]}),l.jsx("button",{type:"button",className:"btn btn-secondary",onClick:A,children:"Erneut versuchen"})]}),_==="error"&&l.jsxs("div",{className:"hygiene-status-box hygiene-error",children:[l.jsx("p",{className:"hygiene-status-title",children:"Fehler beim Laden"}),l.jsx("p",{className:"hygiene-status-text",children:"Globale Git-Inventur konnte nicht geladen werden."}),l.jsx("button",{type:"button",className:"btn btn-secondary",onClick:A,children:"Erneut versuchen"})]}),_==="ready"&&p&&l.jsxs("div",{className:"hygiene-repos-container",children:[l.jsxs("div",{className:"hygiene-summary-banner hygiene-ok",children:[l.jsxs("span",{className:"hygiene-summary-text",children:["Gefunden: ",p.totals.repos," Repositories (",p.totals.dirty," mit Änderungen, ",p.totals.unpushedBranches," ungepushte Branches, ",p.totals.orphanWorktrees," verwaiste Worktrees)"]}),l.jsxs("span",{className:"hygiene-summary-meta",children:["Geprüfte Roots: ",p.roots.join(", ")]})]}),l.jsxs("section",{className:"hygiene-section-container",children:[l.jsxs("h3",{className:"hygiene-section-heading",children:["Gefundene Repositories (",p.repos.length,")"]}),l.jsx("div",{className:"hygiene-checkouts-list",children:p.repos.map((M,T)=>l.jsxs("div",{className:`hygiene-checkout-card ${M.dirtyFiles>0?"hygiene-attention":"hygiene-ok"}`,children:[l.jsxs("div",{className:"hygiene-checkout-header",children:[l.jsx("span",{className:`hygiene-badge ${M.dirtyFiles>0?"hygiene-badge-attention":"hygiene-badge-ok"}`,children:M.dirtyFiles>0?"◌ Achtung":"● Ok"}),l.jsx("span",{className:"hygiene-checkout-path",children:M.path}),l.jsx("span",{className:"hygiene-checkout-meta",children:M.branch})]}),l.jsx("div",{className:"hygiene-checkout-detail",style:{display:"block"},children:l.jsxs("div",{className:"hygiene-stat-grid",children:[l.jsxs("div",{className:"hygiene-stat",children:[l.jsx("span",{className:"hygiene-stat-label",children:"Geändert"}),l.jsx("span",{className:"hygiene-stat-value",children:M.dirtyFiles})]}),l.jsxs("div",{className:"hygiene-stat",children:[l.jsx("span",{className:"hygiene-stat-label",children:"Worktrees"}),l.jsx("span",{className:"hygiene-stat-value",children:M.worktrees})]}),l.jsxs("div",{className:"hygiene-stat",children:[l.jsx("span",{className:"hygiene-stat-label",children:"Größe"}),l.jsxs("span",{className:"hygiene-stat-value",children:[(M.gitSizeMb+M.workTreeSizeMb).toFixed(0)," MB"]})]}),l.jsxs("div",{className:"hygiene-stat",children:[l.jsx("span",{className:"hygiene-stat-label",children:"Letzter Commit vor"}),l.jsxs("span",{className:"hygiene-stat-value",children:[M.lastCommitDays," Tagen"]})]})]})})]},T))})]})]})]})]})}function jD({workspaceId:t,isOpen:e,onClose:n,onOpenSource:i,onNavigateToSearch:a,initialQuestion:s=""}){const[r,o]=P.useState(s),[c,u]=P.useState(!1),[d,m]=P.useState(null),[f,p]=P.useState(!1),[x,_]=P.useState(!1),[g,h]=P.useState(null),v=P.useRef(null);if(P.useEffect(()=>{e&&(o(s),m(null),p(!1),_(!1),h(null),setTimeout(()=>{var T;return(T=v.current)==null?void 0:T.focus()},50))},[e,s]),!e)return null;const b=async T=>{const S=T.trim();if(!(!S||c)){u(!0),h(null),p(!1),_(!1);try{const C=await vT(t,S);"routeMissing"in C?(p(!0),m(null)):"unreachable"in C?(_(!0),m(null)):"serverError"in C?(h(`Fehler ${C.status}: ${C.error}`),m(null)):m(C)}catch(C){h(C instanceof Error?C.message:String(C))}finally{u(!1)}}},y=T=>{o(T),b(T)},A=T=>{i(T.path,T.line),n()},M=()=>{a&&(a(r),n())};return l.jsx("div",{className:"brain-modal-backdrop",onClick:n,role:"presentation",children:l.jsxs("div",{className:"brain-modal ask-modal",role:"dialog","aria-modal":"true","aria-labelledby":"ask-modal-title",onClick:T=>T.stopPropagation(),children:[l.jsxs("header",{className:"ask-modal__header",children:[l.jsxs("div",{className:"ask-modal__input-wrap",children:[l.jsx("span",{className:"ask-modal__search-icon",children:l.jsx(Mt,{path:vt.search})}),l.jsx("input",{ref:v,id:"ask-modal-title",type:"text",className:"ask-modal__input",placeholder:"Frag das Projekt … (z. B. 'Wo ist workspaceIdFor definiert?')",value:r,onChange:T=>o(T.target.value),onKeyDown:T=>{T.key==="Escape"&&n()}}),r&&l.jsx("button",{type:"button",className:"ask-modal__clear-btn",onClick:()=>{var T;o(""),m(null),(T=v.current)==null||T.focus()},"aria-label":"Eingabe leeren",children:"✕"})]}),l.jsx("button",{type:"button",className:"brain-modal__close",onClick:n,"aria-label":"Dialog schließen",children:"✕"})]}),l.jsxs("div",{className:"ask-modal__body",children:[c&&l.jsx("div",{className:"ask-modal__state ask-modal__state--busy",role:"status",children:l.jsx("p",{children:"Durchsuche Wissensgraphen und Code …"})}),f&&!c&&l.jsxs("div",{className:"ask-modal__state ask-modal__state--unavailable",role:"status",children:[l.jsx("div",{className:"ask-modal__state-icon",children:l.jsx(Mt,{path:vt.info})}),l.jsx("h4",{children:"Frag-Funktion in dieser Version noch nicht verfügbar"}),l.jsxs("p",{children:["Die Backend-Route ",l.jsx("code",{children:"POST /api/ask"})," ist auf diesem Kern noch nicht aktiv (wird in Lane ASK-01 bereitgestellt)."]}),a&&r&&l.jsxs("button",{type:"button",className:"pb-button pb-button--primary",onClick:M,style:{marginTop:"12px"},children:[l.jsx(Mt,{path:vt.search})," Suche nach „",r,'" öffnen']})]}),x&&!c&&l.jsxs("div",{className:"ask-modal__state ask-modal__state--error",role:"alert",children:[l.jsx("h4",{children:"Brain nicht erreichbar"}),l.jsxs("p",{children:["Keine Verbindung zum Brain-Server — läuft ",l.jsx("code",{children:"plugbrain serve"}),"?"]})]}),g&&!c&&l.jsxs("div",{className:"ask-modal__state ask-modal__state--error",role:"alert",children:[l.jsx("h4",{children:"Fehler bei der Abfrage"}),l.jsx("p",{children:g})]}),!c&&!f&&!x&&!g&&!d&&l.jsxs("div",{className:"ask-modal__empty",children:[l.jsx("p",{className:"ask-modal__hint",children:"Typische Fragen an den Brain:"}),l.jsxs("div",{className:"ask-modal__suggestions",children:[l.jsx("button",{type:"button",className:"ask-modal__suggestion-chip",onClick:()=>{const T="Wo ist workspaceIdFor definiert?";o(T),b(T)},children:"„Wo ist workspaceIdFor definiert?“"}),l.jsx("button",{type:"button",className:"ask-modal__suggestion-chip",onClick:()=>{const T="Welche Einstiegspunkte hat das Projekt?";o(T),b(T)},children:"„Welche Einstiegspunkte hat das Projekt?“"}),l.jsx("button",{type:"button",className:"ask-modal__suggestion-chip",onClick:()=>{const T="Was hat sich zuletzt geändert?";o(T),b(T)},children:"„Was hat sich zuletzt geändert?“"})]})]}),d&&!c&&l.jsxs("div",{className:"ask-modal__response",children:[l.jsxs("div",{className:"ask-modal__answer-box",children:[l.jsx("p",{className:"ask-modal__answer",children:d.answer}),l.jsxs("div",{className:"ask-modal__meta",children:[l.jsxs("span",{className:`ask-modal__confidence ask-modal__confidence--${d.confidence}`,title:`Konfidenz: ${d.confidence}`,children:["Konfidenz: ",d.confidence]}),d.intent&&l.jsxs("span",{className:"ask-modal__intent-badge",children:["Fokus: ",d.intent]})]})]}),d.unavailable&&d.unavailable.length>0&&l.jsxs("div",{className:"ask-modal__unavailable-note",style:{fontSize:"12px",color:"var(--muted)",margin:"8px 0"},children:["Hinweis: Folgende Bereiche sind nicht verfügbar: ",d.unavailable.join(", ")]}),d.sources&&d.sources.length>0&&l.jsxs("div",{className:"ask-modal__sources",children:[l.jsxs("h5",{children:["Gefundene Quellen (",d.sources.length,"):"]}),l.jsx("div",{className:"ask-modal__sources-list",children:d.sources.map((T,S)=>l.jsxs("button",{type:"button",className:"ask-modal__source-card",onClick:()=>A(T),children:[l.jsxs("div",{className:"ask-modal__source-top",children:[l.jsx(Mt,{path:vt.file}),l.jsx("strong",{className:"ask-modal__source-path",children:T.path}),l.jsxs("span",{className:"ask-modal__source-line",children:["Zeile ",T.line]})]}),T.symbol&&l.jsxs("span",{className:"ask-modal__source-symbol",children:["Symbol: ",l.jsx("code",{children:T.symbol})]}),T.why&&l.jsx("span",{className:"ask-modal__source-why",children:T.why})]},`${T.path}-${T.line}-${S}`))})]}),d.confidence==="low"&&a&&l.jsxs("div",{className:"ask-modal__low-conf-alert",children:[l.jsx("p",{children:"Die Antwort hat eine niedrige Konfidenz."}),l.jsx("button",{type:"button",className:"pb-button",onClick:M,children:"Reguläre Code-Suche öffnen"})]}),d.followUps&&d.followUps.length>0&&l.jsxs("div",{className:"ask-modal__followups",children:[l.jsx("h5",{children:"Folgefragen:"}),l.jsx("div",{className:"ask-modal__followup-list",children:d.followUps.map(T=>l.jsxs("button",{type:"button",className:"ask-modal__followup-btn",onClick:()=>y(T),children:["↳ ",T]},T))})]})]})]}),l.jsxs("footer",{className:"ask-modal__footer",children:[l.jsxs("span",{children:[l.jsx("kbd",{children:"Enter"})," Fragen absenden"]}),l.jsxs("span",{children:[l.jsx("kbd",{children:"Esc"})," Schließen"]}),l.jsxs("span",{children:[l.jsx("kbd",{children:"Strg+K"})," Immer verfügbar"]})]})]})})}const GS=[{id:"briefing",label:"Briefing",testName:"Briefing",hint:"Projekt-Briefing: Zusammenfassung, Kennzahlen, Hotspots"},{id:"notes",label:"Notizen",testName:"Wissen",hint:"Notizen lesen, schreiben und verknüpfen"},{id:"atlas",label:"Graph",testName:"Atlas",hint:"Wissensgraph: Symbole, Notizen und Verbindungen"},{id:"search",label:"Suche",hint:"Code und Notizen durchsuchen"},{id:"explorer",label:"Dateien",testName:"Explorer",hint:"Quelldateien mit echtem Inhalt und Zeilennummern"},{id:"hygiene",label:"Aufräumen",testName:"Hygiene",hint:"Checkouts, ungepushte Branches und ungesicherte Arbeit auf einen Blick"}],sm=[{id:"packs",label:"Kontext-Pakete",hint:"Context-Packs für Agenten-Aufgaben zusammenstellen"},{id:"queue",label:"Aufgaben",hint:"Wartende Aufgaben; der nächste freie Agent nimmt sie"},{id:"mesh",label:"Agenten-Netz",hint:"Nachweisbare Arbeit und Übergaben aus dem Core-Trace"},{id:"city",label:"Code-Stadt",hint:"Workspace als Stadt — Repos als Distrikte, Dateien als Gebäude"}],VS=[...GS,...sm],iu=Hb,HD=2e3;function GD(){const t=new URLSearchParams(location.search).get("view"),e=(()=>{try{return localStorage.getItem("plugbrain.view")}catch{return null}})(),n=t||e;return VS.some(i=>i.id===n)?n:"briefing"}function VD(){const t=new URLSearchParams(location.search);return t.has("workspace")?t.get("workspace")??"":t.has("workspaceRoot")?"":W1()}function WD(){var Ba,U,le,Ie,ft,Yt;const[t,e]=P.useState(null),[n,i]=P.useState(null),[a,s]=P.useState({depth:0,tasks:[]}),[r,o]=P.useState(""),[c,u]=P.useState(0),[d,m]=P.useState(GD),[f,p]=P.useState(VD),x=P.useMemo(()=>{const F=new URLSearchParams(location.search);return F.has("workspace")?null:F.get("workspaceRoot")},[]),[_,g]=P.useState([]),[h,v]=P.useState(!1),[b,y]=P.useState(""),[A,M]=P.useState(!1),[T,S]=P.useState(""),[C,R]=P.useState(""),[L,O]=P.useState(""),[W,z]=P.useState(null),[D,B]=P.useState(null),[k,V]=P.useState(!1),[X,ge]=P.useState([]),[he,Ae]=P.useState(()=>{const F=new URLSearchParams(location.search).get("file"),ne=Number(new URLSearchParams(location.search).get("line"));return F?{path:F,line:Number.isFinite(ne)?ne:null}:null}),[q,fe]=P.useState(null),[Ne,ie]=P.useState(null),[xe,oe]=P.useState(!1),[Ee,Oe]=P.useState("appearance"),[ke,xt]=P.useState(()=>{try{const F=localStorage.getItem("plugbrain.theme");if(F==="dark"||F==="light"||F==="system")return F}catch{}return"system"});P.useEffect(()=>{try{localStorage.setItem("plugbrain.theme",ke)}catch{}const F=ne=>{let Qe=ne;ne==="system"&&(Qe=typeof window<"u"&&window.matchMedia&&window.matchMedia("(prefers-color-scheme: light)").matches?"light":"dark"),document.documentElement.dataset.theme=Qe};if(F(ke),ke==="system"&&typeof window<"u"&&window.matchMedia){const ne=window.matchMedia("(prefers-color-scheme: light)"),Qe=()=>F("system");return ne.addEventListener("change",Qe),()=>ne.removeEventListener("change",Qe)}},[ke]);const[re,Se]=P.useState(!1),[Ce,be]=P.useState(qb()),[Ve,Ge]=P.useState(Fi()),[Pe,we]=P.useState(!1),[We,ve]=P.useState(null),[I,nt]=P.useState([]),[Ke,N]=P.useState(!1),[E,H]=P.useState(""),[K,J]=P.useState(!1),[_e,pe]=P.useState(!1),[te,Z]=P.useState("");P.useEffect(()=>{const F=ne=>{const Qe=ne.target;Qe!=null&&Qe.matches('input, textarea, select, [contenteditable="true"]')||((ne.ctrlKey||ne.metaKey)&&ne.key.toLowerCase()==="k"||ne.key==="/"&&!ne.shiftKey?(ne.preventDefault(),pe(!0)):ne.key==="?"||ne.key==="/"&&ne.shiftKey?(ne.preventDefault(),J(!0)):ne.key==="Escape"?(J(!1),oe(!1),we(!1),pe(!1)):ne.key.toLowerCase()==="o"&&!ne.ctrlKey&&!ne.metaKey&&(ne.preventDefault(),v(!0)))};return window.addEventListener("keydown",F),()=>window.removeEventListener("keydown",F)},[]),P.useEffect(()=>{try{localStorage.setItem("plugbrain.view",d)}catch{}},[d]),P.useEffect(()=>{(d==="city"||d==="mesh"||d==="queue")&&Ae(null),d==="mesh"&&(V(!1),B(null))},[d]);const ae=P.useRef(null);P.useEffect(()=>{ae.current=D},[D]);const ce=F=>{p(F),X1(F);const ne=new URL(location.href);F?ne.searchParams.set("workspace",F):ne.searchParams.delete("workspace"),F&&ne.searchParams.delete("workspaceRoot"),history.replaceState(null,"",ne.toString())};P.useEffect(()=>{if(!f)return;let F=!0;return fetch(`/api/graph?workspace=${encodeURIComponent(f)}&limit=5000`).then(ne=>ne.json()).then(ne=>{if(!F||!(ne!=null&&ne.nodes))return;const Qe=ne.nodes.filter(Ye=>{var Wt;return Ye.type==="file"&&(Ye.path||((Wt=Ye.properties)==null?void 0:Wt.path))}).map(Ye=>{var Wt,Jn,Vi,Cs;return{id:Ye.id,path:Ye.path||((Wt=Ye.properties)==null?void 0:Wt.path),label:Ye.label||Ye.path,lang:Ye.lang||((Jn=Ye.properties)==null?void 0:Jn.lang),loc:Ye.loc??((Vi=Ye.properties)==null?void 0:Vi.lines)??0,agent:Ye.agent||((Cs=Ye.properties)!=null&&Cs.agentId?{id:Ye.properties.agentId,name:Ye.properties.agentName,color:Ye.properties.agentColor}:null)}});ge(Qe)}).catch(()=>{}),()=>{F=!1}},[f,c]),P.useEffect(()=>{let F=!0;return a0().then(ne=>{if(F){if(g(ne),!f&&x!==null){const Qe=q1(ne,x);Qe&&ce(Qe);return}if(!f&&ne.length>0){const Qe=[...ne].sort((Ye,Wt)=>(Wt.indexedAt??"").localeCompare(Ye.indexedAt??""))[0];Qe&&ce(Qe.id)}}}).catch(()=>{F&&S("Die Galaxie ist nicht erreichbar — läuft plugbrain serve?")}),()=>{F=!1}},[]);const me=async F=>{F.preventDefault();const ne=b.trim();if(ne!==""){M(!0),S(""),R(""),O("Vault registriert — Indexlauf wird vorbereitet …");try{const Qe=await Y1(ne,void 0,Ye=>O(rp(Ye)));a0().then(Ye=>{Ye.length>0&&g(Ye)}).catch(()=>{}),R("Vault registriert und indiziert."),y(""),v(!1),ce(Qe),m("notes"),u(Ye=>Ye+1)}catch(Qe){const Ye=Qe instanceof Error?Qe.message:String(Qe);let Wt=Ye;/ENOENT|not found|nicht gefunden/i.test(Ye)?Wt=`Ordner nicht gefunden oder nicht lesbar: "${ne}". Bitte überprüfe den Pfad.`:/fetch|network|connection refused|econnrefused/i.test(Ye)&&(Wt='Brain-Kern nicht erreichbar. Bitte prüfe, ob "plugbrain serve" im Terminal läuft.'),S(Wt)}finally{M(!1),O("")}}},ye=async()=>{if(!(!f||A)){M(!0),S(""),R(""),O("Indexlauf wird vorbereitet …");try{const F=await Wb(f,ne=>O(rp(ne)));R(`Neu indiziert: ${(F==null?void 0:F.files)??0} Dateien, ${(F==null?void 0:F.symbols)??0} Symbole, ${(F==null?void 0:F.edges)??0} Kanten.`),u(ne=>ne+1)}catch(F){S(F instanceof Error?F.message:String(F))}finally{M(!1),O("")}}};P.useEffect(()=>{if(!f)return;let F=!0,ne;const Qe=async()=>{try{const Ye=await fetch(`/api/index/progress?workspace=${encodeURIComponent(f)}`);if(Ye.ok){const Wt=await Ye.json();if(!F)return;O(Jn=>K1(Wt,Jn))}}catch{}F&&(ne=setTimeout(()=>void Qe(),1500))};return Qe(),()=>{F=!1,clearTimeout(ne)}},[f]);const Fe=F=>{F.preventDefault(),Yb(Ce.trim()),eT(Ve.trim()),oT(),u(ne=>ne+1),Se(!1)},Xe=async(F=!1)=>{if(!(!f||Ke)){N(!0),H("");try{const ne=await tT(f);ve(ne),nt([...ne.indexSelection.checkoutIds]),F&&we(!0)}catch(ne){H(ne instanceof Error?ne.message:String(ne)),F&&we(!0)}finally{N(!1)}}},et=F=>{nt(ne=>ne.includes(F)?ne.filter(Qe=>Qe!==F):[...ne,F])},j=async F=>{if(F.preventDefault(),!(!f||Ke)){N(!0),H("");try{const ne=await nT(f,I);ve(ne),nt([...ne.indexSelection.checkoutIds]),R(ne.indexSelection.checkoutIds.length===0?"Code-Auswahl gespeichert: bewusst keine Code-Checkouts aktiv.":`Code-Auswahl gespeichert: ${ne.indexSelection.checkoutIds.length} Checkout(s) aktiv.`),we(!1),u(Qe=>Qe+1)}catch(ne){H(ne instanceof Error?ne.message:String(ne))}finally{N(!1)}}},Me=l.jsxs("form",{className:"brain-vault",onSubmit:me,children:[l.jsxs("div",{className:"brain-vault__row",children:[l.jsx("input",{className:"brain-vault__path",value:b,onChange:F=>y(F.target.value),placeholder:"Pfad eines Ordners (auch ohne .git), z. B. C:\\Notizen\\vault",spellCheck:!1,"aria-label":"Vault-Pfad"}),l.jsx("button",{type:"submit",className:"brain-vault__open",disabled:A||b.trim()==="",children:A?"Indiziere …":"Als Vault öffnen"})]}),f&&l.jsx("div",{className:"brain-vault__row brain-vault__row--tools",children:l.jsx("button",{type:"button",className:"brain-vault__reindex",disabled:A,onClick:()=>void ye(),children:A?"läuft …":"Neu indizieren"})}),L&&l.jsx("p",{className:"brain-vault__progress",role:"status","aria-live":"polite",children:L}),T&&l.jsx("p",{className:"brain-vault__error",role:"alert",children:T}),C&&l.jsx("p",{className:"brain-vault__done",role:"status",children:C})]});P.useEffect(()=>{const F=f||void 0;fetch("/api/timeline"+(F?"?workspace="+encodeURIComponent(F):"")).then(ne=>ne.json()).then(ne=>{var Qe;(Qe=ne==null?void 0:ne.bounds)!=null&&Qe.first&&z(ne.bounds)}).catch(()=>{})},[f]),P.useEffect(()=>{if(!k||!W)return;const F=new Date(W.first).getTime(),ne=new Date(W.last).getTime(),Qe=Math.max(1,ne-F);let Ye=D?Math.round((new Date(D).getTime()-F)/Qe*Za):0;const Wt=setInterval(()=>{if(Ye+=1,Ye>=Za){B(null),V(!1);return}B(new Date(F+Qe*Ye/Za).toISOString())},220);return()=>clearInterval(Wt)},[k,W]),P.useEffect(()=>{const F=new AbortController;let ne,Qe="";const Ye=f||void 0;async function Wt(){var Jn,Vi,Cs;try{const An=new URLSearchParams;Ye&&An.set("workspace",Ye),An.set("limit",String(HD)),ae.current&&An.set("until",ae.current);const hi=await fetch("/api/atlas/snapshot"+(An.toString()?`?${An}`:""),{signal:F.signal});if(!hi.ok)throw new Error(`Brain-Verbindung: HTTP ${hi.status}`);const w=await hi.json();if(!((Jn=w.workspace)!=null&&Jn.canonicalPath)||!Array.isArray((Vi=w.graph)==null?void 0:Vi.nodes)||!Array.isArray((Cs=w.graph)==null?void 0:Cs.edges))throw new Error("Der Brain-Snapshot ist unvollständig.");const G=`${w.workspace.id}:${w.indexGeneration??""}:${D??""}:${w.graph.nodes.length}:${w.graph.edges.length}`;G!==Qe&&(e(w),Qe=G),o("")}catch(An){F.signal.aborted||o(An instanceof Error?An.message:String(An))}if(Ye)try{const An=await iT(Ye);F.signal.aborted||i(An)}catch{F.signal.aborted||i(null)}else F.signal.aborted||i(null);try{const An=await fetch("/api/queue"+(Ye?"?workspace="+encodeURIComponent(Ye):""),{signal:F.signal});if(An.ok){const hi=await An.json();(hi==null?void 0:hi.ok)===!0&&Array.isArray(hi.tasks)&&s({depth:Number(hi.depth??0),tasks:hi.tasks})}}catch{}F.signal.aborted||(ne=setTimeout(Wt,3e3))}return Wt(),()=>{F.abort(),clearTimeout(ne)}},[c,D,f]);const se=t!=null&&t.coverage?!t.coverage.indexComplete:!1,Te=((Ba=t==null?void 0:t.coverage)==null?void 0:Ba.staleFiles)??0,Re=r?{tone:"bad",text:"Offline",title:r}:t?se?{tone:"warn",text:`${Te} warten`,title:`${Te} Datei(en) warten auf den Index`}:{tone:"ok",text:"Aktuell",title:"Der Index ist auf dem neuesten Stand."}:{tone:"idle",text:"Verbinde …",title:"Der aktuelle Stand wird aus dem Brain gelesen."},de=P.useMemo(()=>{var F;return X.length>0?X:(F=t==null?void 0:t.graph)!=null&&F.nodes?t.graph.nodes.filter(ne=>{var Qe;return ne.type==="file"&&(((Qe=ne.properties)==null?void 0:Qe.path)||ne.path)}).map(ne=>{var Ye,Wt,Jn,Vi;const Qe=((Ye=ne.properties)==null?void 0:Ye.path)||ne.path||"";return{id:ne.id,path:Qe,label:ne.label||ne.name||Qe,lang:((Wt=ne.properties)==null?void 0:Wt.lang)??ne.lang??null,loc:((Jn=ne.properties)==null?void 0:Jn.lines)??ne.loc??0,agent:(Vi=ne.properties)!=null&&Vi.agentId?{id:ne.properties.agentId,name:ne.properties.agentName||ne.properties.agentId,color:ne.properties.agentColor||"#60a5fa"}:null}}):[]},[X,t]),ze=(F,ne)=>{F&&(fe(null),Ae({path:F,line:ne}))},je=F=>{ze(F),m("explorer")},_t=F=>{Ae(null),fe(F),m("explorer")},yt=F=>{ie(F),m("mesh")},Ot=P.useCallback(()=>u(F=>F+1),[]),_n=P.useCallback(F=>{fe(null),Ae({path:F,line:null}),F.endsWith(".md")?m("notes"):m("explorer")},[]),ji=P.useMemo(()=>{if(!W||!(d==="atlas"||d==="city"))return null;const F=new Date(W.first).getTime(),ne=Math.max(1,new Date(W.last).getTime()-F),Qe=D?Math.round((new Date(D).getTime()-F)/ne*Za):Za;return{playing:k,step:Qe,label:D?new Date(D).toLocaleString("de-DE",{day:"2-digit",month:"2-digit",hour:"2-digit",minute:"2-digit"}):"jetzt",onTogglePlay:()=>V(Ye=>!Ye),onScrub:Ye=>{if(V(!1),Ye>=Za){B(null);return}B(new Date(F+ne*Ye/Za).toISOString())}}},[W,D,k]),wt=he&&f?l.jsx("div",{className:"atlas-source-overlay",children:l.jsx(Mv,{workspaceId:f,path:he.path,highlightLine:he.line,onClose:()=>Ae(null),onNavigateFile:(F,ne)=>ze(F,ne)})}):null,Hi=F=>he&&f?l.jsx(Mv,{workspaceId:f,path:he.path,highlightLine:he.line,onClose:()=>Ae(null),onNavigateFile:(ne,Qe)=>ze(ne,Qe)}):F,Gi=t?iu(t.workspace.name):((U=_.find(F=>F.id===f))==null?void 0:U.name)??"";return l.jsxs("div",{className:"pb-app",children:[l.jsxs("header",{className:"pb-topbar",children:[l.jsxs("div",{className:"pb-topbar__brand",children:[l.jsx("span",{className:"pb-logo","aria-hidden":"true"}),l.jsx("span",{className:"pb-topbar__product",children:"PlugBrain"}),f&&_.length>1?l.jsxs("label",{className:"pb-select",title:"Vault wechseln",children:[l.jsx("span",{className:"pb-sr",children:"Vault"}),l.jsx("select",{value:f,onChange:F=>{const ne=F.target.value;ne&&ce(ne)},children:_.map(F=>l.jsx("option",{value:F.id,children:iu(F.name)},F.id))}),l.jsx(Mt,{path:vt.chevron})]}):Gi!==""&&l.jsx("span",{className:"pb-topbar__workspace",title:(t==null?void 0:t.workspace.canonicalPath)??"",children:Gi})]}),l.jsxs("div",{className:"pb-topbar__actions",children:[f&&l.jsxs("nav",{className:"pb-tabs pb-tabs--desktop","aria-label":"Ansicht",children:[GS.map(F=>l.jsx("button",{type:"button",className:"pb-tab",title:F.hint,"aria-label":F.testName??F.label,"aria-current":F.id===d?"page":void 0,onClick:()=>m(F.id),children:F.label},F.id)),l.jsxs("div",{className:"pb-tab-group","data-active":sm.some(F=>F.id===d)?"true":void 0,children:[l.jsx("button",{type:"button",className:"pb-tab","aria-haspopup":"true",children:"Agenten"}),l.jsx("div",{className:"pb-tab-group-menu",children:sm.map(F=>l.jsx("button",{type:"button",className:"pb-tab-menu-item",title:F.hint,"aria-current":F.id===d?"page":void 0,onClick:()=>m(F.id),children:F.label},F.id))})]})]}),f&&l.jsxs("div",{className:"pb-mobile-nav",children:[l.jsx("button",{type:"button",className:"pb-tool pb-tool--icon",title:"Menü",onClick:()=>{const F=document.getElementById("mobile-menu");F&&(F.style.display=F.style.display==="block"?"none":"block")},children:l.jsx(Mt,{path:vt.burger})}),l.jsxs("div",{id:"mobile-menu",className:"pb-mobile-menu",style:{display:"none"},children:[VS.map(F=>l.jsx("button",{type:"button",className:"pb-tab-menu-item",title:F.hint,"aria-current":F.id===d?"page":void 0,onClick:()=>{m(F.id),document.getElementById("mobile-menu").style.display="none"},children:F.label},F.id)),l.jsx("button",{type:"button",className:"pb-tab-menu-item",onClick:()=>{pe(!0),document.getElementById("mobile-menu").style.display="none"},children:"Frag das Projekt (Strg+K)"})]})]}),f&&l.jsxs("span",{className:"pb-status","data-tone":Re.tone,role:"status",title:Re.title,children:[l.jsx("i",{"aria-hidden":"true"}),l.jsx("span",{children:Re.text})]}),f&&l.jsxs("button",{type:"button",className:"pb-tool",onClick:()=>pe(!0),title:"Frag das Projekt … (Strg+K / /)","aria-label":"Frag das Projekt",children:[l.jsx(Mt,{path:vt.search}),l.jsx("span",{children:"Frag das Projekt"})]}),f&&l.jsxs("button",{type:"button",className:"pb-tool","aria-expanded":h,onClick:()=>{v(F=>!F),S(""),R("")},title:"Einen Ordner als neuen Vault öffnen (O)",children:[l.jsx(Mt,{path:vt.folder}),l.jsx("span",{children:h?"Schließen":"Vault öffnen"})]}),f&&l.jsx("button",{type:"button",className:"pb-tool",onClick:()=>{Oe("repos"),oe(!0),Xe()},disabled:Ke,title:"Aktive Code-Checkouts auswählen (Repos wählen)",children:l.jsx("span",{children:Ke?"Lädt …":"Repos wählen"})}),l.jsx("button",{type:"button",className:"pb-tool pb-tool--icon",onClick:()=>J(!0),"aria-label":"Tastenkürzel anzeigen",title:"Tastenkürzel (?)",children:l.jsx(Mt,{path:vt.keyboard})}),l.jsx("button",{type:"button",className:"pb-tool pb-tool--icon",onClick:()=>{Oe("appearance"),oe(!0)},"aria-label":"Einstellungen",title:"Einstellungen",children:l.jsx(Mt,{path:vt.gear})})]})]}),r&&l.jsxs("div",{className:"pb-banner",role:"alert",children:[l.jsx("strong",{children:"Brain-Server offline."}),l.jsxs("span",{children:["Der Kern antwortet nicht (",r,"). Bitte prüfe, ob ",l.jsx("code",{children:"plugbrain serve"})," läuft."]}),l.jsxs("button",{type:"button",className:"pb-button pb-button--primary",onClick:Ot,children:[l.jsx(Mt,{path:vt.refresh})," Erneut verbinden"]})]}),h&&f&&l.jsx("div",{className:"pb-drawer",children:Me}),l.jsx("main",{className:"pb-main",children:f?l.jsxs(l.Fragment,{children:[d==="briefing"&&l.jsxs("div",{className:"pb-view",children:[l.jsx(ID,{workspaceId:f,workspaceName:Gi,onOpenFile:F=>ze(F),onOpenNotes:()=>m("notes")}),wt]}),d==="atlas"&&l.jsxs("div",{className:"pb-view",children:[l.jsxs("div",{className:"pb-view-header",children:[l.jsx("h2",{children:"Graph"}),l.jsx("p",{children:"Wissensgraph: Symbole, Notizen und Verbindungen"}),l.jsx("button",{type:"button",className:"pb-button pb-button--primary",onClick:Ot,children:"Graph aktualisieren"})]}),l.jsx("div",{id:"app",className:"atlas-app",children:l.jsx(OD,{graph:(t==null?void 0:t.graph)??null,coverage:(t==null?void 0:t.coverage)??null,error:t?"":r,onRetry:Ot,onOpenSource:_n,timeline:ji})})]}),d==="notes"&&l.jsxs("div",{className:"pb-view",children:[l.jsxs("div",{className:"pb-view-header",children:[l.jsx("h2",{children:"Notizen"}),l.jsx("p",{children:"Notizen lesen, schreiben und verknüpfen wie in Obsidian"}),l.jsx("button",{type:"button",className:"pb-button pb-button--primary",onClick:()=>{const F=document.querySelector(".notes-create button, .notes-empty-list button, .notes-empty--initial button");F==null||F.click()},children:"Neue Notiz"})]}),l.jsx(m2,{workspaceId:f,onOpenSource:je,onOpenRevision:_t,onOpenAgentRun:yt,onNavigateTab:F=>m(F)})]}),d==="explorer"&&l.jsxs("div",{className:"pb-view",children:[l.jsxs("div",{className:"pb-view-header",children:[l.jsx("h2",{children:"Dateien"}),l.jsx("p",{children:"Quelldateien mit echtem Inhalt und Zeilennummern"}),l.jsx("button",{type:"button",className:"pb-button pb-button--primary",onClick:Ot,children:"Dateien aktualisieren"})]}),l.jsxs("div",{className:"workbench-split",children:[l.jsx("div",{className:"workbench-pane workbench-pane--side",children:l.jsx(l2,{workspaceName:(t==null?void 0:t.workspace.name)??(((le=_.find(F=>F.id===f))==null?void 0:le.name)||"Workspace"),files:de,activePath:he==null?void 0:he.path,onSelectFile:F=>ze(F)})}),l.jsx("div",{className:"workbench-pane workbench-pane--main",children:q&&!he?l.jsx(XD,{workspaceId:f,revision:q,onClose:()=>fe(null)}):Hi(l.jsxs("div",{className:"source-placeholder",children:[l.jsx("div",{className:"source-placeholder__icon",children:l.jsx(Mt,{path:vt.folder})}),l.jsx("h3",{children:"Datei im Explorer auswählen"}),l.jsx("p",{children:"Wähle eine Datei im linken Baum, um den echten Inhalt mit Zeilennummern und Revision anzuzeigen."})]}))})]})]}),d==="search"&&l.jsxs("div",{className:"pb-view",children:[l.jsxs("div",{className:"pb-view-header",children:[l.jsx("h2",{children:"Suche"}),l.jsx("p",{children:"Code und Notizen durchsuchen"}),l.jsx("button",{type:"button",className:"pb-button pb-button--primary",onClick:()=>{const F=document.querySelector(".search-input, .notes-search input");F==null||F.focus()},children:"Suche fokussieren"})]}),l.jsxs("div",{className:"workbench-split",children:[l.jsx("div",{className:"workbench-pane workbench-pane--side",children:l.jsx(o2,{workspaceId:f,onSelectHit:(F,ne)=>ze(F,ne)})}),l.jsx("div",{className:"workbench-pane workbench-pane--main",children:Hi(l.jsxs("div",{className:"source-placeholder",children:[l.jsx("div",{className:"source-placeholder__icon",children:l.jsx(Mt,{path:vt.search})}),l.jsx("h3",{children:"Code- und Symbolsuche"}),l.jsxs("p",{children:["Gib einen Suchbegriff ein, zum Beispiel ",l.jsx("code",{children:"authKey"}),". Ein Klick auf einen Treffer öffnet die Quelle."]})]}))})]})]}),d==="packs"&&l.jsxs("div",{className:"pb-view",children:[l.jsxs("div",{className:"pb-view-header",children:[l.jsx("h2",{children:"Kontext-Pakete"}),l.jsx("p",{children:"Context-Packs für Agenten-Aufgaben zusammenstellen"}),l.jsx("button",{type:"button",className:"pb-button pb-button--primary",onClick:Ot,children:"Pakete aktualisieren"})]}),l.jsxs("div",{className:"workbench-split",children:[l.jsx("div",{className:"workbench-pane workbench-pane--side",children:l.jsx(x2,{workspaceId:f,onSelectSource:F=>ze(F)})}),l.jsx("div",{className:"workbench-pane workbench-pane--main",children:Hi(l.jsxs("div",{className:"source-placeholder",children:[l.jsx("div",{className:"source-placeholder__icon",children:l.jsx(Mt,{path:vt.open})}),l.jsx("h3",{children:"Context-Pack-Inspector"}),l.jsx("p",{children:"Erzeuge einen Context Pack für eine Aufgabe. Ein Klick auf eine extrahierte Quelle zeigt ihren Inhalt."})]}))})]})]}),d==="city"&&l.jsxs("div",{className:"pb-view",children:[l.jsxs("div",{className:"pb-view-header",children:[l.jsx("h2",{children:"Code-Stadt"}),l.jsx("p",{children:"Workspace als Stadt — Repos als Distrikte, Dateien als Gebäude"}),l.jsx("button",{type:"button",className:"pb-button pb-button--primary",onClick:Ot,children:"Stadt aktualisieren"})]}),l.jsxs("div",{className:"pb-city",children:[l.jsx(Q3,{snapshot:t,onSelectFile:ze}),ji&&l.jsx("div",{className:"pb-overlay-tools",children:l.jsx(HS,{timeline:ji})}),wt]})]}),d==="queue"&&l.jsxs("div",{className:"pb-view pb-view--scroll",children:[l.jsxs("div",{className:"pb-view-header",children:[l.jsx("h2",{children:"Aufgaben"}),l.jsx("p",{children:"Wartende Aufgaben; der nächste freie Agent nimmt sie"}),l.jsx("button",{type:"button",className:"pb-button pb-button--primary",onClick:Ot,children:"Aufgaben prüfen"})]}),l.jsx(t2,{tasks:a.tasks,depth:a.depth})]}),d==="mesh"&&l.jsxs("div",{className:"pb-view",children:[l.jsxs("div",{className:"pb-view-header",children:[l.jsx("h2",{children:"Agenten-Netz"}),l.jsx("p",{children:"Nachweisbare Arbeit und Übergaben aus dem Core-Trace"}),l.jsx("button",{type:"button",className:"pb-button pb-button--primary",onClick:Ot,children:"Netz aktualisieren"})]}),l.jsxs("div",{className:"pb-mesh",children:[l.jsx(e2,{mesh:n,workspaceId:f,onSelectFile:ze,focusAgentId:Ne}),wt]})]}),d==="hygiene"&&l.jsxs("div",{className:"pb-view",children:[l.jsx(FD,{workspaceId:f}),wt]})]}):l.jsxs("div",{className:"brain-landing",children:[l.jsx("h1",{className:"brain-landing__title",children:"PlugBrain"}),l.jsxs("p",{className:"brain-landing__lead",children:["Einen Ordner als Vault öffnen — auch einen Wissensordner ohne ",l.jsx("code",{children:".git"}),". Der Brain indiziert ihn einmal und hält ihn über den Daemon automatisch aktuell. Wiki-Links, Überschriften, Tags und Code-Symbole werden zu einem durchsuchbaren Graphen."]}),l.jsxs("ol",{className:"brain-first-run","aria-label":"Erste Schritte",children:[l.jsxs("li",{children:[l.jsx("strong",{children:"Ordner wählen"}),l.jsx("span",{children:"Notiz- oder Projektordner angeben; Git ist nicht erforderlich."})]}),l.jsxs("li",{children:[l.jsx("strong",{children:"Index abwarten"}),l.jsx("span",{children:"Der echte Fortschritt bleibt sichtbar, bis Notizen und Graph bereit sind."})]}),l.jsxs("li",{children:[l.jsx("strong",{children:"Wissen öffnen"}),l.jsx("span",{children:"Leere Vaults bleiben ehrlich leer und bieten direkt „Erste Notiz anlegen“."})]})]}),Me,_.length>0&&l.jsxs("div",{className:"brain-vault__known",children:[l.jsx("span",{children:"Oder einen bekannten Vault öffnen:"}),_.map(F=>l.jsxs("button",{type:"button",className:"brain-vault__known-item",onClick:()=>ce(F.id),children:[iu(F.name)," ",l.jsx("em",{title:F.root,children:F.indexedAt?"indiziert":"nicht indiziert"})]},F.id))]})]})}),xe&&l.jsx("div",{className:"brain-modal-backdrop",onClick:()=>oe(!1),children:l.jsxs("section",{className:"brain-modal brain-settings-modal",role:"dialog","aria-modal":"true","aria-label":"Einstellungen",onClick:F=>F.stopPropagation(),children:[l.jsxs("div",{className:"brain-modal__header",children:[l.jsx("h3",{children:"Einstellungen"}),l.jsx("button",{type:"button",className:"brain-modal__close",onClick:()=>oe(!1),"aria-label":"Einstellungen schließen",children:"✕"})]}),l.jsxs("div",{className:"brain-settings-layout",children:[l.jsxs("nav",{className:"brain-settings-nav","aria-label":"Einstellungskategorien",children:[l.jsx("button",{type:"button",className:"brain-settings-nav-item","data-active":Ee==="appearance"?"true":"false",onClick:()=>Oe("appearance"),children:"Erscheinungsbild"}),l.jsx("button",{type:"button",className:"brain-settings-nav-item","data-active":Ee==="vaults"?"true":"false",onClick:()=>Oe("vaults"),children:"Vault-Verwaltung"}),l.jsx("button",{type:"button",className:"brain-settings-nav-item","data-active":Ee==="repos"?"true":"false",onClick:()=>{Oe("repos"),We||Xe()},children:"Repos wählen"}),l.jsx("button",{type:"button",className:"brain-settings-nav-item","data-active":Ee==="shortcuts"?"true":"false",onClick:()=>Oe("shortcuts"),children:"Tastenkürzel"}),l.jsx("button",{type:"button",className:"brain-settings-nav-item","data-active":Ee==="advanced"?"true":"false",onClick:()=>Oe("advanced"),children:"Erweitert"})]}),l.jsxs("div",{className:"brain-settings-content",children:[Ee==="appearance"&&l.jsxs("div",{children:[l.jsx("h4",{className:"brain-settings-section-title",children:"Erscheinungsbild"}),l.jsx("p",{className:"brain-settings-section-desc",children:"Wähle dein bevorzugtes Farbschema für PlugBrain."}),l.jsxs("div",{className:"brain-theme-options",children:[l.jsxs("button",{type:"button",className:"brain-theme-card","data-active":ke==="dark"?"true":"false",onClick:()=>xt("dark"),children:[l.jsx("strong",{children:"Dunkel"}),l.jsx("div",{style:{fontSize:"11px",color:"var(--muted)",marginTop:"4px"},children:"Standard"})]}),l.jsxs("button",{type:"button",className:"brain-theme-card","data-active":ke==="light"?"true":"false",onClick:()=>xt("light"),children:[l.jsx("strong",{children:"Hell"}),l.jsx("div",{style:{fontSize:"11px",color:"var(--muted)",marginTop:"4px"},children:"Hoher Kontrast"})]}),l.jsxs("button",{type:"button",className:"brain-theme-card","data-active":ke==="system"?"true":"false",onClick:()=>xt("system"),children:[l.jsx("strong",{children:"System"}),l.jsx("div",{style:{fontSize:"11px",color:"var(--muted)",marginTop:"4px"},children:"Automatisch"})]})]})]}),Ee==="vaults"&&l.jsxs("div",{children:[l.jsx("h4",{className:"brain-settings-section-title",children:"Vault-Verwaltung"}),l.jsx("p",{className:"brain-settings-section-desc",children:"Verwalte den aktiven Wissensordner oder wechsle zu einem bestehenden Vault."}),f?l.jsx("div",{className:"brain-settings-vault-card",children:l.jsxs("dl",{children:[l.jsx("dt",{children:"Aktiver Vault:"}),l.jsx("dd",{children:((Ie=_.find(F=>F.id===f))==null?void 0:Ie.name)||f}),l.jsx("dt",{children:"Pfad:"}),l.jsx("dd",{children:((ft=_.find(F=>F.id===f))==null?void 0:ft.root)||"—"}),l.jsx("dt",{children:"Status:"}),l.jsx("dd",{children:(Yt=_.find(F=>F.id===f))!=null&&Yt.indexedAt?"Indiziert":"Bereit"})]})}):l.jsx("p",{style:{color:"var(--muted)",fontSize:"12px"},children:"Kein Vault geöffnet."}),l.jsxs("div",{style:{marginTop:"16px"},children:[l.jsx("strong",{style:{fontSize:"12px",display:"block",marginBottom:"8px"},children:"Bekannte Vaults:"}),l.jsx("div",{style:{display:"grid",gap:"6px"},children:_.map(F=>l.jsxs("button",{type:"button",className:"pb-button",style:{justifyContent:"space-between",background:F.id===f?"var(--panel)":"transparent",borderColor:F.id===f?"var(--accent)":"var(--line)"},onClick:()=>{ce(F.id),oe(!1)},children:[l.jsx("span",{children:iu(F.name)}),l.jsx("small",{style:{color:"var(--muted)",fontFamily:"var(--mono)"},children:F.root})]},F.id))})]})]}),Ee==="repos"&&l.jsxs("div",{children:[l.jsx("h4",{className:"brain-settings-section-title",children:"Repos wählen"}),l.jsxs("p",{className:"brain-settings-section-desc",children:["Wähle aus, welche Checkouts indiziert werden sollen. Nur markierte Repos werden gescannt (",We?`${We.checkouts.filter(F=>I.includes(F.id)).length} von ${We.checkouts.length} aktiv`:"lädt …",")."]}),l.jsxs("form",{onSubmit:j,children:[E&&l.jsx("p",{className:"brain-vault__error",role:"alert",children:E}),We===null?l.jsx("p",{className:"brain-selection-modal__hint",children:"Planet-Inventar wird geladen …"}):We.checkouts.length===0?l.jsx("p",{className:"brain-selection-modal__hint",children:"Dieser Workspace hat keine discoverbaren Code-Checkouts."}):l.jsxs("fieldset",{className:"brain-selection-list",disabled:Ke,children:[l.jsx("legend",{children:"Checkout-Inventar"}),We.checkouts.map(F=>l.jsxs("label",{className:F.retiredAt?"is-retired":void 0,children:[l.jsx("input",{type:"checkbox",checked:I.includes(F.id),disabled:F.retiredAt!==null,onChange:()=>et(F.id)}),l.jsxs("span",{children:[l.jsx("strong",{children:F.relPrefix}),l.jsxs("small",{children:[F.id," · ",F.branch??"detached",F.retiredAt?" · retired":""]})]})]},F.id))]}),l.jsx("div",{className:"brain-modal__actions",style:{marginTop:"16px"},children:l.jsx("button",{type:"submit",className:"primary",disabled:Ke||We===null,children:Ke?"Speichert …":"Auswahl speichern"})})]})]}),Ee==="shortcuts"&&l.jsxs("div",{children:[l.jsx("h4",{className:"brain-settings-section-title",children:"Tastenkürzel"}),l.jsx("p",{className:"brain-settings-section-desc",children:"Tastenkombinationen für schnelle Navigation und Bearbeitung."}),l.jsx("table",{className:"brain-settings-shortcuts-table",children:l.jsxs("tbody",{children:[l.jsxs("tr",{children:[l.jsx("td",{children:l.jsx("kbd",{children:"?"})}),l.jsx("td",{children:"Tastenkürzel-Übersicht öffnen"})]}),l.jsxs("tr",{children:[l.jsxs("td",{children:[l.jsx("kbd",{children:"Strg"}),"+",l.jsx("kbd",{children:"O"})]}),l.jsx("td",{children:"Schnellwechsler (Notiz öffnen)"})]}),l.jsxs("tr",{children:[l.jsxs("td",{children:[l.jsx("kbd",{children:"Strg"}),"+",l.jsx("kbd",{children:"S"})]}),l.jsx("td",{children:"Notiz im Editor speichern"})]}),l.jsxs("tr",{children:[l.jsx("td",{children:l.jsx("kbd",{children:"O"})}),l.jsx("td",{children:"Ordner als Vault öffnen"})]}),l.jsxs("tr",{children:[l.jsx("td",{children:l.jsx("kbd",{children:"/"})}),l.jsx("td",{children:"Im Graph suchen"})]}),l.jsxs("tr",{children:[l.jsx("td",{children:l.jsx("kbd",{children:"F"})}),l.jsx("td",{children:"Graph auf Fenster einpassen"})]}),l.jsxs("tr",{children:[l.jsxs("td",{children:[l.jsx("kbd",{children:"+"})," / ",l.jsx("kbd",{children:"−"})]}),l.jsx("td",{children:"Graph vergrößern / verkleinern"})]}),l.jsxs("tr",{children:[l.jsx("td",{children:l.jsx("kbd",{children:"Esc"})}),l.jsx("td",{children:"Dialog oder Menü schließen"})]})]})})]}),Ee==="advanced"&&l.jsxs("div",{children:[l.jsx("h4",{className:"brain-settings-section-title",children:"Erweitert (Authentifizierung)"}),l.jsxs("p",{className:"brain-settings-section-desc",children:["Der Brain-Kern authentifiziert den lokalen Desktop automatisch über ",l.jsx("code",{children:"auth.token"}),". Manuelle Konfiguration ist nur für externe Agenten oder Debugging erforderlich."]}),l.jsxs("form",{onSubmit:Fe,children:[l.jsxs("div",{className:"brain-modal__field",children:[l.jsxs("label",{children:["Bearer Token (aus ",l.jsx("code",{children:"auth.token"}),"):"]}),l.jsx("input",{type:"password",className:"brain-modal__input mono",value:Ce,onChange:F=>be(F.target.value),placeholder:"plug-..."})]}),l.jsxs("div",{className:"brain-modal__field",children:[l.jsx("label",{children:"Agent ID:"}),l.jsx("input",{type:"text",className:"brain-modal__input mono",value:Ve,onChange:F=>Ge(F.target.value),placeholder:"agy"})]}),l.jsx("div",{className:"brain-modal__actions",style:{marginTop:"16px"},children:l.jsx("button",{type:"submit",className:"primary",children:"Zugangsdaten speichern"})})]})]})]})]})]})}),K&&l.jsx("div",{className:"brain-modal-backdrop",onClick:()=>J(!1),children:l.jsxs("section",{className:"brain-modal brain-shortcuts",role:"dialog","aria-modal":"true","aria-labelledby":"shortcut-title",onClick:F=>F.stopPropagation(),children:[l.jsxs("div",{className:"brain-modal__header",children:[l.jsx("h3",{id:"shortcut-title",children:"Tastenkürzel"}),l.jsx("button",{type:"button",className:"brain-modal__close",onClick:()=>J(!1),"aria-label":"Tastenkürzel schließen",children:"✕"})]}),l.jsxs("dl",{children:[l.jsxs("div",{children:[l.jsx("dt",{children:l.jsx("kbd",{children:"Strg+K"})}),l.jsx("dd",{children:"Frag das Projekt"})]}),l.jsxs("div",{children:[l.jsx("dt",{children:l.jsx("kbd",{children:"?"})}),l.jsx("dd",{children:"Diese Übersicht öffnen"})]}),l.jsxs("div",{children:[l.jsx("dt",{children:l.jsx("kbd",{children:"O"})}),l.jsx("dd",{children:"Ordner als Vault öffnen"})]}),l.jsxs("div",{children:[l.jsx("dt",{children:l.jsx("kbd",{children:"Esc"})}),l.jsx("dd",{children:"Übersicht oder Dialog schließen"})]}),l.jsxs("div",{children:[l.jsx("dt",{children:l.jsx("kbd",{children:"/"})}),l.jsx("dd",{children:"Frag das Projekt / Suche"})]}),l.jsxs("div",{children:[l.jsx("dt",{children:l.jsx("kbd",{children:"F"})}),l.jsx("dd",{children:"Graph einpassen"})]}),l.jsxs("div",{children:[l.jsxs("dt",{children:[l.jsx("kbd",{children:"+"}),l.jsx("kbd",{children:"−"})]}),l.jsx("dd",{children:"Graph zoomen"})]}),l.jsxs("div",{children:[l.jsxs("dt",{children:[l.jsx("kbd",{children:"↑"}),l.jsx("kbd",{children:"↓"}),l.jsx("kbd",{children:"Enter"})]}),l.jsx("dd",{children:"In der Liste auswählen und zentrieren"})]})]}),l.jsx("p",{children:"In Eingabefeldern bleiben alle Zeichen Eingabe und lösen keine Kurzbefehle aus."})]})}),l.jsx(jD,{workspaceId:f,isOpen:_e,onClose:()=>pe(!1),onOpenSource:(F,ne)=>ze(F,ne),onNavigateToSearch:F=>{m("search")},initialQuestion:te})]})}function XD({workspaceId:t,revision:e,onClose:n}){var c;const[i,a]=P.useState(null),[s,r]=P.useState("");P.useEffect(()=>{let u=!0;return a(null),r(""),Kb(t).then(d=>{u&&a(d)}).catch(d=>{u&&r(d instanceof Error?d.message:String(d))}),()=>{u=!1}},[t,e]);const o=(c=i==null?void 0:i.commits)==null?void 0:c.find(u=>u.hash===e||u.hash.startsWith(e));return l.jsxs("section",{className:"source-placeholder revision-inspector","aria-live":"polite",children:[l.jsx("div",{className:"source-placeholder__icon",children:l.jsx(Mt,{path:vt.refresh})}),l.jsx("h3",{children:"Revision-Inspector"}),l.jsx("p",{children:l.jsx("code",{children:e})}),s&&l.jsx("p",{role:"alert",children:s}),!s&&!i&&l.jsx("p",{children:"Prüfe die reale Git-Historie …"}),i&&!i.isRepo&&l.jsx("p",{children:"Dieser Wissensordner ist absichtlich kein Git-Workspace; für diese Revision gibt es keine Git-Historie."}),(i==null?void 0:i.isRepo)&&o&&l.jsxs("dl",{children:[l.jsxs("div",{children:[l.jsx("dt",{children:"Hash"}),l.jsx("dd",{children:l.jsx("code",{children:o.hash})})]}),l.jsxs("div",{children:[l.jsx("dt",{children:"Autor"}),l.jsx("dd",{children:o.author})]}),l.jsxs("div",{children:[l.jsx("dt",{children:"Zeit"}),l.jsx("dd",{children:o.date})]}),l.jsxs("div",{children:[l.jsx("dt",{children:"Nachricht"}),l.jsx("dd",{children:o.message})]})]}),(i==null?void 0:i.isRepo)&&!o&&l.jsx("p",{children:"Die geladene Historie enthält diese Revision nicht. Der Link bleibt unverändert; keine Ersatzrevision wird behauptet."}),l.jsx("button",{type:"button",onClick:n,children:"Inspector schließen"})]})}G1.createRoot(document.getElementById("root")).render(l.jsx(P.StrictMode,{children:l.jsx(WD,{})})); diff --git a/ui-dist/assets/index-DeAXfTgC.css b/ui-dist/assets/index-DeAXfTgC.css deleted file mode 100644 index 92667de..0000000 --- a/ui-dist/assets/index-DeAXfTgC.css +++ /dev/null @@ -1 +0,0 @@ -@font-face{font-family:Geist Variable;font-style:normal;font-weight:100 900;font-display:swap;src:url(/fonts/geist-latin-wght.woff2) format("woff2")}@font-face{font-family:Geist Mono Variable;font-style:normal;font-weight:100 900;font-display:swap;src:url(/fonts/geist-mono-latin-wght.woff2) format("woff2")}:root,:root[data-theme=dark]{--bg: #070908;--panel: #0D1210;--raised: #131A17;--hover: #1A2320;--ink: #e7e8eb;--muted: #9b9ea6;--faint: #6c6f77;--line: #1F2B25;--line-soft: #16201B;--line-strong: #2C3D34;--accent: #10b981;--accent-hover: #34d399;--accent-ink: #06101f;--accent-soft: rgb(16 185 129 / .12);--pos: #10b981;--neg: #ef4444;--warn: #eab308;--info: #06b6d4;--halo: #000;color-scheme:dark}:root[data-theme=light]{--bg: #f6f6f3;--panel: #fcfcfa;--raised: #efefec;--hover: #e8e8e4;--ink: #16171a;--muted: #585b62;--faint: #8e9097;--line: #e0dfdb;--line-soft: #ecebe7;--line-strong: #cfcec9;--accent: #2f62d0;--accent-hover: #2552b5;--accent-ink: #ffffff;--accent-soft: rgb(47 98 208 / .1);--pos: #0f7b4f;--neg: #c2352b;--warn: #b4690e;--info: #2563eb;--halo: #fff;color-scheme:light}:root{--r-sm: 5px;--r: 7px;--r-lg: 10px;--sans: "Geist Variable", ui-sans-serif, -apple-system, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;--mono: "Geist Mono Variable", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;--ease: cubic-bezier(.2, 0, 0, 1);--dur-fast: .12s;--dur: .18s;--dur-slow: .26s;--fast: var(--dur-fast) var(--ease);--slow: var(--dur-slow) var(--ease);--fs-xs: 11px;--fs-sm: 12px;--fs: 13px;--fs-md: 14px;--fs-lg: 16px;--fs-xl: 20px;--s1: 4px;--s2: 8px;--s3: 12px;--s4: 16px;--s5: 20px;--s6: 24px;--s8: 32px;--z-toolbar: 10;--z-panel: 20;--z-overlay: 40;--z-modal: 100;--shadow-sm: none;--shadow-lg: none;--pad: 20px;--gap: 14px;--border: var(--line);--fg: var(--ink);--card-bg: var(--panel);--font-mono: var(--mono);--ok: var(--pos);--bad: var(--neg)}*{box-sizing:border-box;margin:0;padding:0}html,body,#root{height:100%;overflow:hidden}body{background:var(--bg);color:var(--ink);font:var(--fs)/1.5 var(--sans);font-feature-settings:"cv11","ss01";-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}h1,h2,h3{font-weight:560;letter-spacing:-.015em;line-height:1.25}h1{font-size:16px}h2{font-size:14px}.eyebrow{font-size:10.5px;font-weight:500;letter-spacing:.09em;text-transform:uppercase;color:var(--faint)}.num,.kpi-val,td.n,th.n{font-family:var(--mono);font-variant-numeric:tabular-nums;font-feature-settings:"tnum","zero"}.dim{color:var(--muted)}.faint{color:var(--faint)}.pos{color:var(--pos)}.neg{color:var(--neg)}.warn{color:var(--warn)}.panel{background:var(--panel);border:1px solid var(--line);border-radius:var(--r)}.panel-head{display:flex;align-items:center;gap:10px;padding:12px var(--pad);border-bottom:1px solid var(--line-soft);min-height:44px}.panel-head h2{font-size:13px;font-weight:550}.panel-head .spacer{flex:1}.panel-body{padding:var(--pad)}.btn{font:inherit;font-size:12.5px;font-weight:500;color:var(--ink);background:var(--panel);border:1px solid var(--line);border-radius:var(--r-sm);padding:5px 11px;cursor:pointer;transition:background var(--fast),border-color var(--fast),color var(--fast);white-space:nowrap}.btn:hover{background:var(--raised);border-color:var(--faint)}.btn:active{transform:translateY(.5px)}.btn.primary{background:var(--accent);border-color:var(--accent);color:var(--accent-ink)}.btn.primary:hover{filter:brightness(1.08)}.btn:focus-visible,.seg>*:focus-visible,select:focus-visible,input:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.seg{position:relative;display:inline-flex;background:var(--raised);border:1px solid var(--line);border-radius:var(--r-sm);padding:2px;gap:2px}.seg>*{position:relative;z-index:1;font:inherit;font-size:12px;font-weight:500;color:var(--muted);background:none;border:0;border-radius:3px;padding:4px 10px;cursor:pointer;transition:color var(--fast)}.seg>*.on{color:var(--ink)}.seg>*:hover{color:var(--ink)}.seg .pill{position:absolute;z-index:0;top:2px;bottom:2px;background:var(--panel);border-radius:3px;box-shadow:0 0 0 1px var(--line);transition:transform var(--slow),width var(--slow);pointer-events:none}select,input[type=text],input[type=search],input[type=number],input[type=date]{font:inherit;font-size:12.5px;color:var(--ink);background:var(--panel);border:1px solid var(--line);border-radius:var(--r-sm);padding:5px 9px;outline:none;transition:border-color var(--fast)}select:hover,input:hover{border-color:var(--faint)}select{cursor:pointer}.chip{font-size:11px;font-weight:500;color:var(--muted);background:var(--raised);border:1px solid var(--line);border-radius:99px;padding:2px 9px;cursor:pointer;transition:all var(--fast)}.chip.on{color:var(--accent);border-color:var(--accent);background:var(--accent-soft)}.tag{font-size:10.5px;font-weight:500;font-family:var(--mono);border-radius:3px;padding:1.5px 6px;border:1px solid currentColor;opacity:.9}table{width:100%;border-collapse:collapse;font-size:12.5px}thead th{position:sticky;top:0;z-index:2;background:var(--panel);text-align:left;font-size:10.5px;font-weight:500;letter-spacing:.07em;text-transform:uppercase;color:var(--faint);padding:9px var(--pad);border-bottom:1px solid var(--line);white-space:nowrap;-webkit-user-select:none;user-select:none}thead th.sortable{cursor:pointer}thead th.sortable:hover{color:var(--ink)}thead th .caret{opacity:0;margin-left:4px;transition:opacity var(--fast)}thead th[data-dir] .caret{opacity:1;color:var(--accent)}tbody td{padding:9px var(--pad);border-bottom:1px solid var(--line-soft)}tbody tr{transition:background var(--fast)}tbody tr:hover{background:var(--raised)}tbody tr:last-child td{border-bottom:0}td.n,th.n{text-align:right}[data-reveal]{opacity:0;transform:translateY(6px)}[data-reveal].in{opacity:1;transform:none;transition:opacity .42s var(--ease) calc(var(--i, 0) * 28ms),transform .42s var(--ease) calc(var(--i, 0) * 28ms)}@keyframes kit-pulse{0%{opacity:1}50%{opacity:.25}to{opacity:1}}.live-dot{width:6px;height:6px;border-radius:50%;background:var(--pos);animation:kit-pulse 2.2s var(--ease) infinite}@media (prefers-reduced-motion: reduce){*,*:before,*:after{animation-duration:.001ms!important;transition-duration:.001ms!important}[data-reveal]{opacity:1;transform:none}}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{background:var(--line);border-radius:99px;border:3px solid var(--bg)}::-webkit-scrollbar-thumb:hover{background:var(--faint)}::-webkit-scrollbar-track{background:transparent}.kit-theme{width:30px;height:30px;display:grid;place-items:center;background:none;border:1px solid transparent;border-radius:var(--r-sm);color:var(--muted);cursor:pointer;flex:none;transition:color var(--fast),background var(--fast),border-color var(--fast)}.kit-theme:hover{color:var(--ink);background:var(--raised)}.kit-theme svg{width:15px;height:15px}.kit-theme-float{position:fixed;right:16px;bottom:16px;z-index:9998;width:34px;height:34px;background:var(--panel);border-color:var(--line)}.kit-theme-float:hover{background:var(--raised)}.pb-status,.pb-pill,[class*=status-chip]{font-family:var(--mono);text-transform:uppercase;letter-spacing:.08em}.pb-mobile-nav{display:none}.pb-mobile-menu{position:absolute;top:100%;right:0;background:var(--panel);border:1px solid var(--line);border-radius:var(--r);padding:var(--s1);z-index:var(--z-modal);box-shadow:0 4px 16px #0006}.pb-mobile-menu .pb-tab-menu-item{display:block;width:100%;text-align:left;padding:8px 16px;border:none;background:transparent;cursor:pointer;color:var(--ink);font-size:var(--fs)}.pb-mobile-menu .pb-tab-menu-item:hover{background:var(--hover)}@media (max-width: 640px){.pb-tabs--desktop{display:none}.pb-mobile-nav{display:block;position:relative}}.pb-tab-group{position:relative;display:inline-flex;align-items:stretch}.pb-tab-group-menu{display:none;position:absolute;top:100%;left:0;background:var(--panel);border:1px solid var(--line);border-radius:var(--r);padding:var(--s1);z-index:var(--z-modal);box-shadow:0 4px 16px #0006}.pb-tab-group:hover .pb-tab-group-menu,.pb-tab-group[data-active=true] .pb-tab-group-menu{display:block}.pb-tab-menu-item{display:block;width:100%;text-align:left;padding:8px 16px;border:none;background:transparent;cursor:pointer;color:var(--ink);font-size:var(--fs);white-space:nowrap}.pb-tab-menu-item:hover{background:var(--hover)}.pb-view-header{display:flex;align-items:center;justify-content:space-between;padding:12px 20px;border-bottom:1px solid var(--line);background:var(--panel);flex:none;gap:var(--s3)}.pb-view-header h2{margin:0;font-size:var(--fs-md);font-weight:600;color:var(--ink);white-space:nowrap}.pb-view-header p{margin:0;font-size:var(--fs-sm);color:var(--muted);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.brain-empty{padding:90px 24px;color:var(--ink)}.atlas-app{height:100%!important;margin-top:0}.atlas-app>aside{display:grid;grid-template-rows:auto auto auto minmax(0,1fr) auto;overflow:hidden}.atlas-app>aside .listwrap{min-height:0}.atlas-app>aside .foot{flex:none}.brain-vault{position:fixed;z-index:40;top:30px;left:0;right:0;padding:14px 16px 12px;box-sizing:border-box;background:var(--panel);color:var(--ink);border-bottom:1px solid color-mix(in srgb,currentColor 18%,transparent);display:flex;flex-direction:column;gap:8px}.brain-vault__row{display:flex;gap:8px}.brain-vault__row--tools{margin-top:2px}.brain-vault__path{flex:1 1 auto;min-width:0;font:inherit;font-family:Geist Mono Variable,ui-monospace,monospace;font-size:12px;color:var(--ink);background:color-mix(in srgb,var(--ink) 6%,transparent);border:1px solid color-mix(in srgb,currentColor 24%,transparent);border-radius:4px;padding:6px 9px}.brain-vault__path:focus-visible{outline:2px solid currentColor;outline-offset:1px}.brain-vault__open,.brain-vault__reindex{flex:none;font:inherit;font-size:12px;letter-spacing:.03em;padding:6px 12px;cursor:pointer;border-radius:4px;border:1px solid currentColor}.brain-vault__open{color:var(--accent-ink);background:var(--accent);border-color:var(--accent);font-weight:600}.brain-vault__open:disabled{opacity:.35;cursor:default}.brain-vault__reindex{color:currentColor;background:transparent;opacity:.7}.brain-vault__reindex:hover:not(:disabled){opacity:1}.brain-vault__error{color:var(--neg);font-size:12px;margin:0}.brain-vault__progress{color:var(--ink);font-size:12px;margin:0;font-family:Geist Mono Variable,ui-monospace,monospace}.brain-vault__done{color:var(--pos);font-size:12px;margin:0}.brain-landing{max-width:680px;height:100%;overflow:auto;margin:0 auto;padding:96px 24px 48px;color:var(--ink)}.brain-landing__title{font-size:28px;font-weight:600;letter-spacing:-.01em;margin:0 0 10px;color:var(--ink)}.brain-landing__lead{font-size:14px;line-height:1.6;opacity:.85;margin:0 0 22px}.brain-first-run{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;margin:0 0 22px;padding:0;list-style:none;counter-reset:steps}.brain-first-run li{counter-increment:steps;padding:12px;border:1px solid var(--border);border-radius:6px;background:#ffffff06;display:grid;gap:5px;font-size:12px}.brain-first-run strong:before{content:counter(steps) ". ";color:var(--info)}.brain-first-run span{color:var(--faint);line-height:1.4}.brain-shortcuts dl{margin:0}.brain-shortcuts dl div{display:grid;grid-template-columns:120px 1fr;gap:12px;padding:9px 0;border-top:1px solid var(--border)}.brain-shortcuts dd{margin:0;color:var(--faint)}.brain-shortcuts kbd{display:inline-block;margin-right:4px;padding:2px 6px;border:1px solid var(--border);border-radius:3px;font:12px var(--font-mono, monospace);color:var(--ink)}@media (max-width: 700px){.brain-first-run{grid-template-columns:1fr}}.brain-landing .brain-vault{position:static;background:color-mix(in srgb,var(--ink) 4%,transparent);border:1px solid color-mix(in srgb,currentColor 16%,transparent);border-radius:8px}.brain-vault__known{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-top:18px;font-size:12px;opacity:.85}.brain-vault__known-item{font:inherit;font-size:12px;color:currentColor;cursor:pointer;background:transparent;border:1px solid color-mix(in srgb,currentColor 22%,transparent);border-radius:999px;padding:4px 10px;opacity:.8;transition:opacity .14s ease,background-color .14s ease}.brain-vault__known-item:hover{opacity:1;background:color-mix(in srgb,currentColor 10%,transparent)}.brain-vault__known-item em{font-style:normal;opacity:.55}.notes-workbench{position:fixed;top:52px;right:0;bottom:0;left:0;display:grid;grid-template-columns:minmax(230px,29%) 1fr;background:var(--bg);z-index:2}.notes-sidebar{min-width:0;height:100%;display:flex;flex-direction:column;border-right:1px solid var(--border);background:var(--panel);overflow:hidden}.notes-sidebar__head,.notes-create,.notes-search,.notes-tag-drawer{flex:none}.notes-sidebar__head{display:flex;justify-content:space-between;align-items:center;padding:10px 12px;border-bottom:1px solid var(--border)}.notes-sidebar button,.notes-editor button,.notes-editor a{font:inherit;font-size:12px}.notes-sidebar__head button,.notes-editor .primary{background:var(--accent);color:var(--accent-ink);border:0;border-radius:5px;padding:6px 9px;cursor:pointer}.notes-create{display:flex;gap:6px;padding:8px 12px;border-bottom:1px solid var(--border);background:var(--panel)}.notes-create select{flex:1 1 auto;min-width:0;font:inherit;font-size:12px;background:var(--raised);color:var(--ink);border:1px solid var(--line);border-radius:4px;padding:5px 8px;cursor:pointer}.notes-create select:focus-visible{outline:2px solid var(--accent)}.notes-create button{flex:none;font:inherit;font-size:12px;font-weight:500;background:var(--accent);color:var(--accent-ink);border:0;border-radius:4px;padding:5px 12px;cursor:pointer;transition:background-color var(--dur-fast) var(--ease)}.notes-create button:hover:not(:disabled){background:var(--accent-hover)}.notes-search{display:flex;gap:6px;padding:8px 12px;border-bottom:1px solid var(--border)}.notes-search input{flex:1 1 auto;min-width:0;font:inherit;font-size:12px;background:var(--raised);color:var(--ink);border:1px solid var(--line);border-radius:4px;padding:5px 8px}.notes-search input:focus-visible{outline:2px solid var(--accent)}.notes-search button{flex:none;font:inherit;font-size:12px;font-weight:500;background:var(--raised);color:var(--ink);border:1px solid var(--line);border-radius:4px;padding:5px 12px;cursor:pointer;transition:background-color var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease)}.notes-search button:hover:not(:disabled){background:var(--hover);border-color:var(--line-strong)}.notes-tags{display:flex;gap:5px;flex-wrap:wrap}.notes-tags button{background:transparent;color:var(--ink);border:1px solid var(--border);border-radius:99px;padding:2px 7px;font-size:11px;cursor:pointer}.notes-tags .on{border-color:var(--accent);color:var(--accent-hover)}.notes-list,.notes-results{overflow-y:auto;min-height:0}.notes-list{flex:1 1 0}.notes-list button,.notes-results button{display:block;width:100%;text-align:left;color:var(--ink);background:transparent;border:0;border-bottom:1px solid var(--border);padding:10px 12px;cursor:pointer}.notes-list button.on{background:var(--hover);border-left:3px solid var(--accent)}.notes-list strong,.notes-list span,.notes-list small,.notes-results strong,.notes-results span,.notes-results small{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.notes-list span,.notes-list small,.notes-results span,.notes-results small{color:var(--faint);font-size:11px;margin-top:3px}.notes-editor{min-width:0;height:100%;display:flex;flex-direction:column;overflow:hidden}.notes-editor__head{flex:none;display:flex;justify-content:space-between;align-items:center;gap:12px;padding:10px 14px;border-bottom:1px solid var(--border);min-width:0}.notes-editor__head strong,.notes-editor__head span,.notes-editor__head small{display:block}.notes-editor__head .notes-editor__title,.notes-editor__head>div:first-child{min-width:0;flex:1 1 auto;overflow:hidden}.notes-editor__head strong{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13.5px}.notes-editor__head span{font-family:var(--mono);color:var(--faint);font-size:11px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.notes-editor__head small{color:var(--muted);font-size:10.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.notes-editor__head>div:last-child{display:flex;gap:6px;align-items:center;flex-shrink:0}.notes-editor__head a,.notes-editor__head button{color:var(--ink);background:transparent;border:1px solid var(--border);border-radius:5px;padding:5px 8px;text-decoration:none;white-space:nowrap}.notes-editor__head button:disabled{opacity:.45}.notes-frontmatter-bar{flex:none}.notes-content-grid{flex:1 1 50%;min-height:50%;overflow:hidden}.notes-editor textarea{resize:none;height:100%;min-height:0;width:100%;box-sizing:border-box;padding:16px;color:var(--ink);background:var(--bg);border:0;outline:0;font:13px/1.65 var(--mono)}.notes-meta-bar{flex:none;border-top:1px solid var(--border);background:var(--panel)}.notes-meta-toggle{width:100%;display:flex;justify-content:space-between;align-items:center;padding:5px 12px;background:transparent;border:0;color:var(--ink);cursor:pointer;font-size:11px;font-weight:600;letter-spacing:.04em;text-align:left}.notes-meta-toggle:hover{background:var(--hover)}.notes-meta-counts{color:var(--faint);font-size:11px;font-weight:400;font-family:var(--mono)}.notes-editor__meta{flex:0 1 auto;max-height:min(220px,35vh);display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:1px;background:var(--line);border-top:1px solid var(--border);overflow:hidden}.notes-editor__meta section{min-height:80px;max-height:min(220px,35vh);padding:8px 12px;background:var(--panel);overflow-y:auto;overflow-x:hidden}.notes-editor__meta h3{margin:0 0 6px;font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--faint);position:sticky;top:0;background:var(--panel);padding-bottom:2px;z-index:1}.notes-editor__meta button,.notes-editor__meta a,.notes-editor__meta span{display:block;margin:4px 0;color:var(--ink);background:none;border:0;padding:0;text-align:left;text-decoration:none;cursor:pointer;font-size:12px}.notes-editor__meta a{color:var(--accent)}.notes-conflict,.notes-notice{flex:none;margin:0;padding:8px 16px;font-size:12px}.notes-conflict{background:color-mix(in srgb,var(--neg) 15%,transparent);color:var(--neg)}.notes-empty{padding:24px;color:var(--faint)}.notes-empty--initial{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:48px 24px;text-align:center;gap:var(--s2)}.notes-empty__icon{width:36px;height:36px;color:var(--faint);margin-bottom:var(--s2)}.notes-empty--initial h3{margin:0;font-size:var(--fs-lg);font-weight:600;color:var(--ink)}.notes-empty--initial p{margin:0 0 var(--s2);font-size:var(--fs);color:var(--muted);max-width:440px;line-height:1.5}.notes-empty-list{padding:24px 16px;text-align:center;display:flex;flex-direction:column;align-items:center;gap:var(--s2)}.notes-empty-list p{margin:0;font-size:var(--fs-sm);color:var(--muted)}@media (max-width: 860px){.notes-editor__head{align-items:flex-start;flex-direction:column;gap:8px;padding:8px 12px}.notes-editor__head .notes-editor__title,.notes-editor__head>div:first-child{width:100%}.notes-editor__actions{width:100%;justify-content:flex-start;flex-wrap:wrap;gap:6px}}@media (max-width: 780px){.notes-workbench{display:flex!important;flex-direction:column!important;grid-template-columns:none!important;position:relative!important;height:100%!important;overflow-y:auto!important;overflow-x:hidden!important;width:100%!important;max-width:100vw!important}.notes-sidebar{max-height:220px!important;flex:none!important;width:100%!important;border-right:none!important;border-bottom:1px solid var(--border)!important;overflow-y:auto!important}.notes-sidebar__head,.notes-create{flex-wrap:wrap;gap:6px}.notes-create select{flex:1 1 120px}.notes-search{flex-wrap:wrap;gap:6px}.notes-editor{flex:1 1 auto!important;width:100%!important;min-height:400px!important;height:auto!important;display:flex!important;flex-direction:column!important;overflow:visible!important}.notes-content-grid{display:flex!important;flex-direction:column!important;grid-template-columns:none!important;height:auto!important;min-height:300px!important;overflow:visible!important;width:100%!important}.notes-editor-pane{border-right:none!important;width:100%!important;min-height:250px!important;box-sizing:border-box}.notes-editor textarea{min-height:220px!important}.notes-preview-pane{border-right:none!important;width:100%!important;min-height:200px!important;box-sizing:border-box}.notes-backlinks-pane{border-left:none!important;border-top:1px solid var(--border)!important;width:100%!important;min-width:0!important;max-height:220px!important;overflow-y:auto!important;box-sizing:border-box}.notes-frontmatter-bar{flex-wrap:wrap!important;gap:4px!important;padding:6px 10px!important}.notes-editor__meta{grid-template-columns:1fr!important}}.notes-save-status{font-family:var(--mono, monospace);text-transform:uppercase;font-size:11px;letter-spacing:.06em;padding:3px 8px;border-radius:4px;font-weight:600}.notes-save-status--saved{background:color-mix(in srgb,var(--pos) 20%,transparent);color:var(--pos)}.notes-save-status--saving{background:color-mix(in srgb,var(--info) 20%,transparent);color:var(--info)}.notes-save-status--dirty{background:color-mix(in srgb,var(--warn) 20%,transparent);color:var(--warn)}.notes-save-status--conflict{background:color-mix(in srgb,var(--neg) 20%,transparent);color:var(--neg)}.notes-results__section-head{padding:6px 12px;font-size:10px;text-transform:uppercase;letter-spacing:.08em;color:var(--faint);background:color-mix(in srgb,var(--ink) 3%,transparent);font-weight:600}.notes-results__sym-row{display:flex;align-items:center;justify-content:space-between}.notes-results__sym-kind{font-size:10px;padding:1px 5px;border-radius:3px;background:color-mix(in srgb,var(--info) 15%,transparent);color:var(--info);font-family:var(--mono)}.brain-note{position:fixed;left:50%;top:64px;transform:translate(-50%);z-index:30;max-width:min(680px,92vw);padding:8px 14px;font-size:13px;line-height:1.45;text-align:center;border:1px solid currentColor;border-radius:8px;background:color-mix(in srgb,canvas 88%,transparent)}.queue{display:flex;flex-direction:column;gap:18px;padding:18px 22px;height:100%;overflow:hidden}.queue__figures{display:flex;gap:34px;flex:0 0 auto}.queue__figure{display:flex;flex-direction:column;gap:2px}.queue__figure strong{font-size:30px;font-weight:600;line-height:1;font-variant-numeric:tabular-nums}.queue__figure span{font-size:10px;letter-spacing:.14em;opacity:.55}.queue__figure--hot strong{color:var(--warn)}.queue__list{list-style:none;margin:0;padding:0;overflow-y:auto;display:flex;flex-direction:column;gap:1px}.queue__row{display:grid;grid-template-columns:78px minmax(0,1fr) auto auto auto;gap:12px;align-items:baseline;padding:7px 10px;border-radius:5px;background:color-mix(in oklab,currentColor 4%,transparent)}.queue__row--delivered{opacity:.5}.queue__state{font-size:9.5px;letter-spacing:.12em;opacity:.6}.queue__row--pending .queue__state{color:var(--info);opacity:.9}.queue__row--claimed .queue__state{color:var(--pos);opacity:.9}.queue__title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.queue__holder,.queue__path{font-size:11px;opacity:.6}.queue__stale{font-size:9.5px;letter-spacing:.1em;color:var(--warn);border:1px solid currentColor;border-radius:3px;padding:1px 5px}.brain-modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;background:#000000bf;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);z-index:10000;display:grid;place-items:center}.brain-modal{background:var(--bg);border:1px solid var(--border);border-radius:8px;width:90%;max-width:520px;padding:24px;box-shadow:0 20px 40px #00000080}.brain-modal__header{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px}.brain-modal__header h3{margin:0;font-size:16px}.brain-modal__close{background:transparent;border:none;color:var(--faint);font-size:18px;cursor:pointer}.brain-modal__field{margin-bottom:16px}.brain-modal__field label{display:block;font-size:12px;margin-bottom:6px;color:var(--faint)}.brain-modal__input{width:100%;padding:8px 12px;background:var(--card-bg);border:1px solid var(--border);border-radius:4px;color:var(--fg);font-size:13px;box-sizing:border-box}.brain-modal__actions{display:flex;justify-content:flex-end;gap:12px;margin-top:24px}.brain-selection-modal{max-width:640px}.brain-selection-modal__hint{color:var(--faint);font-size:12px;line-height:1.5;margin:0 0 14px}.brain-selection-list{display:grid;gap:7px;max-height:min(42vh,360px);overflow:auto;margin:0;padding:10px;border:1px solid var(--border);border-radius:5px}.brain-selection-list legend{color:var(--faint);font-size:11px;padding:0 4px}.brain-selection-list label{display:flex;align-items:flex-start;gap:8px;padding:7px 8px;border-radius:4px;cursor:pointer}.brain-selection-list label:hover{background:var(--card-bg)}.brain-selection-list label.is-retired{cursor:not-allowed;opacity:.52}.brain-selection-list input{margin-top:3px}.brain-selection-list span{display:grid;gap:2px;min-width:0}.brain-selection-list strong{font:600 12px var(--font-mono, ui-monospace, monospace);color:var(--fg)}.brain-selection-list small{font:10px var(--font-mono, ui-monospace, monospace);color:var(--faint);overflow-wrap:anywhere}.workbench-split{display:flex;height:100%;width:100%}.workbench-pane--side{width:380px;min-width:320px;max-width:500px;border-right:1px solid var(--border);height:100%;overflow-y:auto;background:var(--bg);display:flex;flex-direction:column}.workbench-pane--main{flex:1;height:100%;overflow:hidden;background:var(--card-bg);display:flex;flex-direction:column}@media (max-width: 860px){.workbench-split{flex-direction:column}.workbench-pane--side{width:100%;min-width:0;max-width:100%;max-height:40vh;border-right:none;border-bottom:1px solid var(--border)}.workbench-pane--main{width:100%;flex:1;min-height:50vh}}.atlas-source-overlay{position:absolute;top:16px;bottom:16px;right:16px;width:55%;min-width:500px;background:var(--bg);border:1px solid var(--border);border-radius:8px;box-shadow:0 12px 36px #0009;z-index:500;overflow:hidden;display:flex;flex-direction:column}.source-container{display:flex;flex-direction:column;height:100%;width:100%;background:var(--bg);color:var(--fg)}.source-header{display:flex;align-items:center;justify-content:space-between;padding:10px 16px;background:var(--card-bg);border-bottom:1px solid var(--border);flex-shrink:0;gap:12px}.source-header__meta{display:flex;align-items:center;gap:8px;overflow:hidden;flex-wrap:wrap}.source-header__path{font-weight:600;font-size:13px;color:var(--info);white-space:nowrap}.source-badge{font-size:11px;padding:2px 8px;border-radius:4px;border:1px solid var(--border);background:var(--raised);white-space:nowrap}.source-badge--lang{color:var(--pos);border-color:color-mix(in srgb,var(--pos) 30%,transparent)}.source-badge--git{color:var(--warn);border-color:color-mix(in srgb,var(--warn) 30%,transparent)}.source-badge--agent{display:inline-flex;align-items:center;gap:6px}.source-badge--agent i{display:inline-block;width:7px;height:7px;border-radius:50%}.source-badge--highlight{background:var(--raised);color:var(--ink);border:1px dashed var(--line-strong);font-weight:600}.source-close-btn{background:transparent;border:none;color:var(--faint);font-size:16px;cursor:pointer;padding:4px 8px;border-radius:4px}.source-close-btn:hover{background:var(--hover);color:var(--ink)}.source-body{flex:1;overflow:auto;position:relative;background:var(--bg)}.source-code-view{min-width:100%}.source-table{width:100%;border-collapse:collapse;font-size:12.5px;line-height:1.55}.source-line-row{vertical-align:top}.source-line-row:hover{background:var(--hover)}.source-line-row--highlight{background:var(--accent-soft)!important}.source-line-num{width:44px;min-width:44px;padding:0 10px;text-align:right;color:var(--faint);-webkit-user-select:none;user-select:none;border-right:1px solid var(--line);background:var(--panel);position:sticky;left:0}.source-line-row--highlight .source-line-num{color:var(--accent);font-weight:700}.source-line-code{padding:0 14px;white-space:pre;color:var(--ink)}.source-line-code pre{margin:0;font-family:inherit;white-space:pre}.source-container--error{background:var(--bg)}.source-error-box{padding:40px 24px;text-align:center;max-width:500px;margin:40px auto;background:color-mix(in srgb,var(--neg) 8%,transparent);border:1px solid color-mix(in srgb,var(--neg) 30%,transparent);border-radius:8px}.source-error-icon{font-size:36px;margin-bottom:12px}.source-error-box h3{color:var(--neg);margin:0 0 8px;font-size:16px}.source-error-msg{color:var(--ink);font-size:13px;margin-bottom:16px}.source-error-details{background:var(--panel);padding:10px;border-radius:4px;font-size:11px;color:var(--faint);text-align:left}.source-placeholder{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:var(--faint);text-align:center;padding:24px}.source-placeholder__icon{font-size:48px;margin-bottom:16px;opacity:.6}.explorer-view{display:flex;flex-direction:column;height:100%}.explorer-header{padding:12px 16px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between}.explorer-title{display:flex;align-items:center;gap:8px;font-size:13px}.explorer-stats{font-size:11px;color:var(--faint)}.explorer-search{padding:8px 12px;border-bottom:1px solid var(--border);position:relative}.explorer-search__input{width:100%;padding:6px 28px 6px 10px;background:var(--card-bg);border:1px solid var(--border);border-radius:4px;color:var(--fg);font-size:12px;box-sizing:border-box}.explorer-search__clear{position:absolute;right:18px;top:13px;background:transparent;border:none;color:var(--faint);cursor:pointer;font-size:12px}.explorer-tree{flex:1;overflow-y:auto;padding:6px 0}.tree-actions{display:flex;gap:6px;padding:0 16px 8px}.tree-action-btn{background:#ffffff0d;border:1px solid var(--border);color:var(--faint);font-size:11px;padding:3px 8px;border-radius:4px;cursor:pointer;transition:all .15s ease}.tree-action-btn:hover{background:#ffffff1a;color:var(--ink)}.tree-chevron{font-size:8px;color:var(--faint);width:12px;text-align:center;display:inline-block;-webkit-user-select:none;user-select:none}.tree-item{display:flex;align-items:center;gap:6px;padding:4px 10px;cursor:pointer;font-size:12.5px;-webkit-user-select:none;user-select:none}.tree-item:hover{background:#ffffff0d}.tree-item--active{background:var(--hover)!important;color:var(--accent);font-weight:500}.tree-icon{font-size:13px;flex-shrink:0}.tree-label{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tree-badge{font-size:10px;padding:1px 5px;border-radius:3px;background:var(--raised);color:var(--faint)}.tree-agent-dot{width:6px;height:6px;border-radius:50%;flex-shrink:0}.search-view{display:flex;flex-direction:column;height:100%}.search-view__header{padding:12px 16px;border-bottom:1px solid var(--border)}.search-view__title{display:flex;align-items:center;gap:8px;font-size:13px;flex-wrap:wrap}.search-view__endpoint{font-size:10.5px;background:var(--raised);padding:1px 6px;border-radius:3px;color:var(--faint)}.search-form{padding:12px 16px;border-bottom:1px solid var(--border)}.search-input-group{display:flex;gap:8px}.search-input{flex:1;padding:7px 12px;background:var(--card-bg);border:1px solid var(--border);border-radius:4px;color:var(--fg);font-size:13px}.search-submit-btn{background:var(--accent);color:var(--accent-ink);border:none;padding:7px 14px;border-radius:4px;cursor:pointer;font-weight:500;font-size:12px}.search-submit-btn:disabled{opacity:.5;cursor:not-allowed}.search-error-alert{margin:12px 16px;padding:10px 14px;background:color-mix(in srgb,var(--neg) 15%,transparent);border:1px solid var(--neg);border-radius:4px;color:var(--neg);font-size:12px}.search-results{flex:1;overflow-y:auto;padding:12px 16px}.search-results-summary{font-size:12px;color:var(--faint);margin-bottom:12px}.search-hits-list{display:flex;flex-direction:column;gap:8px}.search-hit-card{background:var(--card-bg);border:1px solid var(--border);border-radius:6px;padding:10px 12px;cursor:pointer;transition:border-color .15s}.search-hit-card:hover{border-color:var(--accent);background:var(--hover)}.search-hit-card__head{display:flex;align-items:center;gap:8px;margin-bottom:4px}.search-hit-name{font-weight:600;font-size:13px;color:var(--fg)}.search-hit-kind{font-size:10.5px;padding:1px 6px;border-radius:3px;background:var(--raised);color:var(--info)}.search-hit-line{font-size:11px;color:var(--faint);margin-left:auto}.search-hit-path{font-size:11.5px;color:var(--faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.search-hit-snippet{font-size:12px;line-height:1.45;color:var(--fg);opacity:.85;margin:2px 0 0;overflow-wrap:anywhere}.pack-view{display:flex;flex-direction:column;height:100%}.pack-view__header{padding:12px 16px;border-bottom:1px solid var(--border)}.pack-view__title{display:flex;align-items:center;gap:8px;font-size:13px}.pack-view__endpoint{font-size:10.5px;background:#ffffff0f;padding:1px 6px;border-radius:3px;color:var(--faint)}.pack-form{padding:12px 16px;border-bottom:1px solid var(--border)}.pack-form__field label{display:block;font-size:12px;margin-bottom:6px;color:var(--faint)}.pack-input-row{display:flex;gap:8px}.pack-input{flex:1;padding:7px 12px;background:var(--card-bg);border:1px solid var(--border);border-radius:4px;color:var(--fg);font-size:13px}.pack-create-btn{background:var(--accent);color:var(--accent-ink);border:none;padding:7px 14px;border-radius:4px;cursor:pointer;font-weight:500;font-size:12px}.pack-create-btn:disabled{opacity:.5;cursor:not-allowed}.pack-error-alert{margin:12px 16px;padding:10px 14px;background:color-mix(in srgb,var(--neg) 15%,transparent);border:1px solid var(--neg);border-radius:4px;color:var(--neg);font-size:12px}.pack-details{flex:1;overflow-y:auto;padding:12px 16px}.pack-card{background:var(--card-bg);border:1px solid var(--border);border-radius:8px;padding:16px}.pack-card__header{display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--border);padding-bottom:12px;margin-bottom:12px}.pack-card__meta{display:flex;align-items:center;gap:8px}.pack-id{font-weight:700;color:var(--accent)}.pack-badge{font-size:11px;padding:2px 6px;border-radius:4px;background:var(--raised)}.pack-card__staleness{display:flex;align-items:center;gap:8px}.pack-staleness-badge{font-size:12px;font-weight:600;padding:3px 8px;border-radius:4px}.pack-staleness-badge--fresh{background:color-mix(in srgb,var(--pos) 20%,transparent);color:var(--pos)}.pack-staleness-badge--stale{background:color-mix(in srgb,var(--neg) 20%,transparent);color:var(--neg)}.pack-staleness-btn{background:transparent;border:1px solid var(--border);color:var(--fg);padding:3px 8px;border-radius:4px;font-size:11px;cursor:pointer}.pack-stale-warning{background:color-mix(in srgb,var(--neg) 15%,transparent);border:1px solid var(--neg);border-radius:4px;padding:8px 12px;color:var(--neg);font-size:12px;margin-bottom:16px}.pack-sources-section h4{margin:0 0 10px;font-size:13px;color:var(--faint)}.pack-sources-list{display:flex;flex-direction:column;gap:6px}.pack-source-item{padding:8px 10px;background:#ffffff08;border:1px solid var(--border);border-radius:4px;cursor:pointer;transition:border-color .15s}.pack-source-item:hover{border-color:var(--accent);background:var(--hover)}.pack-source-path{font-size:12px;font-weight:600;color:var(--info);margin-bottom:2px}.pack-source-why{font-size:11.5px;color:var(--faint)}.pack-body-toggle{margin-top:16px;padding-top:12px;border-top:1px solid var(--border)}.pack-toggle-raw-btn{background:transparent;border:none;color:var(--accent);font-size:12px;cursor:pointer;padding:0}.pack-raw-markdown{margin-top:10px;background:var(--panel);padding:12px;border-radius:4px;font-size:11.5px;color:var(--ink);max-height:250px;overflow-y:auto}.pack-raw-markdown pre{margin:0;white-space:pre-wrap}.brain-empty--offline-box{text-align:center;max-width:480px;padding:32px;background:color-mix(in srgb,var(--neg) 8%,transparent);border:1px solid color-mix(in srgb,var(--neg) 25%,transparent);border-radius:8px}.offline-icon{font-size:42px;margin-bottom:12px}.brain-empty--offline-box h3{color:var(--neg);margin:0 0 8px}.brain-empty--offline-box p{font-size:13px;color:var(--neg);margin-bottom:16px}.notes-create{display:flex;gap:6px;padding:8px 10px;flex-wrap:wrap}.notes-create select{min-width:0;flex:1 1 90px}.knowledge-graph{height:100%;min-height:0;display:flex;flex-direction:column;padding:18px;gap:10px;outline:none}.knowledge-graph:focus-visible{box-shadow:inset 0 0 0 2px var(--accent)}.knowledge-graph__head{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.knowledge-graph__head h2{margin:0}.knowledge-graph__head p{color:var(--faint);margin:3px 0;font-size:12px}.knowledge-graph__head label{display:grid;gap:3px;font-size:11px;color:var(--faint)}.knowledge-graph__coverage{color:var(--faint);font-size:12px;margin:0}.knowledge-graph__body{min-height:0;flex:1;display:grid;grid-template-columns:minmax(260px,1fr) minmax(220px,.72fr);gap:12px}.knowledge-graph__list{overflow:auto;margin:0;padding:0;list-style:none;border:1px solid var(--border);border-radius:6px}.knowledge-graph__list button{width:100%;display:grid;grid-template-columns:10px 1fr auto;gap:9px;text-align:left;padding:9px 11px;border:0;border-bottom:1px solid var(--border);background:transparent;color:inherit;cursor:pointer}.knowledge-graph__list button:hover,.knowledge-graph__list button.on{background:#60a5fa1f}.knowledge-graph__list i{width:8px;height:8px;border-radius:99px;margin-top:5px}.knowledge-graph__list small,.knowledge-graph__list b{color:var(--faint);font-size:11px}.knowledge-graph__list span{display:grid;gap:2px}.knowledge-graph__empty{padding:16px;color:var(--faint)}.knowledge-graph__inspector{border:1px solid var(--border);border-radius:6px;padding:14px;overflow:auto}.knowledge-graph__inspector h3{margin-top:0}.knowledge-graph__inspector code{display:block;color:var(--accent);overflow-wrap:anywhere;margin:6px 0 10px}.knowledge-graph__inspector dl{margin:0 0 12px}.knowledge-graph__inspector dl div{display:grid;grid-template-columns:82px 1fr;gap:8px;padding:5px 0;border-top:1px solid var(--border)}.knowledge-graph__inspector dt{color:var(--faint)}.knowledge-graph__inspector dd{margin:0;overflow-wrap:anywhere}.knowledge-graph__inspector section{margin-top:16px;display:grid;gap:5px}.knowledge-graph__inspector section button{text-align:left}@media (max-width: 800px){.knowledge-graph__body{grid-template-columns:1fr}.knowledge-graph__inspector{max-height:280px}}.markdown-preview{color:var(--ink);font-family:var(--sans);font-size:13.5px;line-height:1.68;word-break:break-word}.markdown-preview p{margin:0 0 12px}.markdown-preview p:last-child{margin-bottom:0}.markdown-preview .md-empty{color:var(--faint);font-style:italic}.md-heading{color:var(--ink);font-weight:600;letter-spacing:-.02em;margin:18px 0 10px;line-height:1.3}.md-heading:first-child{margin-top:0}.md-heading--1{font-size:20px;border-bottom:1px solid var(--line);padding-bottom:6px}.md-heading--2{font-size:17px;border-bottom:1px solid var(--line-soft);padding-bottom:4px}.md-heading--3{font-size:15px}.md-heading--4{font-size:13.5px}.md-heading--5{font-size:12.5px}.md-heading--6{font-size:12px;color:var(--muted)}.md-hr{border:0;border-top:1px solid var(--line);margin:18px 0}.md-callout{border-radius:6px;padding:10px 14px;margin:14px 0}.md-callout-header{display:flex;align-items:center;gap:8px;font-family:var(--mono);font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;margin-bottom:6px}.md-callout-glyph{font-size:13px;line-height:1}.md-callout-content{font-size:13px;line-height:1.55}.md-callout-content p{margin:0 0 6px}.md-callout-content p:last-child{margin-bottom:0}.md-blockquote{border-left:3px solid var(--line-strong);padding:6px 12px;margin:12px 0;color:var(--muted);background:#ffffff05;border-radius:0 4px 4px 0}.md-blockquote p{margin:0 0 6px}.md-blockquote p:last-child{margin-bottom:0}.md-table-wrap{overflow-x:auto;margin:14px 0;border:1px solid var(--line);border-radius:6px;background:var(--panel)}.md-table{width:100%;border-collapse:collapse;font-size:12px}.md-table th{background:var(--raised);padding:8px 12px;border-bottom:1px solid var(--line);color:var(--muted);font-weight:600;text-transform:uppercase;font-size:10.5px;letter-spacing:.06em}.md-table td{padding:7px 12px;border-bottom:1px solid var(--line-soft);color:var(--ink)}.md-table tr:hover td{background:var(--hover)}.md-table tr:last-child td{border-bottom:0}.md-checklist{list-style:none;padding-left:0;margin:10px 0}.md-checklist-item{display:flex;align-items:flex-start;gap:8px;margin:4px 0;font-size:13px}.md-checklist-item.is-checked .md-checklist-label{text-decoration:line-through;color:var(--faint)}.md-checkbox{margin-top:3px;accent-color:var(--accent);cursor:pointer}.md-ul,.md-ol{margin:10px 0;padding-left:22px}.md-ul li,.md-ol li{margin:3px 0}.md-inline-code{font-family:var(--mono);font-size:11.5px;padding:1.5px 5px;border-radius:4px;background:#ffffff0f;color:var(--info);border:1px solid rgba(255,255,255,.08)}.md-code-link{display:inline-flex;align-items:center;gap:4px;background:color-mix(in srgb,var(--accent) 8%,transparent);border:1px solid color-mix(in srgb,var(--accent) 25%,transparent);border-radius:4px;padding:1px 6px;color:var(--accent-hover);cursor:pointer;font-size:11.5px;font-family:var(--mono);transition:all var(--fast)}.md-code-link:hover{background:color-mix(in srgb,var(--accent) 18%,transparent);border-color:var(--accent)}.md-code-link code{color:inherit;background:transparent;padding:0;border:0}.md-code-link__glyph{font-size:10px;opacity:.75}.md-wikilink{display:inline-flex;align-items:center;gap:2px;background:color-mix(in srgb,var(--info) 8%,transparent);border:1px solid color-mix(in srgb,var(--info) 20%,transparent);border-radius:4px;padding:1px 5px;color:var(--info);cursor:pointer;font-size:12px;transition:all var(--fast)}.md-wikilink:hover{background:color-mix(in srgb,var(--info) 18%,transparent);border-color:var(--info);color:var(--ink)}.md-wikilink__brackets{color:var(--faint);font-family:var(--mono);font-size:11px}.md-wikilink__arrow{font-size:9px;opacity:.7;margin-left:2px}.md-link{color:var(--info);text-decoration:underline;text-underline-offset:3px}.md-link:hover{color:var(--accent-hover)}.md-code-block{margin:14px 0;border-radius:6px;border:1px solid var(--line);background:var(--panel);overflow:hidden}.md-code-header{display:flex;align-items:center;justify-content:space-between;padding:5px 12px;background:var(--raised);border-bottom:1px solid var(--line);font-family:var(--mono);font-size:10.5px;letter-spacing:.06em;text-transform:uppercase}.md-code-lang{color:var(--muted);font-weight:600}.md-code-copy{background:transparent;border:1px solid var(--line);color:var(--muted);border-radius:4px;padding:2px 7px;font-size:10.5px;cursor:pointer;transition:all var(--fast)}.md-code-copy:hover{color:var(--ink);border-color:var(--accent)}.md-code-pre{margin:0;padding:10px 0;overflow-x:auto;font-family:var(--mono);font-size:12px;line-height:1.6}.md-code-line{display:flex;padding:0 12px}.md-code-line:hover{background:#ffffff05}.md-line-num{width:32px;flex:none;color:var(--faint);-webkit-user-select:none;user-select:none;text-align:right;padding-right:12px;font-size:11px}.md-line-code{flex:1;white-space:pre}.md-tok--keyword{color:var(--info);font-weight:500}.md-tok--string{color:var(--pos)}.md-tok--comment{color:var(--faint);font-style:italic}.md-tok--number{color:var(--warn)}.md-tok--type{color:var(--accent)}.md-tok--fn{color:var(--info)}.md-tok--text{color:var(--ink)}.notes-view-mode-toggle{display:inline-flex;background:var(--raised);border:1px solid var(--line);border-radius:6px;padding:2px}.notes-toggle-btn{background:transparent;border:0;color:var(--muted);border-radius:4px;padding:3px 9px;font-size:11px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;cursor:pointer;transition:all var(--fast)}.notes-toggle-btn.on{background:var(--accent);color:var(--accent-ink);font-weight:700}.notes-frontmatter-bar{display:flex;align-items:center;gap:6px;flex-wrap:wrap;padding:6px 16px;background:var(--raised);border-bottom:1px solid var(--line)}.notes-fm-pill{font-family:var(--mono);font-size:10px;letter-spacing:.08em;text-transform:uppercase;padding:2px 7px;border-radius:4px;font-weight:600}.notes-fm-pill--entscheidung{background:color-mix(in srgb,var(--pos) 15%,transparent);color:var(--pos);border:1px solid color-mix(in srgb,var(--pos) 30%,transparent)}.notes-fm-pill--widerspruch{background:color-mix(in srgb,var(--warn) 15%,transparent);color:var(--warn);border:1px solid color-mix(in srgb,var(--warn) 30%,transparent)}.notes-fm-pill--notiz,.notes-fm-pill--stand{background:color-mix(in srgb,var(--muted) 15%,transparent);color:var(--muted);border:1px solid color-mix(in srgb,var(--muted) 30%,transparent)}.notes-fm-btn{display:inline-flex;align-items:center;gap:4px;font-family:var(--mono);font-size:10.5px;letter-spacing:.04em;padding:2px 7px;border-radius:4px;background:transparent;cursor:pointer;transition:all var(--fast)}.notes-fm-btn--link{color:var(--info);border:1px solid color-mix(in srgb,var(--info) 30%,transparent);background:color-mix(in srgb,var(--info) 8%,transparent)}.notes-fm-btn--link:hover{background:color-mix(in srgb,var(--info) 18%,transparent);border-color:var(--info)}.notes-fm-btn--code{color:var(--pos);border:1px solid color-mix(in srgb,var(--pos) 30%,transparent);background:color-mix(in srgb,var(--pos) 8%,transparent)}.notes-fm-btn--code:hover{background:color-mix(in srgb,var(--pos) 18%,transparent);border-color:var(--pos)}.notes-fm-btn--rev{color:var(--accent);border:1px solid color-mix(in srgb,var(--accent) 30%,transparent);background:color-mix(in srgb,var(--accent) 8%,transparent)}.notes-fm-btn--rev:hover{background:color-mix(in srgb,var(--accent) 18%,transparent);border-color:var(--accent)}.notes-tag-drawer{padding:8px 12px;border-bottom:1px solid var(--border)}.notes-tag-drawer__head{display:flex;align-items:center;justify-content:space-between;margin-bottom:6px}.notes-tag-drawer__toggle{background:transparent;border:0;color:var(--muted);cursor:pointer;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;padding:0;display:flex;align-items:center;gap:4px}.notes-tag-drawer__toggle:hover{color:var(--ink)}.notes-tag-drawer__active{display:flex;align-items:center;gap:4px}.notes-tag-chip{font-size:10.5px;font-family:var(--mono);background:var(--accent-soft);color:var(--accent-hover);border:1px solid color-mix(in srgb,var(--accent) 30%,transparent);border-radius:99px;padding:1px 6px}.notes-tag-clear{background:transparent;border:0;color:var(--faint);cursor:pointer;padding:0 2px;font-size:10px}.notes-tag-clear:hover{color:var(--neg)}.notes-tag-drawer__body{display:flex;flex-direction:column;gap:6px;margin-top:6px}.notes-tag-search{width:100%;box-sizing:border-box;font-size:11.5px;padding:4px 8px;border-radius:4px;border:1px solid var(--line);background:var(--raised);color:var(--ink)}.notes-tag-drawer__list{max-height:140px;overflow-y:auto;display:flex;flex-wrap:wrap;gap:4px;padding-top:4px}.notes-tags--compact{display:flex;flex-wrap:wrap;gap:4px}.notes-tags-more{background:transparent;border:1px dashed var(--line);color:var(--faint);border-radius:99px;padding:2px 6px;font-size:10.5px;cursor:pointer}.notes-tags-more:hover{color:var(--ink);border-color:var(--muted)}.notes-overflow-dropdown{position:absolute;top:100%;right:0;margin-top:4px;background:var(--panel);border:1px solid var(--line);border-radius:var(--r);padding:4px;z-index:var(--z-modal);min-width:180px;box-shadow:0 8px 24px #00000080;display:flex;flex-direction:column;gap:2px}.notes-overflow-dropdown button,.notes-overflow-dropdown a{display:block;width:100%;text-align:left;padding:7px 10px;background:transparent;border:0;border-radius:var(--r-sm);color:var(--ink);font-size:var(--fs-sm);cursor:pointer;text-decoration:none;box-sizing:border-box}.notes-overflow-dropdown button:hover:not(:disabled),.notes-overflow-dropdown a:hover{background:var(--hover);color:var(--ink)}.notes-overflow-dropdown button:disabled{opacity:.45;cursor:default}.brain-quick-switcher{max-width:540px;display:flex;flex-direction:column;gap:var(--s2)}.quick-switcher-input-wrap{width:100%}.quick-switcher-input-wrap input{width:100%;font-size:var(--fs);padding:10px 14px}.quick-switcher-list{max-height:320px;overflow-y:auto;display:flex;flex-direction:column;gap:2px;padding:4px 0}.quick-switcher-empty{color:var(--muted);font-size:var(--fs-sm);text-align:center;padding:24px;margin:0}.quick-switcher-item{display:flex;flex-direction:column;align-items:flex-start;width:100%;text-align:left;padding:8px 12px;background:transparent;border:0;border-radius:var(--r-sm);cursor:pointer;gap:2px}.quick-switcher-item:hover,.quick-switcher-item.is-selected{background:var(--hover)}.quick-switcher-item__title{font-size:var(--fs);font-weight:600;color:var(--ink)}.quick-switcher-item__path{font-size:var(--fs-xs);font-family:var(--mono);color:var(--muted)}.quick-switcher-item__tags{font-size:var(--fs-xs);color:var(--accent);margin-top:2px}.quick-switcher-hint{display:flex;align-items:center;gap:var(--s3);padding-top:var(--s2);border-top:1px solid var(--line);font-size:var(--fs-xs);color:var(--faint)}.quick-switcher-hint kbd{background:var(--raised);border:1px solid var(--line);border-radius:3px;padding:1px 4px;font-family:var(--mono);color:var(--muted);margin-right:4px}.brain-settings-modal{max-width:760px;width:95%;max-height:85vh;display:flex;flex-direction:column}.brain-settings-layout{display:grid;grid-template-columns:190px 1fr;gap:24px;min-height:400px;max-height:60vh}.brain-settings-nav{display:flex;flex-direction:column;gap:4px;border-right:1px solid var(--line);padding-right:14px}.brain-settings-nav-item{display:flex;align-items:center;gap:10px;padding:8px 12px;border-radius:4px;background:transparent;border:1px solid transparent;color:var(--muted);font-size:13px;text-align:left;cursor:pointer;transition:background var(--dur-fast),color var(--dur-fast)}.brain-settings-nav-item:hover{background:var(--hover);color:var(--ink)}.brain-settings-nav-item[data-active=true]{background:var(--panel);border-color:var(--line);color:var(--ink);font-weight:600}.brain-settings-content{overflow-y:auto;padding-right:8px}.brain-settings-section-title{margin:0 0 6px;font-size:15px;font-weight:600;color:var(--ink)}.brain-settings-section-desc{margin:0 0 18px;font-size:12px;color:var(--muted);line-height:1.5}.brain-theme-options{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin-bottom:24px}.brain-theme-card{padding:16px 12px;border:1px solid var(--line);border-radius:6px;background:var(--panel);color:var(--ink);cursor:pointer;text-align:center;font-size:13px;transition:border-color var(--dur-fast),background var(--dur-fast)}.brain-theme-card:hover{border-color:var(--line-strong);background:var(--hover)}.brain-theme-card[data-active=true]{border-color:var(--accent);background:var(--accent-soft);color:var(--accent);font-weight:600}.brain-settings-shortcuts-table{width:100%;border-collapse:collapse;font-size:13px}.brain-settings-shortcuts-table tr{border-bottom:1px solid var(--line-soft)}.brain-settings-shortcuts-table td{padding:8px 6px}.brain-settings-shortcuts-table kbd{background:var(--raised);border:1px solid var(--line);border-radius:3px;padding:2px 6px;font-family:var(--mono);font-size:12px;color:var(--ink);margin-right:4px}.brain-settings-vault-card{background:var(--panel);border:1px solid var(--line);border-radius:6px;padding:14px 16px;margin-bottom:16px}.brain-settings-vault-card dl{display:grid;grid-template-columns:120px 1fr;gap:6px 12px;font-size:12px;margin:0}.brain-settings-vault-card dt{color:var(--muted)}.brain-settings-vault-card dd{margin:0;color:var(--ink);font-family:var(--mono);word-break:break-all}@media (max-width: 640px){.brain-settings-layout{grid-template-columns:1fr;gap:16px}.brain-settings-nav{flex-direction:row;overflow-x:auto;border-right:none;border-bottom:1px solid var(--line);padding-right:0;padding-bottom:10px}.brain-theme-options{grid-template-columns:1fr}}.briefing-view{display:flex;flex-direction:column;height:100%;min-height:0;overflow:hidden;background:var(--bg)}.briefing-body{flex:1;overflow-y:auto;padding:20px;max-width:1080px;margin:0 auto;width:100%;box-sizing:border-box}.briefing-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:16px}.briefing-card{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-lg);padding:18px;display:flex;flex-direction:column;gap:12px}.briefing-card--summary{grid-column:1 / -1;background:var(--raised)}.briefing-card__head h3{margin:0;font-size:var(--fs-md);font-weight:600;color:var(--ink)}.briefing-summary-text{margin:0;font-size:var(--fs);line-height:1.6;color:var(--muted)}.briefing-stats-row{display:grid;grid-template-columns:repeat(4,1fr);gap:12px}.briefing-stat{display:flex;flex-direction:column;align-items:center;justify-content:center;background:var(--bg);padding:12px 8px;border-radius:var(--r);border:1px solid var(--line-soft)}.briefing-stat__num{font-family:var(--mono);font-size:20px;font-weight:700;color:var(--accent)}.briefing-stat__label{font-size:var(--fs-xs);text-transform:uppercase;letter-spacing:.05em;color:var(--muted);margin-top:4px}.briefing-languages{margin-top:8px;display:flex;flex-direction:column;gap:6px}.briefing-subhead{font-size:var(--fs-xs);color:var(--muted);text-transform:uppercase}.briefing-lang-chips{display:flex;flex-wrap:wrap;gap:6px}.briefing-lang-chip{font-family:var(--mono);font-size:var(--fs-xs);background:var(--raised);border:1px solid var(--line);padding:2px 8px;border-radius:999px;color:var(--ink)}.briefing-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}.briefing-list-item{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:6px 8px;border-radius:var(--r);background:var(--bg);border:1px solid var(--line-soft);flex-wrap:wrap}.briefing-link-btn{background:transparent;border:none;color:var(--ink);cursor:pointer;display:inline-flex;align-items:center;gap:6px;font-family:var(--mono);font-size:12px;text-align:left;padding:0;word-break:break-all}.briefing-link-btn:hover{color:var(--accent);text-decoration:underline}.briefing-desc{font-size:var(--fs-xs);color:var(--muted)}.briefing-degree-badge{font-family:var(--mono);font-size:11px;background:var(--raised);border:1px solid var(--line-strong);padding:2px 6px;border-radius:var(--r);color:var(--accent);font-weight:600}.briefing-change-meta{display:inline-flex;align-items:center;gap:8px;font-size:var(--fs-xs)}.briefing-kind-badge{font-family:var(--mono);font-size:10px;text-transform:uppercase;padding:1px 5px;border-radius:3px;background:var(--raised);border:1px solid var(--line);color:var(--muted)}.briefing-time{color:var(--faint)}.briefing-card--unavailable{text-align:center;padding:32px 20px;align-items:center}.briefing-unavailable-icon{width:36px;height:36px;color:var(--muted)}.briefing-card--unavailable h3{margin:0;font-size:var(--fs-md);color:var(--ink)}.briefing-card--unavailable p{margin:0;max-width:500px;color:var(--muted);font-size:var(--fs-sm);line-height:1.5}.briefing-empty{margin:0;color:var(--faint);font-size:var(--fs-sm);font-style:italic}@media (max-width: 640px){.briefing-body{padding:12px 8px}.briefing-grid{grid-template-columns:1fr}.briefing-stats-row{grid-template-columns:repeat(2,1fr)}}.ask-modal{width:680px;max-width:calc(100vw - 32px);max-height:85vh;display:flex;flex-direction:column;background:var(--panel);border:1px solid var(--line);border-radius:var(--r-lg);box-shadow:0 16px 48px #00000080;overflow:hidden;position:relative;margin:auto}.ask-modal__header{display:flex;align-items:center;gap:8px;padding:12px 16px;border-bottom:1px solid var(--line);background:var(--raised)}.ask-modal__input-wrap{display:flex;align-items:center;gap:10px;flex:1;min-width:0;position:relative}.ask-modal__search-icon{width:18px;height:18px;color:var(--muted);flex:none}.ask-modal__input{flex:1;border:none;background:transparent;font-size:15px;color:var(--ink);font-family:inherit;outline:none}.ask-modal__input::placeholder{color:var(--faint)}.ask-modal__clear-btn{background:transparent;border:none;color:var(--faint);font-size:14px;cursor:pointer;padding:4px 6px}.ask-modal__clear-btn:hover{color:var(--ink)}.ask-modal__body{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:14px}.ask-modal__state{text-align:center;padding:28px 16px;display:flex;flex-direction:column;align-items:center;gap:8px}.ask-modal__state-icon{width:32px;height:32px;color:var(--muted)}.ask-modal__state h4{margin:0;font-size:var(--fs-md);color:var(--ink)}.ask-modal__state p{margin:0;max-width:440px;color:var(--muted);font-size:var(--fs-sm);line-height:1.5}.ask-modal__empty{display:flex;flex-direction:column;gap:10px}.ask-modal__hint{margin:0;font-size:var(--fs-xs);color:var(--muted);text-transform:uppercase;letter-spacing:.05em}.ask-modal__suggestions{display:flex;flex-direction:column;gap:6px}.ask-modal__suggestion-chip{background:var(--raised);border:1px solid var(--line);border-radius:var(--r);padding:8px 12px;color:var(--ink);font-size:var(--fs-sm);cursor:pointer;text-align:left;transition:background var(--dur-fast),border-color var(--dur-fast)}.ask-modal__suggestion-chip:hover{background:var(--hover);border-color:var(--line-strong)}.ask-modal__response{display:flex;flex-direction:column;gap:14px}.ask-modal__answer-box{background:var(--raised);border:1px solid var(--line);border-radius:var(--r);padding:14px 16px;display:flex;flex-direction:column;gap:8px}.ask-modal__answer{font-size:14.5px;line-height:1.6;margin:0;color:var(--ink)}.ask-modal__meta{display:flex;align-items:center;gap:8px}.ask-modal__confidence{font-family:var(--mono);font-size:11px;padding:2px 6px;border-radius:3px;text-transform:uppercase}.ask-modal__confidence--high{color:var(--pos);background:var(--accent-soft)}.ask-modal__confidence--medium{color:var(--ink);background:var(--panel);border:1px solid var(--line)}.ask-modal__confidence--low{color:var(--muted);background:var(--panel);border:1px dashed var(--line-strong)}.ask-modal__intent-badge{font-family:var(--mono);font-size:11px;color:var(--muted);background:var(--panel);padding:2px 6px;border-radius:3px}.ask-modal__sources h5,.ask-modal__followups h5{margin:0 0 8px;font-size:var(--fs-xs);color:var(--muted);text-transform:uppercase;letter-spacing:.05em}.ask-modal__sources-list{display:flex;flex-direction:column;gap:8px}.ask-modal__source-card{background:var(--bg);border:1px solid var(--line);border-radius:var(--r);padding:10px 12px;cursor:pointer;text-align:left;display:flex;flex-direction:column;gap:4px;transition:border-color var(--dur-fast),background var(--dur-fast)}.ask-modal__source-card:hover{border-color:var(--accent);background:var(--hover)}.ask-modal__source-top{display:flex;align-items:center;gap:8px;font-size:var(--fs-sm)}.ask-modal__source-path{font-family:var(--mono);color:var(--ink)}.ask-modal__source-line{font-family:var(--mono);font-size:var(--fs-xs);color:var(--muted)}.ask-modal__source-symbol{font-size:var(--fs-xs);color:var(--muted)}.ask-modal__source-symbol code{color:var(--accent)}.ask-modal__source-why{font-size:var(--fs-xs);color:var(--faint)}.ask-modal__low-conf-alert{padding:10px 12px;background:var(--raised);border:1px dashed var(--line-strong);border-radius:var(--r);display:flex;align-items:center;justify-content:space-between;gap:8px;font-size:var(--fs-sm)}.ask-modal__followup-list{display:flex;flex-wrap:wrap;gap:6px}.ask-modal__followup-btn{background:var(--raised);border:1px solid var(--line);border-radius:999px;padding:5px 12px;font-size:12px;color:var(--ink);cursor:pointer;transition:background var(--dur-fast),border-color var(--dur-fast)}.ask-modal__followup-btn:hover{background:var(--hover);border-color:var(--line-strong)}.ask-modal__footer{padding:8px 16px;background:var(--raised);border-top:1px solid var(--line);display:flex;gap:16px;font-size:11px;color:var(--faint);flex-wrap:wrap}.ask-modal__footer kbd{font-family:var(--mono);background:var(--panel);border:1px solid var(--line);padding:1px 4px;border-radius:3px;color:var(--ink)}@media (max-width: 640px){.ask-modal{width:100%;max-width:calc(100vw - 16px);max-height:90vh}}.hygiene-view{padding:24px;max-width:1200px;margin:0 auto;display:flex;flex-direction:column;gap:20px;width:100%;box-sizing:border-box}.hygiene-subnav{display:flex;gap:8px;border-bottom:1px solid var(--line);padding-bottom:8px;overflow-x:auto}.hygiene-subnav-btn{background:transparent;border:1px solid transparent;border-radius:var(--r);padding:6px 14px;font-size:13px;font-weight:500;color:var(--muted);cursor:pointer;white-space:nowrap;transition:all var(--dur-fast)}.hygiene-subnav-btn:hover{background:var(--hover);color:var(--ink)}.hygiene-subnav-btn.active{background:var(--raised);border-color:var(--line-strong);color:var(--ink)}.hygiene-summary-banner{padding:16px 20px;border-radius:var(--r);display:flex;align-items:center;gap:16px;flex-wrap:wrap;background:var(--panel);border:1px solid var(--line)}.hygiene-summary-banner.hygiene-risk{border:1px solid var(--neg);background:#ef44440f}.hygiene-summary-banner.hygiene-attention{border:1px dashed var(--line-strong);background:var(--raised)}.hygiene-summary-banner.hygiene-ok{border-left:3px solid var(--pos);border-top:1px solid var(--line);border-right:1px solid var(--line);border-bottom:1px solid var(--line)}.hygiene-summary-text{font-size:14px;font-weight:500;color:var(--ink);flex:1;min-width:200px}.hygiene-summary-meta{font-size:12px;font-family:var(--mono);color:var(--faint)}.hygiene-badge{display:inline-flex;align-items:center;font-family:var(--mono);font-size:11px;text-transform:uppercase;letter-spacing:.04em;padding:3px 8px;border-radius:999px;white-space:nowrap}.hygiene-badge-risk{color:var(--neg);border:1px solid var(--neg);background:#ef44441f}.hygiene-badge-attention{color:var(--ink);border:1px dashed var(--line-strong);background:var(--raised)}.hygiene-badge-ok{color:var(--pos);border:1px solid var(--pos);background:var(--accent-soft)}.hygiene-section-container{display:flex;flex-direction:column;gap:12px}.hygiene-section-heading{font-size:15px;font-weight:600;color:var(--ink);margin:0}.hygiene-findings-list{display:flex;flex-direction:column;gap:10px}.hygiene-finding-row{display:flex;align-items:flex-start;gap:14px;padding:12px 16px;border-radius:var(--r);background:var(--panel);border:1px solid var(--line);flex-wrap:wrap}.hygiene-finding-row.hygiene-risk{border-color:var(--neg)}.hygiene-finding-row.hygiene-attention{border:1px dashed var(--line-strong)}.hygiene-finding-row.hygiene-ok{border-left:3px solid var(--pos)}.hygiene-finding-body{flex:1;min-width:240px;display:flex;flex-direction:column;gap:6px}.hygiene-finding-text{font-size:13.5px;font-weight:500;color:var(--ink)}.hygiene-finding-paths{font-family:var(--mono);font-size:12px;color:var(--muted);word-break:break-all}.hygiene-cmd-row{display:flex;align-items:center;gap:8px;background:var(--raised);border:1px solid var(--line);border-radius:var(--r);padding:6px 10px;margin-top:4px;max-width:100%;overflow-x:auto}.hygiene-cmd{font-family:var(--mono);font-size:12px;color:var(--ink);flex:1;white-space:nowrap;-webkit-user-select:all;user-select:all}.hygiene-copy-btn{background:var(--panel);border:1px solid var(--line);color:var(--ink);font-size:11px;font-family:var(--mono);padding:3px 8px;border-radius:4px;cursor:pointer;white-space:nowrap;transition:all var(--dur-fast)}.hygiene-copy-btn:hover{background:var(--hover);border-color:var(--line-strong)}.hygiene-checkouts-list{display:flex;flex-direction:column;gap:10px}.hygiene-checkout-card{border-radius:var(--r);background:var(--panel);border:1px solid var(--line);overflow:hidden;transition:border-color var(--dur-fast)}.hygiene-checkout-card.hygiene-risk{border-color:var(--neg)}.hygiene-checkout-card.hygiene-attention{border:1px dashed var(--line-strong)}.hygiene-checkout-card.hygiene-ok{border-left:3px solid var(--pos)}.hygiene-checkout-header{width:100%;display:flex;align-items:center;gap:12px;padding:12px 16px;background:transparent;border:none;cursor:pointer;text-align:left;color:inherit;flex-wrap:wrap}.hygiene-checkout-header:hover{background:var(--hover)}.hygiene-checkout-path{font-family:var(--mono);font-size:13px;font-weight:500;color:var(--ink);flex:1;min-width:180px;word-break:break-all}.hygiene-checkout-meta{font-size:12px;color:var(--muted);white-space:nowrap}.hygiene-toggle{font-size:11px;color:var(--faint);margin-left:4px}.hygiene-checkout-detail{padding:14px 16px;background:var(--raised);border-top:1px solid var(--line);display:flex;flex-direction:column;gap:14px}.hygiene-stat-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:10px}.hygiene-stat{display:flex;flex-direction:column;gap:2px;padding:8px 10px;background:var(--panel);border:1px solid var(--line);border-radius:var(--r)}.hygiene-stat-label{font-size:11px;color:var(--faint)}.hygiene-stat-value{font-size:14px;font-weight:600;font-family:var(--mono);color:var(--ink)}.hygiene-stat-warn{color:var(--neg)}.hygiene-stat-full{grid-column:1 / -1;background:transparent;border:none;padding:4px 0}.hygiene-unpushed-row,.hygiene-claim-row{display:flex;align-items:center;gap:10px;font-size:12px;padding:4px 0;border-bottom:1px solid var(--line-soft);flex-wrap:wrap}.hygiene-branch{font-family:var(--mono);font-weight:600;color:var(--ink)}.hygiene-ahead{color:var(--muted)}.hygiene-upstream{font-family:var(--mono);color:var(--faint)}.hygiene-no-upstream{font-family:var(--mono);color:var(--muted);border:1px dashed var(--line-strong);padding:1px 6px;border-radius:3px}.hygiene-agent{font-family:var(--mono);font-weight:600;color:var(--accent)}.hygiene-claim-paths{font-family:var(--mono);color:var(--muted)}.hygiene-conflicts{color:var(--neg);font-weight:500}.hygiene-status-box{padding:32px 20px;text-align:center;border-radius:var(--r);background:var(--panel);border:1px solid var(--line);display:flex;flex-direction:column;align-items:center;gap:10px}.hygiene-offline{border:1px dashed var(--line-strong)}.hygiene-status-title{font-size:15px;font-weight:600;color:var(--ink);margin:0}.hygiene-status-text{font-size:13px;color:var(--muted);max-width:500px;margin:0;line-height:1.5}.hygiene-spinner{width:24px;height:24px;border:2px solid var(--line);border-top-color:var(--accent);border-radius:50%;animation:spin .8s linear infinite}.hygiene-drives-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px}.hygiene-drive-card{padding:14px 16px;border-radius:var(--r);background:var(--panel);border:1px solid var(--line);display:flex;flex-direction:column;gap:8px}.hygiene-drive-card.hygiene-risk{border-color:var(--neg)}.hygiene-drive-card.hygiene-attention{border:1px dashed var(--line-strong)}.hygiene-drive-card.hygiene-ok{border-left:3px solid var(--pos)}.hygiene-drive-header{display:flex;align-items:center;justify-content:space-between}.hygiene-drive-mount{font-family:var(--mono);font-size:16px;font-weight:700;color:var(--ink)}.hygiene-drive-numbers{display:flex;justify-content:space-between;font-size:12px;font-family:var(--mono)}.hygiene-drive-free{color:var(--ink);font-weight:600}.hygiene-drive-total{color:var(--faint)}.hygiene-progress-bar{height:6px;background:var(--raised);border-radius:3px;overflow:hidden}.hygiene-progress-fill{height:100%;background:var(--accent);border-radius:3px;transition:width var(--dur)}.hygiene-forecast-list{display:flex;flex-direction:column;gap:8px}.hygiene-forecast-card{display:flex;align-items:center;gap:12px;padding:10px 14px;border-radius:var(--r);background:var(--panel);border:1px solid var(--line);font-size:13px}.hygiene-forecast-mount{font-family:var(--mono);font-weight:700;color:var(--ink)}.hygiene-forecast-text{color:var(--muted)}.hygiene-modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;background:#000000a6;display:flex;align-items:center;justify-content:center;z-index:999;padding:16px}.hygiene-modal-content{background:var(--panel);border:1px solid var(--line-strong);border-radius:var(--r);max-width:540px;width:100%;overflow:hidden;box-shadow:0 12px 36px #0006}.hygiene-modal-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--line)}.hygiene-modal-title{margin:0;font-size:16px;font-weight:600;color:var(--ink)}.hygiene-modal-close{background:transparent;border:none;color:var(--faint);font-size:16px;cursor:pointer;padding:4px}.hygiene-modal-body{padding:20px;display:flex;flex-direction:column;gap:14px;font-size:13.5px;line-height:1.55;color:var(--ink)}.hygiene-modal-note{padding:12px;background:var(--raised);border:1px dashed var(--line-strong);border-radius:var(--r);font-size:12px;color:var(--muted)}.hygiene-note-title{font-weight:600;color:var(--ink);margin:0 0 4px}.hygiene-modal-footer{padding:12px 20px;background:var(--raised);border-top:1px solid var(--line);display:flex;justify-content:flex-end}@media (max-width: 640px){.hygiene-view{padding:14px}.hygiene-checkout-header{flex-direction:column;align-items:flex-start}}.pb-city{--bg:#ddd3b4;--panel:#e9e1c8;--raised:#d3c9a8;--ink:#33301f;--muted:#6b6448;--faint:#9a9270;--line:#bfb493;--line-soft:#cfc5a5;--accent:#8a4b2a;--accent-ink:#f6f1e0;--accent-soft:#d8c9ae;--r:4px;--r-sm:3px;--k1:#8a4b2a;--k2:#3f6b52;--k3:#4a5f88;--k4:#7a5580;--k5:#8a7a2a;--k6:#7a6f5a;background:var(--bg);color:var(--ink);font-family:var(--mono);font-size:12px;height:100%;position:relative;overflow:hidden}.pb-city #app{display:grid;grid-template-columns:250px 1fr 268px;height:100%}.pb-city #app.closed{grid-template-columns:250px 1fr 0}@media (max-width:1120px){.pb-city #app{grid-template-columns:210px 1fr 240px}}.pb-city aside{border-right:1px solid var(--line);background:var(--panel);display:flex;flex-direction:column;min-height:0}.pb-city .hd{padding:14px 16px 12px;border-bottom:1px solid var(--line)}.pb-city .hd h1{font-size:13.5px;font-weight:600;letter-spacing:.01em}.pb-city .hd .repo{font-size:10px;color:var(--faint);margin-top:5px;letter-spacing:.05em}.pb-city .hd .kpis{display:flex;gap:16px;margin-top:11px}.pb-city .hd .kpis b{display:block;font-size:15px;font-weight:500;letter-spacing:-.02em;font-variant-numeric:tabular-nums}.pb-city .hd .kpis i{font-style:normal;font-size:8.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint)}.pb-city .q{padding:10px 14px;border-bottom:1px solid var(--line-soft)}.pb-city .q input{width:100%;font-family:var(--mono);font-size:11.5px;background:var(--bg);border-color:var(--line);padding:5px 8px}.pb-city .tree{flex:1;min-height:0;overflow:auto;padding:6px 0 12px}.pb-city .tr{display:flex;align-items:center;gap:7px;padding:3.5px 14px;cursor:pointer;border-left:2px solid transparent;white-space:nowrap;transition:background var(--fast)}.pb-city .tr:hover{background:var(--raised)}.pb-city .tr.on{background:var(--raised);border-left-color:var(--accent)}.pb-city .tr .sw{width:7px;height:7px;border-radius:1px;flex:none}.pb-city .tr .nm{flex:1;overflow:hidden;text-overflow:ellipsis;font-size:11.5px}.pb-city .tr .lc{font-size:9.5px;color:var(--faint);font-variant-numeric:tabular-nums}.pb-city .tr.dir .nm{font-weight:600;letter-spacing:.02em}.pb-city .tr.dir .lc{color:var(--muted)}.pb-city canvas{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;display:block;z-index:0;cursor:grab}.pb-city canvas.drag{cursor:grabbing}.pb-city #tip{position:absolute;z-index:3;pointer-events:none;display:none;transform:translate(14px,-50%);background:color-mix(in srgb,var(--panel) 94%,transparent);border:1px solid var(--ink);border-radius:2px;padding:5px 8px;font-size:10.5px;line-height:1.6;white-space:nowrap}.pb-city #tip b{color:var(--accent)}.pb-city #bar{position:absolute;left:50%;bottom:15px;transform:translate(-50%);z-index:4;display:flex;gap:4px;align-items:center;background:color-mix(in srgb,var(--panel) 92%,transparent);border:1px solid var(--line);border-radius:99px;padding:5px 7px;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);width:max-content;max-width:calc(100% - 28px);flex-wrap:wrap;justify-content:center}.pb-city #zlvl{font-family:var(--mono);min-width:44px;letter-spacing:0}.pb-city .tb{font:inherit;font-size:10.5px;color:var(--muted);background:none;border:0;border-radius:99px;padding:5px 10px;cursor:pointer;transition:all var(--fast);white-space:nowrap;letter-spacing:.04em}.pb-city .vsep{width:1px;height:15px;background:var(--line);margin:0 3px}.pb-city #legend{position:absolute;left:16px;bottom:15px;z-index:3;font-size:9.5px;color:var(--muted);line-height:1.9;letter-spacing:.03em;pointer-events:none}.pb-city #legend i{display:inline-block;width:7px;height:7px;border-radius:1px;margin-right:5px}.pb-city #crumb{position:absolute;left:16px;top:13px;z-index:3;font-size:10px;color:var(--faint);letter-spacing:.08em}.pb-city #crumb b{color:var(--ink);font-weight:600}.pb-city #side{border-left:1px solid var(--line);background:var(--panel);overflow:auto;min-width:0}.pb-city .dt{padding:16px 17px}.pb-city .dt .kind{font-size:9.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint)}.pb-city .dt h2{font-size:14px;font-weight:600;margin-top:7px;word-break:break-all;line-height:1.45}.pb-city .dt .note{font-size:11.5px;line-height:1.8;color:var(--muted);margin-top:10px}.pb-city .dt dl{display:grid;grid-template-columns:auto 1fr;gap:4px 12px;margin-top:13px;font-size:11px}.pb-city .dt dt{color:var(--faint)}.pb-city .dt dd{font-variant-numeric:tabular-nums;text-align:right}.pb-city .dt h3{font-size:9.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint);margin:16px 0 7px;padding-top:12px;border-top:1px solid var(--line-soft)}.pb-city .dep{display:flex;align-items:center;gap:7px;padding:4px 7px;margin:0 -7px;border-radius:2px;cursor:pointer;font-size:11px}.pb-city .dep:hover{background:var(--raised)}.pb-city .dep .sw{width:6px;height:6px;border-radius:1px;flex:none}.pb-city .dep span{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;direction:rtl;text-align:left}.pb-city .empty{padding:22px 17px;font-size:11.5px;color:var(--faint);line-height:1.9}.pb-city .empty code{background:var(--bg);border:1px solid var(--line);border-radius:2px;padding:2px 5px;font-size:10px}.pb-city .ws{padding:8px 14px 9px;cursor:pointer;border-left:2px solid transparent;transition:background var(--fast)}.pb-city .ws:hover{background:var(--raised)}.pb-city .ws.on{background:var(--raised);border-left-color:var(--accent)}.pb-city .ws.dying{opacity:.45}.pb-city .wsrow{display:flex;align-items:center;gap:7px;white-space:nowrap}.pb-city .wsrow .sw{width:8px;height:8px;border-radius:1px;flex:none}.pb-city .wsrow .nm{flex:1;overflow:hidden;text-overflow:ellipsis;font-size:11.5px;font-weight:600;letter-spacing:.02em}.pb-city .wsrow .tag{font-size:8px;letter-spacing:.08em;text-transform:uppercase;color:var(--faint);border:1px solid var(--line);border-radius:2px;padding:1px 4px}.pb-city .wsrow .lc{font-size:9.5px;color:var(--faint);font-variant-numeric:tabular-nums}.pb-city .loadbar{height:2px;background:var(--line-soft);border-radius:1px;margin:6px 0 0 15px;overflow:hidden}.pb-city .loadbar i{display:block;height:100%;border-radius:1px;transition:width .5s ease}.pb-city #feed{position:absolute;left:16px;bottom:52px;z-index:3;max-width:min(430px,calc(100% - 32px));font-size:10px;line-height:1.5;pointer-events:none;background:color-mix(in srgb,var(--panel) 88%,transparent);border:1px solid var(--line);border-radius:3px;padding:7px 10px;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.pb-city .fe{display:flex;gap:8px;white-space:nowrap;overflow:hidden;padding:.5px 0}.pb-city .fe .ft{color:var(--faint);font-variant-numeric:tabular-nums;flex:none}.pb-city .fe .fw{flex:none;max-width:110px;overflow:hidden;text-overflow:ellipsis;font-weight:600}.pb-city .fe .fm{color:var(--muted);overflow:hidden;text-overflow:ellipsis}.pb-city #gate{position:absolute;top:0;right:0;bottom:0;left:0;z-index:9;display:none;place-content:center;text-align:center;background:var(--bg);color:var(--muted);font-size:12px;line-height:2;padding:40px}.pb-city #app{display:grid;grid-template-columns:264px 1fr 0;height:100vh;transition:grid-template-columns .34s var(--ease)}.pb-city #app.open{grid-template-columns:264px 1fr 322px}@media (max-width:1180px){.pb-city #app{grid-template-columns:224px 1fr 0}.pb-city #app.open{grid-template-columns:224px 1fr 300px}}.pb-city .atlas-app{height:100%!important;margin-top:0}.pb-city .atlas-app>aside{display:grid;grid-template-rows:auto auto auto minmax(0,1fr) auto;overflow:hidden}.pb-city .atlas-app>aside .listwrap{min-height:0}.pb-city .atlas-app>aside .foot{flex:none}.pb-city aside{border-right:1px solid var(--line);display:flex;flex-direction:column;min-height:0;background:var(--panel)}.pb-city #stage{position:relative;overflow:hidden}.pb-city #drawer{border-left:1px solid var(--line);background:var(--panel);overflow:hidden;min-width:0}.pb-city .brand{padding:18px 18px 14px;border-bottom:1px solid var(--line-soft)}.pb-city .brand h1{font-size:15px;font-weight:560;letter-spacing:-.01em;display:flex;align-items:center;gap:8px}.pb-city .brand h1 .dot{width:7px;height:7px;border-radius:50%;background:var(--accent);box-shadow:0 0 10px var(--accent);flex:none}.pb-city .brand p{font-size:11.5px;color:var(--faint);margin-top:6px;line-height:1.6}.pb-city .searchbox{padding:12px 14px 10px;position:relative}.pb-city .searchbox input{width:100%;background:var(--raised);border-color:var(--line);padding:7px 9px 7px 27px;font-size:12.5px;border-radius:var(--r-sm)}.pb-city .searchbox input::placeholder{color:var(--faint)}.pb-city .searchbox svg{position:absolute;left:23px;top:50%;transform:translateY(-50%);width:12px;height:12px;color:var(--faint);pointer-events:none}.pb-city .legend{padding:2px 14px 12px;display:flex;flex-wrap:wrap;gap:4px}.pb-city .cl{display:flex;align-items:center;gap:5px;font-size:11px;color:var(--muted);background:none;border:1px solid transparent;border-radius:99px;padding:2.5px 8px 2.5px 6px;cursor:pointer;transition:all var(--fast);font-family:inherit}.pb-city .cl:hover{background:var(--raised)}.pb-city .cl i{width:7px;height:7px;border-radius:50%;flex:none;transition:opacity var(--fast)}.pb-city .cl.off{color:var(--faint)}.pb-city .cl.off i{opacity:.22}.pb-city .cl b{font-family:var(--mono);font-size:10px;font-weight:400;color:var(--faint)}.pb-city .listwrap{flex:1;min-height:0;overflow:auto;border-top:1px solid var(--line-soft);padding:6px 0 10px}.pb-city .lrow{display:flex;align-items:center;gap:8px;padding:5.5px 16px;cursor:pointer;border-left:2px solid transparent;transition:background var(--fast)}.pb-city .lrow:hover{background:var(--raised)}.pb-city .lrow.on{background:var(--raised);border-left-color:var(--accent)}.pb-city .lrow i{width:6px;height:6px;border-radius:50%;flex:none}.pb-city .lrow span{flex:1;font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pb-city .lrow b{font-family:var(--mono);font-size:10px;font-weight:400;color:var(--faint)}.pb-city .lrow mark{background:none;color:var(--accent)}.pb-city .foot{border-top:1px solid var(--line-soft);padding:10px 16px 12px;display:grid;grid-template-columns:1fr 1fr;gap:8px 10px}.pb-city .foot .k{font-family:var(--mono);font-size:14px;font-variant-numeric:tabular-nums;letter-spacing:-.02em}.pb-city .foot .l{font-size:9.5px;letter-spacing:.09em;text-transform:uppercase;color:var(--faint);margin-top:1px}.pb-city canvas{display:block;position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;z-index:0}.pb-city #labels,.pb-city #hud,.pb-city #tools,.pb-city #hint,.pb-city #pathbar{z-index:3}.pb-city #gate{z-index:6}.pb-city #labels{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none;overflow:hidden}.pb-city .lab{position:absolute;transform:translate(-50%,-50%);white-space:nowrap;font-size:11px;letter-spacing:.01em;color:var(--ink);text-shadow:0 0 4px var(--bg),0 0 9px var(--bg),0 1px 2px var(--halo);padding-top:0;will-change:transform,opacity}.pb-city .lab.sm{font-size:10px;color:var(--muted)}.pb-city #hud{position:absolute;right:14px;top:12px;text-align:right;pointer-events:none;font-family:var(--mono);font-size:10px;color:var(--faint);line-height:1.75;letter-spacing:.04em}.pb-city #hud b{color:var(--muted);font-weight:400}.pb-city #tools{position:absolute;left:50%;bottom:16px;transform:translate(-50%);display:flex;align-items:center;gap:6px;background:color-mix(in srgb,var(--panel) 88%,transparent);border:1px solid var(--line);border-radius:99px;padding:5px 7px;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);width:max-content;max-width:calc(100% - 28px);flex-wrap:wrap;justify-content:center}.pb-city #tools .sep{width:1px;height:16px;background:var(--line);margin:0 2px}.pb-city #zlvl{min-width:44px;letter-spacing:0}.pb-city .tb{font:500 11px var(--mono);letter-spacing:.05em;color:var(--muted);background:none;border:0;border-radius:99px;padding:5px 10px;cursor:pointer;transition:all var(--fast);white-space:nowrap}.pb-city .tb:hover{color:var(--ink);background:var(--raised)}.pb-city .tb.on{color:var(--accent-ink);background:var(--accent)}.pb-city #hint{position:absolute;left:16px;bottom:16px;font-family:var(--mono);font-size:10px;color:var(--faint);line-height:1.8;pointer-events:none;letter-spacing:.03em}.pb-city #hint kbd{color:var(--muted);font:inherit}.pb-city #pathbar{position:absolute;left:50%;top:14px;transform:translate(-50%);display:none;align-items:center;gap:8px;max-width:min(70%,760px);background:color-mix(in srgb,var(--panel) 90%,transparent);border:1px solid var(--accent);border-radius:99px;padding:6px 8px 6px 13px;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);font-size:11.5px;overflow:hidden}.pb-city #pathbar.on{display:flex}.pb-city #pathbar .chain{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--ink)}.pb-city #pathbar .chain em{font-style:normal;color:var(--faint);margin:0 5px}.pb-city #pathbar .x{font:inherit;color:var(--faint);background:none;border:0;cursor:pointer;padding:0 4px}.pb-city #pathbar .x:hover{color:var(--ink)}.pb-city .dr{width:100%;height:100%;display:flex;flex-direction:column;min-height:0}.pb-city .dr-head{padding:18px 18px 14px;border-bottom:1px solid var(--line-soft)}.pb-city .dr-head .kind{display:flex;align-items:center;gap:6px;font-family:var(--mono);font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint)}.pb-city .dr-head .kind i{width:7px;height:7px;border-radius:50%}.pb-city .dr-head h2{font-size:17px;font-weight:560;letter-spacing:-.02em;margin-top:9px}.pb-city .dr-head p{font-size:12.5px;line-height:1.75;color:var(--muted);margin-top:9px}.pb-city .prov{display:grid;grid-template-columns:auto 1fr;gap:4px 12px;margin-top:12px;padding-top:10px;border-top:1px solid var(--line-soft)}.pb-city .prov dt{font-family:var(--mono);font-size:9.5px;letter-spacing:.09em;text-transform:uppercase;color:var(--faint);padding-top:1px}.pb-city .prov dd{font-size:11.5px;color:var(--muted);line-height:1.5;word-break:break-all}.pb-city .prov dd.mono{font-family:var(--mono);font-size:10.5px}.pb-city .prov dd.bad{color:var(--neg);font-weight:500}.pb-city #hud-sys{margin-top:2px}.pb-city .dr-body{flex:1;min-height:0;overflow:auto;padding:4px 18px 18px}.pb-city .dr-sec{margin-top:18px}.pb-city .dr-sec h3{font-family:var(--mono);font-size:9.5px;letter-spacing:.11em;text-transform:uppercase;color:var(--faint);font-weight:400;margin-bottom:7px;display:flex;gap:6px;align-items:baseline}.pb-city .dr-sec h3 b{font-weight:400;color:var(--faint);opacity:.6}.pb-city .nb{display:flex;align-items:center;gap:7px;padding:5px 7px;margin:0 -7px;border-radius:var(--r-sm);cursor:pointer;transition:background var(--fast)}.pb-city .nb:hover{background:var(--raised)}.pb-city .nb i{width:6px;height:6px;border-radius:50%;flex:none}.pb-city .nb span{font-size:12px;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pb-city .nb u{font-family:var(--mono);font-size:9.5px;color:var(--faint);text-decoration:none;letter-spacing:.05em}.pb-city .dr-act{display:flex;gap:7px;padding:12px 18px;border-top:1px solid var(--line-soft)}.pb-city .dr-act .btn{flex:1;background:var(--raised);border-color:var(--line);font-size:11.5px;padding:6px 8px}.pb-city .dr-act .btn:hover{border-color:var(--faint);background:var(--raised)}.pb-city .dr-act .btn.primary{background:var(--accent);border-color:var(--accent);color:var(--accent-ink)}.pb-city #gate{position:absolute;top:0;right:0;bottom:0;left:0;display:none;place-content:center;text-align:center;background:var(--bg);color:var(--muted);font-size:13px;line-height:2;padding:40px}.pb-mesh{--bg:#0a0b0d;--panel:#101216;--raised:#181b21;--ink:#dfe3e8;--muted:#98a0aa;--faint:#5f6772;--line:#232830;--line-soft:#181c22;--accent:#cfd9e4;--accent-ink:#0b0e12;--accent-soft:#1a1f27;--ok:#5cc9a0;--warn:#e0a355;--bad:#e0685f;--r:5px;--r-sm:3px;background:var(--bg);color:var(--ink);font-size:12.5px;-webkit-font-smoothing:antialiased;-webkit-user-select:none;user-select:none;height:100%;position:relative;overflow:auto}.pb-mesh #glow,.pb-mesh #field{position:fixed;top:0;right:0;bottom:0;left:0;width:100%;height:100%;display:block}.pb-mesh #glow{z-index:0}.pb-mesh #field{z-index:1;cursor:crosshair}.pb-mesh .ov{position:fixed;z-index:5}.pb-mesh #hud{left:22px;top:18px;width:220px}.pb-mesh #hud h1{font-size:14px;font-weight:560;letter-spacing:-.01em;display:flex;align-items:center;gap:8px}.pb-mesh #hud h1 i{width:6px;height:6px;border-radius:50%;background:var(--accent);flex:none;font-style:normal}.pb-mesh #hud h1 em{font-family:var(--mono);font-size:9px;font-style:normal;letter-spacing:.16em;text-transform:uppercase;color:var(--faint);margin-left:auto}.pb-mesh #hud .tally{font-family:var(--mono);font-size:10px;letter-spacing:.05em;color:var(--faint);margin-top:7px;line-height:1.75}.pb-mesh #hud .tally b{color:var(--ink);font-weight:500;font-variant-numeric:tabular-nums}.pb-mesh #roster{left:22px;top:80px;width:220px;max-height:calc(100vh - 196px);overflow:auto;scrollbar-width:thin;scrollbar-color:var(--line) transparent}.pb-mesh .grp{margin-bottom:11px}.pb-mesh .grp>h2{font-family:var(--mono);font-size:9px;letter-spacing:.14em;text-transform:uppercase;color:var(--faint);font-weight:400;display:flex;align-items:center;gap:6px;padding:0 0 5px;border-bottom:1px solid var(--line-soft);margin-bottom:4px}.pb-mesh .grp>h2 i{width:5px;height:5px;border-radius:1px;flex:none;font-style:normal}.pb-mesh .grp>h2 .sp{flex:1}.pb-mesh .grp>h2 button{font:inherit;font-size:11px;line-height:1;color:var(--faint);background:none;border:1px solid var(--line);border-radius:2px;width:15px;height:15px;cursor:pointer;padding:0;transition:color var(--fast),border-color var(--fast)}.pb-mesh .grp>h2 button:hover:not(:disabled){color:var(--accent);border-color:var(--accent)}.pb-mesh .grp>h2 button:disabled{opacity:.3;cursor:default}.pb-mesh .ag{display:flex;align-items:center;gap:7px;padding:3px 5px 3px 3px;border-radius:var(--r-sm);cursor:pointer;transition:background var(--fast)}.pb-mesh .ag:hover{background:var(--raised)}.pb-mesh .ag.on{background:var(--accent-soft)}.pb-mesh .ag.down{opacity:.34}.pb-mesh .ag canvas{width:20px;height:20px;flex:none}.pb-mesh .ag .nm{font-family:var(--mono);font-size:10px;letter-spacing:.04em;color:var(--ink);flex:none;width:21px}.pb-mesh .ag .st{font-family:var(--mono);font-size:9.5px;letter-spacing:.06em;color:var(--muted);flex:1;white-space:nowrap;overflow:hidden}.pb-mesh .ag .q{font-family:var(--mono);font-size:9px;color:var(--accent);flex:none}.pb-mesh .ag .bar{width:26px;height:2px;background:var(--line);flex:none;border-radius:1px;overflow:hidden}.pb-mesh .ag .bar i{display:block;height:100%;background:var(--muted)}.pb-mesh #inspect{right:20px;top:18px;width:264px;background:color-mix(in srgb,var(--panel) 88%,transparent);border:1px solid var(--line);border-radius:var(--r);padding:13px 14px 14px;-webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px);display:none}.pb-mesh #inspect.on{display:block}.pb-mesh #inspect h3{font-size:13px;font-weight:560;display:flex;align-items:center;gap:7px}.pb-mesh #inspect h3 i{width:6px;height:6px;border-radius:50%;flex:none;font-style:normal}.pb-mesh #inspect h3 .x{margin-left:auto;font:inherit;font-size:12px;color:var(--faint);background:none;border:0;cursor:pointer;padding:0 2px}.pb-mesh #inspect h3 .x:hover{color:var(--ink)}.pb-mesh #inspect .kv{display:grid;grid-template-columns:auto 1fr;gap:3px 12px;margin-top:10px;font-family:var(--mono);font-size:10.5px}.pb-mesh #inspect .kv span{color:var(--faint);letter-spacing:.05em}.pb-mesh #inspect .kv b{color:var(--ink);font-weight:500;text-align:right;font-variant-numeric:tabular-nums}.pb-mesh .hist{display:flex;gap:1px;height:16px;margin-top:11px;align-items:flex-end}.pb-mesh .hist i{flex:1;border-radius:.5px;min-height:2px}.pb-mesh #inspect .note{font-size:10.5px;color:var(--faint);line-height:1.6;margin-top:9px;padding-top:9px;border-top:1px solid var(--line-soft)}.pb-mesh #inspect .act{margin-top:10px;display:flex;gap:6px}.pb-mesh .btn{font:inherit;font-size:11px;color:var(--muted);background:var(--raised);border:1px solid var(--line);border-radius:var(--r-sm);padding:4px 9px;cursor:pointer;transition:color var(--fast),border-color var(--fast);flex:1}.pb-mesh .btn:hover{border-color:var(--faint);color:var(--ink)}.pb-mesh .btn.on{background:var(--accent);border-color:var(--accent);color:var(--accent-ink)}.pb-mesh #ctl{left:0;right:0;bottom:0;border-top:1px solid var(--line);background:color-mix(in srgb,var(--panel) 90%,transparent);-webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px);display:flex;align-items:center;gap:18px;padding:9px 20px}.pb-mesh #ctl .fld{display:flex;align-items:center;gap:8px;font-family:var(--mono);font-size:10px;letter-spacing:.06em;color:var(--faint);white-space:nowrap}.pb-mesh #ctl .fld b{color:var(--ink);font-weight:500;min-width:42px;font-variant-numeric:tabular-nums}.pb-mesh #ctl input[type=range]{width:100px;accent-color:var(--accent);height:14px}.pb-mesh #ctl .sp{flex:1}.pb-mesh .seg{display:inline-flex;border:1px solid var(--line);border-radius:var(--r-sm);overflow:hidden}.pb-mesh .seg button{font:inherit;font-size:11px;color:var(--muted);background:none;border:0;padding:3px 9px;cursor:pointer;transition:background var(--fast),color var(--fast)}.pb-mesh .seg button.on{background:var(--accent);color:var(--accent-ink)}.pb-mesh #stats{display:flex;gap:15px;align-items:baseline}.pb-mesh #stats .m{font-family:var(--mono);font-variant-numeric:tabular-nums}.pb-mesh #stats .m u{display:block;font-size:9px;letter-spacing:.12em;text-transform:uppercase;color:var(--faint);text-decoration:none;white-space:nowrap}.pb-mesh #stats .m b{font-size:15px;font-weight:500;letter-spacing:-.01em;white-space:nowrap}.pb-mesh #stats .m b s{font-size:9.5px;color:var(--faint);text-decoration:none;margin-left:2px}.pb-mesh #verdict{font-size:11px;color:var(--muted);line-height:1.5;width:320px;flex:none}.pb-mesh #verdict b{color:var(--accent);font-weight:500}.pb-mesh #tip{position:fixed;left:22px;bottom:58px;z-index:5;font-family:var(--mono);font-size:9.5px;letter-spacing:.04em;color:var(--faint);pointer-events:none}@media (max-width:1080px){.pb-mesh #roster,.pb-mesh #tip{display:none}.pb-mesh #hud{width:auto}.pb-mesh #verdict{display:none}}.pb-mesh #mesh-root{font-family:var(--mono);z-index:6;position:fixed;top:0;right:0;bottom:0;left:0;pointer-events:none}.pb-mesh #mesh-root>*{pointer-events:auto}.pb-mesh #mesh-toggle{position:fixed;right:20px;bottom:58px;font:inherit;font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--faint);background:none;border:1px solid var(--line);border-radius:var(--r-sm);padding:4px 10px;cursor:pointer;transition:color var(--fast),border-color var(--fast)}.pb-mesh #mesh-toggle:hover{color:var(--ink);border-color:var(--faint)}.pb-mesh #mesh-toggle.on{color:var(--accent);border-color:var(--accent)}.pb-mesh #mesh-panel{position:fixed;right:20px;bottom:92px;width:300px;max-height:min(52vh,430px);display:flex;flex-direction:column;background:color-mix(in srgb,var(--panel) 90%,transparent);border:1px solid var(--line);border-radius:var(--r);-webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px);transition:opacity var(--slow),transform var(--slow)}.pb-mesh #mesh-panel[aria-hidden=true]{opacity:0;transform:translateY(8px);pointer-events:none}.pb-mesh .mp-head{display:flex;align-items:center;gap:8px;padding:10px 12px;border-bottom:1px solid var(--line-soft)}.pb-mesh .mp-head h3{font-size:11px;font-weight:500;letter-spacing:.08em;text-transform:uppercase}.pb-mesh .mp-count{font-size:9.5px;color:var(--faint);letter-spacing:.06em}.pb-mesh .mp-x{margin-left:auto;font:inherit;font-size:11px;color:var(--faint);background:none;border:0;cursor:pointer}.pb-mesh .mp-x:hover{color:var(--ink)}.pb-mesh .mp-list{overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--line) transparent;padding:4px 0}.pb-mesh .mp-row{display:flex;align-items:center;gap:8px;padding:4px 12px;font-size:10px;letter-spacing:.03em}.pb-mesh .mp-hue{width:7px;height:7px;border-radius:50%;flex:none}.pb-mesh .mp-name{color:var(--ink);flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pb-mesh .mp-name s{color:var(--faint);text-decoration:none;font-size:9px}.pb-mesh .mp-state{color:var(--muted);flex:none;font-size:9.5px}.pb-mesh .mp-state.off{color:var(--faint)}.pb-mesh .mp-task{color:var(--accent);flex:none;font-size:9.5px;min-width:22px;text-align:right}.pb-mesh .mp-bar{width:34px;height:2px;background:var(--line);border-radius:1px;overflow:hidden;flex:none}.pb-mesh .mp-bar i{display:block;height:100%}.pb-mesh .mp-foot{padding:7px 12px;border-top:1px solid var(--line-soft);font-size:8.5px;letter-spacing:.06em;color:var(--faint)}.pb-mesh #mesh-feed{position:fixed;left:22px;bottom:82px;width:340px;pointer-events:none;display:flex;flex-direction:column;gap:2px}.pb-mesh .mf-line{display:flex;align-items:center;gap:7px;font-size:9.5px;letter-spacing:.03em;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pb-mesh .mf-line s{color:var(--faint);text-decoration:none;font-size:8.5px;flex:none}.pb-mesh .mf-line i{width:5px;height:5px;border-radius:50%;flex:none}.pb-mesh .mf-info{background:var(--muted)}.pb-mesh .mf-ok{background:var(--ok)}.pb-mesh .mf-warn{background:var(--warn)}.pb-mesh .mf-bad{background:var(--bad)}@media (max-width:1080px){.pb-mesh #mesh-feed,.pb-mesh #mesh-panel,.pb-mesh #mesh-toggle{display:none}}.pb-mesh .mesh-trace{box-sizing:border-box;min-height:100%;max-width:1480px;margin:0 auto;padding:24px;color:var(--ink);font-family:var(--mono);line-height:1.5}.pb-mesh .mesh-trace--empty{display:grid;align-content:center;justify-items:start;gap:10px}.pb-mesh .mesh-trace--empty h2,.pb-mesh .mesh-trace__header h2{margin:0;font-size:18px;font-weight:600}.pb-mesh .mesh-trace--empty p,.pb-mesh .mesh-trace__header p{margin:0;color:var(--muted);max-width:760px}.pb-mesh .mesh-trace__muted{color:var(--faint)!important}.pb-mesh .mesh-trace__header{display:flex;justify-content:space-between;gap:24px;align-items:flex-start;margin-bottom:20px}.pb-mesh .mesh-trace__header h2 span{color:var(--faint);font-size:10px;font-weight:400;letter-spacing:.13em;text-transform:uppercase}.pb-mesh .mesh-trace__totals{display:flex;gap:18px;margin:0}.pb-mesh .mesh-trace__totals div{min-width:78px}.pb-mesh .mesh-trace__totals dt{color:var(--faint);font-size:9px;letter-spacing:.1em;text-transform:uppercase}.pb-mesh .mesh-trace__totals dd{margin:2px 0 0;color:var(--ink);font-size:20px;font-variant-numeric:tabular-nums}.pb-mesh .mesh-trace__notice{border:1px solid color-mix(in srgb,var(--warn) 42%,var(--line));background:color-mix(in srgb,var(--warn) 7%,transparent);padding:12px 14px;margin-bottom:16px;color:var(--muted)}.pb-mesh .mesh-trace__notice strong{color:var(--warn)}.pb-mesh .mesh-trace__notice ul{margin:7px 0 0;padding-left:18px}.pb-mesh .mesh-trace__grid{display:grid;grid-template-columns:minmax(320px,1fr) minmax(320px,1fr);gap:16px}.pb-mesh .mesh-trace__panel,.pb-mesh .mesh-trace__detail{border:1px solid var(--line);background:color-mix(in srgb,var(--panel) 92%,transparent)}.pb-mesh .mesh-trace__panel h3{margin:0;padding:10px 12px;border-bottom:1px solid var(--line);font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted)}.pb-mesh .mesh-trace__panel h3 span{color:var(--ink);float:right;font-variant-numeric:tabular-nums}.pb-mesh .mesh-trace__nodes,.pb-mesh .mesh-trace__edges{list-style:none;padding:0;margin:0}.pb-mesh .mesh-trace__nodes li+li,.pb-mesh .mesh-trace__edges li+li{border-top:1px solid var(--line-soft)}.pb-mesh .mesh-trace__node{width:100%;box-sizing:border-box;display:grid;grid-template-columns:76px minmax(100px,1fr) auto;gap:5px 10px;text-align:left;padding:10px 12px;border:0;color:inherit;background:transparent;cursor:pointer;font:inherit}.pb-mesh .mesh-trace__node:hover,.pb-mesh .mesh-trace__node.is-selected{background:var(--accent-soft)}.pb-mesh .mesh-trace__kind,.pb-mesh .mesh-trace__edge-kind{color:var(--accent);font-size:9px;letter-spacing:.1em;text-transform:uppercase}.pb-mesh .mesh-trace__label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pb-mesh .mesh-trace__events,.pb-mesh .mesh-trace__provenance{color:var(--faint);font-size:10px}.pb-mesh .mesh-trace__proof{grid-column:2 / -1;color:var(--muted);font-size:10px}.pb-mesh .mesh-trace__edges li{display:grid;grid-template-columns:84px minmax(0,1fr) auto minmax(0,1fr);gap:5px 8px;padding:10px 12px;color:var(--muted)}.pb-mesh .mesh-trace__edges li>span:not(.mesh-trace__edge-kind){overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pb-mesh .mesh-trace__edges small{grid-column:2 / -1;color:var(--faint);overflow-wrap:anywhere}.pb-mesh .mesh-trace__detail{margin-top:16px;padding:14px}.pb-mesh .mesh-trace__detail-head{display:flex;justify-content:space-between;gap:12px;align-items:start;border-bottom:1px solid var(--line);padding-bottom:10px}.pb-mesh .mesh-trace__detail-head h3{margin:3px 0 0;font-size:15px;font-family:inherit;overflow-wrap:anywhere}.pb-mesh .mesh-trace__detail-head button,.pb-mesh .mesh-trace__source{color:var(--ink);background:transparent;border:1px solid var(--line);cursor:pointer;font:inherit}.pb-mesh .mesh-trace__detail-head button{font-size:18px;width:30px;height:30px}.pb-mesh .mesh-trace__detail dl{display:grid;grid-template-columns:minmax(120px,180px) minmax(0,1fr);margin:14px 0;font-size:11px}.pb-mesh .mesh-trace__detail dl div{display:contents}.pb-mesh .mesh-trace__detail dt,.pb-mesh .mesh-trace__detail dd{border-bottom:1px solid var(--line-soft);padding:6px 0;overflow-wrap:anywhere}.pb-mesh .mesh-trace__detail dt{color:var(--faint)}.pb-mesh .mesh-trace__detail dd{margin:0;color:var(--muted)}.pb-mesh .mesh-trace__source{padding:7px 10px;border-radius:var(--r-sm)}.pb-mesh .mesh-trace__source:hover{border-color:var(--accent)}.pb-mesh .mesh-trace__timeline{margin-top:16px;border-top:1px solid var(--line);padding-top:12px}.pb-mesh .mesh-trace__timeline h4{margin:0 0 7px;color:var(--muted);font-size:10px;letter-spacing:.1em;text-transform:uppercase}.pb-mesh .mesh-trace__timeline p{color:var(--faint);margin:0}.pb-mesh .mesh-trace__timeline ol{list-style:none;padding:0;margin:8px 0 0}.pb-mesh .mesh-trace__timeline li{display:grid;grid-template-columns:auto auto minmax(0,1fr);gap:7px;padding:5px 0;color:var(--muted);font-size:10px}.pb-mesh .mesh-trace__timeline code{color:var(--accent)}.pb-mesh .mesh-trace__timeline time{color:var(--faint)}.pb-mesh .mesh-trace__timeline span{overflow-wrap:anywhere}@media (max-width: 800px){.pb-mesh .mesh-trace{padding:16px}.pb-mesh .mesh-trace__header,.pb-mesh .mesh-trace__totals{flex-direction:column}.pb-mesh .mesh-trace__grid{grid-template-columns:1fr}.pb-mesh .mesh-trace__timeline li{grid-template-columns:1fr;gap:1px}}.pb-graph{--graph-bg: var(--bg);--graph-edge: #868992;--graph-accent: var(--accent);--graph-label: #d4d6db;--graph-label-halo: var(--bg);--graph-other: #5d6068;--graph-g0: #8fb4f2;--graph-g1: #7ccfa9;--graph-g2: #dcb06a;--graph-g3: #cf9ad4;--graph-g4: #72c4cf;--graph-g5: #de8f88;--graph-g6: #b3bd72;--graph-g7: #a79fe2;--side-w: 288px;--inspector-w: 360px;display:grid;grid-template-columns:var(--side-w) minmax(0,1fr);height:100%;min-height:0;background:var(--bg);color:var(--ink)}.pb-graph[data-inspector=open]{grid-template-columns:var(--side-w) minmax(0,1fr) var(--inspector-w)}.pb-graph .pb-dot{width:8px;height:8px;border-radius:50%;flex:none;display:inline-block;background:var(--graph-other)}.pb-graph .pb-dot[data-slot="0"]{background:var(--graph-g0)}.pb-graph .pb-dot[data-slot="1"]{background:var(--graph-g1)}.pb-graph .pb-dot[data-slot="2"]{background:var(--graph-g2)}.pb-graph .pb-dot[data-slot="3"]{background:var(--graph-g3)}.pb-graph .pb-dot[data-slot="4"]{background:var(--graph-g4)}.pb-graph .pb-dot[data-slot="5"]{background:var(--graph-g5)}.pb-graph .pb-dot[data-slot="6"]{background:var(--graph-g6)}.pb-graph .pb-dot[data-slot="7"]{background:var(--graph-g7)}.pb-graph .pb-heading{font-size:var(--fs-xs);font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--faint);margin:var(--s5) 0 var(--s2);display:flex;align-items:center;gap:var(--s2)}.pb-graph .pb-quiet{color:var(--faint);font-size:var(--fs-sm);padding:var(--s2) var(--s2);line-height:1.5}.pb-graph__side{display:flex;flex-direction:column;min-height:0;padding:var(--s4) var(--s3) 0;border-right:1px solid var(--line);background:var(--panel)}.pb-search{display:flex;align-items:center;gap:var(--s2);height:36px;padding:0 var(--s3);border-radius:var(--r);background:var(--bg);border:1px solid var(--line);color:var(--faint);transition:border-color var(--dur-fast) var(--ease)}.pb-search:focus-within{border-color:var(--accent);color:var(--muted)}.pb-search input{flex:1;min-width:0;height:100%;border:0;outline:0;background:none;color:var(--ink);font:inherit;font-size:var(--fs)}.pb-search input::placeholder{color:var(--faint)}.pb-search input::-webkit-search-cancel-button{display:none}.pb-app .pb-search input:focus-visible{outline:none}.pb-search kbd{font-family:var(--mono);font-size:var(--fs-xs);color:var(--faint);border:1px solid var(--line);border-radius:var(--r-sm);padding:0 5px;line-height:16px}.pb-chips{display:flex;flex-wrap:wrap;gap:var(--s2);margin-top:var(--s3)}.pb-graph .pb-chip{display:inline-flex;align-items:center;gap:6px;height:26px;padding:0 10px;border-radius:999px;border:1px solid var(--line);font-size:var(--fs-sm);color:var(--faint);transition:color var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease),background-color var(--dur-fast) var(--ease)}.pb-graph .pb-chip[aria-pressed=true]{color:var(--ink);background:var(--raised);border-color:var(--line-strong)}.pb-graph .pb-chip:hover{border-color:var(--line-strong)}.pb-groups{list-style:none;display:grid;gap:1px}.pb-groups button{width:100%;display:flex;align-items:center;gap:var(--s2);height:28px;padding:0 var(--s2);border-radius:var(--r-sm);font-size:var(--fs-sm);text-align:left;transition:background-color var(--dur-fast) var(--ease),opacity var(--dur-fast) var(--ease)}.pb-groups button:hover{background:var(--hover)}.pb-groups button[aria-pressed=false]{opacity:.42}.pb-groups__name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pb-graph__list{list-style:none;flex:1;min-height:0;overflow-y:auto;margin:0 calc(var(--s3) * -1);padding:0 var(--s3) var(--s4);scrollbar-width:thin;scrollbar-color:var(--line) transparent}.pb-graph__list button{width:100%;display:flex;align-items:center;gap:var(--s2);min-height:36px;padding:var(--s1) var(--s2);border-radius:var(--r-sm);text-align:left;transition:background-color var(--dur-fast) var(--ease)}.pb-graph__list button:hover{background:var(--hover)}.pb-graph__list button[aria-current=true]{background:var(--accent-soft)}.pb-row__text{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}.pb-row__label{font-size:var(--fs);color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pb-row__path{font-family:var(--mono);font-size:var(--fs-xs);color:var(--faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;direction:rtl;text-align:left}.pb-graph__stage{position:relative;min-width:0;min-height:0;overflow:hidden;background:var(--graph-bg)}.pb-graph__canvas{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;display:block}.pb-graph__canvas:not([data-state=ready]){visibility:hidden}.pb-toolbar{position:absolute;top:var(--s3);left:var(--s3);z-index:var(--z-toolbar);display:flex;align-items:center;gap:2px;height:40px;padding:0 var(--s1);border:1px solid var(--line);border-radius:var(--r-lg);background:var(--panel);box-shadow:var(--shadow-sm);max-width:calc(100% - var(--s3) * 2);overflow-x:auto;scrollbar-width:none}.pb-toolbar__sep{width:1px;height:18px;background:var(--line);margin:0 var(--s1);flex:none}.pb-toolbar__zoom{min-width:52px;text-align:center;color:var(--muted)}.pb-segmented{display:inline-flex;padding:2px;gap:2px;border-radius:var(--r);background:var(--bg);flex:none}.pb-segmented button{height:28px;padding:0 10px;border-radius:calc(var(--r) - 2px);font-size:var(--fs-sm);color:var(--faint);transition:background-color var(--dur-fast) var(--ease),color var(--dur-fast) var(--ease)}.pb-segmented button:hover{color:var(--ink)}.pb-segmented button[aria-checked=true]{background:var(--raised);color:var(--ink)}.pb-graph__status{position:absolute;left:var(--s3);bottom:var(--s3);z-index:var(--z-toolbar);font-size:var(--fs-sm);color:var(--faint);padding:6px 10px;border-radius:var(--r);background:color-mix(in srgb,var(--bg) 82%,transparent);pointer-events:none}.pb-graph__stale{color:var(--warn)}.pb-graph-legend{position:absolute;right:var(--s3);bottom:var(--s3);z-index:var(--z-toolbar);width:260px;background:var(--panel);border:1px solid var(--line);border-radius:var(--r);padding:var(--s2) var(--s3);box-shadow:0 4px 16px #00000059;font-size:var(--fs-xs);display:flex;flex-direction:column;gap:var(--s2)}.pb-graph-legend__head{display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--line);padding-bottom:4px}.pb-graph-legend__head strong{font-size:var(--fs-xs);letter-spacing:.04em;text-transform:uppercase;color:var(--muted)}.pb-graph-legend__body{display:flex;flex-direction:column;gap:6px}.pb-graph-legend__row{display:flex;align-items:flex-start;gap:8px;color:var(--muted);line-height:1.4}.pb-graph-legend__row strong{color:var(--ink)}.pb-legend-dot-sample{width:10px;height:10px;border-radius:50%;background:var(--accent);flex:none;margin-top:2px}.pb-legend-line-sample{width:12px;height:2px;background:var(--muted);flex:none;margin-top:6px}.pb-legend-color-sample{width:10px;height:10px;border-radius:2px;background:var(--graph-g0);flex:none;margin-top:2px}.pb-state{position:absolute;top:0;right:0;bottom:0;left:0;display:grid;place-content:center;justify-items:center;gap:var(--s2);padding:var(--s6);text-align:center}.pb-state h2{font-size:var(--fs-lg);font-weight:600;color:var(--ink)}.pb-state p{font-size:var(--fs);color:var(--muted);max-width:44ch;line-height:1.55}.pb-state .pb-button{margin-top:var(--s3)}.pb-state__bar{width:160px;height:2px;border-radius:2px;background:var(--line);overflow:hidden;margin-bottom:var(--s3)}.pb-state__bar span{display:block;width:40%;height:100%;background:var(--accent);border-radius:2px;animation:pb-indeterminate 1.1s var(--ease) infinite}@keyframes pb-indeterminate{0%{transform:translate(-100%)}to{transform:translate(260%)}}.pb-inspector{display:flex;flex-direction:column;gap:var(--s3);min-height:0;overflow-y:auto;padding:var(--s4) var(--s4) var(--s6);border-left:1px solid var(--line);background:var(--panel);animation:pb-inspector-in var(--dur-slow) var(--ease);scrollbar-width:thin;scrollbar-color:var(--line) transparent}@keyframes pb-inspector-in{0%{opacity:0;transform:translate(8px)}to{opacity:1;transform:none}}.pb-inspector__head{display:flex;align-items:center;justify-content:space-between;gap:var(--s2)}.pb-inspector__kicker{display:flex;align-items:center;gap:6px;min-width:0;font-size:var(--fs-sm);color:var(--muted)}.pb-inspector__group{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pb-inspector__title{font-size:var(--fs-xl);font-weight:600;line-height:1.25;word-break:break-word}.pb-inspector__path{font-family:var(--mono);font-size:var(--fs-xs);color:var(--faint);line-height:1.6;word-break:break-all}.pb-facts{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s2)}.pb-facts div{padding:var(--s2) var(--s3);border-radius:var(--r);background:var(--raised)}.pb-facts dt{font-size:var(--fs-xs);color:var(--faint)}.pb-facts dd{font-size:var(--fs-md);color:var(--ink);margin-top:2px}.pb-graph .pb-facts .pb-num{color:var(--ink);font-size:var(--fs-md)}.pb-inspector__actions{display:flex;gap:var(--s2);flex-wrap:wrap}.pb-inspector__section ul{list-style:none;display:grid;gap:1px}.pb-inspector__section button{width:100%;display:flex;align-items:center;gap:var(--s2);min-height:30px;padding:0 var(--s2);border-radius:var(--r-sm);text-align:left;transition:background-color var(--dur-fast) var(--ease)}.pb-inspector__section button:hover{background:var(--hover)}@media (max-width: 1100px){.pb-graph{--side-w: 248px;--inspector-w: 320px }}@media (max-width: 860px){.pb-graph,.pb-graph[data-inspector=open]{grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr)}.pb-graph__side{border-right:0;border-bottom:1px solid var(--line);padding-bottom:var(--s3)}.pb-graph__side .pb-heading,.pb-graph__side .pb-groups,.pb-graph__side .pb-graph__list,.pb-graph__side>.pb-quiet{display:none}.pb-inspector{position:absolute;left:0;right:0;bottom:0;z-index:var(--z-panel);max-height:58%;border-left:0;border-top:1px solid var(--line);border-radius:var(--r-lg) var(--r-lg) 0 0;box-shadow:var(--shadow-lg);animation-name:pb-sheet-in}.pb-graph{position:relative}.pb-toolbar__zoom,.pb-timeline input[type=range]{display:none}}@keyframes pb-sheet-in{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}@media (prefers-reduced-motion: reduce){.pb-inspector,.pb-state__bar span{animation:none}.pb-graph *{transition:none!important}}.pb-app{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--bg);color:var(--ink);font-family:var(--sans);font-size:var(--fs)}.pb-app .pb-icon{width:16px;height:16px;flex:none}.pb-app .pb-num{font-family:var(--mono);font-variant-numeric:tabular-nums;color:var(--faint);font-size:var(--fs-xs)}.pb-sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}.pb-app :is(.pb-topbar,.pb-graph,.pb-banner,.pb-overlay-tools) button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}.pb-app :is(button,select,input):focus-visible{outline:2px solid var(--accent);outline-offset:2px}.pb-app :is(button):disabled{cursor:default;opacity:.45}.pb-app .pb-button{display:inline-flex;align-items:center;gap:var(--s2);height:32px;padding:0 var(--s3);border-radius:var(--r);border:1px solid var(--line);background:var(--raised);color:var(--ink);font-size:var(--fs-sm);font-weight:500;transition:background-color var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease)}.pb-app .pb-button:hover:not(:disabled){background:var(--hover);border-color:var(--line-strong)}.pb-app .pb-button--primary{background:var(--accent);border-color:var(--accent);color:var(--accent-ink)}.pb-app .pb-button--primary:hover:not(:disabled){background:var(--accent-hover);border-color:var(--accent-hover)}.pb-app .pb-tool{display:inline-flex;align-items:center;gap:6px;flex:none;height:32px;padding:0 var(--s2);border-radius:var(--r);color:var(--muted);font-size:var(--fs-sm);transition:background-color var(--dur-fast) var(--ease),color var(--dur-fast) var(--ease),transform var(--dur-fast) var(--ease)}.pb-app .pb-tool--icon{width:32px;padding:0;justify-content:center}.pb-app .pb-tool:hover:not(:disabled){background:var(--hover);color:var(--ink)}.pb-app .pb-tool:active:not(:disabled){transform:scale(.97)}.pb-timeline{display:inline-flex;align-items:center;gap:var(--s2);flex:none}.pb-timeline input[type=range]{width:140px;accent-color:var(--accent)}.pb-timeline__label{min-width:64px;color:var(--muted)}.pb-topbar{flex:none;display:flex;align-items:center;gap:var(--s4);height:52px;padding:0 var(--s3) 0 var(--s4);border-bottom:1px solid var(--line);background:var(--panel)}.pb-topbar__brand{display:flex;align-items:center;gap:var(--s2);flex:none;min-width:0}.pb-logo{width:10px;height:10px;border-radius:50%;flex:none;background:var(--accent)}.pb-topbar__product{font-size:var(--fs-md);font-weight:600;letter-spacing:-.01em}.pb-topbar__workspace{color:var(--muted);font-size:var(--fs)}.pb-select{position:relative;display:inline-flex;align-items:center;height:30px;border-radius:var(--r);color:var(--muted);transition:background-color var(--dur-fast) var(--ease),color var(--dur-fast) var(--ease)}.pb-select:hover{background:var(--hover);color:var(--ink)}.pb-select select{-webkit-appearance:none;-moz-appearance:none;appearance:none;height:100%;padding:0 28px 0 var(--s2);border:0;background:none;color:inherit;font:inherit;font-size:var(--fs);cursor:pointer;max-width:220px;text-overflow:ellipsis}.pb-select select option{background:var(--panel);color:var(--ink)}.pb-select .pb-icon{position:absolute;right:8px;pointer-events:none}.pb-tabs{display:flex;align-items:stretch;align-self:stretch;gap:2px;min-width:0;overflow-x:auto;scrollbar-width:none}.pb-tabs::-webkit-scrollbar{display:none}.pb-app .pb-tab{position:relative;flex:none;padding:0 var(--s3);color:var(--faint);font-size:var(--fs);font-weight:500;transition:color var(--dur-fast) var(--ease)}.pb-app .pb-tab:hover,.pb-app .pb-tab[aria-current=page]{color:var(--ink)}.pb-tab:after{content:"";position:absolute;left:var(--s3);right:var(--s3);bottom:-1px;height:2px;border-radius:2px 2px 0 0;background:var(--accent);transform:scaleX(0);transition:transform var(--dur) var(--ease)}.pb-tab[aria-current=page]:after{transform:scaleX(1)}.pb-topbar__actions{display:flex;align-items:center;gap:var(--s1);margin-left:auto;flex:none}.pb-status{display:inline-flex;align-items:center;gap:6px;height:26px;padding:0 10px;margin-right:var(--s2);border-radius:999px;border:1px solid var(--line);font-size:var(--fs-sm);color:var(--muted);white-space:nowrap}.pb-status i{width:6px;height:6px;border-radius:50%;background:var(--faint)}.pb-status[data-tone=ok] i{background:var(--pos)}.pb-status[data-tone=warn] i{background:var(--warn)}.pb-status[data-tone=warn]{color:var(--warn)}.pb-status[data-tone=bad] i{background:var(--neg)}.pb-status[data-tone=bad]{color:var(--neg);border-color:color-mix(in srgb,var(--neg) 40%,var(--line))}.pb-banner{flex:none;display:flex;align-items:center;gap:var(--s3);flex-wrap:wrap;padding:var(--s2) var(--s4);border-bottom:1px solid color-mix(in srgb,var(--neg) 35%,var(--line));background:color-mix(in srgb,var(--neg) 9%,var(--panel));font-size:var(--fs);color:var(--ink)}.pb-banner strong{color:var(--neg);font-weight:600}.pb-banner code{font-family:var(--mono);font-size:var(--fs-sm)}.pb-banner .pb-button{margin-left:auto}.pb-drawer{flex:none;padding:var(--s3) var(--s4);border-bottom:1px solid var(--line);background:var(--panel);animation:pb-drawer-in var(--dur) var(--ease)}@keyframes pb-drawer-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}.pb-main{position:relative;flex:1;min-height:0;overflow:hidden}.pb-view{position:absolute;top:0;right:0;bottom:0;left:0;min-height:0;display:flex;flex-direction:column}.pb-view--scroll{overflow:auto;display:block}.pb-main .notes-workbench{position:relative;flex:1;min-height:0;inset:auto;z-index:auto;height:100%}.pb-main .workbench-split{flex:1;min-height:0;height:100%}.pb-main .atlas-app{flex:1;min-height:0}.pb-main .pb-city,.pb-main .pb-mesh{flex:1;min-height:0;position:relative}.pb-main .brain-landing{position:absolute;top:0;right:0;bottom:0;left:0;overflow:auto}.pb-overlay-tools{position:absolute;left:50%;bottom:var(--s4);transform:translate(-50%);z-index:var(--z-toolbar);display:flex;align-items:center;height:40px;padding:0 var(--s1);border:1px solid var(--line);border-radius:var(--r-lg);background:var(--panel)}@media (max-width: 860px){.pb-topbar{gap:var(--s2);padding:0 var(--s2)}.pb-topbar__product,.pb-status span,.pb-topbar__actions .pb-tool:not(.pb-tool--icon) span{display:none}.pb-status{padding:0 8px;margin-right:0}.pb-select select{max-width:120px}}@media (max-width: 640px){html,body{overflow-x:hidden;max-width:100vw;width:100%}.pb-app{max-width:100vw;overflow-x:hidden}.pb-tabs--desktop{display:none!important}.pb-mobile-nav{display:block!important;position:relative}.pb-topbar{gap:4px!important;padding:0 8px!important;max-width:100vw!important;overflow:hidden!important}.pb-topbar__brand{gap:4px!important;max-width:60%}.pb-topbar__workspace,.pb-status{display:none!important}.pb-topbar__actions{gap:4px!important}.pb-view-header{padding:8px 10px!important;gap:6px!important;flex-wrap:wrap!important}.pb-view-header p{display:none!important}.pb-view-header .pb-button{height:28px!important;padding:0 8px!important;font-size:11px!important}}@media (prefers-reduced-motion: reduce){.pb-tab:after,.pb-drawer{transition:none;animation:none}} diff --git a/ui-dist/assets/index-DpN8eRh0.js b/ui-dist/assets/index-DpN8eRh0.js new file mode 100644 index 0000000..ed98f53 --- /dev/null +++ b/ui-dist/assets/index-DpN8eRh0.js @@ -0,0 +1,4302 @@ +(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const a of document.querySelectorAll('link[rel="modulepreload"]'))i(a);new MutationObserver(a=>{for(const s of a)if(s.type==="childList")for(const l of s.addedNodes)l.tagName==="LINK"&&l.rel==="modulepreload"&&i(l)}).observe(document,{childList:!0,subtree:!0});function n(a){const s={};return a.integrity&&(s.integrity=a.integrity),a.referrerPolicy&&(s.referrerPolicy=a.referrerPolicy),a.crossOrigin==="use-credentials"?s.credentials="include":a.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function i(a){if(a.ep)return;a.ep=!0;const s=n(a);fetch(a.href,s)}})();function nM(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var K0={exports:{}},yd={};/** + * @license React + * react-jsx-runtime.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var iM=Symbol.for("react.transitional.element"),aM=Symbol.for("react.fragment");function Z0(t,e,n){var i=null;if(n!==void 0&&(i=""+n),e.key!==void 0&&(i=""+e.key),"key"in e){n={};for(var a in e)a!=="key"&&(n[a]=e[a])}else n=e;return e=n.ref,{$$typeof:iM,type:t,key:i,ref:e!==void 0?e:null,props:n}}yd.Fragment=aM;yd.jsx=Z0;yd.jsxs=Z0;K0.exports=yd;var r=K0.exports,Q0={exports:{}},nt={};/** + * @license React + * react.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var fm=Symbol.for("react.transitional.element"),sM=Symbol.for("react.portal"),rM=Symbol.for("react.fragment"),lM=Symbol.for("react.strict_mode"),oM=Symbol.for("react.profiler"),cM=Symbol.for("react.consumer"),uM=Symbol.for("react.context"),dM=Symbol.for("react.forward_ref"),hM=Symbol.for("react.suspense"),fM=Symbol.for("react.memo"),J0=Symbol.for("react.lazy"),pM=Symbol.for("react.activity"),jg=Symbol.iterator;function mM(t){return t===null||typeof t!="object"?null:(t=jg&&t[jg]||t["@@iterator"],typeof t=="function"?t:null)}var e_={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},t_=Object.assign,n_={};function El(t,e,n){this.props=t,this.context=e,this.refs=n_,this.updater=n||e_}El.prototype.isReactComponent={};El.prototype.setState=function(t,e){if(typeof t!="object"&&typeof t!="function"&&t!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,t,e,"setState")};El.prototype.forceUpdate=function(t){this.updater.enqueueForceUpdate(this,t,"forceUpdate")};function i_(){}i_.prototype=El.prototype;function pm(t,e,n){this.props=t,this.context=e,this.refs=n_,this.updater=n||e_}var mm=pm.prototype=new i_;mm.constructor=pm;t_(mm,El.prototype);mm.isPureReactComponent=!0;var kg=Array.isArray;function gf(){}var It={H:null,A:null,T:null,S:null},a_=Object.prototype.hasOwnProperty;function gm(t,e,n){var i=n.ref;return{$$typeof:fm,type:t,key:e,ref:i!==void 0?i:null,props:n}}function gM(t,e){return gm(t.type,e,t.props)}function xm(t){return typeof t=="object"&&t!==null&&t.$$typeof===fm}function xM(t){var e={"=":"=0",":":"=2"};return"$"+t.replace(/[=:]/g,function(n){return e[n]})}var Ig=/\/+/g;function Xd(t,e){return typeof t=="object"&&t!==null&&t.key!=null?xM(""+t.key):e.toString(36)}function vM(t){switch(t.status){case"fulfilled":return t.value;case"rejected":throw t.reason;default:switch(typeof t.status=="string"?t.then(gf,gf):(t.status="pending",t.then(function(e){t.status==="pending"&&(t.status="fulfilled",t.value=e)},function(e){t.status==="pending"&&(t.status="rejected",t.reason=e)})),t.status){case"fulfilled":return t.value;case"rejected":throw t.reason}}throw t}function Ur(t,e,n,i,a){var s=typeof t;(s==="undefined"||s==="boolean")&&(t=null);var l=!1;if(t===null)l=!0;else switch(s){case"bigint":case"string":case"number":l=!0;break;case"object":switch(t.$$typeof){case fm:case sM:l=!0;break;case J0:return l=t._init,Ur(l(t._payload),e,n,i,a)}}if(l)return a=a(t),l=i===""?"."+Xd(t,0):i,kg(a)?(n="",l!=null&&(n=l.replace(Ig,"$&/")+"/"),Ur(a,e,n,"",function(u){return u})):a!=null&&(xm(a)&&(a=gM(a,n+(a.key==null||t&&t.key===a.key?"":(""+a.key).replace(Ig,"$&/")+"/")+l)),e.push(a)),1;l=0;var o=i===""?".":i+":";if(kg(t))for(var c=0;c<t.length;c++)i=t[c],s=o+Xd(i,c),l+=Ur(i,e,n,s,a);else if(c=mM(t),typeof c=="function")for(t=c.call(t),c=0;!(i=t.next()).done;)i=i.value,s=o+Xd(i,c++),l+=Ur(i,e,n,s,a);else if(s==="object"){if(typeof t.then=="function")return Ur(vM(t),e,n,i,a);throw e=String(t),Error("Objects are not valid as a React child (found: "+(e==="[object Object]"?"object with keys {"+Object.keys(t).join(", ")+"}":e)+"). If you meant to render a collection of children, use an array instead.")}return l}function dc(t,e,n){if(t==null)return t;var i=[],a=0;return Ur(t,i,"","",function(s){return e.call(n,s,a++)}),i}function _M(t){if(t._status===-1){var e=t._result;e=e(),e.then(function(n){(t._status===0||t._status===-1)&&(t._status=1,t._result=n)},function(n){(t._status===0||t._status===-1)&&(t._status=2,t._result=n)}),t._status===-1&&(t._status=0,t._result=e)}if(t._status===1)return t._result.default;throw t._result}var Bg=typeof reportError=="function"?reportError:function(t){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var e=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof t=="object"&&t!==null&&typeof t.message=="string"?String(t.message):String(t),error:t});if(!window.dispatchEvent(e))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",t);return}console.error(t)},bM={map:dc,forEach:function(t,e,n){dc(t,function(){e.apply(this,arguments)},n)},count:function(t){var e=0;return dc(t,function(){e++}),e},toArray:function(t){return dc(t,function(e){return e})||[]},only:function(t){if(!xm(t))throw Error("React.Children.only expected to receive a single React element child.");return t}};nt.Activity=pM;nt.Children=bM;nt.Component=El;nt.Fragment=rM;nt.Profiler=oM;nt.PureComponent=pm;nt.StrictMode=lM;nt.Suspense=hM;nt.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=It;nt.__COMPILER_RUNTIME={__proto__:null,c:function(t){return It.H.useMemoCache(t)}};nt.cache=function(t){return function(){return t.apply(null,arguments)}};nt.cacheSignal=function(){return null};nt.cloneElement=function(t,e,n){if(t==null)throw Error("The argument must be a React element, but you passed "+t+".");var i=t_({},t.props),a=t.key;if(e!=null)for(s in e.key!==void 0&&(a=""+e.key),e)!a_.call(e,s)||s==="key"||s==="__self"||s==="__source"||s==="ref"&&e.ref===void 0||(i[s]=e[s]);var s=arguments.length-2;if(s===1)i.children=n;else if(1<s){for(var l=Array(s),o=0;o<s;o++)l[o]=arguments[o+2];i.children=l}return gm(t.type,a,i)};nt.createContext=function(t){return t={$$typeof:uM,_currentValue:t,_currentValue2:t,_threadCount:0,Provider:null,Consumer:null},t.Provider=t,t.Consumer={$$typeof:cM,_context:t},t};nt.createElement=function(t,e,n){var i,a={},s=null;if(e!=null)for(i in e.key!==void 0&&(s=""+e.key),e)a_.call(e,i)&&i!=="key"&&i!=="__self"&&i!=="__source"&&(a[i]=e[i]);var l=arguments.length-2;if(l===1)a.children=n;else if(1<l){for(var o=Array(l),c=0;c<l;c++)o[c]=arguments[c+2];a.children=o}if(t&&t.defaultProps)for(i in l=t.defaultProps,l)a[i]===void 0&&(a[i]=l[i]);return gm(t,s,a)};nt.createRef=function(){return{current:null}};nt.forwardRef=function(t){return{$$typeof:dM,render:t}};nt.isValidElement=xm;nt.lazy=function(t){return{$$typeof:J0,_payload:{_status:-1,_result:t},_init:_M}};nt.memo=function(t,e){return{$$typeof:fM,type:t,compare:e===void 0?null:e}};nt.startTransition=function(t){var e=It.T,n={};It.T=n;try{var i=t(),a=It.S;a!==null&&a(n,i),typeof i=="object"&&i!==null&&typeof i.then=="function"&&i.then(gf,Bg)}catch(s){Bg(s)}finally{e!==null&&n.types!==null&&(e.types=n.types),It.T=e}};nt.unstable_useCacheRefresh=function(){return It.H.useCacheRefresh()};nt.use=function(t){return It.H.use(t)};nt.useActionState=function(t,e,n){return It.H.useActionState(t,e,n)};nt.useCallback=function(t,e){return It.H.useCallback(t,e)};nt.useContext=function(t){return It.H.useContext(t)};nt.useDebugValue=function(){};nt.useDeferredValue=function(t,e){return It.H.useDeferredValue(t,e)};nt.useEffect=function(t,e){return It.H.useEffect(t,e)};nt.useEffectEvent=function(t){return It.H.useEffectEvent(t)};nt.useId=function(){return It.H.useId()};nt.useImperativeHandle=function(t,e,n){return It.H.useImperativeHandle(t,e,n)};nt.useInsertionEffect=function(t,e){return It.H.useInsertionEffect(t,e)};nt.useLayoutEffect=function(t,e){return It.H.useLayoutEffect(t,e)};nt.useMemo=function(t,e){return It.H.useMemo(t,e)};nt.useOptimistic=function(t,e){return It.H.useOptimistic(t,e)};nt.useReducer=function(t,e,n){return It.H.useReducer(t,e,n)};nt.useRef=function(t){return It.H.useRef(t)};nt.useState=function(t){return It.H.useState(t)};nt.useSyncExternalStore=function(t,e,n){return It.H.useSyncExternalStore(t,e,n)};nt.useTransition=function(){return It.H.useTransition()};nt.version="19.2.8";Q0.exports=nt;var P=Q0.exports;const yM=nM(P);var s_={exports:{}},Sd={},r_={exports:{}},l_={};/** + * @license React + * scheduler.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */(function(t){function e(z,j){var X=z.length;z.push(j);e:for(;0<X;){var q=X-1>>>1,ve=z[q];if(0<a(ve,j))z[q]=j,z[X]=ve,X=q;else break e}}function n(z){return z.length===0?null:z[0]}function i(z){if(z.length===0)return null;var j=z[0],X=z.pop();if(X!==j){z[0]=X;e:for(var q=0,ve=z.length,he=ve>>>1;q<he;){var Ae=2*(q+1)-1,$=z[Ae],pe=Ae+1,we=z[pe];if(0>a($,X))pe<ve&&0>a(we,$)?(z[q]=we,z[pe]=X,q=pe):(z[q]=$,z[Ae]=X,q=Ae);else if(pe<ve&&0>a(we,X))z[q]=we,z[pe]=X,q=pe;else break e}}return j}function a(z,j){var X=z.sortIndex-j.sortIndex;return X!==0?X:z.id-j.id}if(t.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var s=performance;t.unstable_now=function(){return s.now()}}else{var l=Date,o=l.now();t.unstable_now=function(){return l.now()-o}}var c=[],u=[],h=1,g=null,d=3,p=!1,x=!1,_=!1,m=!1,f=typeof setTimeout=="function"?setTimeout:null,v=typeof clearTimeout=="function"?clearTimeout:null,y=typeof setImmediate<"u"?setImmediate:null;function b(z){for(var j=n(u);j!==null;){if(j.callback===null)i(u);else if(j.startTime<=z)i(u),j.sortIndex=j.expirationTime,e(c,j);else break;j=n(u)}}function T(z){if(_=!1,b(z),!x)if(n(c)!==null)x=!0,M||(M=!0,I());else{var j=n(u);j!==null&&B(T,j.startTime-z)}}var M=!1,A=-1,S=5,N=-1;function D(){return m?!0:!(t.unstable_now()-N<S)}function U(){if(m=!1,M){var z=t.unstable_now();N=z;var j=!0;try{e:{x=!1,_&&(_=!1,v(A),A=-1),p=!0;var X=d;try{t:{for(b(z),g=n(c);g!==null&&!(g.expirationTime>z&&D());){var q=g.callback;if(typeof q=="function"){g.callback=null,d=g.priorityLevel;var ve=q(g.expirationTime<=z);if(z=t.unstable_now(),typeof ve=="function"){g.callback=ve,b(z),j=!0;break t}g===n(c)&&i(c),b(z)}else i(c);g=n(c)}if(g!==null)j=!0;else{var he=n(u);he!==null&&B(T,he.startTime-z),j=!1}}break e}finally{g=null,d=X,p=!1}j=void 0}}finally{j?I():M=!1}}}var I;if(typeof y=="function")I=function(){y(U)};else if(typeof MessageChannel<"u"){var Y=new MessageChannel,k=Y.port2;Y.port1.onmessage=U,I=function(){k.postMessage(null)}}else I=function(){f(U,0)};function B(z,j){A=f(function(){z(t.unstable_now())},j)}t.unstable_IdlePriority=5,t.unstable_ImmediatePriority=1,t.unstable_LowPriority=4,t.unstable_NormalPriority=3,t.unstable_Profiling=null,t.unstable_UserBlockingPriority=2,t.unstable_cancelCallback=function(z){z.callback=null},t.unstable_forceFrameRate=function(z){0>z||125<z?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):S=0<z?Math.floor(1e3/z):5},t.unstable_getCurrentPriorityLevel=function(){return d},t.unstable_next=function(z){switch(d){case 1:case 2:case 3:var j=3;break;default:j=d}var X=d;d=j;try{return z()}finally{d=X}},t.unstable_requestPaint=function(){m=!0},t.unstable_runWithPriority=function(z,j){switch(z){case 1:case 2:case 3:case 4:case 5:break;default:z=3}var X=d;d=z;try{return j()}finally{d=X}},t.unstable_scheduleCallback=function(z,j,X){var q=t.unstable_now();switch(typeof X=="object"&&X!==null?(X=X.delay,X=typeof X=="number"&&0<X?q+X:q):X=q,z){case 1:var ve=-1;break;case 2:ve=250;break;case 5:ve=1073741823;break;case 4:ve=1e4;break;default:ve=5e3}return ve=X+ve,z={id:h++,callback:j,priorityLevel:z,startTime:X,expirationTime:ve,sortIndex:-1},X>q?(z.sortIndex=X,e(u,z),n(c)===null&&z===n(u)&&(_?(v(A),A=-1):_=!0,B(T,X-q))):(z.sortIndex=ve,e(c,z),x||p||(x=!0,M||(M=!0,I()))),z},t.unstable_shouldYield=D,t.unstable_wrapCallback=function(z){var j=d;return function(){var X=d;d=j;try{return z.apply(this,arguments)}finally{d=X}}}})(l_);r_.exports=l_;var SM=r_.exports,o_={exports:{}},Pn={};/** + * @license React + * react-dom.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var MM=P;function c_(t){var e="https://react.dev/errors/"+t;if(1<arguments.length){e+="?args[]="+encodeURIComponent(arguments[1]);for(var n=2;n<arguments.length;n++)e+="&args[]="+encodeURIComponent(arguments[n])}return"Minified React error #"+t+"; visit "+e+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}function ka(){}var Dn={d:{f:ka,r:function(){throw Error(c_(522))},D:ka,C:ka,L:ka,m:ka,X:ka,S:ka,M:ka},p:0,findDOMNode:null},EM=Symbol.for("react.portal");function TM(t,e,n){var i=3<arguments.length&&arguments[3]!==void 0?arguments[3]:null;return{$$typeof:EM,key:i==null?null:""+i,children:t,containerInfo:e,implementation:n}}var ho=MM.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;function Md(t,e){if(t==="font")return"";if(typeof e=="string")return e==="use-credentials"?e:""}Pn.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=Dn;Pn.createPortal=function(t,e){var n=2<arguments.length&&arguments[2]!==void 0?arguments[2]:null;if(!e||e.nodeType!==1&&e.nodeType!==9&&e.nodeType!==11)throw Error(c_(299));return TM(t,e,null,n)};Pn.flushSync=function(t){var e=ho.T,n=Dn.p;try{if(ho.T=null,Dn.p=2,t)return t()}finally{ho.T=e,Dn.p=n,Dn.d.f()}};Pn.preconnect=function(t,e){typeof t=="string"&&(e?(e=e.crossOrigin,e=typeof e=="string"?e==="use-credentials"?e:"":void 0):e=null,Dn.d.C(t,e))};Pn.prefetchDNS=function(t){typeof t=="string"&&Dn.d.D(t)};Pn.preinit=function(t,e){if(typeof t=="string"&&e&&typeof e.as=="string"){var n=e.as,i=Md(n,e.crossOrigin),a=typeof e.integrity=="string"?e.integrity:void 0,s=typeof e.fetchPriority=="string"?e.fetchPriority:void 0;n==="style"?Dn.d.S(t,typeof e.precedence=="string"?e.precedence:void 0,{crossOrigin:i,integrity:a,fetchPriority:s}):n==="script"&&Dn.d.X(t,{crossOrigin:i,integrity:a,fetchPriority:s,nonce:typeof e.nonce=="string"?e.nonce:void 0})}};Pn.preinitModule=function(t,e){if(typeof t=="string")if(typeof e=="object"&&e!==null){if(e.as==null||e.as==="script"){var n=Md(e.as,e.crossOrigin);Dn.d.M(t,{crossOrigin:n,integrity:typeof e.integrity=="string"?e.integrity:void 0,nonce:typeof e.nonce=="string"?e.nonce:void 0})}}else e==null&&Dn.d.M(t)};Pn.preload=function(t,e){if(typeof t=="string"&&typeof e=="object"&&e!==null&&typeof e.as=="string"){var n=e.as,i=Md(n,e.crossOrigin);Dn.d.L(t,n,{crossOrigin:i,integrity:typeof e.integrity=="string"?e.integrity:void 0,nonce:typeof e.nonce=="string"?e.nonce:void 0,type:typeof e.type=="string"?e.type:void 0,fetchPriority:typeof e.fetchPriority=="string"?e.fetchPriority:void 0,referrerPolicy:typeof e.referrerPolicy=="string"?e.referrerPolicy:void 0,imageSrcSet:typeof e.imageSrcSet=="string"?e.imageSrcSet:void 0,imageSizes:typeof e.imageSizes=="string"?e.imageSizes:void 0,media:typeof e.media=="string"?e.media:void 0})}};Pn.preloadModule=function(t,e){if(typeof t=="string")if(e){var n=Md(e.as,e.crossOrigin);Dn.d.m(t,{as:typeof e.as=="string"&&e.as!=="script"?e.as:void 0,crossOrigin:n,integrity:typeof e.integrity=="string"?e.integrity:void 0})}else Dn.d.m(t)};Pn.requestFormReset=function(t){Dn.d.r(t)};Pn.unstable_batchedUpdates=function(t,e){return t(e)};Pn.useFormState=function(t,e,n){return ho.H.useFormState(t,e,n)};Pn.useFormStatus=function(){return ho.H.useHostTransitionStatus()};Pn.version="19.2.8";function u_(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(u_)}catch(t){console.error(t)}}u_(),o_.exports=Pn;var AM=o_.exports;/** + * @license React + * react-dom-client.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var un=SM,d_=P,wM=AM;function fe(t){var e="https://react.dev/errors/"+t;if(1<arguments.length){e+="?args[]="+encodeURIComponent(arguments[1]);for(var n=2;n<arguments.length;n++)e+="&args[]="+encodeURIComponent(arguments[n])}return"Minified React error #"+t+"; visit "+e+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}function h_(t){return!(!t||t.nodeType!==1&&t.nodeType!==9&&t.nodeType!==11)}function Yo(t){var e=t,n=t;if(t.alternate)for(;e.return;)e=e.return;else{t=e;do e=t,e.flags&4098&&(n=e.return),t=e.return;while(t)}return e.tag===3?n:null}function f_(t){if(t.tag===13){var e=t.memoizedState;if(e===null&&(t=t.alternate,t!==null&&(e=t.memoizedState)),e!==null)return e.dehydrated}return null}function p_(t){if(t.tag===31){var e=t.memoizedState;if(e===null&&(t=t.alternate,t!==null&&(e=t.memoizedState)),e!==null)return e.dehydrated}return null}function Fg(t){if(Yo(t)!==t)throw Error(fe(188))}function NM(t){var e=t.alternate;if(!e){if(e=Yo(t),e===null)throw Error(fe(188));return e!==t?null:t}for(var n=t,i=e;;){var a=n.return;if(a===null)break;var s=a.alternate;if(s===null){if(i=a.return,i!==null){n=i;continue}break}if(a.child===s.child){for(s=a.child;s;){if(s===n)return Fg(a),t;if(s===i)return Fg(a),e;s=s.sibling}throw Error(fe(188))}if(n.return!==i.return)n=a,i=s;else{for(var l=!1,o=a.child;o;){if(o===n){l=!0,n=a,i=s;break}if(o===i){l=!0,i=a,n=s;break}o=o.sibling}if(!l){for(o=s.child;o;){if(o===n){l=!0,n=s,i=a;break}if(o===i){l=!0,i=s,n=a;break}o=o.sibling}if(!l)throw Error(fe(189))}}if(n.alternate!==i)throw Error(fe(190))}if(n.tag!==3)throw Error(fe(188));return n.stateNode.current===n?t:e}function m_(t){var e=t.tag;if(e===5||e===26||e===27||e===6)return t;for(t=t.child;t!==null;){if(e=m_(t),e!==null)return e;t=t.sibling}return null}var Bt=Object.assign,CM=Symbol.for("react.element"),hc=Symbol.for("react.transitional.element"),to=Symbol.for("react.portal"),zr=Symbol.for("react.fragment"),g_=Symbol.for("react.strict_mode"),xf=Symbol.for("react.profiler"),x_=Symbol.for("react.consumer"),ga=Symbol.for("react.context"),vm=Symbol.for("react.forward_ref"),vf=Symbol.for("react.suspense"),_f=Symbol.for("react.suspense_list"),_m=Symbol.for("react.memo"),Xa=Symbol.for("react.lazy"),bf=Symbol.for("react.activity"),RM=Symbol.for("react.memo_cache_sentinel"),Hg=Symbol.iterator;function zl(t){return t===null||typeof t!="object"?null:(t=Hg&&t[Hg]||t["@@iterator"],typeof t=="function"?t:null)}var DM=Symbol.for("react.client.reference");function yf(t){if(t==null)return null;if(typeof t=="function")return t.$$typeof===DM?null:t.displayName||t.name||null;if(typeof t=="string")return t;switch(t){case zr:return"Fragment";case xf:return"Profiler";case g_:return"StrictMode";case vf:return"Suspense";case _f:return"SuspenseList";case bf:return"Activity"}if(typeof t=="object")switch(t.$$typeof){case to:return"Portal";case ga:return t.displayName||"Context";case x_:return(t._context.displayName||"Context")+".Consumer";case vm:var e=t.render;return t=t.displayName,t||(t=e.displayName||e.name||"",t=t!==""?"ForwardRef("+t+")":"ForwardRef"),t;case _m:return e=t.displayName||null,e!==null?e:yf(t.type)||"Memo";case Xa:e=t._payload,t=t._init;try{return yf(t(e))}catch{}}return null}var no=Array.isArray,et=d_.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,St=wM.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,Bs={pending:!1,data:null,method:null,action:null},Sf=[],jr=-1;function Qi(t){return{current:t}}function xn(t){0>jr||(t.current=Sf[jr],Sf[jr]=null,jr--)}function Pt(t,e){jr++,Sf[jr]=t.current,t.current=e}var Yi=Qi(null),Co=Qi(null),os=Qi(null),Ou=Qi(null);function Pu(t,e){switch(Pt(os,e),Pt(Co,t),Pt(Yi,null),e.nodeType){case 9:case 11:t=(t=e.documentElement)&&(t=t.namespaceURI)?Yx(t):0;break;default:if(t=e.tagName,e=e.namespaceURI)e=Yx(e),t=jy(e,t);else switch(t){case"svg":t=1;break;case"math":t=2;break;default:t=0}}xn(Yi),Pt(Yi,t)}function ll(){xn(Yi),xn(Co),xn(os)}function Mf(t){t.memoizedState!==null&&Pt(Ou,t);var e=Yi.current,n=jy(e,t.type);e!==n&&(Pt(Co,t),Pt(Yi,n))}function zu(t){Co.current===t&&(xn(Yi),xn(Co)),Ou.current===t&&(xn(Ou),Bo._currentValue=Bs)}var qd,Gg;function Ds(t){if(qd===void 0)try{throw Error()}catch(n){var e=n.stack.trim().match(/\n( *(at )?)/);qd=e&&e[1]||"",Gg=-1<n.stack.indexOf(` + at`)?" (<anonymous>)":-1<n.stack.indexOf("@")?"@unknown:0:0":""}return` +`+qd+t+Gg}var Yd=!1;function $d(t,e){if(!t||Yd)return"";Yd=!0;var n=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var i={DetermineComponentFrameRoot:function(){try{if(e){var g=function(){throw Error()};if(Object.defineProperty(g.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(g,[])}catch(p){var d=p}Reflect.construct(t,[],g)}else{try{g.call()}catch(p){d=p}t.call(g.prototype)}}else{try{throw Error()}catch(p){d=p}(g=t())&&typeof g.catch=="function"&&g.catch(function(){})}}catch(p){if(p&&d&&typeof p.stack=="string")return[p.stack,d.stack]}return[null,null]}};i.DetermineComponentFrameRoot.displayName="DetermineComponentFrameRoot";var a=Object.getOwnPropertyDescriptor(i.DetermineComponentFrameRoot,"name");a&&a.configurable&&Object.defineProperty(i.DetermineComponentFrameRoot,"name",{value:"DetermineComponentFrameRoot"});var s=i.DetermineComponentFrameRoot(),l=s[0],o=s[1];if(l&&o){var c=l.split(` +`),u=o.split(` +`);for(a=i=0;i<c.length&&!c[i].includes("DetermineComponentFrameRoot");)i++;for(;a<u.length&&!u[a].includes("DetermineComponentFrameRoot");)a++;if(i===c.length||a===u.length)for(i=c.length-1,a=u.length-1;1<=i&&0<=a&&c[i]!==u[a];)a--;for(;1<=i&&0<=a;i--,a--)if(c[i]!==u[a]){if(i!==1||a!==1)do if(i--,a--,0>a||c[i]!==u[a]){var h=` +`+c[i].replace(" at new "," at ");return t.displayName&&h.includes("<anonymous>")&&(h=h.replace("<anonymous>",t.displayName)),h}while(1<=i&&0<=a);break}}}finally{Yd=!1,Error.prepareStackTrace=n}return(n=t?t.displayName||t.name:"")?Ds(n):""}function LM(t,e){switch(t.tag){case 26:case 27:case 5:return Ds(t.type);case 16:return Ds("Lazy");case 13:return t.child!==e&&e!==null?Ds("Suspense Fallback"):Ds("Suspense");case 19:return Ds("SuspenseList");case 0:case 15:return $d(t.type,!1);case 11:return $d(t.type.render,!1);case 1:return $d(t.type,!0);case 31:return Ds("Activity");default:return""}}function Vg(t){try{var e="",n=null;do e+=LM(t,n),n=t,t=t.return;while(t);return e}catch(i){return` +Error generating stack: `+i.message+` +`+i.stack}}var Ef=Object.prototype.hasOwnProperty,bm=un.unstable_scheduleCallback,Kd=un.unstable_cancelCallback,UM=un.unstable_shouldYield,OM=un.unstable_requestPaint,ni=un.unstable_now,PM=un.unstable_getCurrentPriorityLevel,v_=un.unstable_ImmediatePriority,__=un.unstable_UserBlockingPriority,ju=un.unstable_NormalPriority,zM=un.unstable_LowPriority,b_=un.unstable_IdlePriority,jM=un.log,kM=un.unstable_setDisableYieldValue,$o=null,ii=null;function Ja(t){if(typeof jM=="function"&&kM(t),ii&&typeof ii.setStrictMode=="function")try{ii.setStrictMode($o,t)}catch{}}var ai=Math.clz32?Math.clz32:FM,IM=Math.log,BM=Math.LN2;function FM(t){return t>>>=0,t===0?32:31-(IM(t)/BM|0)|0}var fc=256,pc=262144,mc=4194304;function Ls(t){var e=t&42;if(e!==0)return e;switch(t&-t){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return t&261888;case 262144:case 524288:case 1048576:case 2097152:return t&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return t&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return t}}function Ed(t,e,n){var i=t.pendingLanes;if(i===0)return 0;var a=0,s=t.suspendedLanes,l=t.pingedLanes;t=t.warmLanes;var o=i&134217727;return o!==0?(i=o&~s,i!==0?a=Ls(i):(l&=o,l!==0?a=Ls(l):n||(n=o&~t,n!==0&&(a=Ls(n))))):(o=i&~s,o!==0?a=Ls(o):l!==0?a=Ls(l):n||(n=i&~t,n!==0&&(a=Ls(n)))),a===0?0:e!==0&&e!==a&&!(e&s)&&(s=a&-a,n=e&-e,s>=n||s===32&&(n&4194048)!==0)?e:a}function Ko(t,e){return(t.pendingLanes&~(t.suspendedLanes&~t.pingedLanes)&e)===0}function HM(t,e){switch(t){case 1:case 2:case 4:case 8:case 64:return e+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function y_(){var t=mc;return mc<<=1,!(mc&62914560)&&(mc=4194304),t}function Zd(t){for(var e=[],n=0;31>n;n++)e.push(t);return e}function Zo(t,e){t.pendingLanes|=e,e!==268435456&&(t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0)}function GM(t,e,n,i,a,s){var l=t.pendingLanes;t.pendingLanes=n,t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0,t.expiredLanes&=n,t.entangledLanes&=n,t.errorRecoveryDisabledLanes&=n,t.shellSuspendCounter=0;var o=t.entanglements,c=t.expirationTimes,u=t.hiddenUpdates;for(n=l&~n;0<n;){var h=31-ai(n),g=1<<h;o[h]=0,c[h]=-1;var d=u[h];if(d!==null)for(u[h]=null,h=0;h<d.length;h++){var p=d[h];p!==null&&(p.lane&=-536870913)}n&=~g}i!==0&&S_(t,i,0),s!==0&&a===0&&t.tag!==0&&(t.suspendedLanes|=s&~(l&~e))}function S_(t,e,n){t.pendingLanes|=e,t.suspendedLanes&=~e;var i=31-ai(e);t.entangledLanes|=e,t.entanglements[i]=t.entanglements[i]|1073741824|n&261930}function M_(t,e){var n=t.entangledLanes|=e;for(t=t.entanglements;n;){var i=31-ai(n),a=1<<i;a&e|t[i]&e&&(t[i]|=e),n&=~a}}function E_(t,e){var n=e&-e;return n=n&42?1:ym(n),n&(t.suspendedLanes|e)?0:n}function ym(t){switch(t){case 2:t=1;break;case 8:t=4;break;case 32:t=16;break;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:case 4194304:case 8388608:case 16777216:case 33554432:t=128;break;case 268435456:t=134217728;break;default:t=0}return t}function Sm(t){return t&=-t,2<t?8<t?t&134217727?32:268435456:8:2}function T_(){var t=St.p;return t!==0?t:(t=window.event,t===void 0?32:Yy(t.type))}function Wg(t,e){var n=St.p;try{return St.p=t,e()}finally{St.p=n}}var Ss=Math.random().toString(36).slice(2),_n="__reactFiber$"+Ss,Xn="__reactProps$"+Ss,Tl="__reactContainer$"+Ss,Tf="__reactEvents$"+Ss,VM="__reactListeners$"+Ss,WM="__reactHandles$"+Ss,Xg="__reactResources$"+Ss,Qo="__reactMarker$"+Ss;function Mm(t){delete t[_n],delete t[Xn],delete t[Tf],delete t[VM],delete t[WM]}function kr(t){var e=t[_n];if(e)return e;for(var n=t.parentNode;n;){if(e=n[Tl]||n[_n]){if(n=e.alternate,e.child!==null||n!==null&&n.child!==null)for(t=Jx(t);t!==null;){if(n=t[_n])return n;t=Jx(t)}return e}t=n,n=t.parentNode}return null}function Al(t){if(t=t[_n]||t[Tl]){var e=t.tag;if(e===5||e===6||e===13||e===31||e===26||e===27||e===3)return t}return null}function io(t){var e=t.tag;if(e===5||e===26||e===27||e===6)return t.stateNode;throw Error(fe(33))}function Kr(t){var e=t[Xg];return e||(e=t[Xg]={hoistableStyles:new Map,hoistableScripts:new Map}),e}function mn(t){t[Qo]=!0}var A_=new Set,w_={};function tr(t,e){ol(t,e),ol(t+"Capture",e)}function ol(t,e){for(w_[t]=e,t=0;t<e.length;t++)A_.add(e[t])}var XM=RegExp("^[:A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD][:A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040]*$"),qg={},Yg={};function qM(t){return Ef.call(Yg,t)?!0:Ef.call(qg,t)?!1:XM.test(t)?Yg[t]=!0:(qg[t]=!0,!1)}function cu(t,e,n){if(qM(e))if(n===null)t.removeAttribute(e);else{switch(typeof n){case"undefined":case"function":case"symbol":t.removeAttribute(e);return;case"boolean":var i=e.toLowerCase().slice(0,5);if(i!=="data-"&&i!=="aria-"){t.removeAttribute(e);return}}t.setAttribute(e,""+n)}}function gc(t,e,n){if(n===null)t.removeAttribute(e);else{switch(typeof n){case"undefined":case"function":case"symbol":case"boolean":t.removeAttribute(e);return}t.setAttribute(e,""+n)}}function na(t,e,n,i){if(i===null)t.removeAttribute(n);else{switch(typeof i){case"undefined":case"function":case"symbol":case"boolean":t.removeAttribute(n);return}t.setAttributeNS(e,n,""+i)}}function hi(t){switch(typeof t){case"bigint":case"boolean":case"number":case"string":case"undefined":return t;case"object":return t;default:return""}}function N_(t){var e=t.type;return(t=t.nodeName)&&t.toLowerCase()==="input"&&(e==="checkbox"||e==="radio")}function YM(t,e,n){var i=Object.getOwnPropertyDescriptor(t.constructor.prototype,e);if(!t.hasOwnProperty(e)&&typeof i<"u"&&typeof i.get=="function"&&typeof i.set=="function"){var a=i.get,s=i.set;return Object.defineProperty(t,e,{configurable:!0,get:function(){return a.call(this)},set:function(l){n=""+l,s.call(this,l)}}),Object.defineProperty(t,e,{enumerable:i.enumerable}),{getValue:function(){return n},setValue:function(l){n=""+l},stopTracking:function(){t._valueTracker=null,delete t[e]}}}}function Af(t){if(!t._valueTracker){var e=N_(t)?"checked":"value";t._valueTracker=YM(t,e,""+t[e])}}function C_(t){if(!t)return!1;var e=t._valueTracker;if(!e)return!0;var n=e.getValue(),i="";return t&&(i=N_(t)?t.checked?"true":"false":t.value),t=i,t!==n?(e.setValue(t),!0):!1}function ku(t){if(t=t||(typeof document<"u"?document:void 0),typeof t>"u")return null;try{return t.activeElement||t.body}catch{return t.body}}var $M=/[\n"\\]/g;function mi(t){return t.replace($M,function(e){return"\\"+e.charCodeAt(0).toString(16)+" "})}function wf(t,e,n,i,a,s,l,o){t.name="",l!=null&&typeof l!="function"&&typeof l!="symbol"&&typeof l!="boolean"?t.type=l:t.removeAttribute("type"),e!=null?l==="number"?(e===0&&t.value===""||t.value!=e)&&(t.value=""+hi(e)):t.value!==""+hi(e)&&(t.value=""+hi(e)):l!=="submit"&&l!=="reset"||t.removeAttribute("value"),e!=null?Nf(t,l,hi(e)):n!=null?Nf(t,l,hi(n)):i!=null&&t.removeAttribute("value"),a==null&&s!=null&&(t.defaultChecked=!!s),a!=null&&(t.checked=a&&typeof a!="function"&&typeof a!="symbol"),o!=null&&typeof o!="function"&&typeof o!="symbol"&&typeof o!="boolean"?t.name=""+hi(o):t.removeAttribute("name")}function R_(t,e,n,i,a,s,l,o){if(s!=null&&typeof s!="function"&&typeof s!="symbol"&&typeof s!="boolean"&&(t.type=s),e!=null||n!=null){if(!(s!=="submit"&&s!=="reset"||e!=null)){Af(t);return}n=n!=null?""+hi(n):"",e=e!=null?""+hi(e):n,o||e===t.value||(t.value=e),t.defaultValue=e}i=i??a,i=typeof i!="function"&&typeof i!="symbol"&&!!i,t.checked=o?t.checked:!!i,t.defaultChecked=!!i,l!=null&&typeof l!="function"&&typeof l!="symbol"&&typeof l!="boolean"&&(t.name=l),Af(t)}function Nf(t,e,n){e==="number"&&ku(t.ownerDocument)===t||t.defaultValue===""+n||(t.defaultValue=""+n)}function Zr(t,e,n,i){if(t=t.options,e){e={};for(var a=0;a<n.length;a++)e["$"+n[a]]=!0;for(n=0;n<t.length;n++)a=e.hasOwnProperty("$"+t[n].value),t[n].selected!==a&&(t[n].selected=a),a&&i&&(t[n].defaultSelected=!0)}else{for(n=""+hi(n),e=null,a=0;a<t.length;a++){if(t[a].value===n){t[a].selected=!0,i&&(t[a].defaultSelected=!0);return}e!==null||t[a].disabled||(e=t[a])}e!==null&&(e.selected=!0)}}function D_(t,e,n){if(e!=null&&(e=""+hi(e),e!==t.value&&(t.value=e),n==null)){t.defaultValue!==e&&(t.defaultValue=e);return}t.defaultValue=n!=null?""+hi(n):""}function L_(t,e,n,i){if(e==null){if(i!=null){if(n!=null)throw Error(fe(92));if(no(i)){if(1<i.length)throw Error(fe(93));i=i[0]}n=i}n==null&&(n=""),e=n}n=hi(e),t.defaultValue=n,i=t.textContent,i===n&&i!==""&&i!==null&&(t.value=i),Af(t)}function cl(t,e){if(e){var n=t.firstChild;if(n&&n===t.lastChild&&n.nodeType===3){n.nodeValue=e;return}}t.textContent=e}var KM=new Set("animationIterationCount aspectRatio borderImageOutset borderImageSlice borderImageWidth boxFlex boxFlexGroup boxOrdinalGroup columnCount columns flex flexGrow flexPositive flexShrink flexNegative flexOrder gridArea gridRow gridRowEnd gridRowSpan gridRowStart gridColumn gridColumnEnd gridColumnSpan gridColumnStart fontWeight lineClamp lineHeight opacity order orphans scale tabSize widows zIndex zoom fillOpacity floodOpacity stopOpacity strokeDasharray strokeDashoffset strokeMiterlimit strokeOpacity strokeWidth MozAnimationIterationCount MozBoxFlex MozBoxFlexGroup MozLineClamp msAnimationIterationCount msFlex msZoom msFlexGrow msFlexNegative msFlexOrder msFlexPositive msFlexShrink msGridColumn msGridColumnSpan msGridRow msGridRowSpan WebkitAnimationIterationCount WebkitBoxFlex WebKitBoxFlexGroup WebkitBoxOrdinalGroup WebkitColumnCount WebkitColumns WebkitFlex WebkitFlexGrow WebkitFlexPositive WebkitFlexShrink WebkitLineClamp".split(" "));function $g(t,e,n){var i=e.indexOf("--")===0;n==null||typeof n=="boolean"||n===""?i?t.setProperty(e,""):e==="float"?t.cssFloat="":t[e]="":i?t.setProperty(e,n):typeof n!="number"||n===0||KM.has(e)?e==="float"?t.cssFloat=n:t[e]=(""+n).trim():t[e]=n+"px"}function U_(t,e,n){if(e!=null&&typeof e!="object")throw Error(fe(62));if(t=t.style,n!=null){for(var i in n)!n.hasOwnProperty(i)||e!=null&&e.hasOwnProperty(i)||(i.indexOf("--")===0?t.setProperty(i,""):i==="float"?t.cssFloat="":t[i]="");for(var a in e)i=e[a],e.hasOwnProperty(a)&&n[a]!==i&&$g(t,a,i)}else for(var s in e)e.hasOwnProperty(s)&&$g(t,s,e[s])}function Em(t){if(t.indexOf("-")===-1)return!1;switch(t){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var ZM=new Map([["acceptCharset","accept-charset"],["htmlFor","for"],["httpEquiv","http-equiv"],["crossOrigin","crossorigin"],["accentHeight","accent-height"],["alignmentBaseline","alignment-baseline"],["arabicForm","arabic-form"],["baselineShift","baseline-shift"],["capHeight","cap-height"],["clipPath","clip-path"],["clipRule","clip-rule"],["colorInterpolation","color-interpolation"],["colorInterpolationFilters","color-interpolation-filters"],["colorProfile","color-profile"],["colorRendering","color-rendering"],["dominantBaseline","dominant-baseline"],["enableBackground","enable-background"],["fillOpacity","fill-opacity"],["fillRule","fill-rule"],["floodColor","flood-color"],["floodOpacity","flood-opacity"],["fontFamily","font-family"],["fontSize","font-size"],["fontSizeAdjust","font-size-adjust"],["fontStretch","font-stretch"],["fontStyle","font-style"],["fontVariant","font-variant"],["fontWeight","font-weight"],["glyphName","glyph-name"],["glyphOrientationHorizontal","glyph-orientation-horizontal"],["glyphOrientationVertical","glyph-orientation-vertical"],["horizAdvX","horiz-adv-x"],["horizOriginX","horiz-origin-x"],["imageRendering","image-rendering"],["letterSpacing","letter-spacing"],["lightingColor","lighting-color"],["markerEnd","marker-end"],["markerMid","marker-mid"],["markerStart","marker-start"],["overlinePosition","overline-position"],["overlineThickness","overline-thickness"],["paintOrder","paint-order"],["panose-1","panose-1"],["pointerEvents","pointer-events"],["renderingIntent","rendering-intent"],["shapeRendering","shape-rendering"],["stopColor","stop-color"],["stopOpacity","stop-opacity"],["strikethroughPosition","strikethrough-position"],["strikethroughThickness","strikethrough-thickness"],["strokeDasharray","stroke-dasharray"],["strokeDashoffset","stroke-dashoffset"],["strokeLinecap","stroke-linecap"],["strokeLinejoin","stroke-linejoin"],["strokeMiterlimit","stroke-miterlimit"],["strokeOpacity","stroke-opacity"],["strokeWidth","stroke-width"],["textAnchor","text-anchor"],["textDecoration","text-decoration"],["textRendering","text-rendering"],["transformOrigin","transform-origin"],["underlinePosition","underline-position"],["underlineThickness","underline-thickness"],["unicodeBidi","unicode-bidi"],["unicodeRange","unicode-range"],["unitsPerEm","units-per-em"],["vAlphabetic","v-alphabetic"],["vHanging","v-hanging"],["vIdeographic","v-ideographic"],["vMathematical","v-mathematical"],["vectorEffect","vector-effect"],["vertAdvY","vert-adv-y"],["vertOriginX","vert-origin-x"],["vertOriginY","vert-origin-y"],["wordSpacing","word-spacing"],["writingMode","writing-mode"],["xmlnsXlink","xmlns:xlink"],["xHeight","x-height"]]),QM=/^[\u0000-\u001F ]*j[\r\n\t]*a[\r\n\t]*v[\r\n\t]*a[\r\n\t]*s[\r\n\t]*c[\r\n\t]*r[\r\n\t]*i[\r\n\t]*p[\r\n\t]*t[\r\n\t]*:/i;function uu(t){return QM.test(""+t)?"javascript:throw new Error('React has blocked a javascript: URL as a security precaution.')":t}function xa(){}var Cf=null;function Tm(t){return t=t.target||t.srcElement||window,t.correspondingUseElement&&(t=t.correspondingUseElement),t.nodeType===3?t.parentNode:t}var Ir=null,Qr=null;function Kg(t){var e=Al(t);if(e&&(t=e.stateNode)){var n=t[Xn]||null;e:switch(t=e.stateNode,e.type){case"input":if(wf(t,n.value,n.defaultValue,n.defaultValue,n.checked,n.defaultChecked,n.type,n.name),e=n.name,n.type==="radio"&&e!=null){for(n=t;n.parentNode;)n=n.parentNode;for(n=n.querySelectorAll('input[name="'+mi(""+e)+'"][type="radio"]'),e=0;e<n.length;e++){var i=n[e];if(i!==t&&i.form===t.form){var a=i[Xn]||null;if(!a)throw Error(fe(90));wf(i,a.value,a.defaultValue,a.defaultValue,a.checked,a.defaultChecked,a.type,a.name)}}for(e=0;e<n.length;e++)i=n[e],i.form===t.form&&C_(i)}break e;case"textarea":D_(t,n.value,n.defaultValue);break e;case"select":e=n.value,e!=null&&Zr(t,!!n.multiple,e,!1)}}}var Qd=!1;function O_(t,e,n){if(Qd)return t(e,n);Qd=!0;try{var i=t(e);return i}finally{if(Qd=!1,(Ir!==null||Qr!==null)&&(zd(),Ir&&(e=Ir,t=Qr,Qr=Ir=null,Kg(e),t)))for(e=0;e<t.length;e++)Kg(t[e])}}function Ro(t,e){var n=t.stateNode;if(n===null)return null;var i=n[Xn]||null;if(i===null)return null;n=i[e];e:switch(e){case"onClick":case"onClickCapture":case"onDoubleClick":case"onDoubleClickCapture":case"onMouseDown":case"onMouseDownCapture":case"onMouseMove":case"onMouseMoveCapture":case"onMouseUp":case"onMouseUpCapture":case"onMouseEnter":(i=!i.disabled)||(t=t.type,i=!(t==="button"||t==="input"||t==="select"||t==="textarea")),t=!i;break e;default:t=!1}if(t)return null;if(n&&typeof n!="function")throw Error(fe(231,e,typeof n));return n}var Ta=!(typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Rf=!1;if(Ta)try{var jl={};Object.defineProperty(jl,"passive",{get:function(){Rf=!0}}),window.addEventListener("test",jl,jl),window.removeEventListener("test",jl,jl)}catch{Rf=!1}var es=null,Am=null,du=null;function P_(){if(du)return du;var t,e=Am,n=e.length,i,a="value"in es?es.value:es.textContent,s=a.length;for(t=0;t<n&&e[t]===a[t];t++);var l=n-t;for(i=1;i<=l&&e[n-i]===a[s-i];i++);return du=a.slice(t,1<i?1-i:void 0)}function hu(t){var e=t.keyCode;return"charCode"in t?(t=t.charCode,t===0&&e===13&&(t=13)):t=e,t===10&&(t=13),32<=t||t===13?t:0}function xc(){return!0}function Zg(){return!1}function Yn(t){function e(n,i,a,s,l){this._reactName=n,this._targetInst=a,this.type=i,this.nativeEvent=s,this.target=l,this.currentTarget=null;for(var o in t)t.hasOwnProperty(o)&&(n=t[o],this[o]=n?n(s):s[o]);return this.isDefaultPrevented=(s.defaultPrevented!=null?s.defaultPrevented:s.returnValue===!1)?xc:Zg,this.isPropagationStopped=Zg,this}return Bt(e.prototype,{preventDefault:function(){this.defaultPrevented=!0;var n=this.nativeEvent;n&&(n.preventDefault?n.preventDefault():typeof n.returnValue!="unknown"&&(n.returnValue=!1),this.isDefaultPrevented=xc)},stopPropagation:function(){var n=this.nativeEvent;n&&(n.stopPropagation?n.stopPropagation():typeof n.cancelBubble!="unknown"&&(n.cancelBubble=!0),this.isPropagationStopped=xc)},persist:function(){},isPersistent:xc}),e}var nr={eventPhase:0,bubbles:0,cancelable:0,timeStamp:function(t){return t.timeStamp||Date.now()},defaultPrevented:0,isTrusted:0},Td=Yn(nr),Jo=Bt({},nr,{view:0,detail:0}),JM=Yn(Jo),Jd,eh,kl,Ad=Bt({},Jo,{screenX:0,screenY:0,clientX:0,clientY:0,pageX:0,pageY:0,ctrlKey:0,shiftKey:0,altKey:0,metaKey:0,getModifierState:wm,button:0,buttons:0,relatedTarget:function(t){return t.relatedTarget===void 0?t.fromElement===t.srcElement?t.toElement:t.fromElement:t.relatedTarget},movementX:function(t){return"movementX"in t?t.movementX:(t!==kl&&(kl&&t.type==="mousemove"?(Jd=t.screenX-kl.screenX,eh=t.screenY-kl.screenY):eh=Jd=0,kl=t),Jd)},movementY:function(t){return"movementY"in t?t.movementY:eh}}),Qg=Yn(Ad),eE=Bt({},Ad,{dataTransfer:0}),tE=Yn(eE),nE=Bt({},Jo,{relatedTarget:0}),th=Yn(nE),iE=Bt({},nr,{animationName:0,elapsedTime:0,pseudoElement:0}),aE=Yn(iE),sE=Bt({},nr,{clipboardData:function(t){return"clipboardData"in t?t.clipboardData:window.clipboardData}}),rE=Yn(sE),lE=Bt({},nr,{data:0}),Jg=Yn(lE),oE={Esc:"Escape",Spacebar:" ",Left:"ArrowLeft",Up:"ArrowUp",Right:"ArrowRight",Down:"ArrowDown",Del:"Delete",Win:"OS",Menu:"ContextMenu",Apps:"ContextMenu",Scroll:"ScrollLock",MozPrintableKey:"Unidentified"},cE={8:"Backspace",9:"Tab",12:"Clear",13:"Enter",16:"Shift",17:"Control",18:"Alt",19:"Pause",20:"CapsLock",27:"Escape",32:" ",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"ArrowLeft",38:"ArrowUp",39:"ArrowRight",40:"ArrowDown",45:"Insert",46:"Delete",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"NumLock",145:"ScrollLock",224:"Meta"},uE={Alt:"altKey",Control:"ctrlKey",Meta:"metaKey",Shift:"shiftKey"};function dE(t){var e=this.nativeEvent;return e.getModifierState?e.getModifierState(t):(t=uE[t])?!!e[t]:!1}function wm(){return dE}var hE=Bt({},Jo,{key:function(t){if(t.key){var e=oE[t.key]||t.key;if(e!=="Unidentified")return e}return t.type==="keypress"?(t=hu(t),t===13?"Enter":String.fromCharCode(t)):t.type==="keydown"||t.type==="keyup"?cE[t.keyCode]||"Unidentified":""},code:0,location:0,ctrlKey:0,shiftKey:0,altKey:0,metaKey:0,repeat:0,locale:0,getModifierState:wm,charCode:function(t){return t.type==="keypress"?hu(t):0},keyCode:function(t){return t.type==="keydown"||t.type==="keyup"?t.keyCode:0},which:function(t){return t.type==="keypress"?hu(t):t.type==="keydown"||t.type==="keyup"?t.keyCode:0}}),fE=Yn(hE),pE=Bt({},Ad,{pointerId:0,width:0,height:0,pressure:0,tangentialPressure:0,tiltX:0,tiltY:0,twist:0,pointerType:0,isPrimary:0}),ex=Yn(pE),mE=Bt({},Jo,{touches:0,targetTouches:0,changedTouches:0,altKey:0,metaKey:0,ctrlKey:0,shiftKey:0,getModifierState:wm}),gE=Yn(mE),xE=Bt({},nr,{propertyName:0,elapsedTime:0,pseudoElement:0}),vE=Yn(xE),_E=Bt({},Ad,{deltaX:function(t){return"deltaX"in t?t.deltaX:"wheelDeltaX"in t?-t.wheelDeltaX:0},deltaY:function(t){return"deltaY"in t?t.deltaY:"wheelDeltaY"in t?-t.wheelDeltaY:"wheelDelta"in t?-t.wheelDelta:0},deltaZ:0,deltaMode:0}),bE=Yn(_E),yE=Bt({},nr,{newState:0,oldState:0}),SE=Yn(yE),ME=[9,13,27,32],Nm=Ta&&"CompositionEvent"in window,fo=null;Ta&&"documentMode"in document&&(fo=document.documentMode);var EE=Ta&&"TextEvent"in window&&!fo,z_=Ta&&(!Nm||fo&&8<fo&&11>=fo),tx=" ",nx=!1;function j_(t,e){switch(t){case"keyup":return ME.indexOf(e.keyCode)!==-1;case"keydown":return e.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function k_(t){return t=t.detail,typeof t=="object"&&"data"in t?t.data:null}var Br=!1;function TE(t,e){switch(t){case"compositionend":return k_(e);case"keypress":return e.which!==32?null:(nx=!0,tx);case"textInput":return t=e.data,t===tx&&nx?null:t;default:return null}}function AE(t,e){if(Br)return t==="compositionend"||!Nm&&j_(t,e)?(t=P_(),du=Am=es=null,Br=!1,t):null;switch(t){case"paste":return null;case"keypress":if(!(e.ctrlKey||e.altKey||e.metaKey)||e.ctrlKey&&e.altKey){if(e.char&&1<e.char.length)return e.char;if(e.which)return String.fromCharCode(e.which)}return null;case"compositionend":return z_&&e.locale!=="ko"?null:e.data;default:return null}}var wE={color:!0,date:!0,datetime:!0,"datetime-local":!0,email:!0,month:!0,number:!0,password:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0};function ix(t){var e=t&&t.nodeName&&t.nodeName.toLowerCase();return e==="input"?!!wE[t.type]:e==="textarea"}function I_(t,e,n,i){Ir?Qr?Qr.push(i):Qr=[i]:Ir=i,e=nd(e,"onChange"),0<e.length&&(n=new Td("onChange","change",null,n,i),t.push({event:n,listeners:e}))}var po=null,Do=null;function NE(t){Oy(t,0)}function wd(t){var e=io(t);if(C_(e))return t}function ax(t,e){if(t==="change")return e}var B_=!1;if(Ta){var nh;if(Ta){var ih="oninput"in document;if(!ih){var sx=document.createElement("div");sx.setAttribute("oninput","return;"),ih=typeof sx.oninput=="function"}nh=ih}else nh=!1;B_=nh&&(!document.documentMode||9<document.documentMode)}function rx(){po&&(po.detachEvent("onpropertychange",F_),Do=po=null)}function F_(t){if(t.propertyName==="value"&&wd(Do)){var e=[];I_(e,Do,t,Tm(t)),O_(NE,e)}}function CE(t,e,n){t==="focusin"?(rx(),po=e,Do=n,po.attachEvent("onpropertychange",F_)):t==="focusout"&&rx()}function RE(t){if(t==="selectionchange"||t==="keyup"||t==="keydown")return wd(Do)}function DE(t,e){if(t==="click")return wd(e)}function LE(t,e){if(t==="input"||t==="change")return wd(e)}function UE(t,e){return t===e&&(t!==0||1/t===1/e)||t!==t&&e!==e}var ri=typeof Object.is=="function"?Object.is:UE;function Lo(t,e){if(ri(t,e))return!0;if(typeof t!="object"||t===null||typeof e!="object"||e===null)return!1;var n=Object.keys(t),i=Object.keys(e);if(n.length!==i.length)return!1;for(i=0;i<n.length;i++){var a=n[i];if(!Ef.call(e,a)||!ri(t[a],e[a]))return!1}return!0}function lx(t){for(;t&&t.firstChild;)t=t.firstChild;return t}function ox(t,e){var n=lx(t);t=0;for(var i;n;){if(n.nodeType===3){if(i=t+n.textContent.length,t<=e&&i>=e)return{node:n,offset:e-t};t=i}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=lx(n)}}function H_(t,e){return t&&e?t===e?!0:t&&t.nodeType===3?!1:e&&e.nodeType===3?H_(t,e.parentNode):"contains"in t?t.contains(e):t.compareDocumentPosition?!!(t.compareDocumentPosition(e)&16):!1:!1}function G_(t){t=t!=null&&t.ownerDocument!=null&&t.ownerDocument.defaultView!=null?t.ownerDocument.defaultView:window;for(var e=ku(t.document);e instanceof t.HTMLIFrameElement;){try{var n=typeof e.contentWindow.location.href=="string"}catch{n=!1}if(n)t=e.contentWindow;else break;e=ku(t.document)}return e}function Cm(t){var e=t&&t.nodeName&&t.nodeName.toLowerCase();return e&&(e==="input"&&(t.type==="text"||t.type==="search"||t.type==="tel"||t.type==="url"||t.type==="password")||e==="textarea"||t.contentEditable==="true")}var OE=Ta&&"documentMode"in document&&11>=document.documentMode,Fr=null,Df=null,mo=null,Lf=!1;function cx(t,e,n){var i=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;Lf||Fr==null||Fr!==ku(i)||(i=Fr,"selectionStart"in i&&Cm(i)?i={start:i.selectionStart,end:i.selectionEnd}:(i=(i.ownerDocument&&i.ownerDocument.defaultView||window).getSelection(),i={anchorNode:i.anchorNode,anchorOffset:i.anchorOffset,focusNode:i.focusNode,focusOffset:i.focusOffset}),mo&&Lo(mo,i)||(mo=i,i=nd(Df,"onSelect"),0<i.length&&(e=new Td("onSelect","select",null,e,n),t.push({event:e,listeners:i}),e.target=Fr)))}function Ts(t,e){var n={};return n[t.toLowerCase()]=e.toLowerCase(),n["Webkit"+t]="webkit"+e,n["Moz"+t]="moz"+e,n}var Hr={animationend:Ts("Animation","AnimationEnd"),animationiteration:Ts("Animation","AnimationIteration"),animationstart:Ts("Animation","AnimationStart"),transitionrun:Ts("Transition","TransitionRun"),transitionstart:Ts("Transition","TransitionStart"),transitioncancel:Ts("Transition","TransitionCancel"),transitionend:Ts("Transition","TransitionEnd")},ah={},V_={};Ta&&(V_=document.createElement("div").style,"AnimationEvent"in window||(delete Hr.animationend.animation,delete Hr.animationiteration.animation,delete Hr.animationstart.animation),"TransitionEvent"in window||delete Hr.transitionend.transition);function ir(t){if(ah[t])return ah[t];if(!Hr[t])return t;var e=Hr[t],n;for(n in e)if(e.hasOwnProperty(n)&&n in V_)return ah[t]=e[n];return t}var W_=ir("animationend"),X_=ir("animationiteration"),q_=ir("animationstart"),PE=ir("transitionrun"),zE=ir("transitionstart"),jE=ir("transitioncancel"),Y_=ir("transitionend"),$_=new Map,Uf="abort auxClick beforeToggle cancel canPlay canPlayThrough click close contextMenu copy cut drag dragEnd dragEnter dragExit dragLeave dragOver dragStart drop durationChange emptied encrypted ended error gotPointerCapture input invalid keyDown keyPress keyUp load loadedData loadedMetadata loadStart lostPointerCapture mouseDown mouseMove mouseOut mouseOver mouseUp paste pause play playing pointerCancel pointerDown pointerMove pointerOut pointerOver pointerUp progress rateChange reset resize seeked seeking stalled submit suspend timeUpdate touchCancel touchEnd touchStart volumeChange scroll toggle touchMove waiting wheel".split(" ");Uf.push("scrollEnd");function Oi(t,e){$_.set(t,e),tr(e,[t])}var Iu=typeof reportError=="function"?reportError:function(t){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var e=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof t=="object"&&t!==null&&typeof t.message=="string"?String(t.message):String(t),error:t});if(!window.dispatchEvent(e))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",t);return}console.error(t)},ui=[],Gr=0,Rm=0;function Nd(){for(var t=Gr,e=Rm=Gr=0;e<t;){var n=ui[e];ui[e++]=null;var i=ui[e];ui[e++]=null;var a=ui[e];ui[e++]=null;var s=ui[e];if(ui[e++]=null,i!==null&&a!==null){var l=i.pending;l===null?a.next=a:(a.next=l.next,l.next=a),i.pending=a}s!==0&&K_(n,a,s)}}function Cd(t,e,n,i){ui[Gr++]=t,ui[Gr++]=e,ui[Gr++]=n,ui[Gr++]=i,Rm|=i,t.lanes|=i,t=t.alternate,t!==null&&(t.lanes|=i)}function Dm(t,e,n,i){return Cd(t,e,n,i),Bu(t)}function ar(t,e){return Cd(t,null,null,e),Bu(t)}function K_(t,e,n){t.lanes|=n;var i=t.alternate;i!==null&&(i.lanes|=n);for(var a=!1,s=t.return;s!==null;)s.childLanes|=n,i=s.alternate,i!==null&&(i.childLanes|=n),s.tag===22&&(t=s.stateNode,t===null||t._visibility&1||(a=!0)),t=s,s=s.return;return t.tag===3?(s=t.stateNode,a&&e!==null&&(a=31-ai(n),t=s.hiddenUpdates,i=t[a],i===null?t[a]=[e]:i.push(e),e.lane=n|536870912),s):null}function Bu(t){if(50<Eo)throw Eo=0,ep=null,Error(fe(185));for(var e=t.return;e!==null;)t=e,e=t.return;return t.tag===3?t.stateNode:null}var Vr={};function kE(t,e,n,i){this.tag=t,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.refCleanup=this.ref=null,this.pendingProps=e,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=i,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function ei(t,e,n,i){return new kE(t,e,n,i)}function Lm(t){return t=t.prototype,!(!t||!t.isReactComponent)}function ba(t,e){var n=t.alternate;return n===null?(n=ei(t.tag,e,t.key,t.mode),n.elementType=t.elementType,n.type=t.type,n.stateNode=t.stateNode,n.alternate=t,t.alternate=n):(n.pendingProps=e,n.type=t.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=t.flags&65011712,n.childLanes=t.childLanes,n.lanes=t.lanes,n.child=t.child,n.memoizedProps=t.memoizedProps,n.memoizedState=t.memoizedState,n.updateQueue=t.updateQueue,e=t.dependencies,n.dependencies=e===null?null:{lanes:e.lanes,firstContext:e.firstContext},n.sibling=t.sibling,n.index=t.index,n.ref=t.ref,n.refCleanup=t.refCleanup,n}function Z_(t,e){t.flags&=65011714;var n=t.alternate;return n===null?(t.childLanes=0,t.lanes=e,t.child=null,t.subtreeFlags=0,t.memoizedProps=null,t.memoizedState=null,t.updateQueue=null,t.dependencies=null,t.stateNode=null):(t.childLanes=n.childLanes,t.lanes=n.lanes,t.child=n.child,t.subtreeFlags=0,t.deletions=null,t.memoizedProps=n.memoizedProps,t.memoizedState=n.memoizedState,t.updateQueue=n.updateQueue,t.type=n.type,e=n.dependencies,t.dependencies=e===null?null:{lanes:e.lanes,firstContext:e.firstContext}),t}function fu(t,e,n,i,a,s){var l=0;if(i=t,typeof t=="function")Lm(t)&&(l=1);else if(typeof t=="string")l=GT(t,n,Yi.current)?26:t==="html"||t==="head"||t==="body"?27:5;else e:switch(t){case bf:return t=ei(31,n,e,a),t.elementType=bf,t.lanes=s,t;case zr:return Fs(n.children,a,s,e);case g_:l=8,a|=24;break;case xf:return t=ei(12,n,e,a|2),t.elementType=xf,t.lanes=s,t;case vf:return t=ei(13,n,e,a),t.elementType=vf,t.lanes=s,t;case _f:return t=ei(19,n,e,a),t.elementType=_f,t.lanes=s,t;default:if(typeof t=="object"&&t!==null)switch(t.$$typeof){case ga:l=10;break e;case x_:l=9;break e;case vm:l=11;break e;case _m:l=14;break e;case Xa:l=16,i=null;break e}l=29,n=Error(fe(130,t===null?"null":typeof t,"")),i=null}return e=ei(l,n,e,a),e.elementType=t,e.type=i,e.lanes=s,e}function Fs(t,e,n,i){return t=ei(7,t,i,e),t.lanes=n,t}function sh(t,e,n){return t=ei(6,t,null,e),t.lanes=n,t}function Q_(t){var e=ei(18,null,null,0);return e.stateNode=t,e}function rh(t,e,n){return e=ei(4,t.children!==null?t.children:[],t.key,e),e.lanes=n,e.stateNode={containerInfo:t.containerInfo,pendingChildren:null,implementation:t.implementation},e}var ux=new WeakMap;function gi(t,e){if(typeof t=="object"&&t!==null){var n=ux.get(t);return n!==void 0?n:(e={value:t,source:e,stack:Vg(e)},ux.set(t,e),e)}return{value:t,source:e,stack:Vg(e)}}var Wr=[],Xr=0,Fu=null,Uo=0,fi=[],pi=0,xs=null,Vi=1,Wi="";function fa(t,e){Wr[Xr++]=Uo,Wr[Xr++]=Fu,Fu=t,Uo=e}function J_(t,e,n){fi[pi++]=Vi,fi[pi++]=Wi,fi[pi++]=xs,xs=t;var i=Vi;t=Wi;var a=32-ai(i)-1;i&=~(1<<a),n+=1;var s=32-ai(e)+a;if(30<s){var l=a-a%5;s=(i&(1<<l)-1).toString(32),i>>=l,a-=l,Vi=1<<32-ai(e)+a|n<<a|i,Wi=s+t}else Vi=1<<s|n<<a|i,Wi=t}function Um(t){t.return!==null&&(fa(t,1),J_(t,1,0))}function Om(t){for(;t===Fu;)Fu=Wr[--Xr],Wr[Xr]=null,Uo=Wr[--Xr],Wr[Xr]=null;for(;t===xs;)xs=fi[--pi],fi[pi]=null,Wi=fi[--pi],fi[pi]=null,Vi=fi[--pi],fi[pi]=null}function eb(t,e){fi[pi++]=Vi,fi[pi++]=Wi,fi[pi++]=xs,Vi=e.id,Wi=e.overflow,xs=t}var bn=null,kt=null,mt=!1,cs=null,xi=!1,Of=Error(fe(519));function vs(t){var e=Error(fe(418,1<arguments.length&&arguments[1]!==void 0&&arguments[1]?"text":"HTML",""));throw Oo(gi(e,t)),Of}function dx(t){var e=t.stateNode,n=t.type,i=t.memoizedProps;switch(e[_n]=t,e[Xn]=i,n){case"dialog":lt("cancel",e),lt("close",e);break;case"iframe":case"object":case"embed":lt("load",e);break;case"video":case"audio":for(n=0;n<ko.length;n++)lt(ko[n],e);break;case"source":lt("error",e);break;case"img":case"image":case"link":lt("error",e),lt("load",e);break;case"details":lt("toggle",e);break;case"input":lt("invalid",e),R_(e,i.value,i.defaultValue,i.checked,i.defaultChecked,i.type,i.name,!0);break;case"select":lt("invalid",e);break;case"textarea":lt("invalid",e),L_(e,i.value,i.defaultValue,i.children)}n=i.children,typeof n!="string"&&typeof n!="number"&&typeof n!="bigint"||e.textContent===""+n||i.suppressHydrationWarning===!0||zy(e.textContent,n)?(i.popover!=null&&(lt("beforetoggle",e),lt("toggle",e)),i.onScroll!=null&<("scroll",e),i.onScrollEnd!=null&<("scrollend",e),i.onClick!=null&&(e.onclick=xa),e=!0):e=!1,e||vs(t,!0)}function hx(t){for(bn=t.return;bn;)switch(bn.tag){case 5:case 31:case 13:xi=!1;return;case 27:case 3:xi=!0;return;default:bn=bn.return}}function fr(t){if(t!==bn)return!1;if(!mt)return hx(t),mt=!0,!1;var e=t.tag,n;if((n=e!==3&&e!==27)&&((n=e===5)&&(n=t.type,n=!(n!=="form"&&n!=="button")||sp(t.type,t.memoizedProps)),n=!n),n&&kt&&vs(t),hx(t),e===13){if(t=t.memoizedState,t=t!==null?t.dehydrated:null,!t)throw Error(fe(317));kt=Qx(t)}else if(e===31){if(t=t.memoizedState,t=t!==null?t.dehydrated:null,!t)throw Error(fe(317));kt=Qx(t)}else e===27?(e=kt,Ms(t.type)?(t=cp,cp=null,kt=t):kt=e):kt=bn?yi(t.stateNode.nextSibling):null;return!0}function Ws(){kt=bn=null,mt=!1}function lh(){var t=cs;return t!==null&&(Fn===null?Fn=t:Fn.push.apply(Fn,t),cs=null),t}function Oo(t){cs===null?cs=[t]:cs.push(t)}var Pf=Qi(null),sr=null,va=null;function Ya(t,e,n){Pt(Pf,e._currentValue),e._currentValue=n}function ya(t){t._currentValue=Pf.current,xn(Pf)}function zf(t,e,n){for(;t!==null;){var i=t.alternate;if((t.childLanes&e)!==e?(t.childLanes|=e,i!==null&&(i.childLanes|=e)):i!==null&&(i.childLanes&e)!==e&&(i.childLanes|=e),t===n)break;t=t.return}}function jf(t,e,n,i){var a=t.child;for(a!==null&&(a.return=t);a!==null;){var s=a.dependencies;if(s!==null){var l=a.child;s=s.firstContext;e:for(;s!==null;){var o=s;s=a;for(var c=0;c<e.length;c++)if(o.context===e[c]){s.lanes|=n,o=s.alternate,o!==null&&(o.lanes|=n),zf(s.return,n,t),i||(l=null);break e}s=o.next}}else if(a.tag===18){if(l=a.return,l===null)throw Error(fe(341));l.lanes|=n,s=l.alternate,s!==null&&(s.lanes|=n),zf(l,n,t),l=null}else l=a.child;if(l!==null)l.return=a;else for(l=a;l!==null;){if(l===t){l=null;break}if(a=l.sibling,a!==null){a.return=l.return,l=a;break}l=l.return}a=l}}function wl(t,e,n,i){t=null;for(var a=e,s=!1;a!==null;){if(!s){if(a.flags&524288)s=!0;else if(a.flags&262144)break}if(a.tag===10){var l=a.alternate;if(l===null)throw Error(fe(387));if(l=l.memoizedProps,l!==null){var o=a.type;ri(a.pendingProps.value,l.value)||(t!==null?t.push(o):t=[o])}}else if(a===Ou.current){if(l=a.alternate,l===null)throw Error(fe(387));l.memoizedState.memoizedState!==a.memoizedState.memoizedState&&(t!==null?t.push(Bo):t=[Bo])}a=a.return}t!==null&&jf(e,t,n,i),e.flags|=262144}function Hu(t){for(t=t.firstContext;t!==null;){if(!ri(t.context._currentValue,t.memoizedValue))return!0;t=t.next}return!1}function Xs(t){sr=t,va=null,t=t.dependencies,t!==null&&(t.firstContext=null)}function yn(t){return tb(sr,t)}function vc(t,e){return sr===null&&Xs(t),tb(t,e)}function tb(t,e){var n=e._currentValue;if(e={context:e,memoizedValue:n,next:null},va===null){if(t===null)throw Error(fe(308));va=e,t.dependencies={lanes:0,firstContext:e},t.flags|=524288}else va=va.next=e;return n}var IE=typeof AbortController<"u"?AbortController:function(){var t=[],e=this.signal={aborted:!1,addEventListener:function(n,i){t.push(i)}};this.abort=function(){e.aborted=!0,t.forEach(function(n){return n()})}},BE=un.unstable_scheduleCallback,FE=un.unstable_NormalPriority,an={$$typeof:ga,Consumer:null,Provider:null,_currentValue:null,_currentValue2:null,_threadCount:0};function Pm(){return{controller:new IE,data:new Map,refCount:0}}function ec(t){t.refCount--,t.refCount===0&&BE(FE,function(){t.controller.abort()})}var go=null,kf=0,ul=0,Jr=null;function HE(t,e){if(go===null){var n=go=[];kf=0,ul=rg(),Jr={status:"pending",value:void 0,then:function(i){n.push(i)}}}return kf++,e.then(fx,fx),e}function fx(){if(--kf===0&&go!==null){Jr!==null&&(Jr.status="fulfilled");var t=go;go=null,ul=0,Jr=null;for(var e=0;e<t.length;e++)(0,t[e])()}}function GE(t,e){var n=[],i={status:"pending",value:null,reason:null,then:function(a){n.push(a)}};return t.then(function(){i.status="fulfilled",i.value=e;for(var a=0;a<n.length;a++)(0,n[a])(e)},function(a){for(i.status="rejected",i.reason=a,a=0;a<n.length;a++)(0,n[a])(void 0)}),i}var px=et.S;et.S=function(t,e){my=ni(),typeof e=="object"&&e!==null&&typeof e.then=="function"&&HE(t,e),px!==null&&px(t,e)};var Hs=Qi(null);function zm(){var t=Hs.current;return t!==null?t:Ut.pooledCache}function pu(t,e){e===null?Pt(Hs,Hs.current):Pt(Hs,e.pool)}function nb(){var t=zm();return t===null?null:{parent:an._currentValue,pool:t}}var Nl=Error(fe(460)),jm=Error(fe(474)),Rd=Error(fe(542)),Gu={then:function(){}};function mx(t){return t=t.status,t==="fulfilled"||t==="rejected"}function ib(t,e,n){switch(n=t[n],n===void 0?t.push(e):n!==e&&(e.then(xa,xa),e=n),e.status){case"fulfilled":return e.value;case"rejected":throw t=e.reason,xx(t),t;default:if(typeof e.status=="string")e.then(xa,xa);else{if(t=Ut,t!==null&&100<t.shellSuspendCounter)throw Error(fe(482));t=e,t.status="pending",t.then(function(i){if(e.status==="pending"){var a=e;a.status="fulfilled",a.value=i}},function(i){if(e.status==="pending"){var a=e;a.status="rejected",a.reason=i}})}switch(e.status){case"fulfilled":return e.value;case"rejected":throw t=e.reason,xx(t),t}throw Gs=e,Nl}}function Us(t){try{var e=t._init;return e(t._payload)}catch(n){throw n!==null&&typeof n=="object"&&typeof n.then=="function"?(Gs=n,Nl):n}}var Gs=null;function gx(){if(Gs===null)throw Error(fe(459));var t=Gs;return Gs=null,t}function xx(t){if(t===Nl||t===Rd)throw Error(fe(483))}var el=null,Po=0;function _c(t){var e=Po;return Po+=1,el===null&&(el=[]),ib(el,t,e)}function Il(t,e){e=e.props.ref,t.ref=e!==void 0?e:null}function bc(t,e){throw e.$$typeof===CM?Error(fe(525)):(t=Object.prototype.toString.call(e),Error(fe(31,t==="[object Object]"?"object with keys {"+Object.keys(e).join(", ")+"}":t)))}function ab(t){function e(f,v){if(t){var y=f.deletions;y===null?(f.deletions=[v],f.flags|=16):y.push(v)}}function n(f,v){if(!t)return null;for(;v!==null;)e(f,v),v=v.sibling;return null}function i(f){for(var v=new Map;f!==null;)f.key!==null?v.set(f.key,f):v.set(f.index,f),f=f.sibling;return v}function a(f,v){return f=ba(f,v),f.index=0,f.sibling=null,f}function s(f,v,y){return f.index=y,t?(y=f.alternate,y!==null?(y=y.index,y<v?(f.flags|=67108866,v):y):(f.flags|=67108866,v)):(f.flags|=1048576,v)}function l(f){return t&&f.alternate===null&&(f.flags|=67108866),f}function o(f,v,y,b){return v===null||v.tag!==6?(v=sh(y,f.mode,b),v.return=f,v):(v=a(v,y),v.return=f,v)}function c(f,v,y,b){var T=y.type;return T===zr?h(f,v,y.props.children,b,y.key):v!==null&&(v.elementType===T||typeof T=="object"&&T!==null&&T.$$typeof===Xa&&Us(T)===v.type)?(v=a(v,y.props),Il(v,y),v.return=f,v):(v=fu(y.type,y.key,y.props,null,f.mode,b),Il(v,y),v.return=f,v)}function u(f,v,y,b){return v===null||v.tag!==4||v.stateNode.containerInfo!==y.containerInfo||v.stateNode.implementation!==y.implementation?(v=rh(y,f.mode,b),v.return=f,v):(v=a(v,y.children||[]),v.return=f,v)}function h(f,v,y,b,T){return v===null||v.tag!==7?(v=Fs(y,f.mode,b,T),v.return=f,v):(v=a(v,y),v.return=f,v)}function g(f,v,y){if(typeof v=="string"&&v!==""||typeof v=="number"||typeof v=="bigint")return v=sh(""+v,f.mode,y),v.return=f,v;if(typeof v=="object"&&v!==null){switch(v.$$typeof){case hc:return y=fu(v.type,v.key,v.props,null,f.mode,y),Il(y,v),y.return=f,y;case to:return v=rh(v,f.mode,y),v.return=f,v;case Xa:return v=Us(v),g(f,v,y)}if(no(v)||zl(v))return v=Fs(v,f.mode,y,null),v.return=f,v;if(typeof v.then=="function")return g(f,_c(v),y);if(v.$$typeof===ga)return g(f,vc(f,v),y);bc(f,v)}return null}function d(f,v,y,b){var T=v!==null?v.key:null;if(typeof y=="string"&&y!==""||typeof y=="number"||typeof y=="bigint")return T!==null?null:o(f,v,""+y,b);if(typeof y=="object"&&y!==null){switch(y.$$typeof){case hc:return y.key===T?c(f,v,y,b):null;case to:return y.key===T?u(f,v,y,b):null;case Xa:return y=Us(y),d(f,v,y,b)}if(no(y)||zl(y))return T!==null?null:h(f,v,y,b,null);if(typeof y.then=="function")return d(f,v,_c(y),b);if(y.$$typeof===ga)return d(f,v,vc(f,y),b);bc(f,y)}return null}function p(f,v,y,b,T){if(typeof b=="string"&&b!==""||typeof b=="number"||typeof b=="bigint")return f=f.get(y)||null,o(v,f,""+b,T);if(typeof b=="object"&&b!==null){switch(b.$$typeof){case hc:return f=f.get(b.key===null?y:b.key)||null,c(v,f,b,T);case to:return f=f.get(b.key===null?y:b.key)||null,u(v,f,b,T);case Xa:return b=Us(b),p(f,v,y,b,T)}if(no(b)||zl(b))return f=f.get(y)||null,h(v,f,b,T,null);if(typeof b.then=="function")return p(f,v,y,_c(b),T);if(b.$$typeof===ga)return p(f,v,y,vc(v,b),T);bc(v,b)}return null}function x(f,v,y,b){for(var T=null,M=null,A=v,S=v=0,N=null;A!==null&&S<y.length;S++){A.index>S?(N=A,A=null):N=A.sibling;var D=d(f,A,y[S],b);if(D===null){A===null&&(A=N);break}t&&A&&D.alternate===null&&e(f,A),v=s(D,v,S),M===null?T=D:M.sibling=D,M=D,A=N}if(S===y.length)return n(f,A),mt&&fa(f,S),T;if(A===null){for(;S<y.length;S++)A=g(f,y[S],b),A!==null&&(v=s(A,v,S),M===null?T=A:M.sibling=A,M=A);return mt&&fa(f,S),T}for(A=i(A);S<y.length;S++)N=p(A,f,S,y[S],b),N!==null&&(t&&N.alternate!==null&&A.delete(N.key===null?S:N.key),v=s(N,v,S),M===null?T=N:M.sibling=N,M=N);return t&&A.forEach(function(U){return e(f,U)}),mt&&fa(f,S),T}function _(f,v,y,b){if(y==null)throw Error(fe(151));for(var T=null,M=null,A=v,S=v=0,N=null,D=y.next();A!==null&&!D.done;S++,D=y.next()){A.index>S?(N=A,A=null):N=A.sibling;var U=d(f,A,D.value,b);if(U===null){A===null&&(A=N);break}t&&A&&U.alternate===null&&e(f,A),v=s(U,v,S),M===null?T=U:M.sibling=U,M=U,A=N}if(D.done)return n(f,A),mt&&fa(f,S),T;if(A===null){for(;!D.done;S++,D=y.next())D=g(f,D.value,b),D!==null&&(v=s(D,v,S),M===null?T=D:M.sibling=D,M=D);return mt&&fa(f,S),T}for(A=i(A);!D.done;S++,D=y.next())D=p(A,f,S,D.value,b),D!==null&&(t&&D.alternate!==null&&A.delete(D.key===null?S:D.key),v=s(D,v,S),M===null?T=D:M.sibling=D,M=D);return t&&A.forEach(function(I){return e(f,I)}),mt&&fa(f,S),T}function m(f,v,y,b){if(typeof y=="object"&&y!==null&&y.type===zr&&y.key===null&&(y=y.props.children),typeof y=="object"&&y!==null){switch(y.$$typeof){case hc:e:{for(var T=y.key;v!==null;){if(v.key===T){if(T=y.type,T===zr){if(v.tag===7){n(f,v.sibling),b=a(v,y.props.children),b.return=f,f=b;break e}}else if(v.elementType===T||typeof T=="object"&&T!==null&&T.$$typeof===Xa&&Us(T)===v.type){n(f,v.sibling),b=a(v,y.props),Il(b,y),b.return=f,f=b;break e}n(f,v);break}else e(f,v);v=v.sibling}y.type===zr?(b=Fs(y.props.children,f.mode,b,y.key),b.return=f,f=b):(b=fu(y.type,y.key,y.props,null,f.mode,b),Il(b,y),b.return=f,f=b)}return l(f);case to:e:{for(T=y.key;v!==null;){if(v.key===T)if(v.tag===4&&v.stateNode.containerInfo===y.containerInfo&&v.stateNode.implementation===y.implementation){n(f,v.sibling),b=a(v,y.children||[]),b.return=f,f=b;break e}else{n(f,v);break}else e(f,v);v=v.sibling}b=rh(y,f.mode,b),b.return=f,f=b}return l(f);case Xa:return y=Us(y),m(f,v,y,b)}if(no(y))return x(f,v,y,b);if(zl(y)){if(T=zl(y),typeof T!="function")throw Error(fe(150));return y=T.call(y),_(f,v,y,b)}if(typeof y.then=="function")return m(f,v,_c(y),b);if(y.$$typeof===ga)return m(f,v,vc(f,y),b);bc(f,y)}return typeof y=="string"&&y!==""||typeof y=="number"||typeof y=="bigint"?(y=""+y,v!==null&&v.tag===6?(n(f,v.sibling),b=a(v,y),b.return=f,f=b):(n(f,v),b=sh(y,f.mode,b),b.return=f,f=b),l(f)):n(f,v)}return function(f,v,y,b){try{Po=0;var T=m(f,v,y,b);return el=null,T}catch(A){if(A===Nl||A===Rd)throw A;var M=ei(29,A,null,f.mode);return M.lanes=b,M.return=f,M}finally{}}}var qs=ab(!0),sb=ab(!1),qa=!1;function km(t){t.updateQueue={baseState:t.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function If(t,e){t=t.updateQueue,e.updateQueue===t&&(e.updateQueue={baseState:t.baseState,firstBaseUpdate:t.firstBaseUpdate,lastBaseUpdate:t.lastBaseUpdate,shared:t.shared,callbacks:null})}function us(t){return{lane:t,tag:0,payload:null,callback:null,next:null}}function ds(t,e,n){var i=t.updateQueue;if(i===null)return null;if(i=i.shared,yt&2){var a=i.pending;return a===null?e.next=e:(e.next=a.next,a.next=e),i.pending=e,e=Bu(t),K_(t,null,n),e}return Cd(t,i,e,n),Bu(t)}function xo(t,e,n){if(e=e.updateQueue,e!==null&&(e=e.shared,(n&4194048)!==0)){var i=e.lanes;i&=t.pendingLanes,n|=i,e.lanes=n,M_(t,n)}}function oh(t,e){var n=t.updateQueue,i=t.alternate;if(i!==null&&(i=i.updateQueue,n===i)){var a=null,s=null;if(n=n.firstBaseUpdate,n!==null){do{var l={lane:n.lane,tag:n.tag,payload:n.payload,callback:null,next:null};s===null?a=s=l:s=s.next=l,n=n.next}while(n!==null);s===null?a=s=e:s=s.next=e}else a=s=e;n={baseState:i.baseState,firstBaseUpdate:a,lastBaseUpdate:s,shared:i.shared,callbacks:i.callbacks},t.updateQueue=n;return}t=n.lastBaseUpdate,t===null?n.firstBaseUpdate=e:t.next=e,n.lastBaseUpdate=e}var Bf=!1;function vo(){if(Bf){var t=Jr;if(t!==null)throw t}}function _o(t,e,n,i){Bf=!1;var a=t.updateQueue;qa=!1;var s=a.firstBaseUpdate,l=a.lastBaseUpdate,o=a.shared.pending;if(o!==null){a.shared.pending=null;var c=o,u=c.next;c.next=null,l===null?s=u:l.next=u,l=c;var h=t.alternate;h!==null&&(h=h.updateQueue,o=h.lastBaseUpdate,o!==l&&(o===null?h.firstBaseUpdate=u:o.next=u,h.lastBaseUpdate=c))}if(s!==null){var g=a.baseState;l=0,h=u=c=null,o=s;do{var d=o.lane&-536870913,p=d!==o.lane;if(p?(ut&d)===d:(i&d)===d){d!==0&&d===ul&&(Bf=!0),h!==null&&(h=h.next={lane:0,tag:o.tag,payload:o.payload,callback:null,next:null});e:{var x=t,_=o;d=e;var m=n;switch(_.tag){case 1:if(x=_.payload,typeof x=="function"){g=x.call(m,g,d);break e}g=x;break e;case 3:x.flags=x.flags&-65537|128;case 0:if(x=_.payload,d=typeof x=="function"?x.call(m,g,d):x,d==null)break e;g=Bt({},g,d);break e;case 2:qa=!0}}d=o.callback,d!==null&&(t.flags|=64,p&&(t.flags|=8192),p=a.callbacks,p===null?a.callbacks=[d]:p.push(d))}else p={lane:d,tag:o.tag,payload:o.payload,callback:o.callback,next:null},h===null?(u=h=p,c=g):h=h.next=p,l|=d;if(o=o.next,o===null){if(o=a.shared.pending,o===null)break;p=o,o=p.next,p.next=null,a.lastBaseUpdate=p,a.shared.pending=null}}while(!0);h===null&&(c=g),a.baseState=c,a.firstBaseUpdate=u,a.lastBaseUpdate=h,s===null&&(a.shared.lanes=0),bs|=l,t.lanes=l,t.memoizedState=g}}function rb(t,e){if(typeof t!="function")throw Error(fe(191,t));t.call(e)}function lb(t,e){var n=t.callbacks;if(n!==null)for(t.callbacks=null,t=0;t<n.length;t++)rb(n[t],e)}var dl=Qi(null),Vu=Qi(0);function vx(t,e){t=Ca,Pt(Vu,t),Pt(dl,e),Ca=t|e.baseLanes}function Ff(){Pt(Vu,Ca),Pt(dl,dl.current)}function Im(){Ca=Vu.current,xn(dl),xn(Vu)}var li=Qi(null),bi=null;function $a(t){var e=t.alternate;Pt(Zt,Zt.current&1),Pt(li,t),bi===null&&(e===null||dl.current!==null||e.memoizedState!==null)&&(bi=t)}function Hf(t){Pt(Zt,Zt.current),Pt(li,t),bi===null&&(bi=t)}function ob(t){t.tag===22?(Pt(Zt,Zt.current),Pt(li,t),bi===null&&(bi=t)):Ka()}function Ka(){Pt(Zt,Zt.current),Pt(li,li.current)}function Jn(t){xn(li),bi===t&&(bi=null),xn(Zt)}var Zt=Qi(0);function Wu(t){for(var e=t;e!==null;){if(e.tag===13){var n=e.memoizedState;if(n!==null&&(n=n.dehydrated,n===null||lp(n)||op(n)))return e}else if(e.tag===19&&(e.memoizedProps.revealOrder==="forwards"||e.memoizedProps.revealOrder==="backwards"||e.memoizedProps.revealOrder==="unstable_legacy-backwards"||e.memoizedProps.revealOrder==="together")){if(e.flags&128)return e}else if(e.child!==null){e.child.return=e,e=e.child;continue}if(e===t)break;for(;e.sibling===null;){if(e.return===null||e.return===t)return null;e=e.return}e.sibling.return=e.return,e=e.sibling}return null}var Aa=0,it=null,Nt=null,tn=null,Xu=!1,tl=!1,Ys=!1,qu=0,zo=0,nl=null,VE=0;function Xt(){throw Error(fe(321))}function Bm(t,e){if(e===null)return!1;for(var n=0;n<e.length&&n<t.length;n++)if(!ri(t[n],e[n]))return!1;return!0}function Fm(t,e,n,i,a,s){return Aa=s,it=e,e.memoizedState=null,e.updateQueue=null,e.lanes=0,et.H=t===null||t.memoizedState===null?Ib:Qm,Ys=!1,s=n(i,a),Ys=!1,tl&&(s=ub(e,n,i,a)),cb(t),s}function cb(t){et.H=jo;var e=Nt!==null&&Nt.next!==null;if(Aa=0,tn=Nt=it=null,Xu=!1,zo=0,nl=null,e)throw Error(fe(300));t===null||sn||(t=t.dependencies,t!==null&&Hu(t)&&(sn=!0))}function ub(t,e,n,i){it=t;var a=0;do{if(tl&&(nl=null),zo=0,tl=!1,25<=a)throw Error(fe(301));if(a+=1,tn=Nt=null,t.updateQueue!=null){var s=t.updateQueue;s.lastEffect=null,s.events=null,s.stores=null,s.memoCache!=null&&(s.memoCache.index=0)}et.H=Bb,s=e(n,i)}while(tl);return s}function WE(){var t=et.H,e=t.useState()[0];return e=typeof e.then=="function"?tc(e):e,t=t.useState()[0],(Nt!==null?Nt.memoizedState:null)!==t&&(it.flags|=1024),e}function Hm(){var t=qu!==0;return qu=0,t}function Gm(t,e,n){e.updateQueue=t.updateQueue,e.flags&=-2053,t.lanes&=~n}function Vm(t){if(Xu){for(t=t.memoizedState;t!==null;){var e=t.queue;e!==null&&(e.pending=null),t=t.next}Xu=!1}Aa=0,tn=Nt=it=null,tl=!1,zo=qu=0,nl=null}function Cn(){var t={memoizedState:null,baseState:null,baseQueue:null,queue:null,next:null};return tn===null?it.memoizedState=tn=t:tn=tn.next=t,tn}function Qt(){if(Nt===null){var t=it.alternate;t=t!==null?t.memoizedState:null}else t=Nt.next;var e=tn===null?it.memoizedState:tn.next;if(e!==null)tn=e,Nt=t;else{if(t===null)throw it.alternate===null?Error(fe(467)):Error(fe(310));Nt=t,t={memoizedState:Nt.memoizedState,baseState:Nt.baseState,baseQueue:Nt.baseQueue,queue:Nt.queue,next:null},tn===null?it.memoizedState=tn=t:tn=tn.next=t}return tn}function Dd(){return{lastEffect:null,events:null,stores:null,memoCache:null}}function tc(t){var e=zo;return zo+=1,nl===null&&(nl=[]),t=ib(nl,t,e),e=it,(tn===null?e.memoizedState:tn.next)===null&&(e=e.alternate,et.H=e===null||e.memoizedState===null?Ib:Qm),t}function Ld(t){if(t!==null&&typeof t=="object"){if(typeof t.then=="function")return tc(t);if(t.$$typeof===ga)return yn(t)}throw Error(fe(438,String(t)))}function Wm(t){var e=null,n=it.updateQueue;if(n!==null&&(e=n.memoCache),e==null){var i=it.alternate;i!==null&&(i=i.updateQueue,i!==null&&(i=i.memoCache,i!=null&&(e={data:i.data.map(function(a){return a.slice()}),index:0})))}if(e==null&&(e={data:[],index:0}),n===null&&(n=Dd(),it.updateQueue=n),n.memoCache=e,n=e.data[e.index],n===void 0)for(n=e.data[e.index]=Array(t),i=0;i<t;i++)n[i]=RM;return e.index++,n}function wa(t,e){return typeof e=="function"?e(t):e}function mu(t){var e=Qt();return Xm(e,Nt,t)}function Xm(t,e,n){var i=t.queue;if(i===null)throw Error(fe(311));i.lastRenderedReducer=n;var a=t.baseQueue,s=i.pending;if(s!==null){if(a!==null){var l=a.next;a.next=s.next,s.next=l}e.baseQueue=a=s,i.pending=null}if(s=t.baseState,a===null)t.memoizedState=s;else{e=a.next;var o=l=null,c=null,u=e,h=!1;do{var g=u.lane&-536870913;if(g!==u.lane?(ut&g)===g:(Aa&g)===g){var d=u.revertLane;if(d===0)c!==null&&(c=c.next={lane:0,revertLane:0,gesture:null,action:u.action,hasEagerState:u.hasEagerState,eagerState:u.eagerState,next:null}),g===ul&&(h=!0);else if((Aa&d)===d){u=u.next,d===ul&&(h=!0);continue}else g={lane:0,revertLane:u.revertLane,gesture:null,action:u.action,hasEagerState:u.hasEagerState,eagerState:u.eagerState,next:null},c===null?(o=c=g,l=s):c=c.next=g,it.lanes|=d,bs|=d;g=u.action,Ys&&n(s,g),s=u.hasEagerState?u.eagerState:n(s,g)}else d={lane:g,revertLane:u.revertLane,gesture:u.gesture,action:u.action,hasEagerState:u.hasEagerState,eagerState:u.eagerState,next:null},c===null?(o=c=d,l=s):c=c.next=d,it.lanes|=g,bs|=g;u=u.next}while(u!==null&&u!==e);if(c===null?l=s:c.next=o,!ri(s,t.memoizedState)&&(sn=!0,h&&(n=Jr,n!==null)))throw n;t.memoizedState=s,t.baseState=l,t.baseQueue=c,i.lastRenderedState=s}return a===null&&(i.lanes=0),[t.memoizedState,i.dispatch]}function ch(t){var e=Qt(),n=e.queue;if(n===null)throw Error(fe(311));n.lastRenderedReducer=t;var i=n.dispatch,a=n.pending,s=e.memoizedState;if(a!==null){n.pending=null;var l=a=a.next;do s=t(s,l.action),l=l.next;while(l!==a);ri(s,e.memoizedState)||(sn=!0),e.memoizedState=s,e.baseQueue===null&&(e.baseState=s),n.lastRenderedState=s}return[s,i]}function db(t,e,n){var i=it,a=Qt(),s=mt;if(s){if(n===void 0)throw Error(fe(407));n=n()}else n=e();var l=!ri((Nt||a).memoizedState,n);if(l&&(a.memoizedState=n,sn=!0),a=a.queue,qm(pb.bind(null,i,a,t),[t]),a.getSnapshot!==e||l||tn!==null&&tn.memoizedState.tag&1){if(i.flags|=2048,hl(9,{destroy:void 0},fb.bind(null,i,a,n,e),null),Ut===null)throw Error(fe(349));s||Aa&127||hb(i,e,n)}return n}function hb(t,e,n){t.flags|=16384,t={getSnapshot:e,value:n},e=it.updateQueue,e===null?(e=Dd(),it.updateQueue=e,e.stores=[t]):(n=e.stores,n===null?e.stores=[t]:n.push(t))}function fb(t,e,n,i){e.value=n,e.getSnapshot=i,mb(e)&&gb(t)}function pb(t,e,n){return n(function(){mb(e)&&gb(t)})}function mb(t){var e=t.getSnapshot;t=t.value;try{var n=e();return!ri(t,n)}catch{return!0}}function gb(t){var e=ar(t,2);e!==null&&Vn(e,t,2)}function Gf(t){var e=Cn();if(typeof t=="function"){var n=t;if(t=n(),Ys){Ja(!0);try{n()}finally{Ja(!1)}}}return e.memoizedState=e.baseState=t,e.queue={pending:null,lanes:0,dispatch:null,lastRenderedReducer:wa,lastRenderedState:t},e}function xb(t,e,n,i){return t.baseState=n,Xm(t,Nt,typeof i=="function"?i:wa)}function XE(t,e,n,i,a){if(Od(t))throw Error(fe(485));if(t=e.action,t!==null){var s={payload:a,action:t,next:null,isTransition:!0,status:"pending",value:null,reason:null,listeners:[],then:function(l){s.listeners.push(l)}};et.T!==null?n(!0):s.isTransition=!1,i(s),n=e.pending,n===null?(s.next=e.pending=s,vb(e,s)):(s.next=n.next,e.pending=n.next=s)}}function vb(t,e){var n=e.action,i=e.payload,a=t.state;if(e.isTransition){var s=et.T,l={};et.T=l;try{var o=n(a,i),c=et.S;c!==null&&c(l,o),_x(t,e,o)}catch(u){Vf(t,e,u)}finally{s!==null&&l.types!==null&&(s.types=l.types),et.T=s}}else try{s=n(a,i),_x(t,e,s)}catch(u){Vf(t,e,u)}}function _x(t,e,n){n!==null&&typeof n=="object"&&typeof n.then=="function"?n.then(function(i){bx(t,e,i)},function(i){return Vf(t,e,i)}):bx(t,e,n)}function bx(t,e,n){e.status="fulfilled",e.value=n,_b(e),t.state=n,e=t.pending,e!==null&&(n=e.next,n===e?t.pending=null:(n=n.next,e.next=n,vb(t,n)))}function Vf(t,e,n){var i=t.pending;if(t.pending=null,i!==null){i=i.next;do e.status="rejected",e.reason=n,_b(e),e=e.next;while(e!==i)}t.action=null}function _b(t){t=t.listeners;for(var e=0;e<t.length;e++)(0,t[e])()}function bb(t,e){return e}function yx(t,e){if(mt){var n=Ut.formState;if(n!==null){e:{var i=it;if(mt){if(kt){t:{for(var a=kt,s=xi;a.nodeType!==8;){if(!s){a=null;break t}if(a=yi(a.nextSibling),a===null){a=null;break t}}s=a.data,a=s==="F!"||s==="F"?a:null}if(a){kt=yi(a.nextSibling),i=a.data==="F!";break e}}vs(i)}i=!1}i&&(e=n[0])}}return n=Cn(),n.memoizedState=n.baseState=e,i={pending:null,lanes:0,dispatch:null,lastRenderedReducer:bb,lastRenderedState:e},n.queue=i,n=zb.bind(null,it,i),i.dispatch=n,i=Gf(!1),s=Zm.bind(null,it,!1,i.queue),i=Cn(),a={state:e,dispatch:null,action:t,pending:null},i.queue=a,n=XE.bind(null,it,a,s,n),a.dispatch=n,i.memoizedState=t,[e,n,!1]}function Sx(t){var e=Qt();return yb(e,Nt,t)}function yb(t,e,n){if(e=Xm(t,e,bb)[0],t=mu(wa)[0],typeof e=="object"&&e!==null&&typeof e.then=="function")try{var i=tc(e)}catch(l){throw l===Nl?Rd:l}else i=e;e=Qt();var a=e.queue,s=a.dispatch;return n!==e.memoizedState&&(it.flags|=2048,hl(9,{destroy:void 0},qE.bind(null,a,n),null)),[i,s,t]}function qE(t,e){t.action=e}function Mx(t){var e=Qt(),n=Nt;if(n!==null)return yb(e,n,t);Qt(),e=e.memoizedState,n=Qt();var i=n.queue.dispatch;return n.memoizedState=t,[e,i,!1]}function hl(t,e,n,i){return t={tag:t,create:n,deps:i,inst:e,next:null},e=it.updateQueue,e===null&&(e=Dd(),it.updateQueue=e),n=e.lastEffect,n===null?e.lastEffect=t.next=t:(i=n.next,n.next=t,t.next=i,e.lastEffect=t),t}function Sb(){return Qt().memoizedState}function gu(t,e,n,i){var a=Cn();it.flags|=t,a.memoizedState=hl(1|e,{destroy:void 0},n,i===void 0?null:i)}function Ud(t,e,n,i){var a=Qt();i=i===void 0?null:i;var s=a.memoizedState.inst;Nt!==null&&i!==null&&Bm(i,Nt.memoizedState.deps)?a.memoizedState=hl(e,s,n,i):(it.flags|=t,a.memoizedState=hl(1|e,s,n,i))}function Ex(t,e){gu(8390656,8,t,e)}function qm(t,e){Ud(2048,8,t,e)}function YE(t){it.flags|=4;var e=it.updateQueue;if(e===null)e=Dd(),it.updateQueue=e,e.events=[t];else{var n=e.events;n===null?e.events=[t]:n.push(t)}}function Mb(t){var e=Qt().memoizedState;return YE({ref:e,nextImpl:t}),function(){if(yt&2)throw Error(fe(440));return e.impl.apply(void 0,arguments)}}function Eb(t,e){return Ud(4,2,t,e)}function Tb(t,e){return Ud(4,4,t,e)}function Ab(t,e){if(typeof e=="function"){t=t();var n=e(t);return function(){typeof n=="function"?n():e(null)}}if(e!=null)return t=t(),e.current=t,function(){e.current=null}}function wb(t,e,n){n=n!=null?n.concat([t]):null,Ud(4,4,Ab.bind(null,e,t),n)}function Ym(){}function Nb(t,e){var n=Qt();e=e===void 0?null:e;var i=n.memoizedState;return e!==null&&Bm(e,i[1])?i[0]:(n.memoizedState=[t,e],t)}function Cb(t,e){var n=Qt();e=e===void 0?null:e;var i=n.memoizedState;if(e!==null&&Bm(e,i[1]))return i[0];if(i=t(),Ys){Ja(!0);try{t()}finally{Ja(!1)}}return n.memoizedState=[i,e],i}function $m(t,e,n){return n===void 0||Aa&1073741824&&!(ut&261930)?t.memoizedState=e:(t.memoizedState=n,t=xy(),it.lanes|=t,bs|=t,n)}function Rb(t,e,n,i){return ri(n,e)?n:dl.current!==null?(t=$m(t,n,i),ri(t,e)||(sn=!0),t):!(Aa&42)||Aa&1073741824&&!(ut&261930)?(sn=!0,t.memoizedState=n):(t=xy(),it.lanes|=t,bs|=t,e)}function Db(t,e,n,i,a){var s=St.p;St.p=s!==0&&8>s?s:8;var l=et.T,o={};et.T=o,Zm(t,!1,e,n);try{var c=a(),u=et.S;if(u!==null&&u(o,c),c!==null&&typeof c=="object"&&typeof c.then=="function"){var h=GE(c,i);bo(t,e,h,si(t))}else bo(t,e,i,si(t))}catch(g){bo(t,e,{then:function(){},status:"rejected",reason:g},si())}finally{St.p=s,l!==null&&o.types!==null&&(l.types=o.types),et.T=l}}function $E(){}function Wf(t,e,n,i){if(t.tag!==5)throw Error(fe(476));var a=Lb(t).queue;Db(t,a,e,Bs,n===null?$E:function(){return Ub(t),n(i)})}function Lb(t){var e=t.memoizedState;if(e!==null)return e;e={memoizedState:Bs,baseState:Bs,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:wa,lastRenderedState:Bs},next:null};var n={};return e.next={memoizedState:n,baseState:n,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:wa,lastRenderedState:n},next:null},t.memoizedState=e,t=t.alternate,t!==null&&(t.memoizedState=e),e}function Ub(t){var e=Lb(t);e.next===null&&(e=t.alternate.memoizedState),bo(t,e.next.queue,{},si())}function Km(){return yn(Bo)}function Ob(){return Qt().memoizedState}function Pb(){return Qt().memoizedState}function KE(t){for(var e=t.return;e!==null;){switch(e.tag){case 24:case 3:var n=si();t=us(n);var i=ds(e,t,n);i!==null&&(Vn(i,e,n),xo(i,e,n)),e={cache:Pm()},t.payload=e;return}e=e.return}}function ZE(t,e,n){var i=si();n={lane:i,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null},Od(t)?jb(e,n):(n=Dm(t,e,n,i),n!==null&&(Vn(n,t,i),kb(n,e,i)))}function zb(t,e,n){var i=si();bo(t,e,n,i)}function bo(t,e,n,i){var a={lane:i,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null};if(Od(t))jb(e,a);else{var s=t.alternate;if(t.lanes===0&&(s===null||s.lanes===0)&&(s=e.lastRenderedReducer,s!==null))try{var l=e.lastRenderedState,o=s(l,n);if(a.hasEagerState=!0,a.eagerState=o,ri(o,l))return Cd(t,e,a,0),Ut===null&&Nd(),!1}catch{}finally{}if(n=Dm(t,e,a,i),n!==null)return Vn(n,t,i),kb(n,e,i),!0}return!1}function Zm(t,e,n,i){if(i={lane:2,revertLane:rg(),gesture:null,action:i,hasEagerState:!1,eagerState:null,next:null},Od(t)){if(e)throw Error(fe(479))}else e=Dm(t,n,i,2),e!==null&&Vn(e,t,2)}function Od(t){var e=t.alternate;return t===it||e!==null&&e===it}function jb(t,e){tl=Xu=!0;var n=t.pending;n===null?e.next=e:(e.next=n.next,n.next=e),t.pending=e}function kb(t,e,n){if(n&4194048){var i=e.lanes;i&=t.pendingLanes,n|=i,e.lanes=n,M_(t,n)}}var jo={readContext:yn,use:Ld,useCallback:Xt,useContext:Xt,useEffect:Xt,useImperativeHandle:Xt,useLayoutEffect:Xt,useInsertionEffect:Xt,useMemo:Xt,useReducer:Xt,useRef:Xt,useState:Xt,useDebugValue:Xt,useDeferredValue:Xt,useTransition:Xt,useSyncExternalStore:Xt,useId:Xt,useHostTransitionStatus:Xt,useFormState:Xt,useActionState:Xt,useOptimistic:Xt,useMemoCache:Xt,useCacheRefresh:Xt};jo.useEffectEvent=Xt;var Ib={readContext:yn,use:Ld,useCallback:function(t,e){return Cn().memoizedState=[t,e===void 0?null:e],t},useContext:yn,useEffect:Ex,useImperativeHandle:function(t,e,n){n=n!=null?n.concat([t]):null,gu(4194308,4,Ab.bind(null,e,t),n)},useLayoutEffect:function(t,e){return gu(4194308,4,t,e)},useInsertionEffect:function(t,e){gu(4,2,t,e)},useMemo:function(t,e){var n=Cn();e=e===void 0?null:e;var i=t();if(Ys){Ja(!0);try{t()}finally{Ja(!1)}}return n.memoizedState=[i,e],i},useReducer:function(t,e,n){var i=Cn();if(n!==void 0){var a=n(e);if(Ys){Ja(!0);try{n(e)}finally{Ja(!1)}}}else a=e;return i.memoizedState=i.baseState=a,t={pending:null,lanes:0,dispatch:null,lastRenderedReducer:t,lastRenderedState:a},i.queue=t,t=t.dispatch=ZE.bind(null,it,t),[i.memoizedState,t]},useRef:function(t){var e=Cn();return t={current:t},e.memoizedState=t},useState:function(t){t=Gf(t);var e=t.queue,n=zb.bind(null,it,e);return e.dispatch=n,[t.memoizedState,n]},useDebugValue:Ym,useDeferredValue:function(t,e){var n=Cn();return $m(n,t,e)},useTransition:function(){var t=Gf(!1);return t=Db.bind(null,it,t.queue,!0,!1),Cn().memoizedState=t,[!1,t]},useSyncExternalStore:function(t,e,n){var i=it,a=Cn();if(mt){if(n===void 0)throw Error(fe(407));n=n()}else{if(n=e(),Ut===null)throw Error(fe(349));ut&127||hb(i,e,n)}a.memoizedState=n;var s={value:n,getSnapshot:e};return a.queue=s,Ex(pb.bind(null,i,s,t),[t]),i.flags|=2048,hl(9,{destroy:void 0},fb.bind(null,i,s,n,e),null),n},useId:function(){var t=Cn(),e=Ut.identifierPrefix;if(mt){var n=Wi,i=Vi;n=(i&~(1<<32-ai(i)-1)).toString(32)+n,e="_"+e+"R_"+n,n=qu++,0<n&&(e+="H"+n.toString(32)),e+="_"}else n=VE++,e="_"+e+"r_"+n.toString(32)+"_";return t.memoizedState=e},useHostTransitionStatus:Km,useFormState:yx,useActionState:yx,useOptimistic:function(t){var e=Cn();e.memoizedState=e.baseState=t;var n={pending:null,lanes:0,dispatch:null,lastRenderedReducer:null,lastRenderedState:null};return e.queue=n,e=Zm.bind(null,it,!0,n),n.dispatch=e,[t,e]},useMemoCache:Wm,useCacheRefresh:function(){return Cn().memoizedState=KE.bind(null,it)},useEffectEvent:function(t){var e=Cn(),n={impl:t};return e.memoizedState=n,function(){if(yt&2)throw Error(fe(440));return n.impl.apply(void 0,arguments)}}},Qm={readContext:yn,use:Ld,useCallback:Nb,useContext:yn,useEffect:qm,useImperativeHandle:wb,useInsertionEffect:Eb,useLayoutEffect:Tb,useMemo:Cb,useReducer:mu,useRef:Sb,useState:function(){return mu(wa)},useDebugValue:Ym,useDeferredValue:function(t,e){var n=Qt();return Rb(n,Nt.memoizedState,t,e)},useTransition:function(){var t=mu(wa)[0],e=Qt().memoizedState;return[typeof t=="boolean"?t:tc(t),e]},useSyncExternalStore:db,useId:Ob,useHostTransitionStatus:Km,useFormState:Sx,useActionState:Sx,useOptimistic:function(t,e){var n=Qt();return xb(n,Nt,t,e)},useMemoCache:Wm,useCacheRefresh:Pb};Qm.useEffectEvent=Mb;var Bb={readContext:yn,use:Ld,useCallback:Nb,useContext:yn,useEffect:qm,useImperativeHandle:wb,useInsertionEffect:Eb,useLayoutEffect:Tb,useMemo:Cb,useReducer:ch,useRef:Sb,useState:function(){return ch(wa)},useDebugValue:Ym,useDeferredValue:function(t,e){var n=Qt();return Nt===null?$m(n,t,e):Rb(n,Nt.memoizedState,t,e)},useTransition:function(){var t=ch(wa)[0],e=Qt().memoizedState;return[typeof t=="boolean"?t:tc(t),e]},useSyncExternalStore:db,useId:Ob,useHostTransitionStatus:Km,useFormState:Mx,useActionState:Mx,useOptimistic:function(t,e){var n=Qt();return Nt!==null?xb(n,Nt,t,e):(n.baseState=t,[t,n.queue.dispatch])},useMemoCache:Wm,useCacheRefresh:Pb};Bb.useEffectEvent=Mb;function uh(t,e,n,i){e=t.memoizedState,n=n(i,e),n=n==null?e:Bt({},e,n),t.memoizedState=n,t.lanes===0&&(t.updateQueue.baseState=n)}var Xf={enqueueSetState:function(t,e,n){t=t._reactInternals;var i=si(),a=us(i);a.payload=e,n!=null&&(a.callback=n),e=ds(t,a,i),e!==null&&(Vn(e,t,i),xo(e,t,i))},enqueueReplaceState:function(t,e,n){t=t._reactInternals;var i=si(),a=us(i);a.tag=1,a.payload=e,n!=null&&(a.callback=n),e=ds(t,a,i),e!==null&&(Vn(e,t,i),xo(e,t,i))},enqueueForceUpdate:function(t,e){t=t._reactInternals;var n=si(),i=us(n);i.tag=2,e!=null&&(i.callback=e),e=ds(t,i,n),e!==null&&(Vn(e,t,n),xo(e,t,n))}};function Tx(t,e,n,i,a,s,l){return t=t.stateNode,typeof t.shouldComponentUpdate=="function"?t.shouldComponentUpdate(i,s,l):e.prototype&&e.prototype.isPureReactComponent?!Lo(n,i)||!Lo(a,s):!0}function Ax(t,e,n,i){t=e.state,typeof e.componentWillReceiveProps=="function"&&e.componentWillReceiveProps(n,i),typeof e.UNSAFE_componentWillReceiveProps=="function"&&e.UNSAFE_componentWillReceiveProps(n,i),e.state!==t&&Xf.enqueueReplaceState(e,e.state,null)}function $s(t,e){var n=e;if("ref"in e){n={};for(var i in e)i!=="ref"&&(n[i]=e[i])}if(t=t.defaultProps){n===e&&(n=Bt({},n));for(var a in t)n[a]===void 0&&(n[a]=t[a])}return n}function Fb(t){Iu(t)}function Hb(t){console.error(t)}function Gb(t){Iu(t)}function Yu(t,e){try{var n=t.onUncaughtError;n(e.value,{componentStack:e.stack})}catch(i){setTimeout(function(){throw i})}}function wx(t,e,n){try{var i=t.onCaughtError;i(n.value,{componentStack:n.stack,errorBoundary:e.tag===1?e.stateNode:null})}catch(a){setTimeout(function(){throw a})}}function qf(t,e,n){return n=us(n),n.tag=3,n.payload={element:null},n.callback=function(){Yu(t,e)},n}function Vb(t){return t=us(t),t.tag=3,t}function Wb(t,e,n,i){var a=n.type.getDerivedStateFromError;if(typeof a=="function"){var s=i.value;t.payload=function(){return a(s)},t.callback=function(){wx(e,n,i)}}var l=n.stateNode;l!==null&&typeof l.componentDidCatch=="function"&&(t.callback=function(){wx(e,n,i),typeof a!="function"&&(hs===null?hs=new Set([this]):hs.add(this));var o=i.stack;this.componentDidCatch(i.value,{componentStack:o!==null?o:""})})}function QE(t,e,n,i,a){if(n.flags|=32768,i!==null&&typeof i=="object"&&typeof i.then=="function"){if(e=n.alternate,e!==null&&wl(e,n,a,!0),n=li.current,n!==null){switch(n.tag){case 31:case 13:return bi===null?Ju():n.alternate===null&&qt===0&&(qt=3),n.flags&=-257,n.flags|=65536,n.lanes=a,i===Gu?n.flags|=16384:(e=n.updateQueue,e===null?n.updateQueue=new Set([i]):e.add(i),yh(t,i,a)),!1;case 22:return n.flags|=65536,i===Gu?n.flags|=16384:(e=n.updateQueue,e===null?(e={transitions:null,markerInstances:null,retryQueue:new Set([i])},n.updateQueue=e):(n=e.retryQueue,n===null?e.retryQueue=new Set([i]):n.add(i)),yh(t,i,a)),!1}throw Error(fe(435,n.tag))}return yh(t,i,a),Ju(),!1}if(mt)return e=li.current,e!==null?(!(e.flags&65536)&&(e.flags|=256),e.flags|=65536,e.lanes=a,i!==Of&&(t=Error(fe(422),{cause:i}),Oo(gi(t,n)))):(i!==Of&&(e=Error(fe(423),{cause:i}),Oo(gi(e,n))),t=t.current.alternate,t.flags|=65536,a&=-a,t.lanes|=a,i=gi(i,n),a=qf(t.stateNode,i,a),oh(t,a),qt!==4&&(qt=2)),!1;var s=Error(fe(520),{cause:i});if(s=gi(s,n),Mo===null?Mo=[s]:Mo.push(s),qt!==4&&(qt=2),e===null)return!0;i=gi(i,n),n=e;do{switch(n.tag){case 3:return n.flags|=65536,t=a&-a,n.lanes|=t,t=qf(n.stateNode,i,t),oh(n,t),!1;case 1:if(e=n.type,s=n.stateNode,(n.flags&128)===0&&(typeof e.getDerivedStateFromError=="function"||s!==null&&typeof s.componentDidCatch=="function"&&(hs===null||!hs.has(s))))return n.flags|=65536,a&=-a,n.lanes|=a,a=Vb(a),Wb(a,t,n,i),oh(n,a),!1}n=n.return}while(n!==null);return!1}var Jm=Error(fe(461)),sn=!1;function vn(t,e,n,i){e.child=t===null?sb(e,null,n,i):qs(e,t.child,n,i)}function Nx(t,e,n,i,a){n=n.render;var s=e.ref;if("ref"in i){var l={};for(var o in i)o!=="ref"&&(l[o]=i[o])}else l=i;return Xs(e),i=Fm(t,e,n,l,s,a),o=Hm(),t!==null&&!sn?(Gm(t,e,a),Na(t,e,a)):(mt&&o&&Um(e),e.flags|=1,vn(t,e,i,a),e.child)}function Cx(t,e,n,i,a){if(t===null){var s=n.type;return typeof s=="function"&&!Lm(s)&&s.defaultProps===void 0&&n.compare===null?(e.tag=15,e.type=s,Xb(t,e,s,i,a)):(t=fu(n.type,null,i,e,e.mode,a),t.ref=e.ref,t.return=e,e.child=t)}if(s=t.child,!eg(t,a)){var l=s.memoizedProps;if(n=n.compare,n=n!==null?n:Lo,n(l,i)&&t.ref===e.ref)return Na(t,e,a)}return e.flags|=1,t=ba(s,i),t.ref=e.ref,t.return=e,e.child=t}function Xb(t,e,n,i,a){if(t!==null){var s=t.memoizedProps;if(Lo(s,i)&&t.ref===e.ref)if(sn=!1,e.pendingProps=i=s,eg(t,a))t.flags&131072&&(sn=!0);else return e.lanes=t.lanes,Na(t,e,a)}return Yf(t,e,n,i,a)}function qb(t,e,n,i){var a=i.children,s=t!==null?t.memoizedState:null;if(t===null&&e.stateNode===null&&(e.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null}),i.mode==="hidden"){if(e.flags&128){if(s=s!==null?s.baseLanes|n:n,t!==null){for(i=e.child=t.child,a=0;i!==null;)a=a|i.lanes|i.childLanes,i=i.sibling;i=a&~s}else i=0,e.child=null;return Rx(t,e,s,n,i)}if(n&536870912)e.memoizedState={baseLanes:0,cachePool:null},t!==null&&pu(e,s!==null?s.cachePool:null),s!==null?vx(e,s):Ff(),ob(e);else return i=e.lanes=536870912,Rx(t,e,s!==null?s.baseLanes|n:n,n,i)}else s!==null?(pu(e,s.cachePool),vx(e,s),Ka(),e.memoizedState=null):(t!==null&&pu(e,null),Ff(),Ka());return vn(t,e,a,n),e.child}function ao(t,e){return t!==null&&t.tag===22||e.stateNode!==null||(e.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null}),e.sibling}function Rx(t,e,n,i,a){var s=zm();return s=s===null?null:{parent:an._currentValue,pool:s},e.memoizedState={baseLanes:n,cachePool:s},t!==null&&pu(e,null),Ff(),ob(e),t!==null&&wl(t,e,i,!0),e.childLanes=a,null}function xu(t,e){return e=$u({mode:e.mode,children:e.children},t.mode),e.ref=t.ref,t.child=e,e.return=t,e}function Dx(t,e,n){return qs(e,t.child,null,n),t=xu(e,e.pendingProps),t.flags|=2,Jn(e),e.memoizedState=null,t}function JE(t,e,n){var i=e.pendingProps,a=(e.flags&128)!==0;if(e.flags&=-129,t===null){if(mt){if(i.mode==="hidden")return t=xu(e,i),e.lanes=536870912,ao(null,t);if(Hf(e),(t=kt)?(t=Iy(t,xi),t=t!==null&&t.data==="&"?t:null,t!==null&&(e.memoizedState={dehydrated:t,treeContext:xs!==null?{id:Vi,overflow:Wi}:null,retryLane:536870912,hydrationErrors:null},n=Q_(t),n.return=e,e.child=n,bn=e,kt=null)):t=null,t===null)throw vs(e);return e.lanes=536870912,null}return xu(e,i)}var s=t.memoizedState;if(s!==null){var l=s.dehydrated;if(Hf(e),a)if(e.flags&256)e.flags&=-257,e=Dx(t,e,n);else if(e.memoizedState!==null)e.child=t.child,e.flags|=128,e=null;else throw Error(fe(558));else if(sn||wl(t,e,n,!1),a=(n&t.childLanes)!==0,sn||a){if(i=Ut,i!==null&&(l=E_(i,n),l!==0&&l!==s.retryLane))throw s.retryLane=l,ar(t,l),Vn(i,t,l),Jm;Ju(),e=Dx(t,e,n)}else t=s.treeContext,kt=yi(l.nextSibling),bn=e,mt=!0,cs=null,xi=!1,t!==null&&eb(e,t),e=xu(e,i),e.flags|=4096;return e}return t=ba(t.child,{mode:i.mode,children:i.children}),t.ref=e.ref,e.child=t,t.return=e,t}function vu(t,e){var n=e.ref;if(n===null)t!==null&&t.ref!==null&&(e.flags|=4194816);else{if(typeof n!="function"&&typeof n!="object")throw Error(fe(284));(t===null||t.ref!==n)&&(e.flags|=4194816)}}function Yf(t,e,n,i,a){return Xs(e),n=Fm(t,e,n,i,void 0,a),i=Hm(),t!==null&&!sn?(Gm(t,e,a),Na(t,e,a)):(mt&&i&&Um(e),e.flags|=1,vn(t,e,n,a),e.child)}function Lx(t,e,n,i,a,s){return Xs(e),e.updateQueue=null,n=ub(e,i,n,a),cb(t),i=Hm(),t!==null&&!sn?(Gm(t,e,s),Na(t,e,s)):(mt&&i&&Um(e),e.flags|=1,vn(t,e,n,s),e.child)}function Ux(t,e,n,i,a){if(Xs(e),e.stateNode===null){var s=Vr,l=n.contextType;typeof l=="object"&&l!==null&&(s=yn(l)),s=new n(i,s),e.memoizedState=s.state!==null&&s.state!==void 0?s.state:null,s.updater=Xf,e.stateNode=s,s._reactInternals=e,s=e.stateNode,s.props=i,s.state=e.memoizedState,s.refs={},km(e),l=n.contextType,s.context=typeof l=="object"&&l!==null?yn(l):Vr,s.state=e.memoizedState,l=n.getDerivedStateFromProps,typeof l=="function"&&(uh(e,n,l,i),s.state=e.memoizedState),typeof n.getDerivedStateFromProps=="function"||typeof s.getSnapshotBeforeUpdate=="function"||typeof s.UNSAFE_componentWillMount!="function"&&typeof s.componentWillMount!="function"||(l=s.state,typeof s.componentWillMount=="function"&&s.componentWillMount(),typeof s.UNSAFE_componentWillMount=="function"&&s.UNSAFE_componentWillMount(),l!==s.state&&Xf.enqueueReplaceState(s,s.state,null),_o(e,i,s,a),vo(),s.state=e.memoizedState),typeof s.componentDidMount=="function"&&(e.flags|=4194308),i=!0}else if(t===null){s=e.stateNode;var o=e.memoizedProps,c=$s(n,o);s.props=c;var u=s.context,h=n.contextType;l=Vr,typeof h=="object"&&h!==null&&(l=yn(h));var g=n.getDerivedStateFromProps;h=typeof g=="function"||typeof s.getSnapshotBeforeUpdate=="function",o=e.pendingProps!==o,h||typeof s.UNSAFE_componentWillReceiveProps!="function"&&typeof s.componentWillReceiveProps!="function"||(o||u!==l)&&Ax(e,s,i,l),qa=!1;var d=e.memoizedState;s.state=d,_o(e,i,s,a),vo(),u=e.memoizedState,o||d!==u||qa?(typeof g=="function"&&(uh(e,n,g,i),u=e.memoizedState),(c=qa||Tx(e,n,c,i,d,u,l))?(h||typeof s.UNSAFE_componentWillMount!="function"&&typeof s.componentWillMount!="function"||(typeof s.componentWillMount=="function"&&s.componentWillMount(),typeof s.UNSAFE_componentWillMount=="function"&&s.UNSAFE_componentWillMount()),typeof s.componentDidMount=="function"&&(e.flags|=4194308)):(typeof s.componentDidMount=="function"&&(e.flags|=4194308),e.memoizedProps=i,e.memoizedState=u),s.props=i,s.state=u,s.context=l,i=c):(typeof s.componentDidMount=="function"&&(e.flags|=4194308),i=!1)}else{s=e.stateNode,If(t,e),l=e.memoizedProps,h=$s(n,l),s.props=h,g=e.pendingProps,d=s.context,u=n.contextType,c=Vr,typeof u=="object"&&u!==null&&(c=yn(u)),o=n.getDerivedStateFromProps,(u=typeof o=="function"||typeof s.getSnapshotBeforeUpdate=="function")||typeof s.UNSAFE_componentWillReceiveProps!="function"&&typeof s.componentWillReceiveProps!="function"||(l!==g||d!==c)&&Ax(e,s,i,c),qa=!1,d=e.memoizedState,s.state=d,_o(e,i,s,a),vo();var p=e.memoizedState;l!==g||d!==p||qa||t!==null&&t.dependencies!==null&&Hu(t.dependencies)?(typeof o=="function"&&(uh(e,n,o,i),p=e.memoizedState),(h=qa||Tx(e,n,h,i,d,p,c)||t!==null&&t.dependencies!==null&&Hu(t.dependencies))?(u||typeof s.UNSAFE_componentWillUpdate!="function"&&typeof s.componentWillUpdate!="function"||(typeof s.componentWillUpdate=="function"&&s.componentWillUpdate(i,p,c),typeof s.UNSAFE_componentWillUpdate=="function"&&s.UNSAFE_componentWillUpdate(i,p,c)),typeof s.componentDidUpdate=="function"&&(e.flags|=4),typeof s.getSnapshotBeforeUpdate=="function"&&(e.flags|=1024)):(typeof s.componentDidUpdate!="function"||l===t.memoizedProps&&d===t.memoizedState||(e.flags|=4),typeof s.getSnapshotBeforeUpdate!="function"||l===t.memoizedProps&&d===t.memoizedState||(e.flags|=1024),e.memoizedProps=i,e.memoizedState=p),s.props=i,s.state=p,s.context=c,i=h):(typeof s.componentDidUpdate!="function"||l===t.memoizedProps&&d===t.memoizedState||(e.flags|=4),typeof s.getSnapshotBeforeUpdate!="function"||l===t.memoizedProps&&d===t.memoizedState||(e.flags|=1024),i=!1)}return s=i,vu(t,e),i=(e.flags&128)!==0,s||i?(s=e.stateNode,n=i&&typeof n.getDerivedStateFromError!="function"?null:s.render(),e.flags|=1,t!==null&&i?(e.child=qs(e,t.child,null,a),e.child=qs(e,null,n,a)):vn(t,e,n,a),e.memoizedState=s.state,t=e.child):t=Na(t,e,a),t}function Ox(t,e,n,i){return Ws(),e.flags|=256,vn(t,e,n,i),e.child}var dh={dehydrated:null,treeContext:null,retryLane:0,hydrationErrors:null};function hh(t){return{baseLanes:t,cachePool:nb()}}function fh(t,e,n){return t=t!==null?t.childLanes&~n:0,e&&(t|=ti),t}function Yb(t,e,n){var i=e.pendingProps,a=!1,s=(e.flags&128)!==0,l;if((l=s)||(l=t!==null&&t.memoizedState===null?!1:(Zt.current&2)!==0),l&&(a=!0,e.flags&=-129),l=(e.flags&32)!==0,e.flags&=-33,t===null){if(mt){if(a?$a(e):Ka(),(t=kt)?(t=Iy(t,xi),t=t!==null&&t.data!=="&"?t:null,t!==null&&(e.memoizedState={dehydrated:t,treeContext:xs!==null?{id:Vi,overflow:Wi}:null,retryLane:536870912,hydrationErrors:null},n=Q_(t),n.return=e,e.child=n,bn=e,kt=null)):t=null,t===null)throw vs(e);return op(t)?e.lanes=32:e.lanes=536870912,null}var o=i.children;return i=i.fallback,a?(Ka(),a=e.mode,o=$u({mode:"hidden",children:o},a),i=Fs(i,a,n,null),o.return=e,i.return=e,o.sibling=i,e.child=o,i=e.child,i.memoizedState=hh(n),i.childLanes=fh(t,l,n),e.memoizedState=dh,ao(null,i)):($a(e),$f(e,o))}var c=t.memoizedState;if(c!==null&&(o=c.dehydrated,o!==null)){if(s)e.flags&256?($a(e),e.flags&=-257,e=ph(t,e,n)):e.memoizedState!==null?(Ka(),e.child=t.child,e.flags|=128,e=null):(Ka(),o=i.fallback,a=e.mode,i=$u({mode:"visible",children:i.children},a),o=Fs(o,a,n,null),o.flags|=2,i.return=e,o.return=e,i.sibling=o,e.child=i,qs(e,t.child,null,n),i=e.child,i.memoizedState=hh(n),i.childLanes=fh(t,l,n),e.memoizedState=dh,e=ao(null,i));else if($a(e),op(o)){if(l=o.nextSibling&&o.nextSibling.dataset,l)var u=l.dgst;l=u,i=Error(fe(419)),i.stack="",i.digest=l,Oo({value:i,source:null,stack:null}),e=ph(t,e,n)}else if(sn||wl(t,e,n,!1),l=(n&t.childLanes)!==0,sn||l){if(l=Ut,l!==null&&(i=E_(l,n),i!==0&&i!==c.retryLane))throw c.retryLane=i,ar(t,i),Vn(l,t,i),Jm;lp(o)||Ju(),e=ph(t,e,n)}else lp(o)?(e.flags|=192,e.child=t.child,e=null):(t=c.treeContext,kt=yi(o.nextSibling),bn=e,mt=!0,cs=null,xi=!1,t!==null&&eb(e,t),e=$f(e,i.children),e.flags|=4096);return e}return a?(Ka(),o=i.fallback,a=e.mode,c=t.child,u=c.sibling,i=ba(c,{mode:"hidden",children:i.children}),i.subtreeFlags=c.subtreeFlags&65011712,u!==null?o=ba(u,o):(o=Fs(o,a,n,null),o.flags|=2),o.return=e,i.return=e,i.sibling=o,e.child=i,ao(null,i),i=e.child,o=t.child.memoizedState,o===null?o=hh(n):(a=o.cachePool,a!==null?(c=an._currentValue,a=a.parent!==c?{parent:c,pool:c}:a):a=nb(),o={baseLanes:o.baseLanes|n,cachePool:a}),i.memoizedState=o,i.childLanes=fh(t,l,n),e.memoizedState=dh,ao(t.child,i)):($a(e),n=t.child,t=n.sibling,n=ba(n,{mode:"visible",children:i.children}),n.return=e,n.sibling=null,t!==null&&(l=e.deletions,l===null?(e.deletions=[t],e.flags|=16):l.push(t)),e.child=n,e.memoizedState=null,n)}function $f(t,e){return e=$u({mode:"visible",children:e},t.mode),e.return=t,t.child=e}function $u(t,e){return t=ei(22,t,null,e),t.lanes=0,t}function ph(t,e,n){return qs(e,t.child,null,n),t=$f(e,e.pendingProps.children),t.flags|=2,e.memoizedState=null,t}function Px(t,e,n){t.lanes|=e;var i=t.alternate;i!==null&&(i.lanes|=e),zf(t.return,e,n)}function mh(t,e,n,i,a,s){var l=t.memoizedState;l===null?t.memoizedState={isBackwards:e,rendering:null,renderingStartTime:0,last:i,tail:n,tailMode:a,treeForkCount:s}:(l.isBackwards=e,l.rendering=null,l.renderingStartTime=0,l.last=i,l.tail=n,l.tailMode=a,l.treeForkCount=s)}function $b(t,e,n){var i=e.pendingProps,a=i.revealOrder,s=i.tail;i=i.children;var l=Zt.current,o=(l&2)!==0;if(o?(l=l&1|2,e.flags|=128):l&=1,Pt(Zt,l),vn(t,e,i,n),i=mt?Uo:0,!o&&t!==null&&t.flags&128)e:for(t=e.child;t!==null;){if(t.tag===13)t.memoizedState!==null&&Px(t,n,e);else if(t.tag===19)Px(t,n,e);else if(t.child!==null){t.child.return=t,t=t.child;continue}if(t===e)break e;for(;t.sibling===null;){if(t.return===null||t.return===e)break e;t=t.return}t.sibling.return=t.return,t=t.sibling}switch(a){case"forwards":for(n=e.child,a=null;n!==null;)t=n.alternate,t!==null&&Wu(t)===null&&(a=n),n=n.sibling;n=a,n===null?(a=e.child,e.child=null):(a=n.sibling,n.sibling=null),mh(e,!1,a,n,s,i);break;case"backwards":case"unstable_legacy-backwards":for(n=null,a=e.child,e.child=null;a!==null;){if(t=a.alternate,t!==null&&Wu(t)===null){e.child=a;break}t=a.sibling,a.sibling=n,n=a,a=t}mh(e,!0,n,null,s,i);break;case"together":mh(e,!1,null,null,void 0,i);break;default:e.memoizedState=null}return e.child}function Na(t,e,n){if(t!==null&&(e.dependencies=t.dependencies),bs|=e.lanes,!(n&e.childLanes))if(t!==null){if(wl(t,e,n,!1),(n&e.childLanes)===0)return null}else return null;if(t!==null&&e.child!==t.child)throw Error(fe(153));if(e.child!==null){for(t=e.child,n=ba(t,t.pendingProps),e.child=n,n.return=e;t.sibling!==null;)t=t.sibling,n=n.sibling=ba(t,t.pendingProps),n.return=e;n.sibling=null}return e.child}function eg(t,e){return t.lanes&e?!0:(t=t.dependencies,!!(t!==null&&Hu(t)))}function eT(t,e,n){switch(e.tag){case 3:Pu(e,e.stateNode.containerInfo),Ya(e,an,t.memoizedState.cache),Ws();break;case 27:case 5:Mf(e);break;case 4:Pu(e,e.stateNode.containerInfo);break;case 10:Ya(e,e.type,e.memoizedProps.value);break;case 31:if(e.memoizedState!==null)return e.flags|=128,Hf(e),null;break;case 13:var i=e.memoizedState;if(i!==null)return i.dehydrated!==null?($a(e),e.flags|=128,null):n&e.child.childLanes?Yb(t,e,n):($a(e),t=Na(t,e,n),t!==null?t.sibling:null);$a(e);break;case 19:var a=(t.flags&128)!==0;if(i=(n&e.childLanes)!==0,i||(wl(t,e,n,!1),i=(n&e.childLanes)!==0),a){if(i)return $b(t,e,n);e.flags|=128}if(a=e.memoizedState,a!==null&&(a.rendering=null,a.tail=null,a.lastEffect=null),Pt(Zt,Zt.current),i)break;return null;case 22:return e.lanes=0,qb(t,e,n,e.pendingProps);case 24:Ya(e,an,t.memoizedState.cache)}return Na(t,e,n)}function Kb(t,e,n){if(t!==null)if(t.memoizedProps!==e.pendingProps)sn=!0;else{if(!eg(t,n)&&!(e.flags&128))return sn=!1,eT(t,e,n);sn=!!(t.flags&131072)}else sn=!1,mt&&e.flags&1048576&&J_(e,Uo,e.index);switch(e.lanes=0,e.tag){case 16:e:{var i=e.pendingProps;if(t=Us(e.elementType),e.type=t,typeof t=="function")Lm(t)?(i=$s(t,i),e.tag=1,e=Ux(null,e,t,i,n)):(e.tag=0,e=Yf(null,e,t,i,n));else{if(t!=null){var a=t.$$typeof;if(a===vm){e.tag=11,e=Nx(null,e,t,i,n);break e}else if(a===_m){e.tag=14,e=Cx(null,e,t,i,n);break e}}throw e=yf(t)||t,Error(fe(306,e,""))}}return e;case 0:return Yf(t,e,e.type,e.pendingProps,n);case 1:return i=e.type,a=$s(i,e.pendingProps),Ux(t,e,i,a,n);case 3:e:{if(Pu(e,e.stateNode.containerInfo),t===null)throw Error(fe(387));i=e.pendingProps;var s=e.memoizedState;a=s.element,If(t,e),_o(e,i,null,n);var l=e.memoizedState;if(i=l.cache,Ya(e,an,i),i!==s.cache&&jf(e,[an],n,!0),vo(),i=l.element,s.isDehydrated)if(s={element:i,isDehydrated:!1,cache:l.cache},e.updateQueue.baseState=s,e.memoizedState=s,e.flags&256){e=Ox(t,e,i,n);break e}else if(i!==a){a=gi(Error(fe(424)),e),Oo(a),e=Ox(t,e,i,n);break e}else{switch(t=e.stateNode.containerInfo,t.nodeType){case 9:t=t.body;break;default:t=t.nodeName==="HTML"?t.ownerDocument.body:t}for(kt=yi(t.firstChild),bn=e,mt=!0,cs=null,xi=!0,n=sb(e,null,i,n),e.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling}else{if(Ws(),i===a){e=Na(t,e,n);break e}vn(t,e,i,n)}e=e.child}return e;case 26:return vu(t,e),t===null?(n=tv(e.type,null,e.pendingProps,null))?e.memoizedState=n:mt||(n=e.type,t=e.pendingProps,i=id(os.current).createElement(n),i[_n]=e,i[Xn]=t,Mn(i,n,t),mn(i),e.stateNode=i):e.memoizedState=tv(e.type,t.memoizedProps,e.pendingProps,t.memoizedState),null;case 27:return Mf(e),t===null&&mt&&(i=e.stateNode=By(e.type,e.pendingProps,os.current),bn=e,xi=!0,a=kt,Ms(e.type)?(cp=a,kt=yi(i.firstChild)):kt=a),vn(t,e,e.pendingProps.children,n),vu(t,e),t===null&&(e.flags|=4194304),e.child;case 5:return t===null&&mt&&((a=i=kt)&&(i=RT(i,e.type,e.pendingProps,xi),i!==null?(e.stateNode=i,bn=e,kt=yi(i.firstChild),xi=!1,a=!0):a=!1),a||vs(e)),Mf(e),a=e.type,s=e.pendingProps,l=t!==null?t.memoizedProps:null,i=s.children,sp(a,s)?i=null:l!==null&&sp(a,l)&&(e.flags|=32),e.memoizedState!==null&&(a=Fm(t,e,WE,null,null,n),Bo._currentValue=a),vu(t,e),vn(t,e,i,n),e.child;case 6:return t===null&&mt&&((t=n=kt)&&(n=DT(n,e.pendingProps,xi),n!==null?(e.stateNode=n,bn=e,kt=null,t=!0):t=!1),t||vs(e)),null;case 13:return Yb(t,e,n);case 4:return Pu(e,e.stateNode.containerInfo),i=e.pendingProps,t===null?e.child=qs(e,null,i,n):vn(t,e,i,n),e.child;case 11:return Nx(t,e,e.type,e.pendingProps,n);case 7:return vn(t,e,e.pendingProps,n),e.child;case 8:return vn(t,e,e.pendingProps.children,n),e.child;case 12:return vn(t,e,e.pendingProps.children,n),e.child;case 10:return i=e.pendingProps,Ya(e,e.type,i.value),vn(t,e,i.children,n),e.child;case 9:return a=e.type._context,i=e.pendingProps.children,Xs(e),a=yn(a),i=i(a),e.flags|=1,vn(t,e,i,n),e.child;case 14:return Cx(t,e,e.type,e.pendingProps,n);case 15:return Xb(t,e,e.type,e.pendingProps,n);case 19:return $b(t,e,n);case 31:return JE(t,e,n);case 22:return qb(t,e,n,e.pendingProps);case 24:return Xs(e),i=yn(an),t===null?(a=zm(),a===null&&(a=Ut,s=Pm(),a.pooledCache=s,s.refCount++,s!==null&&(a.pooledCacheLanes|=n),a=s),e.memoizedState={parent:i,cache:a},km(e),Ya(e,an,a)):(t.lanes&n&&(If(t,e),_o(e,null,null,n),vo()),a=t.memoizedState,s=e.memoizedState,a.parent!==i?(a={parent:i,cache:i},e.memoizedState=a,e.lanes===0&&(e.memoizedState=e.updateQueue.baseState=a),Ya(e,an,i)):(i=s.cache,Ya(e,an,i),i!==a.cache&&jf(e,[an],n,!0))),vn(t,e,e.pendingProps.children,n),e.child;case 29:throw e.pendingProps}throw Error(fe(156,e.tag))}function ia(t){t.flags|=4}function gh(t,e,n,i,a){if((e=(t.mode&32)!==0)&&(e=!1),e){if(t.flags|=16777216,(a&335544128)===a)if(t.stateNode.complete)t.flags|=8192;else if(by())t.flags|=8192;else throw Gs=Gu,jm}else t.flags&=-16777217}function zx(t,e){if(e.type!=="stylesheet"||e.state.loading&4)t.flags&=-16777217;else if(t.flags|=16777216,!Gy(e))if(by())t.flags|=8192;else throw Gs=Gu,jm}function yc(t,e){e!==null&&(t.flags|=4),t.flags&16384&&(e=t.tag!==22?y_():536870912,t.lanes|=e,fl|=e)}function Bl(t,e){if(!mt)switch(t.tailMode){case"hidden":e=t.tail;for(var n=null;e!==null;)e.alternate!==null&&(n=e),e=e.sibling;n===null?t.tail=null:n.sibling=null;break;case"collapsed":n=t.tail;for(var i=null;n!==null;)n.alternate!==null&&(i=n),n=n.sibling;i===null?e||t.tail===null?t.tail=null:t.tail.sibling=null:i.sibling=null}}function jt(t){var e=t.alternate!==null&&t.alternate.child===t.child,n=0,i=0;if(e)for(var a=t.child;a!==null;)n|=a.lanes|a.childLanes,i|=a.subtreeFlags&65011712,i|=a.flags&65011712,a.return=t,a=a.sibling;else for(a=t.child;a!==null;)n|=a.lanes|a.childLanes,i|=a.subtreeFlags,i|=a.flags,a.return=t,a=a.sibling;return t.subtreeFlags|=i,t.childLanes=n,e}function tT(t,e,n){var i=e.pendingProps;switch(Om(e),e.tag){case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return jt(e),null;case 1:return jt(e),null;case 3:return n=e.stateNode,i=null,t!==null&&(i=t.memoizedState.cache),e.memoizedState.cache!==i&&(e.flags|=2048),ya(an),ll(),n.pendingContext&&(n.context=n.pendingContext,n.pendingContext=null),(t===null||t.child===null)&&(fr(e)?ia(e):t===null||t.memoizedState.isDehydrated&&!(e.flags&256)||(e.flags|=1024,lh())),jt(e),null;case 26:var a=e.type,s=e.memoizedState;return t===null?(ia(e),s!==null?(jt(e),zx(e,s)):(jt(e),gh(e,a,null,i,n))):s?s!==t.memoizedState?(ia(e),jt(e),zx(e,s)):(jt(e),e.flags&=-16777217):(t=t.memoizedProps,t!==i&&ia(e),jt(e),gh(e,a,t,i,n)),null;case 27:if(zu(e),n=os.current,a=e.type,t!==null&&e.stateNode!=null)t.memoizedProps!==i&&ia(e);else{if(!i){if(e.stateNode===null)throw Error(fe(166));return jt(e),null}t=Yi.current,fr(e)?dx(e):(t=By(a,i,n),e.stateNode=t,ia(e))}return jt(e),null;case 5:if(zu(e),a=e.type,t!==null&&e.stateNode!=null)t.memoizedProps!==i&&ia(e);else{if(!i){if(e.stateNode===null)throw Error(fe(166));return jt(e),null}if(s=Yi.current,fr(e))dx(e);else{var l=id(os.current);switch(s){case 1:s=l.createElementNS("http://www.w3.org/2000/svg",a);break;case 2:s=l.createElementNS("http://www.w3.org/1998/Math/MathML",a);break;default:switch(a){case"svg":s=l.createElementNS("http://www.w3.org/2000/svg",a);break;case"math":s=l.createElementNS("http://www.w3.org/1998/Math/MathML",a);break;case"script":s=l.createElement("div"),s.innerHTML="<script><\/script>",s=s.removeChild(s.firstChild);break;case"select":s=typeof i.is=="string"?l.createElement("select",{is:i.is}):l.createElement("select"),i.multiple?s.multiple=!0:i.size&&(s.size=i.size);break;default:s=typeof i.is=="string"?l.createElement(a,{is:i.is}):l.createElement(a)}}s[_n]=e,s[Xn]=i;e:for(l=e.child;l!==null;){if(l.tag===5||l.tag===6)s.appendChild(l.stateNode);else if(l.tag!==4&&l.tag!==27&&l.child!==null){l.child.return=l,l=l.child;continue}if(l===e)break e;for(;l.sibling===null;){if(l.return===null||l.return===e)break e;l=l.return}l.sibling.return=l.return,l=l.sibling}e.stateNode=s;e:switch(Mn(s,a,i),a){case"button":case"input":case"select":case"textarea":i=!!i.autoFocus;break e;case"img":i=!0;break e;default:i=!1}i&&ia(e)}}return jt(e),gh(e,e.type,t===null?null:t.memoizedProps,e.pendingProps,n),null;case 6:if(t&&e.stateNode!=null)t.memoizedProps!==i&&ia(e);else{if(typeof i!="string"&&e.stateNode===null)throw Error(fe(166));if(t=os.current,fr(e)){if(t=e.stateNode,n=e.memoizedProps,i=null,a=bn,a!==null)switch(a.tag){case 27:case 5:i=a.memoizedProps}t[_n]=e,t=!!(t.nodeValue===n||i!==null&&i.suppressHydrationWarning===!0||zy(t.nodeValue,n)),t||vs(e,!0)}else t=id(t).createTextNode(i),t[_n]=e,e.stateNode=t}return jt(e),null;case 31:if(n=e.memoizedState,t===null||t.memoizedState!==null){if(i=fr(e),n!==null){if(t===null){if(!i)throw Error(fe(318));if(t=e.memoizedState,t=t!==null?t.dehydrated:null,!t)throw Error(fe(557));t[_n]=e}else Ws(),!(e.flags&128)&&(e.memoizedState=null),e.flags|=4;jt(e),t=!1}else n=lh(),t!==null&&t.memoizedState!==null&&(t.memoizedState.hydrationErrors=n),t=!0;if(!t)return e.flags&256?(Jn(e),e):(Jn(e),null);if(e.flags&128)throw Error(fe(558))}return jt(e),null;case 13:if(i=e.memoizedState,t===null||t.memoizedState!==null&&t.memoizedState.dehydrated!==null){if(a=fr(e),i!==null&&i.dehydrated!==null){if(t===null){if(!a)throw Error(fe(318));if(a=e.memoizedState,a=a!==null?a.dehydrated:null,!a)throw Error(fe(317));a[_n]=e}else Ws(),!(e.flags&128)&&(e.memoizedState=null),e.flags|=4;jt(e),a=!1}else a=lh(),t!==null&&t.memoizedState!==null&&(t.memoizedState.hydrationErrors=a),a=!0;if(!a)return e.flags&256?(Jn(e),e):(Jn(e),null)}return Jn(e),e.flags&128?(e.lanes=n,e):(n=i!==null,t=t!==null&&t.memoizedState!==null,n&&(i=e.child,a=null,i.alternate!==null&&i.alternate.memoizedState!==null&&i.alternate.memoizedState.cachePool!==null&&(a=i.alternate.memoizedState.cachePool.pool),s=null,i.memoizedState!==null&&i.memoizedState.cachePool!==null&&(s=i.memoizedState.cachePool.pool),s!==a&&(i.flags|=2048)),n!==t&&n&&(e.child.flags|=8192),yc(e,e.updateQueue),jt(e),null);case 4:return ll(),t===null&&lg(e.stateNode.containerInfo),jt(e),null;case 10:return ya(e.type),jt(e),null;case 19:if(xn(Zt),i=e.memoizedState,i===null)return jt(e),null;if(a=(e.flags&128)!==0,s=i.rendering,s===null)if(a)Bl(i,!1);else{if(qt!==0||t!==null&&t.flags&128)for(t=e.child;t!==null;){if(s=Wu(t),s!==null){for(e.flags|=128,Bl(i,!1),t=s.updateQueue,e.updateQueue=t,yc(e,t),e.subtreeFlags=0,t=n,n=e.child;n!==null;)Z_(n,t),n=n.sibling;return Pt(Zt,Zt.current&1|2),mt&&fa(e,i.treeForkCount),e.child}t=t.sibling}i.tail!==null&&ni()>Zu&&(e.flags|=128,a=!0,Bl(i,!1),e.lanes=4194304)}else{if(!a)if(t=Wu(s),t!==null){if(e.flags|=128,a=!0,t=t.updateQueue,e.updateQueue=t,yc(e,t),Bl(i,!0),i.tail===null&&i.tailMode==="hidden"&&!s.alternate&&!mt)return jt(e),null}else 2*ni()-i.renderingStartTime>Zu&&n!==536870912&&(e.flags|=128,a=!0,Bl(i,!1),e.lanes=4194304);i.isBackwards?(s.sibling=e.child,e.child=s):(t=i.last,t!==null?t.sibling=s:e.child=s,i.last=s)}return i.tail!==null?(t=i.tail,i.rendering=t,i.tail=t.sibling,i.renderingStartTime=ni(),t.sibling=null,n=Zt.current,Pt(Zt,a?n&1|2:n&1),mt&&fa(e,i.treeForkCount),t):(jt(e),null);case 22:case 23:return Jn(e),Im(),i=e.memoizedState!==null,t!==null?t.memoizedState!==null!==i&&(e.flags|=8192):i&&(e.flags|=8192),i?n&536870912&&!(e.flags&128)&&(jt(e),e.subtreeFlags&6&&(e.flags|=8192)):jt(e),n=e.updateQueue,n!==null&&yc(e,n.retryQueue),n=null,t!==null&&t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(n=t.memoizedState.cachePool.pool),i=null,e.memoizedState!==null&&e.memoizedState.cachePool!==null&&(i=e.memoizedState.cachePool.pool),i!==n&&(e.flags|=2048),t!==null&&xn(Hs),null;case 24:return n=null,t!==null&&(n=t.memoizedState.cache),e.memoizedState.cache!==n&&(e.flags|=2048),ya(an),jt(e),null;case 25:return null;case 30:return null}throw Error(fe(156,e.tag))}function nT(t,e){switch(Om(e),e.tag){case 1:return t=e.flags,t&65536?(e.flags=t&-65537|128,e):null;case 3:return ya(an),ll(),t=e.flags,t&65536&&!(t&128)?(e.flags=t&-65537|128,e):null;case 26:case 27:case 5:return zu(e),null;case 31:if(e.memoizedState!==null){if(Jn(e),e.alternate===null)throw Error(fe(340));Ws()}return t=e.flags,t&65536?(e.flags=t&-65537|128,e):null;case 13:if(Jn(e),t=e.memoizedState,t!==null&&t.dehydrated!==null){if(e.alternate===null)throw Error(fe(340));Ws()}return t=e.flags,t&65536?(e.flags=t&-65537|128,e):null;case 19:return xn(Zt),null;case 4:return ll(),null;case 10:return ya(e.type),null;case 22:case 23:return Jn(e),Im(),t!==null&&xn(Hs),t=e.flags,t&65536?(e.flags=t&-65537|128,e):null;case 24:return ya(an),null;case 25:return null;default:return null}}function Zb(t,e){switch(Om(e),e.tag){case 3:ya(an),ll();break;case 26:case 27:case 5:zu(e);break;case 4:ll();break;case 31:e.memoizedState!==null&&Jn(e);break;case 13:Jn(e);break;case 19:xn(Zt);break;case 10:ya(e.type);break;case 22:case 23:Jn(e),Im(),t!==null&&xn(Hs);break;case 24:ya(an)}}function nc(t,e){try{var n=e.updateQueue,i=n!==null?n.lastEffect:null;if(i!==null){var a=i.next;n=a;do{if((n.tag&t)===t){i=void 0;var s=n.create,l=n.inst;i=s(),l.destroy=i}n=n.next}while(n!==a)}}catch(o){At(e,e.return,o)}}function _s(t,e,n){try{var i=e.updateQueue,a=i!==null?i.lastEffect:null;if(a!==null){var s=a.next;i=s;do{if((i.tag&t)===t){var l=i.inst,o=l.destroy;if(o!==void 0){l.destroy=void 0,a=e;var c=n,u=o;try{u()}catch(h){At(a,c,h)}}}i=i.next}while(i!==s)}}catch(h){At(e,e.return,h)}}function Qb(t){var e=t.updateQueue;if(e!==null){var n=t.stateNode;try{lb(e,n)}catch(i){At(t,t.return,i)}}}function Jb(t,e,n){n.props=$s(t.type,t.memoizedProps),n.state=t.memoizedState;try{n.componentWillUnmount()}catch(i){At(t,e,i)}}function yo(t,e){try{var n=t.ref;if(n!==null){switch(t.tag){case 26:case 27:case 5:var i=t.stateNode;break;case 30:i=t.stateNode;break;default:i=t.stateNode}typeof n=="function"?t.refCleanup=n(i):n.current=i}}catch(a){At(t,e,a)}}function Xi(t,e){var n=t.ref,i=t.refCleanup;if(n!==null)if(typeof i=="function")try{i()}catch(a){At(t,e,a)}finally{t.refCleanup=null,t=t.alternate,t!=null&&(t.refCleanup=null)}else if(typeof n=="function")try{n(null)}catch(a){At(t,e,a)}else n.current=null}function ey(t){var e=t.type,n=t.memoizedProps,i=t.stateNode;try{e:switch(e){case"button":case"input":case"select":case"textarea":n.autoFocus&&i.focus();break e;case"img":n.src?i.src=n.src:n.srcSet&&(i.srcset=n.srcSet)}}catch(a){At(t,t.return,a)}}function xh(t,e,n){try{var i=t.stateNode;ET(i,t.type,n,e),i[Xn]=e}catch(a){At(t,t.return,a)}}function ty(t){return t.tag===5||t.tag===3||t.tag===26||t.tag===27&&Ms(t.type)||t.tag===4}function vh(t){e:for(;;){for(;t.sibling===null;){if(t.return===null||ty(t.return))return null;t=t.return}for(t.sibling.return=t.return,t=t.sibling;t.tag!==5&&t.tag!==6&&t.tag!==18;){if(t.tag===27&&Ms(t.type)||t.flags&2||t.child===null||t.tag===4)continue e;t.child.return=t,t=t.child}if(!(t.flags&2))return t.stateNode}}function Kf(t,e,n){var i=t.tag;if(i===5||i===6)t=t.stateNode,e?(n.nodeType===9?n.body:n.nodeName==="HTML"?n.ownerDocument.body:n).insertBefore(t,e):(e=n.nodeType===9?n.body:n.nodeName==="HTML"?n.ownerDocument.body:n,e.appendChild(t),n=n._reactRootContainer,n!=null||e.onclick!==null||(e.onclick=xa));else if(i!==4&&(i===27&&Ms(t.type)&&(n=t.stateNode,e=null),t=t.child,t!==null))for(Kf(t,e,n),t=t.sibling;t!==null;)Kf(t,e,n),t=t.sibling}function Ku(t,e,n){var i=t.tag;if(i===5||i===6)t=t.stateNode,e?n.insertBefore(t,e):n.appendChild(t);else if(i!==4&&(i===27&&Ms(t.type)&&(n=t.stateNode),t=t.child,t!==null))for(Ku(t,e,n),t=t.sibling;t!==null;)Ku(t,e,n),t=t.sibling}function ny(t){var e=t.stateNode,n=t.memoizedProps;try{for(var i=t.type,a=e.attributes;a.length;)e.removeAttributeNode(a[0]);Mn(e,i,n),e[_n]=t,e[Xn]=n}catch(s){At(t,t.return,s)}}var pa=!1,nn=!1,_h=!1,jx=typeof WeakSet=="function"?WeakSet:Set,pn=null;function iT(t,e){if(t=t.containerInfo,ip=ld,t=G_(t),Cm(t)){if("selectionStart"in t)var n={start:t.selectionStart,end:t.selectionEnd};else e:{n=(n=t.ownerDocument)&&n.defaultView||window;var i=n.getSelection&&n.getSelection();if(i&&i.rangeCount!==0){n=i.anchorNode;var a=i.anchorOffset,s=i.focusNode;i=i.focusOffset;try{n.nodeType,s.nodeType}catch{n=null;break e}var l=0,o=-1,c=-1,u=0,h=0,g=t,d=null;t:for(;;){for(var p;g!==n||a!==0&&g.nodeType!==3||(o=l+a),g!==s||i!==0&&g.nodeType!==3||(c=l+i),g.nodeType===3&&(l+=g.nodeValue.length),(p=g.firstChild)!==null;)d=g,g=p;for(;;){if(g===t)break t;if(d===n&&++u===a&&(o=l),d===s&&++h===i&&(c=l),(p=g.nextSibling)!==null)break;g=d,d=g.parentNode}g=p}n=o===-1||c===-1?null:{start:o,end:c}}else n=null}n=n||{start:0,end:0}}else n=null;for(ap={focusedElem:t,selectionRange:n},ld=!1,pn=e;pn!==null;)if(e=pn,t=e.child,(e.subtreeFlags&1028)!==0&&t!==null)t.return=e,pn=t;else for(;pn!==null;){switch(e=pn,s=e.alternate,t=e.flags,e.tag){case 0:if(t&4&&(t=e.updateQueue,t=t!==null?t.events:null,t!==null))for(n=0;n<t.length;n++)a=t[n],a.ref.impl=a.nextImpl;break;case 11:case 15:break;case 1:if(t&1024&&s!==null){t=void 0,n=e,a=s.memoizedProps,s=s.memoizedState,i=n.stateNode;try{var x=$s(n.type,a);t=i.getSnapshotBeforeUpdate(x,s),i.__reactInternalSnapshotBeforeUpdate=t}catch(_){At(n,n.return,_)}}break;case 3:if(t&1024){if(t=e.stateNode.containerInfo,n=t.nodeType,n===9)rp(t);else if(n===1)switch(t.nodeName){case"HEAD":case"HTML":case"BODY":rp(t);break;default:t.textContent=""}}break;case 5:case 26:case 27:case 6:case 4:case 17:break;default:if(t&1024)throw Error(fe(163))}if(t=e.sibling,t!==null){t.return=e.return,pn=t;break}pn=e.return}}function iy(t,e,n){var i=n.flags;switch(n.tag){case 0:case 11:case 15:sa(t,n),i&4&&nc(5,n);break;case 1:if(sa(t,n),i&4)if(t=n.stateNode,e===null)try{t.componentDidMount()}catch(l){At(n,n.return,l)}else{var a=$s(n.type,e.memoizedProps);e=e.memoizedState;try{t.componentDidUpdate(a,e,t.__reactInternalSnapshotBeforeUpdate)}catch(l){At(n,n.return,l)}}i&64&&Qb(n),i&512&&yo(n,n.return);break;case 3:if(sa(t,n),i&64&&(t=n.updateQueue,t!==null)){if(e=null,n.child!==null)switch(n.child.tag){case 27:case 5:e=n.child.stateNode;break;case 1:e=n.child.stateNode}try{lb(t,e)}catch(l){At(n,n.return,l)}}break;case 27:e===null&&i&4&&ny(n);case 26:case 5:sa(t,n),e===null&&i&4&&ey(n),i&512&&yo(n,n.return);break;case 12:sa(t,n);break;case 31:sa(t,n),i&4&&ry(t,n);break;case 13:sa(t,n),i&4&&ly(t,n),i&64&&(t=n.memoizedState,t!==null&&(t=t.dehydrated,t!==null&&(n=hT.bind(null,n),LT(t,n))));break;case 22:if(i=n.memoizedState!==null||pa,!i){e=e!==null&&e.memoizedState!==null||nn,a=pa;var s=nn;pa=i,(nn=e)&&!s?da(t,n,(n.subtreeFlags&8772)!==0):sa(t,n),pa=a,nn=s}break;case 30:break;default:sa(t,n)}}function ay(t){var e=t.alternate;e!==null&&(t.alternate=null,ay(e)),t.child=null,t.deletions=null,t.sibling=null,t.tag===5&&(e=t.stateNode,e!==null&&Mm(e)),t.stateNode=null,t.return=null,t.dependencies=null,t.memoizedProps=null,t.memoizedState=null,t.pendingProps=null,t.stateNode=null,t.updateQueue=null}var Gt=null,Bn=!1;function aa(t,e,n){for(n=n.child;n!==null;)sy(t,e,n),n=n.sibling}function sy(t,e,n){if(ii&&typeof ii.onCommitFiberUnmount=="function")try{ii.onCommitFiberUnmount($o,n)}catch{}switch(n.tag){case 26:nn||Xi(n,e),aa(t,e,n),n.memoizedState?n.memoizedState.count--:n.stateNode&&(n=n.stateNode,n.parentNode.removeChild(n));break;case 27:nn||Xi(n,e);var i=Gt,a=Bn;Ms(n.type)&&(Gt=n.stateNode,Bn=!1),aa(t,e,n),To(n.stateNode),Gt=i,Bn=a;break;case 5:nn||Xi(n,e);case 6:if(i=Gt,a=Bn,Gt=null,aa(t,e,n),Gt=i,Bn=a,Gt!==null)if(Bn)try{(Gt.nodeType===9?Gt.body:Gt.nodeName==="HTML"?Gt.ownerDocument.body:Gt).removeChild(n.stateNode)}catch(s){At(n,e,s)}else try{Gt.removeChild(n.stateNode)}catch(s){At(n,e,s)}break;case 18:Gt!==null&&(Bn?(t=Gt,Kx(t.nodeType===9?t.body:t.nodeName==="HTML"?t.ownerDocument.body:t,n.stateNode),xl(t)):Kx(Gt,n.stateNode));break;case 4:i=Gt,a=Bn,Gt=n.stateNode.containerInfo,Bn=!0,aa(t,e,n),Gt=i,Bn=a;break;case 0:case 11:case 14:case 15:_s(2,n,e),nn||_s(4,n,e),aa(t,e,n);break;case 1:nn||(Xi(n,e),i=n.stateNode,typeof i.componentWillUnmount=="function"&&Jb(n,e,i)),aa(t,e,n);break;case 21:aa(t,e,n);break;case 22:nn=(i=nn)||n.memoizedState!==null,aa(t,e,n),nn=i;break;default:aa(t,e,n)}}function ry(t,e){if(e.memoizedState===null&&(t=e.alternate,t!==null&&(t=t.memoizedState,t!==null))){t=t.dehydrated;try{xl(t)}catch(n){At(e,e.return,n)}}}function ly(t,e){if(e.memoizedState===null&&(t=e.alternate,t!==null&&(t=t.memoizedState,t!==null&&(t=t.dehydrated,t!==null))))try{xl(t)}catch(n){At(e,e.return,n)}}function aT(t){switch(t.tag){case 31:case 13:case 19:var e=t.stateNode;return e===null&&(e=t.stateNode=new jx),e;case 22:return t=t.stateNode,e=t._retryCache,e===null&&(e=t._retryCache=new jx),e;default:throw Error(fe(435,t.tag))}}function Sc(t,e){var n=aT(t);e.forEach(function(i){if(!n.has(i)){n.add(i);var a=fT.bind(null,t,i);i.then(a,a)}})}function jn(t,e){var n=e.deletions;if(n!==null)for(var i=0;i<n.length;i++){var a=n[i],s=t,l=e,o=l;e:for(;o!==null;){switch(o.tag){case 27:if(Ms(o.type)){Gt=o.stateNode,Bn=!1;break e}break;case 5:Gt=o.stateNode,Bn=!1;break e;case 3:case 4:Gt=o.stateNode.containerInfo,Bn=!0;break e}o=o.return}if(Gt===null)throw Error(fe(160));sy(s,l,a),Gt=null,Bn=!1,s=a.alternate,s!==null&&(s.return=null),a.return=null}if(e.subtreeFlags&13886)for(e=e.child;e!==null;)oy(e,t),e=e.sibling}var Ri=null;function oy(t,e){var n=t.alternate,i=t.flags;switch(t.tag){case 0:case 11:case 14:case 15:jn(e,t),kn(t),i&4&&(_s(3,t,t.return),nc(3,t),_s(5,t,t.return));break;case 1:jn(e,t),kn(t),i&512&&(nn||n===null||Xi(n,n.return)),i&64&&pa&&(t=t.updateQueue,t!==null&&(i=t.callbacks,i!==null&&(n=t.shared.hiddenCallbacks,t.shared.hiddenCallbacks=n===null?i:n.concat(i))));break;case 26:var a=Ri;if(jn(e,t),kn(t),i&512&&(nn||n===null||Xi(n,n.return)),i&4){var s=n!==null?n.memoizedState:null;if(i=t.memoizedState,n===null)if(i===null)if(t.stateNode===null){e:{i=t.type,n=t.memoizedProps,a=a.ownerDocument||a;t:switch(i){case"title":s=a.getElementsByTagName("title")[0],(!s||s[Qo]||s[_n]||s.namespaceURI==="http://www.w3.org/2000/svg"||s.hasAttribute("itemprop"))&&(s=a.createElement(i),a.head.insertBefore(s,a.querySelector("head > title"))),Mn(s,i,n),s[_n]=t,mn(s),i=s;break e;case"link":var l=iv("link","href",a).get(i+(n.href||""));if(l){for(var o=0;o<l.length;o++)if(s=l[o],s.getAttribute("href")===(n.href==null||n.href===""?null:n.href)&&s.getAttribute("rel")===(n.rel==null?null:n.rel)&&s.getAttribute("title")===(n.title==null?null:n.title)&&s.getAttribute("crossorigin")===(n.crossOrigin==null?null:n.crossOrigin)){l.splice(o,1);break t}}s=a.createElement(i),Mn(s,i,n),a.head.appendChild(s);break;case"meta":if(l=iv("meta","content",a).get(i+(n.content||""))){for(o=0;o<l.length;o++)if(s=l[o],s.getAttribute("content")===(n.content==null?null:""+n.content)&&s.getAttribute("name")===(n.name==null?null:n.name)&&s.getAttribute("property")===(n.property==null?null:n.property)&&s.getAttribute("http-equiv")===(n.httpEquiv==null?null:n.httpEquiv)&&s.getAttribute("charset")===(n.charSet==null?null:n.charSet)){l.splice(o,1);break t}}s=a.createElement(i),Mn(s,i,n),a.head.appendChild(s);break;default:throw Error(fe(468,i))}s[_n]=t,mn(s),i=s}t.stateNode=i}else av(a,t.type,t.stateNode);else t.stateNode=nv(a,i,t.memoizedProps);else s!==i?(s===null?n.stateNode!==null&&(n=n.stateNode,n.parentNode.removeChild(n)):s.count--,i===null?av(a,t.type,t.stateNode):nv(a,i,t.memoizedProps)):i===null&&t.stateNode!==null&&xh(t,t.memoizedProps,n.memoizedProps)}break;case 27:jn(e,t),kn(t),i&512&&(nn||n===null||Xi(n,n.return)),n!==null&&i&4&&xh(t,t.memoizedProps,n.memoizedProps);break;case 5:if(jn(e,t),kn(t),i&512&&(nn||n===null||Xi(n,n.return)),t.flags&32){a=t.stateNode;try{cl(a,"")}catch(x){At(t,t.return,x)}}i&4&&t.stateNode!=null&&(a=t.memoizedProps,xh(t,a,n!==null?n.memoizedProps:a)),i&1024&&(_h=!0);break;case 6:if(jn(e,t),kn(t),i&4){if(t.stateNode===null)throw Error(fe(162));i=t.memoizedProps,n=t.stateNode;try{n.nodeValue=i}catch(x){At(t,t.return,x)}}break;case 3:if(yu=null,a=Ri,Ri=ad(e.containerInfo),jn(e,t),Ri=a,kn(t),i&4&&n!==null&&n.memoizedState.isDehydrated)try{xl(e.containerInfo)}catch(x){At(t,t.return,x)}_h&&(_h=!1,cy(t));break;case 4:i=Ri,Ri=ad(t.stateNode.containerInfo),jn(e,t),kn(t),Ri=i;break;case 12:jn(e,t),kn(t);break;case 31:jn(e,t),kn(t),i&4&&(i=t.updateQueue,i!==null&&(t.updateQueue=null,Sc(t,i)));break;case 13:jn(e,t),kn(t),t.child.flags&8192&&t.memoizedState!==null!=(n!==null&&n.memoizedState!==null)&&(Pd=ni()),i&4&&(i=t.updateQueue,i!==null&&(t.updateQueue=null,Sc(t,i)));break;case 22:a=t.memoizedState!==null;var c=n!==null&&n.memoizedState!==null,u=pa,h=nn;if(pa=u||a,nn=h||c,jn(e,t),nn=h,pa=u,kn(t),i&8192)e:for(e=t.stateNode,e._visibility=a?e._visibility&-2:e._visibility|1,a&&(n===null||c||pa||nn||Os(t)),n=null,e=t;;){if(e.tag===5||e.tag===26){if(n===null){c=n=e;try{if(s=c.stateNode,a)l=s.style,typeof l.setProperty=="function"?l.setProperty("display","none","important"):l.display="none";else{o=c.stateNode;var g=c.memoizedProps.style,d=g!=null&&g.hasOwnProperty("display")?g.display:null;o.style.display=d==null||typeof d=="boolean"?"":(""+d).trim()}}catch(x){At(c,c.return,x)}}}else if(e.tag===6){if(n===null){c=e;try{c.stateNode.nodeValue=a?"":c.memoizedProps}catch(x){At(c,c.return,x)}}}else if(e.tag===18){if(n===null){c=e;try{var p=c.stateNode;a?Zx(p,!0):Zx(c.stateNode,!1)}catch(x){At(c,c.return,x)}}}else if((e.tag!==22&&e.tag!==23||e.memoizedState===null||e===t)&&e.child!==null){e.child.return=e,e=e.child;continue}if(e===t)break e;for(;e.sibling===null;){if(e.return===null||e.return===t)break e;n===e&&(n=null),e=e.return}n===e&&(n=null),e.sibling.return=e.return,e=e.sibling}i&4&&(i=t.updateQueue,i!==null&&(n=i.retryQueue,n!==null&&(i.retryQueue=null,Sc(t,n))));break;case 19:jn(e,t),kn(t),i&4&&(i=t.updateQueue,i!==null&&(t.updateQueue=null,Sc(t,i)));break;case 30:break;case 21:break;default:jn(e,t),kn(t)}}function kn(t){var e=t.flags;if(e&2){try{for(var n,i=t.return;i!==null;){if(ty(i)){n=i;break}i=i.return}if(n==null)throw Error(fe(160));switch(n.tag){case 27:var a=n.stateNode,s=vh(t);Ku(t,s,a);break;case 5:var l=n.stateNode;n.flags&32&&(cl(l,""),n.flags&=-33);var o=vh(t);Ku(t,o,l);break;case 3:case 4:var c=n.stateNode.containerInfo,u=vh(t);Kf(t,u,c);break;default:throw Error(fe(161))}}catch(h){At(t,t.return,h)}t.flags&=-3}e&4096&&(t.flags&=-4097)}function cy(t){if(t.subtreeFlags&1024)for(t=t.child;t!==null;){var e=t;cy(e),e.tag===5&&e.flags&1024&&e.stateNode.reset(),t=t.sibling}}function sa(t,e){if(e.subtreeFlags&8772)for(e=e.child;e!==null;)iy(t,e.alternate,e),e=e.sibling}function Os(t){for(t=t.child;t!==null;){var e=t;switch(e.tag){case 0:case 11:case 14:case 15:_s(4,e,e.return),Os(e);break;case 1:Xi(e,e.return);var n=e.stateNode;typeof n.componentWillUnmount=="function"&&Jb(e,e.return,n),Os(e);break;case 27:To(e.stateNode);case 26:case 5:Xi(e,e.return),Os(e);break;case 22:e.memoizedState===null&&Os(e);break;case 30:Os(e);break;default:Os(e)}t=t.sibling}}function da(t,e,n){for(n=n&&(e.subtreeFlags&8772)!==0,e=e.child;e!==null;){var i=e.alternate,a=t,s=e,l=s.flags;switch(s.tag){case 0:case 11:case 15:da(a,s,n),nc(4,s);break;case 1:if(da(a,s,n),i=s,a=i.stateNode,typeof a.componentDidMount=="function")try{a.componentDidMount()}catch(u){At(i,i.return,u)}if(i=s,a=i.updateQueue,a!==null){var o=i.stateNode;try{var c=a.shared.hiddenCallbacks;if(c!==null)for(a.shared.hiddenCallbacks=null,a=0;a<c.length;a++)rb(c[a],o)}catch(u){At(i,i.return,u)}}n&&l&64&&Qb(s),yo(s,s.return);break;case 27:ny(s);case 26:case 5:da(a,s,n),n&&i===null&&l&4&&ey(s),yo(s,s.return);break;case 12:da(a,s,n);break;case 31:da(a,s,n),n&&l&4&&ry(a,s);break;case 13:da(a,s,n),n&&l&4&&ly(a,s);break;case 22:s.memoizedState===null&&da(a,s,n),yo(s,s.return);break;case 30:break;default:da(a,s,n)}e=e.sibling}}function tg(t,e){var n=null;t!==null&&t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(n=t.memoizedState.cachePool.pool),t=null,e.memoizedState!==null&&e.memoizedState.cachePool!==null&&(t=e.memoizedState.cachePool.pool),t!==n&&(t!=null&&t.refCount++,n!=null&&ec(n))}function ng(t,e){t=null,e.alternate!==null&&(t=e.alternate.memoizedState.cache),e=e.memoizedState.cache,e!==t&&(e.refCount++,t!=null&&ec(t))}function Ai(t,e,n,i){if(e.subtreeFlags&10256)for(e=e.child;e!==null;)uy(t,e,n,i),e=e.sibling}function uy(t,e,n,i){var a=e.flags;switch(e.tag){case 0:case 11:case 15:Ai(t,e,n,i),a&2048&&nc(9,e);break;case 1:Ai(t,e,n,i);break;case 3:Ai(t,e,n,i),a&2048&&(t=null,e.alternate!==null&&(t=e.alternate.memoizedState.cache),e=e.memoizedState.cache,e!==t&&(e.refCount++,t!=null&&ec(t)));break;case 12:if(a&2048){Ai(t,e,n,i),t=e.stateNode;try{var s=e.memoizedProps,l=s.id,o=s.onPostCommit;typeof o=="function"&&o(l,e.alternate===null?"mount":"update",t.passiveEffectDuration,-0)}catch(c){At(e,e.return,c)}}else Ai(t,e,n,i);break;case 31:Ai(t,e,n,i);break;case 13:Ai(t,e,n,i);break;case 23:break;case 22:s=e.stateNode,l=e.alternate,e.memoizedState!==null?s._visibility&2?Ai(t,e,n,i):So(t,e):s._visibility&2?Ai(t,e,n,i):(s._visibility|=2,Or(t,e,n,i,(e.subtreeFlags&10256)!==0||!1)),a&2048&&tg(l,e);break;case 24:Ai(t,e,n,i),a&2048&&ng(e.alternate,e);break;default:Ai(t,e,n,i)}}function Or(t,e,n,i,a){for(a=a&&((e.subtreeFlags&10256)!==0||!1),e=e.child;e!==null;){var s=t,l=e,o=n,c=i,u=l.flags;switch(l.tag){case 0:case 11:case 15:Or(s,l,o,c,a),nc(8,l);break;case 23:break;case 22:var h=l.stateNode;l.memoizedState!==null?h._visibility&2?Or(s,l,o,c,a):So(s,l):(h._visibility|=2,Or(s,l,o,c,a)),a&&u&2048&&tg(l.alternate,l);break;case 24:Or(s,l,o,c,a),a&&u&2048&&ng(l.alternate,l);break;default:Or(s,l,o,c,a)}e=e.sibling}}function So(t,e){if(e.subtreeFlags&10256)for(e=e.child;e!==null;){var n=t,i=e,a=i.flags;switch(i.tag){case 22:So(n,i),a&2048&&tg(i.alternate,i);break;case 24:So(n,i),a&2048&&ng(i.alternate,i);break;default:So(n,i)}e=e.sibling}}var so=8192;function pr(t,e,n){if(t.subtreeFlags&so)for(t=t.child;t!==null;)dy(t,e,n),t=t.sibling}function dy(t,e,n){switch(t.tag){case 26:pr(t,e,n),t.flags&so&&t.memoizedState!==null&&VT(n,Ri,t.memoizedState,t.memoizedProps);break;case 5:pr(t,e,n);break;case 3:case 4:var i=Ri;Ri=ad(t.stateNode.containerInfo),pr(t,e,n),Ri=i;break;case 22:t.memoizedState===null&&(i=t.alternate,i!==null&&i.memoizedState!==null?(i=so,so=16777216,pr(t,e,n),so=i):pr(t,e,n));break;default:pr(t,e,n)}}function hy(t){var e=t.alternate;if(e!==null&&(t=e.child,t!==null)){e.child=null;do e=t.sibling,t.sibling=null,t=e;while(t!==null)}}function Fl(t){var e=t.deletions;if(t.flags&16){if(e!==null)for(var n=0;n<e.length;n++){var i=e[n];pn=i,py(i,t)}hy(t)}if(t.subtreeFlags&10256)for(t=t.child;t!==null;)fy(t),t=t.sibling}function fy(t){switch(t.tag){case 0:case 11:case 15:Fl(t),t.flags&2048&&_s(9,t,t.return);break;case 3:Fl(t);break;case 12:Fl(t);break;case 22:var e=t.stateNode;t.memoizedState!==null&&e._visibility&2&&(t.return===null||t.return.tag!==13)?(e._visibility&=-3,_u(t)):Fl(t);break;default:Fl(t)}}function _u(t){var e=t.deletions;if(t.flags&16){if(e!==null)for(var n=0;n<e.length;n++){var i=e[n];pn=i,py(i,t)}hy(t)}for(t=t.child;t!==null;){switch(e=t,e.tag){case 0:case 11:case 15:_s(8,e,e.return),_u(e);break;case 22:n=e.stateNode,n._visibility&2&&(n._visibility&=-3,_u(e));break;default:_u(e)}t=t.sibling}}function py(t,e){for(;pn!==null;){var n=pn;switch(n.tag){case 0:case 11:case 15:_s(8,n,e);break;case 23:case 22:if(n.memoizedState!==null&&n.memoizedState.cachePool!==null){var i=n.memoizedState.cachePool.pool;i!=null&&i.refCount++}break;case 24:ec(n.memoizedState.cache)}if(i=n.child,i!==null)i.return=n,pn=i;else e:for(n=t;pn!==null;){i=pn;var a=i.sibling,s=i.return;if(ay(i),i===n){pn=null;break e}if(a!==null){a.return=s,pn=a;break e}pn=s}}}var sT={getCacheForType:function(t){var e=yn(an),n=e.data.get(t);return n===void 0&&(n=t(),e.data.set(t,n)),n},cacheSignal:function(){return yn(an).controller.signal}},rT=typeof WeakMap=="function"?WeakMap:Map,yt=0,Ut=null,ct=null,ut=0,Tt=0,Qn=null,ts=!1,Cl=!1,ig=!1,Ca=0,qt=0,bs=0,Vs=0,ag=0,ti=0,fl=0,Mo=null,Fn=null,Zf=!1,Pd=0,my=0,Zu=1/0,Qu=null,hs=null,cn=0,fs=null,pl=null,Sa=0,Qf=0,Jf=null,gy=null,Eo=0,ep=null;function si(){return yt&2&&ut!==0?ut&-ut:et.T!==null?rg():T_()}function xy(){if(ti===0)if(!(ut&536870912)||mt){var t=pc;pc<<=1,!(pc&3932160)&&(pc=262144),ti=t}else ti=536870912;return t=li.current,t!==null&&(t.flags|=32),ti}function Vn(t,e,n){(t===Ut&&(Tt===2||Tt===9)||t.cancelPendingCommit!==null)&&(ml(t,0),ns(t,ut,ti,!1)),Zo(t,n),(!(yt&2)||t!==Ut)&&(t===Ut&&(!(yt&2)&&(Vs|=n),qt===4&&ns(t,ut,ti,!1)),Ji(t))}function vy(t,e,n){if(yt&6)throw Error(fe(327));var i=!n&&(e&127)===0&&(e&t.expiredLanes)===0||Ko(t,e),a=i?cT(t,e):bh(t,e,!0),s=i;do{if(a===0){Cl&&!i&&ns(t,e,0,!1);break}else{if(n=t.current.alternate,s&&!lT(n)){a=bh(t,e,!1),s=!1;continue}if(a===2){if(s=e,t.errorRecoveryDisabledLanes&s)var l=0;else l=t.pendingLanes&-536870913,l=l!==0?l:l&536870912?536870912:0;if(l!==0){e=l;e:{var o=t;a=Mo;var c=o.current.memoizedState.isDehydrated;if(c&&(ml(o,l).flags|=256),l=bh(o,l,!1),l!==2){if(ig&&!c){o.errorRecoveryDisabledLanes|=s,Vs|=s,a=4;break e}s=Fn,Fn=a,s!==null&&(Fn===null?Fn=s:Fn.push.apply(Fn,s))}a=l}if(s=!1,a!==2)continue}}if(a===1){ml(t,0),ns(t,e,0,!0);break}e:{switch(i=t,s=a,s){case 0:case 1:throw Error(fe(345));case 4:if((e&4194048)!==e)break;case 6:ns(i,e,ti,!ts);break e;case 2:Fn=null;break;case 3:case 5:break;default:throw Error(fe(329))}if((e&62914560)===e&&(a=Pd+300-ni(),10<a)){if(ns(i,e,ti,!ts),Ed(i,0,!0)!==0)break e;Sa=e,i.timeoutHandle=ky(kx.bind(null,i,n,Fn,Qu,Zf,e,ti,Vs,fl,ts,s,"Throttled",-0,0),a);break e}kx(i,n,Fn,Qu,Zf,e,ti,Vs,fl,ts,s,null,-0,0)}}break}while(!0);Ji(t)}function kx(t,e,n,i,a,s,l,o,c,u,h,g,d,p){if(t.timeoutHandle=-1,g=e.subtreeFlags,g&8192||(g&16785408)===16785408){g={stylesheets:null,count:0,imgCount:0,imgBytes:0,suspenseyImages:[],waitingForImages:!0,waitingForViewTransition:!1,unsuspend:xa},dy(e,s,g);var x=(s&62914560)===s?Pd-ni():(s&4194048)===s?my-ni():0;if(x=WT(g,x),x!==null){Sa=s,t.cancelPendingCommit=x(Bx.bind(null,t,e,s,n,i,a,l,o,c,h,g,null,d,p)),ns(t,s,l,!u);return}}Bx(t,e,s,n,i,a,l,o,c)}function lT(t){for(var e=t;;){var n=e.tag;if((n===0||n===11||n===15)&&e.flags&16384&&(n=e.updateQueue,n!==null&&(n=n.stores,n!==null)))for(var i=0;i<n.length;i++){var a=n[i],s=a.getSnapshot;a=a.value;try{if(!ri(s(),a))return!1}catch{return!1}}if(n=e.child,e.subtreeFlags&16384&&n!==null)n.return=e,e=n;else{if(e===t)break;for(;e.sibling===null;){if(e.return===null||e.return===t)return!0;e=e.return}e.sibling.return=e.return,e=e.sibling}}return!0}function ns(t,e,n,i){e&=~ag,e&=~Vs,t.suspendedLanes|=e,t.pingedLanes&=~e,i&&(t.warmLanes|=e),i=t.expirationTimes;for(var a=e;0<a;){var s=31-ai(a),l=1<<s;i[s]=-1,a&=~l}n!==0&&S_(t,n,e)}function zd(){return yt&6?!0:(ic(0),!1)}function sg(){if(ct!==null){if(Tt===0)var t=ct.return;else t=ct,va=sr=null,Vm(t),el=null,Po=0,t=ct;for(;t!==null;)Zb(t.alternate,t),t=t.return;ct=null}}function ml(t,e){var n=t.timeoutHandle;n!==-1&&(t.timeoutHandle=-1,wT(n)),n=t.cancelPendingCommit,n!==null&&(t.cancelPendingCommit=null,n()),Sa=0,sg(),Ut=t,ct=n=ba(t.current,null),ut=e,Tt=0,Qn=null,ts=!1,Cl=Ko(t,e),ig=!1,fl=ti=ag=Vs=bs=qt=0,Fn=Mo=null,Zf=!1,e&8&&(e|=e&32);var i=t.entangledLanes;if(i!==0)for(t=t.entanglements,i&=e;0<i;){var a=31-ai(i),s=1<<a;e|=t[a],i&=~s}return Ca=e,Nd(),n}function _y(t,e){it=null,et.H=jo,e===Nl||e===Rd?(e=gx(),Tt=3):e===jm?(e=gx(),Tt=4):Tt=e===Jm?8:e!==null&&typeof e=="object"&&typeof e.then=="function"?6:1,Qn=e,ct===null&&(qt=1,Yu(t,gi(e,t.current)))}function by(){var t=li.current;return t===null?!0:(ut&4194048)===ut?bi===null:(ut&62914560)===ut||ut&536870912?t===bi:!1}function yy(){var t=et.H;return et.H=jo,t===null?jo:t}function Sy(){var t=et.A;return et.A=sT,t}function Ju(){qt=4,ts||(ut&4194048)!==ut&&li.current!==null||(Cl=!0),!(bs&134217727)&&!(Vs&134217727)||Ut===null||ns(Ut,ut,ti,!1)}function bh(t,e,n){var i=yt;yt|=2;var a=yy(),s=Sy();(Ut!==t||ut!==e)&&(Qu=null,ml(t,e)),e=!1;var l=qt;e:do try{if(Tt!==0&&ct!==null){var o=ct,c=Qn;switch(Tt){case 8:sg(),l=6;break e;case 3:case 2:case 9:case 6:li.current===null&&(e=!0);var u=Tt;if(Tt=0,Qn=null,qr(t,o,c,u),n&&Cl){l=0;break e}break;default:u=Tt,Tt=0,Qn=null,qr(t,o,c,u)}}oT(),l=qt;break}catch(h){_y(t,h)}while(!0);return e&&t.shellSuspendCounter++,va=sr=null,yt=i,et.H=a,et.A=s,ct===null&&(Ut=null,ut=0,Nd()),l}function oT(){for(;ct!==null;)My(ct)}function cT(t,e){var n=yt;yt|=2;var i=yy(),a=Sy();Ut!==t||ut!==e?(Qu=null,Zu=ni()+500,ml(t,e)):Cl=Ko(t,e);e:do try{if(Tt!==0&&ct!==null){e=ct;var s=Qn;t:switch(Tt){case 1:Tt=0,Qn=null,qr(t,e,s,1);break;case 2:case 9:if(mx(s)){Tt=0,Qn=null,Ix(e);break}e=function(){Tt!==2&&Tt!==9||Ut!==t||(Tt=7),Ji(t)},s.then(e,e);break e;case 3:Tt=7;break e;case 4:Tt=5;break e;case 7:mx(s)?(Tt=0,Qn=null,Ix(e)):(Tt=0,Qn=null,qr(t,e,s,7));break;case 5:var l=null;switch(ct.tag){case 26:l=ct.memoizedState;case 5:case 27:var o=ct;if(l?Gy(l):o.stateNode.complete){Tt=0,Qn=null;var c=o.sibling;if(c!==null)ct=c;else{var u=o.return;u!==null?(ct=u,jd(u)):ct=null}break t}}Tt=0,Qn=null,qr(t,e,s,5);break;case 6:Tt=0,Qn=null,qr(t,e,s,6);break;case 8:sg(),qt=6;break e;default:throw Error(fe(462))}}uT();break}catch(h){_y(t,h)}while(!0);return va=sr=null,et.H=i,et.A=a,yt=n,ct!==null?0:(Ut=null,ut=0,Nd(),qt)}function uT(){for(;ct!==null&&!UM();)My(ct)}function My(t){var e=Kb(t.alternate,t,Ca);t.memoizedProps=t.pendingProps,e===null?jd(t):ct=e}function Ix(t){var e=t,n=e.alternate;switch(e.tag){case 15:case 0:e=Lx(n,e,e.pendingProps,e.type,void 0,ut);break;case 11:e=Lx(n,e,e.pendingProps,e.type.render,e.ref,ut);break;case 5:Vm(e);default:Zb(n,e),e=ct=Z_(e,Ca),e=Kb(n,e,Ca)}t.memoizedProps=t.pendingProps,e===null?jd(t):ct=e}function qr(t,e,n,i){va=sr=null,Vm(e),el=null,Po=0;var a=e.return;try{if(QE(t,a,e,n,ut)){qt=1,Yu(t,gi(n,t.current)),ct=null;return}}catch(s){if(a!==null)throw ct=a,s;qt=1,Yu(t,gi(n,t.current)),ct=null;return}e.flags&32768?(mt||i===1?t=!0:Cl||ut&536870912?t=!1:(ts=t=!0,(i===2||i===9||i===3||i===6)&&(i=li.current,i!==null&&i.tag===13&&(i.flags|=16384))),Ey(e,t)):jd(e)}function jd(t){var e=t;do{if(e.flags&32768){Ey(e,ts);return}t=e.return;var n=tT(e.alternate,e,Ca);if(n!==null){ct=n;return}if(e=e.sibling,e!==null){ct=e;return}ct=e=t}while(e!==null);qt===0&&(qt=5)}function Ey(t,e){do{var n=nT(t.alternate,t);if(n!==null){n.flags&=32767,ct=n;return}if(n=t.return,n!==null&&(n.flags|=32768,n.subtreeFlags=0,n.deletions=null),!e&&(t=t.sibling,t!==null)){ct=t;return}ct=t=n}while(t!==null);qt=6,ct=null}function Bx(t,e,n,i,a,s,l,o,c){t.cancelPendingCommit=null;do kd();while(cn!==0);if(yt&6)throw Error(fe(327));if(e!==null){if(e===t.current)throw Error(fe(177));if(s=e.lanes|e.childLanes,s|=Rm,GM(t,n,s,l,o,c),t===Ut&&(ct=Ut=null,ut=0),pl=e,fs=t,Sa=n,Qf=s,Jf=a,gy=i,e.subtreeFlags&10256||e.flags&10256?(t.callbackNode=null,t.callbackPriority=0,pT(ju,function(){return Cy(),null})):(t.callbackNode=null,t.callbackPriority=0),i=(e.flags&13878)!==0,e.subtreeFlags&13878||i){i=et.T,et.T=null,a=St.p,St.p=2,l=yt,yt|=4;try{iT(t,e,n)}finally{yt=l,St.p=a,et.T=i}}cn=1,Ty(),Ay(),wy()}}function Ty(){if(cn===1){cn=0;var t=fs,e=pl,n=(e.flags&13878)!==0;if(e.subtreeFlags&13878||n){n=et.T,et.T=null;var i=St.p;St.p=2;var a=yt;yt|=4;try{oy(e,t);var s=ap,l=G_(t.containerInfo),o=s.focusedElem,c=s.selectionRange;if(l!==o&&o&&o.ownerDocument&&H_(o.ownerDocument.documentElement,o)){if(c!==null&&Cm(o)){var u=c.start,h=c.end;if(h===void 0&&(h=u),"selectionStart"in o)o.selectionStart=u,o.selectionEnd=Math.min(h,o.value.length);else{var g=o.ownerDocument||document,d=g&&g.defaultView||window;if(d.getSelection){var p=d.getSelection(),x=o.textContent.length,_=Math.min(c.start,x),m=c.end===void 0?_:Math.min(c.end,x);!p.extend&&_>m&&(l=m,m=_,_=l);var f=ox(o,_),v=ox(o,m);if(f&&v&&(p.rangeCount!==1||p.anchorNode!==f.node||p.anchorOffset!==f.offset||p.focusNode!==v.node||p.focusOffset!==v.offset)){var y=g.createRange();y.setStart(f.node,f.offset),p.removeAllRanges(),_>m?(p.addRange(y),p.extend(v.node,v.offset)):(y.setEnd(v.node,v.offset),p.addRange(y))}}}}for(g=[],p=o;p=p.parentNode;)p.nodeType===1&&g.push({element:p,left:p.scrollLeft,top:p.scrollTop});for(typeof o.focus=="function"&&o.focus(),o=0;o<g.length;o++){var b=g[o];b.element.scrollLeft=b.left,b.element.scrollTop=b.top}}ld=!!ip,ap=ip=null}finally{yt=a,St.p=i,et.T=n}}t.current=e,cn=2}}function Ay(){if(cn===2){cn=0;var t=fs,e=pl,n=(e.flags&8772)!==0;if(e.subtreeFlags&8772||n){n=et.T,et.T=null;var i=St.p;St.p=2;var a=yt;yt|=4;try{iy(t,e.alternate,e)}finally{yt=a,St.p=i,et.T=n}}cn=3}}function wy(){if(cn===4||cn===3){cn=0,OM();var t=fs,e=pl,n=Sa,i=gy;e.subtreeFlags&10256||e.flags&10256?cn=5:(cn=0,pl=fs=null,Ny(t,t.pendingLanes));var a=t.pendingLanes;if(a===0&&(hs=null),Sm(n),e=e.stateNode,ii&&typeof ii.onCommitFiberRoot=="function")try{ii.onCommitFiberRoot($o,e,void 0,(e.current.flags&128)===128)}catch{}if(i!==null){e=et.T,a=St.p,St.p=2,et.T=null;try{for(var s=t.onRecoverableError,l=0;l<i.length;l++){var o=i[l];s(o.value,{componentStack:o.stack})}}finally{et.T=e,St.p=a}}Sa&3&&kd(),Ji(t),a=t.pendingLanes,n&261930&&a&42?t===ep?Eo++:(Eo=0,ep=t):Eo=0,ic(0)}}function Ny(t,e){(t.pooledCacheLanes&=e)===0&&(e=t.pooledCache,e!=null&&(t.pooledCache=null,ec(e)))}function kd(){return Ty(),Ay(),wy(),Cy()}function Cy(){if(cn!==5)return!1;var t=fs,e=Qf;Qf=0;var n=Sm(Sa),i=et.T,a=St.p;try{St.p=32>n?32:n,et.T=null,n=Jf,Jf=null;var s=fs,l=Sa;if(cn=0,pl=fs=null,Sa=0,yt&6)throw Error(fe(331));var o=yt;if(yt|=4,fy(s.current),uy(s,s.current,l,n),yt=o,ic(0,!1),ii&&typeof ii.onPostCommitFiberRoot=="function")try{ii.onPostCommitFiberRoot($o,s)}catch{}return!0}finally{St.p=a,et.T=i,Ny(t,e)}}function Fx(t,e,n){e=gi(n,e),e=qf(t.stateNode,e,2),t=ds(t,e,2),t!==null&&(Zo(t,2),Ji(t))}function At(t,e,n){if(t.tag===3)Fx(t,t,n);else for(;e!==null;){if(e.tag===3){Fx(e,t,n);break}else if(e.tag===1){var i=e.stateNode;if(typeof e.type.getDerivedStateFromError=="function"||typeof i.componentDidCatch=="function"&&(hs===null||!hs.has(i))){t=gi(n,t),n=Vb(2),i=ds(e,n,2),i!==null&&(Wb(n,i,e,t),Zo(i,2),Ji(i));break}}e=e.return}}function yh(t,e,n){var i=t.pingCache;if(i===null){i=t.pingCache=new rT;var a=new Set;i.set(e,a)}else a=i.get(e),a===void 0&&(a=new Set,i.set(e,a));a.has(n)||(ig=!0,a.add(n),t=dT.bind(null,t,e,n),e.then(t,t))}function dT(t,e,n){var i=t.pingCache;i!==null&&i.delete(e),t.pingedLanes|=t.suspendedLanes&n,t.warmLanes&=~n,Ut===t&&(ut&n)===n&&(qt===4||qt===3&&(ut&62914560)===ut&&300>ni()-Pd?!(yt&2)&&ml(t,0):ag|=n,fl===ut&&(fl=0)),Ji(t)}function Ry(t,e){e===0&&(e=y_()),t=ar(t,e),t!==null&&(Zo(t,e),Ji(t))}function hT(t){var e=t.memoizedState,n=0;e!==null&&(n=e.retryLane),Ry(t,n)}function fT(t,e){var n=0;switch(t.tag){case 31:case 13:var i=t.stateNode,a=t.memoizedState;a!==null&&(n=a.retryLane);break;case 19:i=t.stateNode;break;case 22:i=t.stateNode._retryCache;break;default:throw Error(fe(314))}i!==null&&i.delete(e),Ry(t,n)}function pT(t,e){return bm(t,e)}var ed=null,Pr=null,tp=!1,td=!1,Sh=!1,is=0;function Ji(t){t!==Pr&&t.next===null&&(Pr===null?ed=Pr=t:Pr=Pr.next=t),td=!0,tp||(tp=!0,gT())}function ic(t,e){if(!Sh&&td){Sh=!0;do for(var n=!1,i=ed;i!==null;){if(t!==0){var a=i.pendingLanes;if(a===0)var s=0;else{var l=i.suspendedLanes,o=i.pingedLanes;s=(1<<31-ai(42|t)+1)-1,s&=a&~(l&~o),s=s&201326741?s&201326741|1:s?s|2:0}s!==0&&(n=!0,Hx(i,s))}else s=ut,s=Ed(i,i===Ut?s:0,i.cancelPendingCommit!==null||i.timeoutHandle!==-1),!(s&3)||Ko(i,s)||(n=!0,Hx(i,s));i=i.next}while(n);Sh=!1}}function mT(){Dy()}function Dy(){td=tp=!1;var t=0;is!==0&&AT()&&(t=is);for(var e=ni(),n=null,i=ed;i!==null;){var a=i.next,s=Ly(i,e);s===0?(i.next=null,n===null?ed=a:n.next=a,a===null&&(Pr=n)):(n=i,(t!==0||s&3)&&(td=!0)),i=a}cn!==0&&cn!==5||ic(t),is!==0&&(is=0)}function Ly(t,e){for(var n=t.suspendedLanes,i=t.pingedLanes,a=t.expirationTimes,s=t.pendingLanes&-62914561;0<s;){var l=31-ai(s),o=1<<l,c=a[l];c===-1?(!(o&n)||o&i)&&(a[l]=HM(o,e)):c<=e&&(t.expiredLanes|=o),s&=~o}if(e=Ut,n=ut,n=Ed(t,t===e?n:0,t.cancelPendingCommit!==null||t.timeoutHandle!==-1),i=t.callbackNode,n===0||t===e&&(Tt===2||Tt===9)||t.cancelPendingCommit!==null)return i!==null&&i!==null&&Kd(i),t.callbackNode=null,t.callbackPriority=0;if(!(n&3)||Ko(t,n)){if(e=n&-n,e===t.callbackPriority)return e;switch(i!==null&&Kd(i),Sm(n)){case 2:case 8:n=__;break;case 32:n=ju;break;case 268435456:n=b_;break;default:n=ju}return i=Uy.bind(null,t),n=bm(n,i),t.callbackPriority=e,t.callbackNode=n,e}return i!==null&&i!==null&&Kd(i),t.callbackPriority=2,t.callbackNode=null,2}function Uy(t,e){if(cn!==0&&cn!==5)return t.callbackNode=null,t.callbackPriority=0,null;var n=t.callbackNode;if(kd()&&t.callbackNode!==n)return null;var i=ut;return i=Ed(t,t===Ut?i:0,t.cancelPendingCommit!==null||t.timeoutHandle!==-1),i===0?null:(vy(t,i,e),Ly(t,ni()),t.callbackNode!=null&&t.callbackNode===n?Uy.bind(null,t):null)}function Hx(t,e){if(kd())return null;vy(t,e,!0)}function gT(){NT(function(){yt&6?bm(v_,mT):Dy()})}function rg(){if(is===0){var t=ul;t===0&&(t=fc,fc<<=1,!(fc&261888)&&(fc=256)),is=t}return is}function Gx(t){return t==null||typeof t=="symbol"||typeof t=="boolean"?null:typeof t=="function"?t:uu(""+t)}function Vx(t,e){var n=e.ownerDocument.createElement("input");return n.name=e.name,n.value=e.value,t.id&&n.setAttribute("form",t.id),e.parentNode.insertBefore(n,e),t=new FormData(t),n.parentNode.removeChild(n),t}function xT(t,e,n,i,a){if(e==="submit"&&n&&n.stateNode===a){var s=Gx((a[Xn]||null).action),l=i.submitter;l&&(e=(e=l[Xn]||null)?Gx(e.formAction):l.getAttribute("formAction"),e!==null&&(s=e,l=null));var o=new Td("action","action",null,i,a);t.push({event:o,listeners:[{instance:null,listener:function(){if(i.defaultPrevented){if(is!==0){var c=l?Vx(a,l):new FormData(a);Wf(n,{pending:!0,data:c,method:a.method,action:s},null,c)}}else typeof s=="function"&&(o.preventDefault(),c=l?Vx(a,l):new FormData(a),Wf(n,{pending:!0,data:c,method:a.method,action:s},s,c))},currentTarget:a}]})}}for(var Mh=0;Mh<Uf.length;Mh++){var Eh=Uf[Mh],vT=Eh.toLowerCase(),_T=Eh[0].toUpperCase()+Eh.slice(1);Oi(vT,"on"+_T)}Oi(W_,"onAnimationEnd");Oi(X_,"onAnimationIteration");Oi(q_,"onAnimationStart");Oi("dblclick","onDoubleClick");Oi("focusin","onFocus");Oi("focusout","onBlur");Oi(PE,"onTransitionRun");Oi(zE,"onTransitionStart");Oi(jE,"onTransitionCancel");Oi(Y_,"onTransitionEnd");ol("onMouseEnter",["mouseout","mouseover"]);ol("onMouseLeave",["mouseout","mouseover"]);ol("onPointerEnter",["pointerout","pointerover"]);ol("onPointerLeave",["pointerout","pointerover"]);tr("onChange","change click focusin focusout input keydown keyup selectionchange".split(" "));tr("onSelect","focusout contextmenu dragend focusin keydown keyup mousedown mouseup selectionchange".split(" "));tr("onBeforeInput",["compositionend","keypress","textInput","paste"]);tr("onCompositionEnd","compositionend focusout keydown keypress keyup mousedown".split(" "));tr("onCompositionStart","compositionstart focusout keydown keypress keyup mousedown".split(" "));tr("onCompositionUpdate","compositionupdate focusout keydown keypress keyup mousedown".split(" "));var ko="abort canplay canplaythrough durationchange emptied encrypted ended error loadeddata loadedmetadata loadstart pause play playing progress ratechange resize seeked seeking stalled suspend timeupdate volumechange waiting".split(" "),bT=new Set("beforetoggle cancel close invalid load scroll scrollend toggle".split(" ").concat(ko));function Oy(t,e){e=(e&4)!==0;for(var n=0;n<t.length;n++){var i=t[n],a=i.event;i=i.listeners;e:{var s=void 0;if(e)for(var l=i.length-1;0<=l;l--){var o=i[l],c=o.instance,u=o.currentTarget;if(o=o.listener,c!==s&&a.isPropagationStopped())break e;s=o,a.currentTarget=u;try{s(a)}catch(h){Iu(h)}a.currentTarget=null,s=c}else for(l=0;l<i.length;l++){if(o=i[l],c=o.instance,u=o.currentTarget,o=o.listener,c!==s&&a.isPropagationStopped())break e;s=o,a.currentTarget=u;try{s(a)}catch(h){Iu(h)}a.currentTarget=null,s=c}}}}function lt(t,e){var n=e[Tf];n===void 0&&(n=e[Tf]=new Set);var i=t+"__bubble";n.has(i)||(Py(e,t,2,!1),n.add(i))}function Th(t,e,n){var i=0;e&&(i|=4),Py(n,t,i,e)}var Mc="_reactListening"+Math.random().toString(36).slice(2);function lg(t){if(!t[Mc]){t[Mc]=!0,A_.forEach(function(n){n!=="selectionchange"&&(bT.has(n)||Th(n,!1,t),Th(n,!0,t))});var e=t.nodeType===9?t:t.ownerDocument;e===null||e[Mc]||(e[Mc]=!0,Th("selectionchange",!1,e))}}function Py(t,e,n,i){switch(Yy(e)){case 2:var a=YT;break;case 8:a=$T;break;default:a=dg}n=a.bind(null,e,n,t),a=void 0,!Rf||e!=="touchstart"&&e!=="touchmove"&&e!=="wheel"||(a=!0),i?a!==void 0?t.addEventListener(e,n,{capture:!0,passive:a}):t.addEventListener(e,n,!0):a!==void 0?t.addEventListener(e,n,{passive:a}):t.addEventListener(e,n,!1)}function Ah(t,e,n,i,a){var s=i;if(!(e&1)&&!(e&2)&&i!==null)e:for(;;){if(i===null)return;var l=i.tag;if(l===3||l===4){var o=i.stateNode.containerInfo;if(o===a)break;if(l===4)for(l=i.return;l!==null;){var c=l.tag;if((c===3||c===4)&&l.stateNode.containerInfo===a)return;l=l.return}for(;o!==null;){if(l=kr(o),l===null)return;if(c=l.tag,c===5||c===6||c===26||c===27){i=s=l;continue e}o=o.parentNode}}i=i.return}O_(function(){var u=s,h=Tm(n),g=[];e:{var d=$_.get(t);if(d!==void 0){var p=Td,x=t;switch(t){case"keypress":if(hu(n)===0)break e;case"keydown":case"keyup":p=fE;break;case"focusin":x="focus",p=th;break;case"focusout":x="blur",p=th;break;case"beforeblur":case"afterblur":p=th;break;case"click":if(n.button===2)break e;case"auxclick":case"dblclick":case"mousedown":case"mousemove":case"mouseup":case"mouseout":case"mouseover":case"contextmenu":p=Qg;break;case"drag":case"dragend":case"dragenter":case"dragexit":case"dragleave":case"dragover":case"dragstart":case"drop":p=tE;break;case"touchcancel":case"touchend":case"touchmove":case"touchstart":p=gE;break;case W_:case X_:case q_:p=aE;break;case Y_:p=vE;break;case"scroll":case"scrollend":p=JM;break;case"wheel":p=bE;break;case"copy":case"cut":case"paste":p=rE;break;case"gotpointercapture":case"lostpointercapture":case"pointercancel":case"pointerdown":case"pointermove":case"pointerout":case"pointerover":case"pointerup":p=ex;break;case"toggle":case"beforetoggle":p=SE}var _=(e&4)!==0,m=!_&&(t==="scroll"||t==="scrollend"),f=_?d!==null?d+"Capture":null:d;_=[];for(var v=u,y;v!==null;){var b=v;if(y=b.stateNode,b=b.tag,b!==5&&b!==26&&b!==27||y===null||f===null||(b=Ro(v,f),b!=null&&_.push(Io(v,b,y))),m)break;v=v.return}0<_.length&&(d=new p(d,x,null,n,h),g.push({event:d,listeners:_}))}}if(!(e&7)){e:{if(d=t==="mouseover"||t==="pointerover",p=t==="mouseout"||t==="pointerout",d&&n!==Cf&&(x=n.relatedTarget||n.fromElement)&&(kr(x)||x[Tl]))break e;if((p||d)&&(d=h.window===h?h:(d=h.ownerDocument)?d.defaultView||d.parentWindow:window,p?(x=n.relatedTarget||n.toElement,p=u,x=x?kr(x):null,x!==null&&(m=Yo(x),_=x.tag,x!==m||_!==5&&_!==27&&_!==6)&&(x=null)):(p=null,x=u),p!==x)){if(_=Qg,b="onMouseLeave",f="onMouseEnter",v="mouse",(t==="pointerout"||t==="pointerover")&&(_=ex,b="onPointerLeave",f="onPointerEnter",v="pointer"),m=p==null?d:io(p),y=x==null?d:io(x),d=new _(b,v+"leave",p,n,h),d.target=m,d.relatedTarget=y,b=null,kr(h)===u&&(_=new _(f,v+"enter",x,n,h),_.target=y,_.relatedTarget=m,b=_),m=b,p&&x)t:{for(_=yT,f=p,v=x,y=0,b=f;b;b=_(b))y++;b=0;for(var T=v;T;T=_(T))b++;for(;0<y-b;)f=_(f),y--;for(;0<b-y;)v=_(v),b--;for(;y--;){if(f===v||v!==null&&f===v.alternate){_=f;break t}f=_(f),v=_(v)}_=null}else _=null;p!==null&&Wx(g,d,p,_,!1),x!==null&&m!==null&&Wx(g,m,x,_,!0)}}e:{if(d=u?io(u):window,p=d.nodeName&&d.nodeName.toLowerCase(),p==="select"||p==="input"&&d.type==="file")var M=ax;else if(ix(d))if(B_)M=LE;else{M=RE;var A=CE}else p=d.nodeName,!p||p.toLowerCase()!=="input"||d.type!=="checkbox"&&d.type!=="radio"?u&&Em(u.elementType)&&(M=ax):M=DE;if(M&&(M=M(t,u))){I_(g,M,n,h);break e}A&&A(t,d,u),t==="focusout"&&u&&d.type==="number"&&u.memoizedProps.value!=null&&Nf(d,"number",d.value)}switch(A=u?io(u):window,t){case"focusin":(ix(A)||A.contentEditable==="true")&&(Fr=A,Df=u,mo=null);break;case"focusout":mo=Df=Fr=null;break;case"mousedown":Lf=!0;break;case"contextmenu":case"mouseup":case"dragend":Lf=!1,cx(g,n,h);break;case"selectionchange":if(OE)break;case"keydown":case"keyup":cx(g,n,h)}var S;if(Nm)e:{switch(t){case"compositionstart":var N="onCompositionStart";break e;case"compositionend":N="onCompositionEnd";break e;case"compositionupdate":N="onCompositionUpdate";break e}N=void 0}else Br?j_(t,n)&&(N="onCompositionEnd"):t==="keydown"&&n.keyCode===229&&(N="onCompositionStart");N&&(z_&&n.locale!=="ko"&&(Br||N!=="onCompositionStart"?N==="onCompositionEnd"&&Br&&(S=P_()):(es=h,Am="value"in es?es.value:es.textContent,Br=!0)),A=nd(u,N),0<A.length&&(N=new Jg(N,t,null,n,h),g.push({event:N,listeners:A}),S?N.data=S:(S=k_(n),S!==null&&(N.data=S)))),(S=EE?TE(t,n):AE(t,n))&&(N=nd(u,"onBeforeInput"),0<N.length&&(A=new Jg("onBeforeInput","beforeinput",null,n,h),g.push({event:A,listeners:N}),A.data=S)),xT(g,t,u,n,h)}Oy(g,e)})}function Io(t,e,n){return{instance:t,listener:e,currentTarget:n}}function nd(t,e){for(var n=e+"Capture",i=[];t!==null;){var a=t,s=a.stateNode;if(a=a.tag,a!==5&&a!==26&&a!==27||s===null||(a=Ro(t,n),a!=null&&i.unshift(Io(t,a,s)),a=Ro(t,e),a!=null&&i.push(Io(t,a,s))),t.tag===3)return i;t=t.return}return[]}function yT(t){if(t===null)return null;do t=t.return;while(t&&t.tag!==5&&t.tag!==27);return t||null}function Wx(t,e,n,i,a){for(var s=e._reactName,l=[];n!==null&&n!==i;){var o=n,c=o.alternate,u=o.stateNode;if(o=o.tag,c!==null&&c===i)break;o!==5&&o!==26&&o!==27||u===null||(c=u,a?(u=Ro(n,s),u!=null&&l.unshift(Io(n,u,c))):a||(u=Ro(n,s),u!=null&&l.push(Io(n,u,c)))),n=n.return}l.length!==0&&t.push({event:e,listeners:l})}var ST=/\r\n?/g,MT=/\u0000|\uFFFD/g;function Xx(t){return(typeof t=="string"?t:""+t).replace(ST,` +`).replace(MT,"")}function zy(t,e){return e=Xx(e),Xx(t)===e}function wt(t,e,n,i,a,s){switch(n){case"children":typeof i=="string"?e==="body"||e==="textarea"&&i===""||cl(t,i):(typeof i=="number"||typeof i=="bigint")&&e!=="body"&&cl(t,""+i);break;case"className":gc(t,"class",i);break;case"tabIndex":gc(t,"tabindex",i);break;case"dir":case"role":case"viewBox":case"width":case"height":gc(t,n,i);break;case"style":U_(t,i,s);break;case"data":if(e!=="object"){gc(t,"data",i);break}case"src":case"href":if(i===""&&(e!=="a"||n!=="href")){t.removeAttribute(n);break}if(i==null||typeof i=="function"||typeof i=="symbol"||typeof i=="boolean"){t.removeAttribute(n);break}i=uu(""+i),t.setAttribute(n,i);break;case"action":case"formAction":if(typeof i=="function"){t.setAttribute(n,"javascript:throw new Error('A React form was unexpectedly submitted. If you called form.submit() manually, consider using form.requestSubmit() instead. If you\\'re trying to use event.stopPropagation() in a submit event handler, consider also calling event.preventDefault().')");break}else typeof s=="function"&&(n==="formAction"?(e!=="input"&&wt(t,e,"name",a.name,a,null),wt(t,e,"formEncType",a.formEncType,a,null),wt(t,e,"formMethod",a.formMethod,a,null),wt(t,e,"formTarget",a.formTarget,a,null)):(wt(t,e,"encType",a.encType,a,null),wt(t,e,"method",a.method,a,null),wt(t,e,"target",a.target,a,null)));if(i==null||typeof i=="symbol"||typeof i=="boolean"){t.removeAttribute(n);break}i=uu(""+i),t.setAttribute(n,i);break;case"onClick":i!=null&&(t.onclick=xa);break;case"onScroll":i!=null&<("scroll",t);break;case"onScrollEnd":i!=null&<("scrollend",t);break;case"dangerouslySetInnerHTML":if(i!=null){if(typeof i!="object"||!("__html"in i))throw Error(fe(61));if(n=i.__html,n!=null){if(a.children!=null)throw Error(fe(60));t.innerHTML=n}}break;case"multiple":t.multiple=i&&typeof i!="function"&&typeof i!="symbol";break;case"muted":t.muted=i&&typeof i!="function"&&typeof i!="symbol";break;case"suppressContentEditableWarning":case"suppressHydrationWarning":case"defaultValue":case"defaultChecked":case"innerHTML":case"ref":break;case"autoFocus":break;case"xlinkHref":if(i==null||typeof i=="function"||typeof i=="boolean"||typeof i=="symbol"){t.removeAttribute("xlink:href");break}n=uu(""+i),t.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",n);break;case"contentEditable":case"spellCheck":case"draggable":case"value":case"autoReverse":case"externalResourcesRequired":case"focusable":case"preserveAlpha":i!=null&&typeof i!="function"&&typeof i!="symbol"?t.setAttribute(n,""+i):t.removeAttribute(n);break;case"inert":case"allowFullScreen":case"async":case"autoPlay":case"controls":case"default":case"defer":case"disabled":case"disablePictureInPicture":case"disableRemotePlayback":case"formNoValidate":case"hidden":case"loop":case"noModule":case"noValidate":case"open":case"playsInline":case"readOnly":case"required":case"reversed":case"scoped":case"seamless":case"itemScope":i&&typeof i!="function"&&typeof i!="symbol"?t.setAttribute(n,""):t.removeAttribute(n);break;case"capture":case"download":i===!0?t.setAttribute(n,""):i!==!1&&i!=null&&typeof i!="function"&&typeof i!="symbol"?t.setAttribute(n,i):t.removeAttribute(n);break;case"cols":case"rows":case"size":case"span":i!=null&&typeof i!="function"&&typeof i!="symbol"&&!isNaN(i)&&1<=i?t.setAttribute(n,i):t.removeAttribute(n);break;case"rowSpan":case"start":i==null||typeof i=="function"||typeof i=="symbol"||isNaN(i)?t.removeAttribute(n):t.setAttribute(n,i);break;case"popover":lt("beforetoggle",t),lt("toggle",t),cu(t,"popover",i);break;case"xlinkActuate":na(t,"http://www.w3.org/1999/xlink","xlink:actuate",i);break;case"xlinkArcrole":na(t,"http://www.w3.org/1999/xlink","xlink:arcrole",i);break;case"xlinkRole":na(t,"http://www.w3.org/1999/xlink","xlink:role",i);break;case"xlinkShow":na(t,"http://www.w3.org/1999/xlink","xlink:show",i);break;case"xlinkTitle":na(t,"http://www.w3.org/1999/xlink","xlink:title",i);break;case"xlinkType":na(t,"http://www.w3.org/1999/xlink","xlink:type",i);break;case"xmlBase":na(t,"http://www.w3.org/XML/1998/namespace","xml:base",i);break;case"xmlLang":na(t,"http://www.w3.org/XML/1998/namespace","xml:lang",i);break;case"xmlSpace":na(t,"http://www.w3.org/XML/1998/namespace","xml:space",i);break;case"is":cu(t,"is",i);break;case"innerText":case"textContent":break;default:(!(2<n.length)||n[0]!=="o"&&n[0]!=="O"||n[1]!=="n"&&n[1]!=="N")&&(n=ZM.get(n)||n,cu(t,n,i))}}function np(t,e,n,i,a,s){switch(n){case"style":U_(t,i,s);break;case"dangerouslySetInnerHTML":if(i!=null){if(typeof i!="object"||!("__html"in i))throw Error(fe(61));if(n=i.__html,n!=null){if(a.children!=null)throw Error(fe(60));t.innerHTML=n}}break;case"children":typeof i=="string"?cl(t,i):(typeof i=="number"||typeof i=="bigint")&&cl(t,""+i);break;case"onScroll":i!=null&<("scroll",t);break;case"onScrollEnd":i!=null&<("scrollend",t);break;case"onClick":i!=null&&(t.onclick=xa);break;case"suppressContentEditableWarning":case"suppressHydrationWarning":case"innerHTML":case"ref":break;case"innerText":case"textContent":break;default:if(!w_.hasOwnProperty(n))e:{if(n[0]==="o"&&n[1]==="n"&&(a=n.endsWith("Capture"),e=n.slice(2,a?n.length-7:void 0),s=t[Xn]||null,s=s!=null?s[n]:null,typeof s=="function"&&t.removeEventListener(e,s,a),typeof i=="function")){typeof s!="function"&&s!==null&&(n in t?t[n]=null:t.hasAttribute(n)&&t.removeAttribute(n)),t.addEventListener(e,i,a);break e}n in t?t[n]=i:i===!0?t.setAttribute(n,""):cu(t,n,i)}}}function Mn(t,e,n){switch(e){case"div":case"span":case"svg":case"path":case"a":case"g":case"p":case"li":break;case"img":lt("error",t),lt("load",t);var i=!1,a=!1,s;for(s in n)if(n.hasOwnProperty(s)){var l=n[s];if(l!=null)switch(s){case"src":i=!0;break;case"srcSet":a=!0;break;case"children":case"dangerouslySetInnerHTML":throw Error(fe(137,e));default:wt(t,e,s,l,n,null)}}a&&wt(t,e,"srcSet",n.srcSet,n,null),i&&wt(t,e,"src",n.src,n,null);return;case"input":lt("invalid",t);var o=s=l=a=null,c=null,u=null;for(i in n)if(n.hasOwnProperty(i)){var h=n[i];if(h!=null)switch(i){case"name":a=h;break;case"type":l=h;break;case"checked":c=h;break;case"defaultChecked":u=h;break;case"value":s=h;break;case"defaultValue":o=h;break;case"children":case"dangerouslySetInnerHTML":if(h!=null)throw Error(fe(137,e));break;default:wt(t,e,i,h,n,null)}}R_(t,s,o,c,u,l,a,!1);return;case"select":lt("invalid",t),i=l=s=null;for(a in n)if(n.hasOwnProperty(a)&&(o=n[a],o!=null))switch(a){case"value":s=o;break;case"defaultValue":l=o;break;case"multiple":i=o;default:wt(t,e,a,o,n,null)}e=s,n=l,t.multiple=!!i,e!=null?Zr(t,!!i,e,!1):n!=null&&Zr(t,!!i,n,!0);return;case"textarea":lt("invalid",t),s=a=i=null;for(l in n)if(n.hasOwnProperty(l)&&(o=n[l],o!=null))switch(l){case"value":i=o;break;case"defaultValue":a=o;break;case"children":s=o;break;case"dangerouslySetInnerHTML":if(o!=null)throw Error(fe(91));break;default:wt(t,e,l,o,n,null)}L_(t,i,a,s);return;case"option":for(c in n)if(n.hasOwnProperty(c)&&(i=n[c],i!=null))switch(c){case"selected":t.selected=i&&typeof i!="function"&&typeof i!="symbol";break;default:wt(t,e,c,i,n,null)}return;case"dialog":lt("beforetoggle",t),lt("toggle",t),lt("cancel",t),lt("close",t);break;case"iframe":case"object":lt("load",t);break;case"video":case"audio":for(i=0;i<ko.length;i++)lt(ko[i],t);break;case"image":lt("error",t),lt("load",t);break;case"details":lt("toggle",t);break;case"embed":case"source":case"link":lt("error",t),lt("load",t);case"area":case"base":case"br":case"col":case"hr":case"keygen":case"meta":case"param":case"track":case"wbr":case"menuitem":for(u in n)if(n.hasOwnProperty(u)&&(i=n[u],i!=null))switch(u){case"children":case"dangerouslySetInnerHTML":throw Error(fe(137,e));default:wt(t,e,u,i,n,null)}return;default:if(Em(e)){for(h in n)n.hasOwnProperty(h)&&(i=n[h],i!==void 0&&np(t,e,h,i,n,void 0));return}}for(o in n)n.hasOwnProperty(o)&&(i=n[o],i!=null&&wt(t,e,o,i,n,null))}function ET(t,e,n,i){switch(e){case"div":case"span":case"svg":case"path":case"a":case"g":case"p":case"li":break;case"input":var a=null,s=null,l=null,o=null,c=null,u=null,h=null;for(p in n){var g=n[p];if(n.hasOwnProperty(p)&&g!=null)switch(p){case"checked":break;case"value":break;case"defaultValue":c=g;default:i.hasOwnProperty(p)||wt(t,e,p,null,i,g)}}for(var d in i){var p=i[d];if(g=n[d],i.hasOwnProperty(d)&&(p!=null||g!=null))switch(d){case"type":s=p;break;case"name":a=p;break;case"checked":u=p;break;case"defaultChecked":h=p;break;case"value":l=p;break;case"defaultValue":o=p;break;case"children":case"dangerouslySetInnerHTML":if(p!=null)throw Error(fe(137,e));break;default:p!==g&&wt(t,e,d,p,i,g)}}wf(t,l,o,c,u,h,s,a);return;case"select":p=l=o=d=null;for(s in n)if(c=n[s],n.hasOwnProperty(s)&&c!=null)switch(s){case"value":break;case"multiple":p=c;default:i.hasOwnProperty(s)||wt(t,e,s,null,i,c)}for(a in i)if(s=i[a],c=n[a],i.hasOwnProperty(a)&&(s!=null||c!=null))switch(a){case"value":d=s;break;case"defaultValue":o=s;break;case"multiple":l=s;default:s!==c&&wt(t,e,a,s,i,c)}e=o,n=l,i=p,d!=null?Zr(t,!!n,d,!1):!!i!=!!n&&(e!=null?Zr(t,!!n,e,!0):Zr(t,!!n,n?[]:"",!1));return;case"textarea":p=d=null;for(o in n)if(a=n[o],n.hasOwnProperty(o)&&a!=null&&!i.hasOwnProperty(o))switch(o){case"value":break;case"children":break;default:wt(t,e,o,null,i,a)}for(l in i)if(a=i[l],s=n[l],i.hasOwnProperty(l)&&(a!=null||s!=null))switch(l){case"value":d=a;break;case"defaultValue":p=a;break;case"children":break;case"dangerouslySetInnerHTML":if(a!=null)throw Error(fe(91));break;default:a!==s&&wt(t,e,l,a,i,s)}D_(t,d,p);return;case"option":for(var x in n)if(d=n[x],n.hasOwnProperty(x)&&d!=null&&!i.hasOwnProperty(x))switch(x){case"selected":t.selected=!1;break;default:wt(t,e,x,null,i,d)}for(c in i)if(d=i[c],p=n[c],i.hasOwnProperty(c)&&d!==p&&(d!=null||p!=null))switch(c){case"selected":t.selected=d&&typeof d!="function"&&typeof d!="symbol";break;default:wt(t,e,c,d,i,p)}return;case"img":case"link":case"area":case"base":case"br":case"col":case"embed":case"hr":case"keygen":case"meta":case"param":case"source":case"track":case"wbr":case"menuitem":for(var _ in n)d=n[_],n.hasOwnProperty(_)&&d!=null&&!i.hasOwnProperty(_)&&wt(t,e,_,null,i,d);for(u in i)if(d=i[u],p=n[u],i.hasOwnProperty(u)&&d!==p&&(d!=null||p!=null))switch(u){case"children":case"dangerouslySetInnerHTML":if(d!=null)throw Error(fe(137,e));break;default:wt(t,e,u,d,i,p)}return;default:if(Em(e)){for(var m in n)d=n[m],n.hasOwnProperty(m)&&d!==void 0&&!i.hasOwnProperty(m)&&np(t,e,m,void 0,i,d);for(h in i)d=i[h],p=n[h],!i.hasOwnProperty(h)||d===p||d===void 0&&p===void 0||np(t,e,h,d,i,p);return}}for(var f in n)d=n[f],n.hasOwnProperty(f)&&d!=null&&!i.hasOwnProperty(f)&&wt(t,e,f,null,i,d);for(g in i)d=i[g],p=n[g],!i.hasOwnProperty(g)||d===p||d==null&&p==null||wt(t,e,g,d,i,p)}function qx(t){switch(t){case"css":case"script":case"font":case"img":case"image":case"input":case"link":return!0;default:return!1}}function TT(){if(typeof performance.getEntriesByType=="function"){for(var t=0,e=0,n=performance.getEntriesByType("resource"),i=0;i<n.length;i++){var a=n[i],s=a.transferSize,l=a.initiatorType,o=a.duration;if(s&&o&&qx(l)){for(l=0,o=a.responseEnd,i+=1;i<n.length;i++){var c=n[i],u=c.startTime;if(u>o)break;var h=c.transferSize,g=c.initiatorType;h&&qx(g)&&(c=c.responseEnd,l+=h*(c<o?1:(o-u)/(c-u)))}if(--i,e+=8*(s+l)/(a.duration/1e3),t++,10<t)break}}if(0<t)return e/t/1e6}return navigator.connection&&(t=navigator.connection.downlink,typeof t=="number")?t:5}var ip=null,ap=null;function id(t){return t.nodeType===9?t:t.ownerDocument}function Yx(t){switch(t){case"http://www.w3.org/2000/svg":return 1;case"http://www.w3.org/1998/Math/MathML":return 2;default:return 0}}function jy(t,e){if(t===0)switch(e){case"svg":return 1;case"math":return 2;default:return 0}return t===1&&e==="foreignObject"?0:t}function sp(t,e){return t==="textarea"||t==="noscript"||typeof e.children=="string"||typeof e.children=="number"||typeof e.children=="bigint"||typeof e.dangerouslySetInnerHTML=="object"&&e.dangerouslySetInnerHTML!==null&&e.dangerouslySetInnerHTML.__html!=null}var wh=null;function AT(){var t=window.event;return t&&t.type==="popstate"?t===wh?!1:(wh=t,!0):(wh=null,!1)}var ky=typeof setTimeout=="function"?setTimeout:void 0,wT=typeof clearTimeout=="function"?clearTimeout:void 0,$x=typeof Promise=="function"?Promise:void 0,NT=typeof queueMicrotask=="function"?queueMicrotask:typeof $x<"u"?function(t){return $x.resolve(null).then(t).catch(CT)}:ky;function CT(t){setTimeout(function(){throw t})}function Ms(t){return t==="head"}function Kx(t,e){var n=e,i=0;do{var a=n.nextSibling;if(t.removeChild(n),a&&a.nodeType===8)if(n=a.data,n==="/$"||n==="/&"){if(i===0){t.removeChild(a),xl(e);return}i--}else if(n==="$"||n==="$?"||n==="$~"||n==="$!"||n==="&")i++;else if(n==="html")To(t.ownerDocument.documentElement);else if(n==="head"){n=t.ownerDocument.head,To(n);for(var s=n.firstChild;s;){var l=s.nextSibling,o=s.nodeName;s[Qo]||o==="SCRIPT"||o==="STYLE"||o==="LINK"&&s.rel.toLowerCase()==="stylesheet"||n.removeChild(s),s=l}}else n==="body"&&To(t.ownerDocument.body);n=a}while(n);xl(e)}function Zx(t,e){var n=t;t=0;do{var i=n.nextSibling;if(n.nodeType===1?e?(n._stashedDisplay=n.style.display,n.style.display="none"):(n.style.display=n._stashedDisplay||"",n.getAttribute("style")===""&&n.removeAttribute("style")):n.nodeType===3&&(e?(n._stashedText=n.nodeValue,n.nodeValue=""):n.nodeValue=n._stashedText||""),i&&i.nodeType===8)if(n=i.data,n==="/$"){if(t===0)break;t--}else n!=="$"&&n!=="$?"&&n!=="$~"&&n!=="$!"||t++;n=i}while(n)}function rp(t){var e=t.firstChild;for(e&&e.nodeType===10&&(e=e.nextSibling);e;){var n=e;switch(e=e.nextSibling,n.nodeName){case"HTML":case"HEAD":case"BODY":rp(n),Mm(n);continue;case"SCRIPT":case"STYLE":continue;case"LINK":if(n.rel.toLowerCase()==="stylesheet")continue}t.removeChild(n)}}function RT(t,e,n,i){for(;t.nodeType===1;){var a=n;if(t.nodeName.toLowerCase()!==e.toLowerCase()){if(!i&&(t.nodeName!=="INPUT"||t.type!=="hidden"))break}else if(i){if(!t[Qo])switch(e){case"meta":if(!t.hasAttribute("itemprop"))break;return t;case"link":if(s=t.getAttribute("rel"),s==="stylesheet"&&t.hasAttribute("data-precedence"))break;if(s!==a.rel||t.getAttribute("href")!==(a.href==null||a.href===""?null:a.href)||t.getAttribute("crossorigin")!==(a.crossOrigin==null?null:a.crossOrigin)||t.getAttribute("title")!==(a.title==null?null:a.title))break;return t;case"style":if(t.hasAttribute("data-precedence"))break;return t;case"script":if(s=t.getAttribute("src"),(s!==(a.src==null?null:a.src)||t.getAttribute("type")!==(a.type==null?null:a.type)||t.getAttribute("crossorigin")!==(a.crossOrigin==null?null:a.crossOrigin))&&s&&t.hasAttribute("async")&&!t.hasAttribute("itemprop"))break;return t;default:return t}}else if(e==="input"&&t.type==="hidden"){var s=a.name==null?null:""+a.name;if(a.type==="hidden"&&t.getAttribute("name")===s)return t}else return t;if(t=yi(t.nextSibling),t===null)break}return null}function DT(t,e,n){if(e==="")return null;for(;t.nodeType!==3;)if((t.nodeType!==1||t.nodeName!=="INPUT"||t.type!=="hidden")&&!n||(t=yi(t.nextSibling),t===null))return null;return t}function Iy(t,e){for(;t.nodeType!==8;)if((t.nodeType!==1||t.nodeName!=="INPUT"||t.type!=="hidden")&&!e||(t=yi(t.nextSibling),t===null))return null;return t}function lp(t){return t.data==="$?"||t.data==="$~"}function op(t){return t.data==="$!"||t.data==="$?"&&t.ownerDocument.readyState!=="loading"}function LT(t,e){var n=t.ownerDocument;if(t.data==="$~")t._reactRetry=e;else if(t.data!=="$?"||n.readyState!=="loading")e();else{var i=function(){e(),n.removeEventListener("DOMContentLoaded",i)};n.addEventListener("DOMContentLoaded",i),t._reactRetry=i}}function yi(t){for(;t!=null;t=t.nextSibling){var e=t.nodeType;if(e===1||e===3)break;if(e===8){if(e=t.data,e==="$"||e==="$!"||e==="$?"||e==="$~"||e==="&"||e==="F!"||e==="F")break;if(e==="/$"||e==="/&")return null}}return t}var cp=null;function Qx(t){t=t.nextSibling;for(var e=0;t;){if(t.nodeType===8){var n=t.data;if(n==="/$"||n==="/&"){if(e===0)return yi(t.nextSibling);e--}else n!=="$"&&n!=="$!"&&n!=="$?"&&n!=="$~"&&n!=="&"||e++}t=t.nextSibling}return null}function Jx(t){t=t.previousSibling;for(var e=0;t;){if(t.nodeType===8){var n=t.data;if(n==="$"||n==="$!"||n==="$?"||n==="$~"||n==="&"){if(e===0)return t;e--}else n!=="/$"&&n!=="/&"||e++}t=t.previousSibling}return null}function By(t,e,n){switch(e=id(n),t){case"html":if(t=e.documentElement,!t)throw Error(fe(452));return t;case"head":if(t=e.head,!t)throw Error(fe(453));return t;case"body":if(t=e.body,!t)throw Error(fe(454));return t;default:throw Error(fe(451))}}function To(t){for(var e=t.attributes;e.length;)t.removeAttributeNode(e[0]);Mm(t)}var Mi=new Map,ev=new Set;function ad(t){return typeof t.getRootNode=="function"?t.getRootNode():t.nodeType===9?t:t.ownerDocument}var La=St.d;St.d={f:UT,r:OT,D:PT,C:zT,L:jT,m:kT,X:BT,S:IT,M:FT};function UT(){var t=La.f(),e=zd();return t||e}function OT(t){var e=Al(t);e!==null&&e.tag===5&&e.type==="form"?Ub(e):La.r(t)}var Rl=typeof document>"u"?null:document;function Fy(t,e,n){var i=Rl;if(i&&typeof e=="string"&&e){var a=mi(e);a='link[rel="'+t+'"][href="'+a+'"]',typeof n=="string"&&(a+='[crossorigin="'+n+'"]'),ev.has(a)||(ev.add(a),t={rel:t,crossOrigin:n,href:e},i.querySelector(a)===null&&(e=i.createElement("link"),Mn(e,"link",t),mn(e),i.head.appendChild(e)))}}function PT(t){La.D(t),Fy("dns-prefetch",t,null)}function zT(t,e){La.C(t,e),Fy("preconnect",t,e)}function jT(t,e,n){La.L(t,e,n);var i=Rl;if(i&&t&&e){var a='link[rel="preload"][as="'+mi(e)+'"]';e==="image"&&n&&n.imageSrcSet?(a+='[imagesrcset="'+mi(n.imageSrcSet)+'"]',typeof n.imageSizes=="string"&&(a+='[imagesizes="'+mi(n.imageSizes)+'"]')):a+='[href="'+mi(t)+'"]';var s=a;switch(e){case"style":s=gl(t);break;case"script":s=Dl(t)}Mi.has(s)||(t=Bt({rel:"preload",href:e==="image"&&n&&n.imageSrcSet?void 0:t,as:e},n),Mi.set(s,t),i.querySelector(a)!==null||e==="style"&&i.querySelector(ac(s))||e==="script"&&i.querySelector(sc(s))||(e=i.createElement("link"),Mn(e,"link",t),mn(e),i.head.appendChild(e)))}}function kT(t,e){La.m(t,e);var n=Rl;if(n&&t){var i=e&&typeof e.as=="string"?e.as:"script",a='link[rel="modulepreload"][as="'+mi(i)+'"][href="'+mi(t)+'"]',s=a;switch(i){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":s=Dl(t)}if(!Mi.has(s)&&(t=Bt({rel:"modulepreload",href:t},e),Mi.set(s,t),n.querySelector(a)===null)){switch(i){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(n.querySelector(sc(s)))return}i=n.createElement("link"),Mn(i,"link",t),mn(i),n.head.appendChild(i)}}}function IT(t,e,n){La.S(t,e,n);var i=Rl;if(i&&t){var a=Kr(i).hoistableStyles,s=gl(t);e=e||"default";var l=a.get(s);if(!l){var o={loading:0,preload:null};if(l=i.querySelector(ac(s)))o.loading=5;else{t=Bt({rel:"stylesheet",href:t,"data-precedence":e},n),(n=Mi.get(s))&&og(t,n);var c=l=i.createElement("link");mn(c),Mn(c,"link",t),c._p=new Promise(function(u,h){c.onload=u,c.onerror=h}),c.addEventListener("load",function(){o.loading|=1}),c.addEventListener("error",function(){o.loading|=2}),o.loading|=4,bu(l,e,i)}l={type:"stylesheet",instance:l,count:1,state:o},a.set(s,l)}}}function BT(t,e){La.X(t,e);var n=Rl;if(n&&t){var i=Kr(n).hoistableScripts,a=Dl(t),s=i.get(a);s||(s=n.querySelector(sc(a)),s||(t=Bt({src:t,async:!0},e),(e=Mi.get(a))&&cg(t,e),s=n.createElement("script"),mn(s),Mn(s,"link",t),n.head.appendChild(s)),s={type:"script",instance:s,count:1,state:null},i.set(a,s))}}function FT(t,e){La.M(t,e);var n=Rl;if(n&&t){var i=Kr(n).hoistableScripts,a=Dl(t),s=i.get(a);s||(s=n.querySelector(sc(a)),s||(t=Bt({src:t,async:!0,type:"module"},e),(e=Mi.get(a))&&cg(t,e),s=n.createElement("script"),mn(s),Mn(s,"link",t),n.head.appendChild(s)),s={type:"script",instance:s,count:1,state:null},i.set(a,s))}}function tv(t,e,n,i){var a=(a=os.current)?ad(a):null;if(!a)throw Error(fe(446));switch(t){case"meta":case"title":return null;case"style":return typeof n.precedence=="string"&&typeof n.href=="string"?(e=gl(n.href),n=Kr(a).hoistableStyles,i=n.get(e),i||(i={type:"style",instance:null,count:0,state:null},n.set(e,i)),i):{type:"void",instance:null,count:0,state:null};case"link":if(n.rel==="stylesheet"&&typeof n.href=="string"&&typeof n.precedence=="string"){t=gl(n.href);var s=Kr(a).hoistableStyles,l=s.get(t);if(l||(a=a.ownerDocument||a,l={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},s.set(t,l),(s=a.querySelector(ac(t)))&&!s._p&&(l.instance=s,l.state.loading=5),Mi.has(t)||(n={rel:"preload",as:"style",href:n.href,crossOrigin:n.crossOrigin,integrity:n.integrity,media:n.media,hrefLang:n.hrefLang,referrerPolicy:n.referrerPolicy},Mi.set(t,n),s||HT(a,t,n,l.state))),e&&i===null)throw Error(fe(528,""));return l}if(e&&i!==null)throw Error(fe(529,""));return null;case"script":return e=n.async,n=n.src,typeof n=="string"&&e&&typeof e!="function"&&typeof e!="symbol"?(e=Dl(n),n=Kr(a).hoistableScripts,i=n.get(e),i||(i={type:"script",instance:null,count:0,state:null},n.set(e,i)),i):{type:"void",instance:null,count:0,state:null};default:throw Error(fe(444,t))}}function gl(t){return'href="'+mi(t)+'"'}function ac(t){return'link[rel="stylesheet"]['+t+"]"}function Hy(t){return Bt({},t,{"data-precedence":t.precedence,precedence:null})}function HT(t,e,n,i){t.querySelector('link[rel="preload"][as="style"]['+e+"]")?i.loading=1:(e=t.createElement("link"),i.preload=e,e.addEventListener("load",function(){return i.loading|=1}),e.addEventListener("error",function(){return i.loading|=2}),Mn(e,"link",n),mn(e),t.head.appendChild(e))}function Dl(t){return'[src="'+mi(t)+'"]'}function sc(t){return"script[async]"+t}function nv(t,e,n){if(e.count++,e.instance===null)switch(e.type){case"style":var i=t.querySelector('style[data-href~="'+mi(n.href)+'"]');if(i)return e.instance=i,mn(i),i;var a=Bt({},n,{"data-href":n.href,"data-precedence":n.precedence,href:null,precedence:null});return i=(t.ownerDocument||t).createElement("style"),mn(i),Mn(i,"style",a),bu(i,n.precedence,t),e.instance=i;case"stylesheet":a=gl(n.href);var s=t.querySelector(ac(a));if(s)return e.state.loading|=4,e.instance=s,mn(s),s;i=Hy(n),(a=Mi.get(a))&&og(i,a),s=(t.ownerDocument||t).createElement("link"),mn(s);var l=s;return l._p=new Promise(function(o,c){l.onload=o,l.onerror=c}),Mn(s,"link",i),e.state.loading|=4,bu(s,n.precedence,t),e.instance=s;case"script":return s=Dl(n.src),(a=t.querySelector(sc(s)))?(e.instance=a,mn(a),a):(i=n,(a=Mi.get(s))&&(i=Bt({},n),cg(i,a)),t=t.ownerDocument||t,a=t.createElement("script"),mn(a),Mn(a,"link",i),t.head.appendChild(a),e.instance=a);case"void":return null;default:throw Error(fe(443,e.type))}else e.type==="stylesheet"&&!(e.state.loading&4)&&(i=e.instance,e.state.loading|=4,bu(i,n.precedence,t));return e.instance}function bu(t,e,n){for(var i=n.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),a=i.length?i[i.length-1]:null,s=a,l=0;l<i.length;l++){var o=i[l];if(o.dataset.precedence===e)s=o;else if(s!==a)break}s?s.parentNode.insertBefore(t,s.nextSibling):(e=n.nodeType===9?n.head:n,e.insertBefore(t,e.firstChild))}function og(t,e){t.crossOrigin==null&&(t.crossOrigin=e.crossOrigin),t.referrerPolicy==null&&(t.referrerPolicy=e.referrerPolicy),t.title==null&&(t.title=e.title)}function cg(t,e){t.crossOrigin==null&&(t.crossOrigin=e.crossOrigin),t.referrerPolicy==null&&(t.referrerPolicy=e.referrerPolicy),t.integrity==null&&(t.integrity=e.integrity)}var yu=null;function iv(t,e,n){if(yu===null){var i=new Map,a=yu=new Map;a.set(n,i)}else a=yu,i=a.get(n),i||(i=new Map,a.set(n,i));if(i.has(t))return i;for(i.set(t,null),n=n.getElementsByTagName(t),a=0;a<n.length;a++){var s=n[a];if(!(s[Qo]||s[_n]||t==="link"&&s.getAttribute("rel")==="stylesheet")&&s.namespaceURI!=="http://www.w3.org/2000/svg"){var l=s.getAttribute(e)||"";l=t+l;var o=i.get(l);o?o.push(s):i.set(l,[s])}}return i}function av(t,e,n){t=t.ownerDocument||t,t.head.insertBefore(n,e==="title"?t.querySelector("head > title"):null)}function GT(t,e,n){if(n===1||e.itemProp!=null)return!1;switch(t){case"meta":case"title":return!0;case"style":if(typeof e.precedence!="string"||typeof e.href!="string"||e.href==="")break;return!0;case"link":if(typeof e.rel!="string"||typeof e.href!="string"||e.href===""||e.onLoad||e.onError)break;switch(e.rel){case"stylesheet":return t=e.disabled,typeof e.precedence=="string"&&t==null;default:return!0}case"script":if(e.async&&typeof e.async!="function"&&typeof e.async!="symbol"&&!e.onLoad&&!e.onError&&e.src&&typeof e.src=="string")return!0}return!1}function Gy(t){return!(t.type==="stylesheet"&&!(t.state.loading&3))}function VT(t,e,n,i){if(n.type==="stylesheet"&&(typeof i.media!="string"||matchMedia(i.media).matches!==!1)&&!(n.state.loading&4)){if(n.instance===null){var a=gl(i.href),s=e.querySelector(ac(a));if(s){e=s._p,e!==null&&typeof e=="object"&&typeof e.then=="function"&&(t.count++,t=sd.bind(t),e.then(t,t)),n.state.loading|=4,n.instance=s,mn(s);return}s=e.ownerDocument||e,i=Hy(i),(a=Mi.get(a))&&og(i,a),s=s.createElement("link"),mn(s);var l=s;l._p=new Promise(function(o,c){l.onload=o,l.onerror=c}),Mn(s,"link",i),n.instance=s}t.stylesheets===null&&(t.stylesheets=new Map),t.stylesheets.set(n,e),(e=n.state.preload)&&!(n.state.loading&3)&&(t.count++,n=sd.bind(t),e.addEventListener("load",n),e.addEventListener("error",n))}}var Nh=0;function WT(t,e){return t.stylesheets&&t.count===0&&Su(t,t.stylesheets),0<t.count||0<t.imgCount?function(n){var i=setTimeout(function(){if(t.stylesheets&&Su(t,t.stylesheets),t.unsuspend){var s=t.unsuspend;t.unsuspend=null,s()}},6e4+e);0<t.imgBytes&&Nh===0&&(Nh=62500*TT());var a=setTimeout(function(){if(t.waitingForImages=!1,t.count===0&&(t.stylesheets&&Su(t,t.stylesheets),t.unsuspend)){var s=t.unsuspend;t.unsuspend=null,s()}},(t.imgBytes>Nh?50:800)+e);return t.unsuspend=n,function(){t.unsuspend=null,clearTimeout(i),clearTimeout(a)}}:null}function sd(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)Su(this,this.stylesheets);else if(this.unsuspend){var t=this.unsuspend;this.unsuspend=null,t()}}}var rd=null;function Su(t,e){t.stylesheets=null,t.unsuspend!==null&&(t.count++,rd=new Map,e.forEach(XT,t),rd=null,sd.call(t))}function XT(t,e){if(!(e.state.loading&4)){var n=rd.get(t);if(n)var i=n.get(null);else{n=new Map,rd.set(t,n);for(var a=t.querySelectorAll("link[data-precedence],style[data-precedence]"),s=0;s<a.length;s++){var l=a[s];(l.nodeName==="LINK"||l.getAttribute("media")!=="not all")&&(n.set(l.dataset.precedence,l),i=l)}i&&n.set(null,i)}a=e.instance,l=a.getAttribute("data-precedence"),s=n.get(l)||i,s===i&&n.set(null,a),n.set(l,a),this.count++,i=sd.bind(this),a.addEventListener("load",i),a.addEventListener("error",i),s?s.parentNode.insertBefore(a,s.nextSibling):(t=t.nodeType===9?t.head:t,t.insertBefore(a,t.firstChild)),e.state.loading|=4}}var Bo={$$typeof:ga,Provider:null,Consumer:null,_currentValue:Bs,_currentValue2:Bs,_threadCount:0};function qT(t,e,n,i,a,s,l,o,c){this.tag=1,this.containerInfo=t,this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.next=this.pendingContext=this.context=this.cancelPendingCommit=null,this.callbackPriority=0,this.expirationTimes=Zd(-1),this.entangledLanes=this.shellSuspendCounter=this.errorRecoveryDisabledLanes=this.expiredLanes=this.warmLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=Zd(0),this.hiddenUpdates=Zd(null),this.identifierPrefix=i,this.onUncaughtError=a,this.onCaughtError=s,this.onRecoverableError=l,this.pooledCache=null,this.pooledCacheLanes=0,this.formState=c,this.incompleteTransitions=new Map}function Vy(t,e,n,i,a,s,l,o,c,u,h,g){return t=new qT(t,e,n,l,c,u,h,g,o),e=1,s===!0&&(e|=24),s=ei(3,null,null,e),t.current=s,s.stateNode=t,e=Pm(),e.refCount++,t.pooledCache=e,e.refCount++,s.memoizedState={element:i,isDehydrated:n,cache:e},km(s),t}function Wy(t){return t?(t=Vr,t):Vr}function Xy(t,e,n,i,a,s){a=Wy(a),i.context===null?i.context=a:i.pendingContext=a,i=us(e),i.payload={element:n},s=s===void 0?null:s,s!==null&&(i.callback=s),n=ds(t,i,e),n!==null&&(Vn(n,t,e),xo(n,t,e))}function sv(t,e){if(t=t.memoizedState,t!==null&&t.dehydrated!==null){var n=t.retryLane;t.retryLane=n!==0&&n<e?n:e}}function ug(t,e){sv(t,e),(t=t.alternate)&&sv(t,e)}function qy(t){if(t.tag===13||t.tag===31){var e=ar(t,67108864);e!==null&&Vn(e,t,67108864),ug(t,67108864)}}function rv(t){if(t.tag===13||t.tag===31){var e=si();e=ym(e);var n=ar(t,e);n!==null&&Vn(n,t,e),ug(t,e)}}var ld=!0;function YT(t,e,n,i){var a=et.T;et.T=null;var s=St.p;try{St.p=2,dg(t,e,n,i)}finally{St.p=s,et.T=a}}function $T(t,e,n,i){var a=et.T;et.T=null;var s=St.p;try{St.p=8,dg(t,e,n,i)}finally{St.p=s,et.T=a}}function dg(t,e,n,i){if(ld){var a=up(i);if(a===null)Ah(t,e,i,od,n),lv(t,i);else if(ZT(a,t,e,n,i))i.stopPropagation();else if(lv(t,i),e&4&&-1<KT.indexOf(t)){for(;a!==null;){var s=Al(a);if(s!==null)switch(s.tag){case 3:if(s=s.stateNode,s.current.memoizedState.isDehydrated){var l=Ls(s.pendingLanes);if(l!==0){var o=s;for(o.pendingLanes|=2,o.entangledLanes|=2;l;){var c=1<<31-ai(l);o.entanglements[1]|=c,l&=~c}Ji(s),!(yt&6)&&(Zu=ni()+500,ic(0))}}break;case 31:case 13:o=ar(s,2),o!==null&&Vn(o,s,2),zd(),ug(s,2)}if(s=up(i),s===null&&Ah(t,e,i,od,n),s===a)break;a=s}a!==null&&i.stopPropagation()}else Ah(t,e,i,null,n)}}function up(t){return t=Tm(t),hg(t)}var od=null;function hg(t){if(od=null,t=kr(t),t!==null){var e=Yo(t);if(e===null)t=null;else{var n=e.tag;if(n===13){if(t=f_(e),t!==null)return t;t=null}else if(n===31){if(t=p_(e),t!==null)return t;t=null}else if(n===3){if(e.stateNode.current.memoizedState.isDehydrated)return e.tag===3?e.stateNode.containerInfo:null;t=null}else e!==t&&(t=null)}}return od=t,null}function Yy(t){switch(t){case"beforetoggle":case"cancel":case"click":case"close":case"contextmenu":case"copy":case"cut":case"auxclick":case"dblclick":case"dragend":case"dragstart":case"drop":case"focusin":case"focusout":case"input":case"invalid":case"keydown":case"keypress":case"keyup":case"mousedown":case"mouseup":case"paste":case"pause":case"play":case"pointercancel":case"pointerdown":case"pointerup":case"ratechange":case"reset":case"resize":case"seeked":case"submit":case"toggle":case"touchcancel":case"touchend":case"touchstart":case"volumechange":case"change":case"selectionchange":case"textInput":case"compositionstart":case"compositionend":case"compositionupdate":case"beforeblur":case"afterblur":case"beforeinput":case"blur":case"fullscreenchange":case"focus":case"hashchange":case"popstate":case"select":case"selectstart":return 2;case"drag":case"dragenter":case"dragexit":case"dragleave":case"dragover":case"mousemove":case"mouseout":case"mouseover":case"pointermove":case"pointerout":case"pointerover":case"scroll":case"touchmove":case"wheel":case"mouseenter":case"mouseleave":case"pointerenter":case"pointerleave":return 8;case"message":switch(PM()){case v_:return 2;case __:return 8;case ju:case zM:return 32;case b_:return 268435456;default:return 32}default:return 32}}var dp=!1,ps=null,ms=null,gs=null,Fo=new Map,Ho=new Map,Za=[],KT="mousedown mouseup touchcancel touchend touchstart auxclick dblclick pointercancel pointerdown pointerup dragend dragstart drop compositionend compositionstart keydown keypress keyup input textInput copy cut paste click change contextmenu reset".split(" ");function lv(t,e){switch(t){case"focusin":case"focusout":ps=null;break;case"dragenter":case"dragleave":ms=null;break;case"mouseover":case"mouseout":gs=null;break;case"pointerover":case"pointerout":Fo.delete(e.pointerId);break;case"gotpointercapture":case"lostpointercapture":Ho.delete(e.pointerId)}}function Hl(t,e,n,i,a,s){return t===null||t.nativeEvent!==s?(t={blockedOn:e,domEventName:n,eventSystemFlags:i,nativeEvent:s,targetContainers:[a]},e!==null&&(e=Al(e),e!==null&&qy(e)),t):(t.eventSystemFlags|=i,e=t.targetContainers,a!==null&&e.indexOf(a)===-1&&e.push(a),t)}function ZT(t,e,n,i,a){switch(e){case"focusin":return ps=Hl(ps,t,e,n,i,a),!0;case"dragenter":return ms=Hl(ms,t,e,n,i,a),!0;case"mouseover":return gs=Hl(gs,t,e,n,i,a),!0;case"pointerover":var s=a.pointerId;return Fo.set(s,Hl(Fo.get(s)||null,t,e,n,i,a)),!0;case"gotpointercapture":return s=a.pointerId,Ho.set(s,Hl(Ho.get(s)||null,t,e,n,i,a)),!0}return!1}function $y(t){var e=kr(t.target);if(e!==null){var n=Yo(e);if(n!==null){if(e=n.tag,e===13){if(e=f_(n),e!==null){t.blockedOn=e,Wg(t.priority,function(){rv(n)});return}}else if(e===31){if(e=p_(n),e!==null){t.blockedOn=e,Wg(t.priority,function(){rv(n)});return}}else if(e===3&&n.stateNode.current.memoizedState.isDehydrated){t.blockedOn=n.tag===3?n.stateNode.containerInfo:null;return}}}t.blockedOn=null}function Mu(t){if(t.blockedOn!==null)return!1;for(var e=t.targetContainers;0<e.length;){var n=up(t.nativeEvent);if(n===null){n=t.nativeEvent;var i=new n.constructor(n.type,n);Cf=i,n.target.dispatchEvent(i),Cf=null}else return e=Al(n),e!==null&&qy(e),t.blockedOn=n,!1;e.shift()}return!0}function ov(t,e,n){Mu(t)&&n.delete(e)}function QT(){dp=!1,ps!==null&&Mu(ps)&&(ps=null),ms!==null&&Mu(ms)&&(ms=null),gs!==null&&Mu(gs)&&(gs=null),Fo.forEach(ov),Ho.forEach(ov)}function Ec(t,e){t.blockedOn===e&&(t.blockedOn=null,dp||(dp=!0,un.unstable_scheduleCallback(un.unstable_NormalPriority,QT)))}var Tc=null;function cv(t){Tc!==t&&(Tc=t,un.unstable_scheduleCallback(un.unstable_NormalPriority,function(){Tc===t&&(Tc=null);for(var e=0;e<t.length;e+=3){var n=t[e],i=t[e+1],a=t[e+2];if(typeof i!="function"){if(hg(i||n)===null)continue;break}var s=Al(n);s!==null&&(t.splice(e,3),e-=3,Wf(s,{pending:!0,data:a,method:n.method,action:i},i,a))}}))}function xl(t){function e(c){return Ec(c,t)}ps!==null&&Ec(ps,t),ms!==null&&Ec(ms,t),gs!==null&&Ec(gs,t),Fo.forEach(e),Ho.forEach(e);for(var n=0;n<Za.length;n++){var i=Za[n];i.blockedOn===t&&(i.blockedOn=null)}for(;0<Za.length&&(n=Za[0],n.blockedOn===null);)$y(n),n.blockedOn===null&&Za.shift();if(n=(t.ownerDocument||t).$$reactFormReplay,n!=null)for(i=0;i<n.length;i+=3){var a=n[i],s=n[i+1],l=a[Xn]||null;if(typeof s=="function")l||cv(n);else if(l){var o=null;if(s&&s.hasAttribute("formAction")){if(a=s,l=s[Xn]||null)o=l.formAction;else if(hg(a)!==null)continue}else o=l.action;typeof o=="function"?n[i+1]=o:(n.splice(i,3),i-=3),cv(n)}}}function Ky(){function t(s){s.canIntercept&&s.info==="react-transition"&&s.intercept({handler:function(){return new Promise(function(l){return a=l})},focusReset:"manual",scroll:"manual"})}function e(){a!==null&&(a(),a=null),i||setTimeout(n,20)}function n(){if(!i&&!navigation.transition){var s=navigation.currentEntry;s&&s.url!=null&&navigation.navigate(s.url,{state:s.getState(),info:"react-transition",history:"replace"})}}if(typeof navigation=="object"){var i=!1,a=null;return navigation.addEventListener("navigate",t),navigation.addEventListener("navigatesuccess",e),navigation.addEventListener("navigateerror",e),setTimeout(n,100),function(){i=!0,navigation.removeEventListener("navigate",t),navigation.removeEventListener("navigatesuccess",e),navigation.removeEventListener("navigateerror",e),a!==null&&(a(),a=null)}}}function fg(t){this._internalRoot=t}Id.prototype.render=fg.prototype.render=function(t){var e=this._internalRoot;if(e===null)throw Error(fe(409));var n=e.current,i=si();Xy(n,i,t,e,null,null)};Id.prototype.unmount=fg.prototype.unmount=function(){var t=this._internalRoot;if(t!==null){this._internalRoot=null;var e=t.containerInfo;Xy(t.current,2,null,t,null,null),zd(),e[Tl]=null}};function Id(t){this._internalRoot=t}Id.prototype.unstable_scheduleHydration=function(t){if(t){var e=T_();t={blockedOn:null,target:t,priority:e};for(var n=0;n<Za.length&&e!==0&&e<Za[n].priority;n++);Za.splice(n,0,t),n===0&&$y(t)}};var uv=d_.version;if(uv!=="19.2.8")throw Error(fe(527,uv,"19.2.8"));St.findDOMNode=function(t){var e=t._reactInternals;if(e===void 0)throw typeof t.render=="function"?Error(fe(188)):(t=Object.keys(t).join(","),Error(fe(268,t)));return t=NM(e),t=t!==null?m_(t):null,t=t===null?null:t.stateNode,t};var JT={bundleType:0,version:"19.2.8",rendererPackageName:"react-dom",currentDispatcherRef:et,reconcilerVersion:"19.2.8"};if(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__<"u"){var Ac=__REACT_DEVTOOLS_GLOBAL_HOOK__;if(!Ac.isDisabled&&Ac.supportsFiber)try{$o=Ac.inject(JT),ii=Ac}catch{}}Sd.createRoot=function(t,e){if(!h_(t))throw Error(fe(299));var n=!1,i="",a=Fb,s=Hb,l=Gb;return e!=null&&(e.unstable_strictMode===!0&&(n=!0),e.identifierPrefix!==void 0&&(i=e.identifierPrefix),e.onUncaughtError!==void 0&&(a=e.onUncaughtError),e.onCaughtError!==void 0&&(s=e.onCaughtError),e.onRecoverableError!==void 0&&(l=e.onRecoverableError)),e=Vy(t,1,!1,null,null,n,i,null,a,s,l,Ky),t[Tl]=e.current,lg(t),new fg(e)};Sd.hydrateRoot=function(t,e,n){if(!h_(t))throw Error(fe(299));var i=!1,a="",s=Fb,l=Hb,o=Gb,c=null;return n!=null&&(n.unstable_strictMode===!0&&(i=!0),n.identifierPrefix!==void 0&&(a=n.identifierPrefix),n.onUncaughtError!==void 0&&(s=n.onUncaughtError),n.onCaughtError!==void 0&&(l=n.onCaughtError),n.onRecoverableError!==void 0&&(o=n.onRecoverableError),n.formState!==void 0&&(c=n.formState)),e=Vy(t,1,!0,e,n??null,i,a,c,s,l,o,Ky),e.context=Wy(null),n=e.current,i=si(),i=ym(i),a=us(i),a.callback=null,ds(n,a,i),n=i,e.current.lanes=n,Zo(e,n),Ji(e),t[Tl]=e.current,lg(t),new Id(e)};Sd.version="19.2.8";function Zy(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(Zy)}catch(t){console.error(t)}}Zy(),s_.exports=Sd;var e1=s_.exports;function Qy(t){if(typeof t!="string"||t==="")return t;const e=t.split(/[\\/]/).filter(Boolean);return e.length>0?e[e.length-1]:t}const Jy="plugbrain.workspace",t1="plugbrain.auth_token";function eS(){var e,n,i,a;let t="";try{t=((e=new URLSearchParams(window.location.search).get("token"))==null?void 0:e.trim())||((i=(n=window.__PLUGBRAIN__)==null?void 0:n.token)==null?void 0:i.trim())||((a=localStorage.getItem(t1))==null?void 0:a.trim())||""}catch{}return{"Content-Type":"application/json",...t?{Authorization:`Bearer ${t}`,"x-plug-auth-token":t}:{}}}function n1(){try{return localStorage.getItem(Jy)||""}catch{return""}}function i1(t){try{localStorage.setItem(Jy,t)}catch{}}async function dv(){const t=await fetch("/api/galaxy");if(!t.ok)throw new Error(`Galaxie: HTTP ${t.status}`);const e=await t.json();if(!(e!=null&&e.ok)||!Array.isArray(e.planets))throw new Error("Die Galaxie antwortet unvollständig.");return e.planets}function hv(t){if(typeof t!="string")return"";const e=t.trim();if(e==="")return"";if(/^[a-zA-Z]:[\\/]/.test(e)||/^[\\/]{2}/.test(e)){const i=e.replace(/\\/g,"/"),a=i.startsWith("//")?`//${i.slice(2).replace(/\/{2,}/g,"/")}`:i.replace(/\/{2,}/g,"/");return(a==="//"||/^[a-zA-Z]:\/$/.test(a)?a:a.replace(/\/+$/,"")).toLowerCase()}return e==="/"?e:e.replace(/\/+$/,"")}function a1(t,e){var i;const n=hv(e);return!n||!Array.isArray(t)?"":((i=t.find(a=>typeof(a==null?void 0:a.id)=="string"&&hv(a.root)===n))==null?void 0:i.id)??""}async function s1(t,e,n){var s;const i=await fetch("/api/workspaces",{method:"POST",headers:eS(),body:JSON.stringify({root:t,name:e})});if(!i.ok){const l=await i.json().catch(()=>null);throw new Error((l==null?void 0:l.error)??`Registrieren: HTTP ${i.status}`)}const a=await i.json();if(!(a!=null&&a.ok)||!((s=a.workspace)!=null&&s.id))throw new Error("Registrieren: unvollständige Antwort.");return await tS(a.workspace.id,n),a.workspace.id}function hp(t){var l,o,c;const e=t==null?void 0:t.run;if(!e)return"Kein Indexlauf bekannt.";if(t.stale)return t.ownerAlive&&!t.recoverable?"Indexlauf ohne neuen Fortschritt; der Owner-Prozess läuft noch. Die Sperre bleibt geschützt.":"Indexlauf ohne neuen Fortschritt; der frühere Owner ist nicht mehr aktiv. Er kann erneut gestartet werden.";const n=Math.max(0,Math.round((Date.now()-Date.parse(e.startedAt))/1e3)),i={starting:"startet",scan:"sammelt Dateien",classify:"vergleicht",write:"schreibt",resolve:"verknüpft",publish:"veröffentlicht",done:"fertig",failed:"fehlgeschlagen"}[e.phase]??e.phase;if(e.finishedAt)return e.ok?`Fertig: ${((l=e.result)==null?void 0:l.files)??e.scanned} Dateien, ${((o=e.result)==null?void 0:o.symbols)??0} Symbole, ${((c=e.result)==null?void 0:c.edges)??0} Kanten in ${n} s.`:`Indexlauf fehlgeschlagen: ${e.error??"unbekannter Grund"}`;const a=e.total>0?`/${e.total}`:"",s=e.total>0?` (${Math.round(e.processed/e.total*100)} %)`:"";return`Indexiert: ${i} ${e.processed}${a}${s} — ${n} s`}function r1(t,e){return t!=null&&t.running||t!=null&&t.stale?hp(t):e.startsWith("Indexiert:")||e.startsWith("Indexlauf ohne neuen Fortschritt;")?"":e}async function l1(t){const e=await fetch(`/api/index/progress?workspace=${encodeURIComponent(t)}`);return e.ok?e.json():null}const o1=t=>new Promise(e=>setTimeout(e,t));async function c1(t,e){for(;;){await o1(900);const n=await l1(t);if(n===null)throw new Error("Der Fortschritt ist nicht abrufbar.");if(e==null||e(n),n.running)continue;if(n.stale)throw n.ownerAlive&&!n.recoverable?new Error("Der Indexlauf meldet keinen neuen Fortschritt, aber der Owner-Prozess läuft noch. Die Sperre bleibt geschützt; nach Ende oder Neustart des Owners erneut indizieren."):new Error("Der Indexlauf ist verstummt und sein Owner ist nicht mehr aktiv. Er kann erneut gestartet werden.");const i=n.run;if(!i)throw new Error("Kein Indexlauf bekannt.");if(i.ok)return i.result;throw new Error(i.error??"Indexlauf fehlgeschlagen.")}}async function tS(t,e){const n=await fetch("/api/reindex",{method:"POST",headers:eS(),body:JSON.stringify({workspace:t})}),i=await n.json().catch(()=>null);if(n.status===409&&(i!=null&&i.busy))throw i.ownerAlive&&!i.recoverable?new Error("Ein stiller Indexlauf gehört noch einem lebenden Owner-Prozess. Die Sperre bleibt geschützt; nach Ende oder Neustart des Owners erneut indizieren."):i.recoverable?new Error("Der vorherige Index-Owner ist nicht mehr aktiv. Der Lauf kann erneut gestartet werden."):new Error(i.error??"Ein Indexlauf ist bereits unterwegs.");if(!n.ok)throw new Error(`Indizieren: HTTP ${n.status}`);if(!(i!=null&&i.ok))throw new Error("Indizieren: unvollständige Antwort.");return i.result!==void 0&&i.result!==null?i.result:c1(t,e)}async function nS(){const t=await fetch("/api/resources",{headers:Lt()});if(!t.ok)throw new Error(`Ressourcen HTTP ${t.status}`);const e=await t.json();if(!(e!=null&&e.ok)||!e.host||!Array.isArray(e.quotas)||!Array.isArray(e.admission))throw new Error("Ressourcen-Antwort unvollständig");return{host:e.host,quotas:e.quotas,admission:e.admission}}async function u1(t){const e=await fetch(`/api/plan?workspace=${encodeURIComponent(t)}`,{headers:Lt()}),n=await e.json().catch(()=>null);if(!e.ok||!(n!=null&&n.ok)||!n.plan)throw new Error((n==null?void 0:n.error)??`Plan HTTP ${e.status}`);return n.plan}async function d1(t){var x;const e=Lt(),n="",[i,a,s,l,o]=await Promise.all([fetch("/api/disk/scan",{headers:e}),fetch(`/api/disk/tree${n}`,{headers:e}),fetch("/api/disk/history",{headers:e}),fetch("/api/disk/recommendations",{headers:e}),fetch("/api/disk/wipe-check",{headers:e})]),c=[i,a,s,l,o];if(c.some(_=>!_.ok))throw new Error(`Festplatten-Projektion HTTP ${(x=c.find(_=>!_.ok))==null?void 0:x.status}`);const[u,h,g,d,p]=await Promise.all(c.map(_=>_.json()));if(![u,h,g,d,p].every(_=>_==null?void 0:_.ok))throw new Error("Festplatten-Projektion unvollständig");return{scan:u.scan,directories:Array.isArray(h.directories)?h.directories:[],history:g.scans??[],largestGrowth:g.largestGrowth??[],recommendations:d,wipeCheck:p}}async function h1(){const t=await fetch("/api/disk/scan",{method:"POST",headers:Lt(),body:JSON.stringify({})});if(!t.ok)throw new Error(`Inventur konnte nicht gestartet werden (HTTP ${t.status})`)}async function f1(t){const e=await fetch("/api/agent/approve-commit",{method:"POST",headers:Lt(),body:JSON.stringify(t)}),n=await e.json().catch(()=>null);if(!e.ok||!(n!=null&&n.ok))throw new Error((n==null?void 0:n.error)??`Commit-Freigabe HTTP ${e.status}`)}const iS="plugbrain.auth_token",fp="plugbrain.agent_id";function p1(){var t,e;try{return((e=(t=window.__PLUGBRAIN__)==null?void 0:t.token)==null?void 0:e.trim())??""}catch{return""}}function aS(){var n,i;let t="";try{t=((n=new URLSearchParams(window.location.search).get("token"))==null?void 0:n.trim())??""}catch{}if(t)return sS(t),t;const e=p1();if(e)return e;try{return((i=localStorage.getItem(iS))==null?void 0:i.trim())??""}catch{return""}}function sS(t){try{localStorage.setItem(iS,t)}catch{}}function Ti(){try{const t=new URLSearchParams(window.location.search).get("agent");return t?(localStorage.setItem(fp,t),t):localStorage.getItem(fp)||"agy"}catch{return"agy"}}function m1(t){try{localStorage.setItem(fp,t)}catch{}}function Lt(){const t=aS(),e={"Content-Type":"application/json"};return t&&(e.Authorization=`Bearer ${t}`,e["x-plug-auth-token"]=t),e}async function g1(t){const e=await fetch(`/api/planet?workspace=${encodeURIComponent(t)}`);if(!e.ok){const i=await e.json().catch(()=>null);throw new Error((i==null?void 0:i.error)??`Planet inventory HTTP ${e.status}`)}const n=await e.json();if(!(n!=null&&n.ok)||!n.planet||!Array.isArray(n.planet.checkouts))throw new Error("Planet-Inventar unvollständig");return n.planet}async function x1(t,e){const n=await fetch("/api/planet/selection",{method:"POST",headers:Lt(),body:JSON.stringify({workspace:t,checkoutIds:e})}),i=await n.json().catch(()=>null);if(!n.ok||!(i!=null&&i.ok)||!i.planet)throw new Error((i==null?void 0:i.error)??`Code-Auswahl HTTP ${n.status}`);return i.planet}async function rS(t){const e=await fetch(`/api/git?workspace=${encodeURIComponent(t)}`);if(!e.ok)throw new Error(`Git-Status HTTP ${e.status}`);return e.json()}async function v1(t){const e=await fetch(`/api/mesh?workspace=${encodeURIComponent(t)}`);if(!e.ok)throw new Error(`Mesh HTTP ${e.status}`);const n=await e.json(),i=n==null?void 0:n.mesh;if(!(n!=null&&n.ok)||!i||i.workspaceId!==t||!Array.isArray(i.nodes)||!Array.isArray(i.edges))throw new Error("Mesh-Projektion unvollständig oder für einen anderen Workspace");return i}async function _1(t,e={}){const n=new URLSearchParams({workspace:t});e.agentId&&n.set("agentId",e.agentId),e.taskId&&n.set("taskId",e.taskId),e.workerId&&n.set("workerId",e.workerId),e.limit!==void 0&&n.set("limit",String(e.limit));const i=await fetch(`/api/mesh/timeline?${n}`);if(!i.ok)throw new Error(`Mesh-Zeitleiste HTTP ${i.status}`);const a=await i.json();if(!(a!=null&&a.ok)||!Array.isArray(a.timeline))throw new Error("Mesh-Zeitleiste unvollständig");return a.timeline}async function fv(t){var p;const e=`?workspace=${encodeURIComponent(t)}`,n=Lt(),[i,a,s,l,o]=await Promise.all([fetch(`/api/swarm/board${e}`,{headers:n}),fetch(`/api/swarm/turns${e}`,{headers:n}),fetch(`/api/swarm/messages${e}`,{headers:n}),fetch(`/api/swarm/approvals${e}`,{headers:n}),fetch(`/api/swarm/queue${e}`,{headers:n})]);if([i,a,s,l,o].some(x=>!x.ok))throw new Error("Die Fleet-Zeitleiste ist nicht verfügbar.");const[c,u,h,g,d]=await Promise.all([i.json(),a.json(),s.json(),l.json(),o.json()]);if(!(c!=null&&c.ok)||!Array.isArray((p=c.board)==null?void 0:p.agents)||!(u!=null&&u.ok)||!Array.isArray(u.turns)||!(h!=null&&h.ok)||!Array.isArray(h.messages)||!(g!=null&&g.ok)||!Array.isArray(g.approvals)||!(d!=null&&d.ok)||!Array.isArray(d.tasks))throw new Error("Die Fleet-Antwort ist unvollständig.");return{board:c.board,turns:u.turns,historyAvailable:u.historyAvailable===!0,messages:h.messages,approvals:g.approvals,tasks:d.tasks}}async function b1(t){if(!(await fetch("/api/agent/message",{method:"POST",headers:Lt(),body:JSON.stringify(t)})).ok)throw new Error("Nachricht konnte nicht gesendet werden.")}async function y1(t){const e=await fetch("/api/queue",{method:"POST",headers:Lt(),body:JSON.stringify(t)}),n=await e.json().catch(()=>null);if(!e.ok||(n==null?void 0:n.ok)!==!0){const a=typeof(n==null?void 0:n.error)=="string"&&n.error.trim()?n.error:`Aufgabe konnte nicht eingereiht werden (HTTP ${e.status}).`;throw new Error(a)}const i=n.task;if(!i||typeof i.id!="string"||!i.id.trim()||!["pending","claimed","delivered","cancelled"].includes(i.state)||typeof i.title!="string")throw new Error("Brain-Antwort unvollständig: Task-ID oder Status fehlt.");return{id:i.id,title:i.title,state:i.state,addressed_to:typeof i.addressed_to=="string"?i.addressed_to:null}}async function S1(t,e){const n=await fetch(`/api/provenance?workspace=${encodeURIComponent(t)}&path=${encodeURIComponent(e)}`);if(!n.ok)throw new Error(`Provenance HTTP ${n.status}`);return n.json()}const M1=6e4;async function E1(t,e=Ti(),n="AGY",{maxWaitMs:i=M1}={}){const a=Date.now()+i;for(;;){const s=await fetch("/api/agent/attach",{method:"POST",headers:Lt(),body:JSON.stringify({workspace:t,agentId:e,name:n})});if(s.ok)return s.json();const l=await s.json().catch(()=>null),o=a-Date.now();if(s.status===503&&(l==null?void 0:l.busy)===!0&&o>0){const c=Math.min(Math.max(Number(l.retryAfterMs)||1e3,5),5e3,o);await new Promise(u=>setTimeout(u,c));continue}throw new Error((l==null?void 0:l.error)??(l==null?void 0:l.reason)??`Agent Attach HTTP ${s.status}`)}}const Eu=new Map;function rc(t,e=Ti()){const n=`${t}\0${e}`,i=Eu.get(n);if(i)return i;const a=E1(t,e).then(()=>{}).catch(()=>{Eu.delete(n)});return Eu.set(n,a),a}function T1(){Eu.clear()}async function pp(t,e,n=Ti()){await rc(t,n);const i=await fetch("/api/agent/search",{method:"POST",headers:Lt(),body:JSON.stringify({workspace:t,agentId:n,query:e})});if(!i.ok){const s=await i.json().catch(()=>null);throw new Error((s==null?void 0:s.error)??`Search HTTP ${i.status}`)}const a=await i.json();return Array.isArray(a==null?void 0:a.hits)?a.hits:[]}async function A1(t,e,n=Ti()){await rc(t,n);const i=await fetch("/api/agent/read",{method:"POST",headers:Lt(),body:JSON.stringify({workspace:t,agentId:n,path:e})});if(!i.ok){const s=await i.json().catch(()=>null),l=(s==null?void 0:s.error)??`HTTP ${i.status}`;return{ok:!1,path:e,content:"",bytes:0,lang:null,error:l}}const a=await i.json();return{ok:!0,path:a.path??e,content:a.content??"",bytes:a.bytes??0,lang:a.lang??null}}async function pv(t,e,n=Ti()){const i=await fetch("/api/context/pack",{method:"POST",headers:Lt(),body:JSON.stringify({workspaceId:t,goal:e,agentId:n})});if(!i.ok){const a=await i.json().catch(()=>null);throw new Error((a==null?void 0:a.error)??`Context Pack HTTP ${i.status}`)}return i.json()}async function w1(t){const e=await fetch(`/api/context/pack/${encodeURIComponent(t)}/staleness`);if(!e.ok){const n=await e.json().catch(()=>null);throw new Error((n==null?void 0:n.error)??`Staleness HTTP ${e.status}`)}return e.json()}async function N1(t,e){const n=await fetch(`/api/notes/query?workspace=${encodeURIComponent(t)}&q=${encodeURIComponent(e)}`,{headers:Lt()});if(!n.ok){const i=await n.json().catch(()=>null);throw new Error((i==null?void 0:i.error)??`Notes Query HTTP ${n.status}`)}return n.json()}async function mp(t,e,n=30){const i=await fetch(`/api/notes/search?workspace=${encodeURIComponent(t)}&q=${encodeURIComponent(e)}&limit=${n}&lines=1`,{headers:Lt()});if(!i.ok){const a=await i.json().catch(()=>null);throw new Error((a==null?void 0:a.error)??`Notizsuche HTTP ${i.status}`)}return i.json()}async function C1(t,e){const n=await fetch(`/api/notes/backlinks?workspace=${encodeURIComponent(t)}&path=${encodeURIComponent(e)}`);if(!n.ok)return[];const i=await n.json();return Array.isArray(i==null?void 0:i.backlinks)?i.backlinks:[]}async function R1(t){const e=await fetch(`/api/notes?workspace=${encodeURIComponent(t)}&limit=5000`),n=await e.json().catch(()=>null);if(!e.ok||!(n!=null&&n.ok))throw new Error((n==null?void 0:n.error)??`Notizen HTTP ${e.status}`);return Array.isArray(n.notes)?n.notes:[]}async function wc(t,e,n=Ti()){await rc(t,n);const i=await fetch(`/api/notes/read?workspace=${encodeURIComponent(t)}&path=${encodeURIComponent(e)}&agentId=${encodeURIComponent(n)}`),a=await i.json().catch(()=>null);if(!i.ok||!(a!=null&&a.ok)||!a.note)throw new Error((a==null?void 0:a.error)??`Notiz lesen HTTP ${i.status}`);return a.note}async function Gl(t,e,n,i,a=Ti(),s=!1){await rc(t,a);const l=await fetch("/api/notes/write",{method:"POST",headers:Lt(),body:JSON.stringify({workspace:t,agentId:a,path:e,content:n,...i?{expectedHash:i}:{},...s?{createOnly:!0}:{}})}),o=await l.json().catch(()=>null);if(!l.ok||!(o!=null&&o.ok)){const c=new Error((o==null?void 0:o.error)??`Notiz speichern HTTP ${l.status}`);throw Object.assign(c,{conflict:o==null?void 0:o.conflict,status:l.status}),c}return o}async function D1(t,e={}){const n=new URLSearchParams({workspace:t});e.focus&&n.set("focus",e.focus),e.depth!==void 0&&n.set("depth",String(e.depth)),e.filter&&n.set("filter",e.filter),e.limit!==void 0&&n.set("limit",String(e.limit));const i=await fetch(`/api/notes/graph?${n}`),a=await i.json().catch(()=>null);if(!i.ok||!(a!=null&&a.ok)||!a.graph)throw new Error((a==null?void 0:a.error)??`Wissensgraph HTTP ${i.status}`);return a.graph}async function mv(t,e){const n=await fetch(`/api/notes/attachments?workspace=${encodeURIComponent(t)}¬e=${encodeURIComponent(e)}`),i=await n.json().catch(()=>null);if(!n.ok||!(i!=null&&i.ok))throw new Error((i==null?void 0:i.error)??`Anhänge HTTP ${n.status}`);return Array.isArray(i.attachments)?i.attachments:[]}async function L1(t,e,n,i=Ti()){await rc(t,i);const a=new Uint8Array(await n.arrayBuffer());let s="";for(let u=0;u<a.length;u+=32768)s+=String.fromCharCode(...a.subarray(u,u+32768));const l=btoa(s),o=await fetch("/api/notes/attachment",{method:"POST",headers:Lt(),body:JSON.stringify({workspace:t,agentId:i,note:e,name:n.name,base64:l})}),c=await o.json().catch(()=>null);if(!o.ok||!(c!=null&&c.ok)||!c.attachment)throw new Error((c==null?void 0:c.error)??`Anhang speichern HTTP ${o.status}`);return c.attachment}function U1(t,e,n,i=Ti()){return`/api/notes/attachment?workspace=${encodeURIComponent(t)}¬e=${encodeURIComponent(e)}&name=${encodeURIComponent(n)}&agentId=${encodeURIComponent(i)}`}function gv(t,e,n=Ti()){const i=new URLSearchParams({workspace:t,agentId:n});return e&&i.set("note",e),`/api/notes/export?${i}`}async function O1(t){try{const e=await fetch(`/api/briefing?workspace=${encodeURIComponent(t)}`,{headers:Lt()});if(e.status===404||e.status===501)return{routeMissing:!0};if(!e.ok){const n=await e.json().catch(()=>null);return{serverError:!0,status:e.status,error:(n==null?void 0:n.error)??`HTTP ${e.status}`}}return await e.json()}catch(e){return{unreachable:!0,error:e instanceof Error?e.message:String(e)}}}async function P1(t,e,n=5){try{const i=await fetch("/api/ask",{method:"POST",headers:Lt(),body:JSON.stringify({workspace:t,question:e,limit:n})});if(i.status===404||i.status===501)return{routeMissing:!0};if(!i.ok){const a=await i.json().catch(()=>null);return{serverError:!0,status:i.status,error:(a==null?void 0:a.error)??`HTTP ${i.status}`}}return await i.json()}catch(i){return{unreachable:!0,error:i instanceof Error?i.message:String(i)}}}async function z1(t){try{const e=await fetch(`/api/hygiene?workspace=${encodeURIComponent(t)}`,{headers:Lt()});if(e.status===404||e.status===501)return{routeMissing:!0};if(!e.ok){const n=await e.json().catch(()=>null);return{serverError:!0,status:e.status,error:(n==null?void 0:n.error)??`HTTP ${e.status}`}}return await e.json()}catch(e){return{unreachable:!0,error:e instanceof Error?e.message:String(e)}}}async function j1(){try{const t=await fetch("/api/machine",{headers:Lt()});if(t.status===404||t.status===501)return{routeMissing:!0};if(!t.ok){const e=await t.json().catch(()=>null);return{serverError:!0,status:t.status,error:(e==null?void 0:e.error)??`HTTP ${t.status}`}}return await t.json()}catch(t){return{unreachable:!0,error:t instanceof Error?t.message:String(t)}}}async function k1(t=!1){try{const n=await fetch(`/api/repos${t?"?dirty=1&page=1":"?page=1"}`,{headers:Lt()});if(n.status===404||n.status===501)return{routeMissing:!0};if(!n.ok){const i=await n.json().catch(()=>null);return{serverError:!0,status:n.status,error:(i==null?void 0:i.error)??`HTTP ${n.status}`}}return await n.json()}catch(e){return{unreachable:!0,error:e instanceof Error?e.message:String(e)}}}const Vt=[],as=[],ha=[],Go=[],Ao={},gn=[],Tu=[],Hi=7.2,Yr=6,Vo=["--k1","--k2","--k3","--k4","--k5","--k6"],pg=t=>getComputedStyle(document.documentElement).getPropertyValue(t).trim(),I1=t=>t.agentColor||pg(Vo[(t.ki??0)%Vo.length]),xv=t=>pg(Vo[t.ki%Vo.length]),lS=new Map;let oS="loc";function B1(t){oS=t}const F1=t=>{const e=Math.max(1,...Vt.map(i=>i.loc)),n=Math.max(1,...Vt.map(i=>i.usedBy.length));return t.dying?0:oS==="loc"?1.5+t.loc/e*26:1.5+t.usedBy.length/n*26},gp=new Set,H1=t=>(gp.add(t),()=>gp.delete(t)),vl=()=>gp.forEach(t=>t());function xp(t,e,n="ok"){Tu.unshift({t:new Date,ws:t,msg:e,kind:n,id:Math.random().toString(36).slice(2)}),Tu.length>60&&Tu.pop()}function Bd(){var o;let e=0,n=0,i=0;const a=Go.filter(c=>gn.find(u=>u.id===c)),s=new Set;for(const c of a){const u=Vt.filter(x=>x.dir===c&&!x.dying);if(!u.length&&((o=gn.find(x=>x.id===c))!=null&&o.dying))continue;const h=Math.max(1,Math.ceil(Math.sqrt(Math.max(1,u.length)))),g=h*Hi+Yr,d=Math.max(1,Math.ceil(Math.max(1,u.length)/h))*Hi+Yr;n+g>74&&n>0&&(e+=i,n=0,i=0);let p=ha.find(x=>x.dir===c);p||(p={dir:c,x:n+g/2,z:e+d/2,w:.01,h:.01},ha.push(p)),Object.assign(p,{tx:n,tz:e,tw:g,th:d,cols:h}),s.add(c),n+=g,i=Math.max(i,d)}const l=ha.filter(c=>s.has(c.dir));if(l.length){const c=Math.max(...l.map(h=>h.tx+h.tw))/2,u=Math.max(...l.map(h=>h.tz+h.th))/2;for(const h of l)h.tx-=c,h.tz-=u;for(const h of l)Vt.filter(d=>d.dir===h.dir).forEach((d,p)=>{d.tx=h.tx+Yr/2+p%h.cols*Hi+Hi/2,d.tz=h.tz+Yr/2+Math.floor(p/h.cols)*Hi+Hi/2,d.x===void 0&&(d.x=d.tx,d.z=d.tz)})}for(let c=ha.length-1;c>=0;c--)!s.has(ha[c].dir)&&!Vt.some(u=>u.dir===ha[c].dir)&&ha.splice(c,1);for(const c of a)lS.set(c,.5)}function G1(t,e,n=!1){let i=gn.find(a=>a.id===t);return i||(i={id:t,name:e||t,ki:gn.length,load:0,events:0,createdAt:new Date,dying:!1,sim:n},gn.push(i),Go.includes(t)||Go.push(t),xp(e||t,"workspace registered","reg"),Bd(),vl(),i)}function V1(t,{path:e,loc:n=40,deps:i=[],note:a="",agentColor:s=null,agentName:l=null,access:o=null}){const c=e.split("/").pop(),u=e.includes("/")&&e.startsWith(t.id+"/")?e:`${t.id}/${e}`;let h=Ao[u];if(h)return h.loc+=Math.max(2,Math.round(n*.25)),h.pulse=1,s&&(h.agentColor=s,h.agentName=l,h.access=o),h;h={path:u,name:c,dir:t.id,top:t.id,ki:t.ki,loc:n,deps:[],usedBy:[],note:a,agentColor:s,agentName:l,access:o,x:void 0,z:void 0,h:0,pulse:1,dying:!1};for(let g of i){g.includes("/")||(g=`${t.id}/${g}`);const d=Ao[g];d&&(h.deps.push(g),as.push({from:h,to:d}),d.usedBy.push(u))}return Vt.push(h),Ao[u]=h,Bd(),vl(),h}function W1(){let t=!1;for(let e=Vt.length-1;e>=0;e--){const n=Vt[e];if(n.dying&&n.h<.25){Vt.splice(e,1),delete Ao[n.path],t=!0;for(let i=as.length-1;i>=0;i--)(as[i].from===n||as[i].to===n)&&as.splice(i,1);for(const i of Vt){const a=i.deps.indexOf(n.path);a>=0&&i.deps.splice(a,1);const s=i.usedBy.indexOf(n.path);s>=0&&i.usedBy.splice(s,1)}}}for(let e=gn.length-1;e>=0;e--){const n=gn[e];if(n.dying&&!Vt.some(i=>i.dir===n.id)){gn.splice(e,1);const i=Go.indexOf(n.id);i>=0&&Go.splice(i,1),t=!0}}t&&(Bd(),vl())}setInterval(()=>{let t=!1;for(const e of gn)e.load>.01&&(e.load*=.82,t=!0);t&&vl()},600);const ro={register({id:t,name:e}={}){return t?G1(String(t),e&&String(e),!1):console.warn("[PlugBrainCity] register() needs an id")},grow(t,{path:e,loc:n=40,deps:i=[],note:a="",agentColor:s=null,agentName:l=null,access:o=null}={}){const c=gn.find(u=>u.id===t);return!c||!e?console.warn("[PlugBrainCity] grow() needs a registered workspace id and a path"):(c.load=Math.min(1,c.load+.3),c.events++,V1(c,{path:e,loc:n,deps:i,note:a,agentColor:s,agentName:l,access:o}))},event(t,e){const n=gn.find(a=>a.id===t);if(!n)return;const i=Vt.filter(a=>a.dir===t&&!a.dying);i.length&&(i[Math.floor(Math.random()*i.length)].pulse=1),n.load=Math.min(1,n.load+.25),n.events++,xp(n.name,String(e||"event")),vl()},unregister(t){const e=gn.find(n=>n.id===t);e&&(e.dying=!0,Vt.filter(n=>n.dir===t).forEach(n=>{n.dying=!0}),xp(e.name,"workspace unregistered","sys"),Bd(),vl())},list:()=>gn.map(t=>({id:t.id,name:t.name,buildings:Vt.filter(e=>e.dir===t.id).length})),simulated:()=>!1};window.PlugBrainCity=ro;/** + * @license + * Copyright 2010-2026 Three.js Authors + * SPDX-License-Identifier: MIT + */const mg="185",X1=0,vv=1,q1=2,Au=1,Y1=2,lo=3,ys=0,Wn=1,ma=2,Ma=0,il=1,_v=2,bv=3,yv=4,$1=5,zs=100,K1=101,Z1=102,Q1=103,J1=104,eA=200,tA=201,nA=202,iA=203,vp=204,_p=205,aA=206,sA=207,rA=208,lA=209,oA=210,cA=211,uA=212,dA=213,hA=214,bp=0,yp=1,Sp=2,_l=3,Mp=4,Ep=5,Tp=6,Ap=7,cS=0,fA=1,pA=2,$i=0,uS=1,dS=2,hS=3,fS=4,pS=5,mS=6,gS=7,xS=300,Ks=301,bl=302,Ch=303,Rh=304,Fd=306,wp=1e3,_a=1001,Np=1002,Sn=1003,mA=1004,Nc=1005,wn=1006,Dh=1007,ks=1008,vi=1009,vS=1010,_S=1011,Wo=1012,gg=1013,Zi=1014,Li=1015,Ra=1016,xg=1017,vg=1018,Xo=1020,bS=35902,yS=35899,SS=1021,MS=1022,Ui=1023,Da=1026,Is=1027,_g=1028,bg=1029,Zs=1030,yg=1031,Sg=1033,wu=33776,Nu=33777,Cu=33778,Ru=33779,Cp=35840,Rp=35841,Dp=35842,Lp=35843,Up=36196,Op=37492,Pp=37496,zp=37488,jp=37489,cd=37490,kp=37491,Ip=37808,Bp=37809,Fp=37810,Hp=37811,Gp=37812,Vp=37813,Wp=37814,Xp=37815,qp=37816,Yp=37817,$p=37818,Kp=37819,Zp=37820,Qp=37821,Jp=36492,em=36494,tm=36495,nm=36283,im=36284,ud=36285,am=36286,gA=3200,Sv=0,xA=1,Qa="",di="srgb",dd="srgb-linear",hd="linear",Et="srgb",mr=7680,Mv=519,vA=512,_A=513,bA=514,Mg=515,yA=516,SA=517,Eg=518,MA=519,Ev=35044,Tv="300 es",qi=2e3,fd=2001;function EA(t){for(let e=t.length-1;e>=0;--e)if(t[e]>=65535)return!0;return!1}function pd(t){return document.createElementNS("http://www.w3.org/1999/xhtml",t)}function TA(){const t=pd("canvas");return t.style.display="block",t}const Av={};function wv(...t){const e="THREE."+t.shift();console.log(e,...t)}function ES(t){const e=t[0];if(typeof e=="string"&&e.startsWith("TSL:")){const n=t[1];n&&n.isStackTrace?t[0]+=" "+n.getLocation():t[1]='Stack trace not available. Enable "THREE.Node.captureStackTrace" to capture stack traces.'}return t}function Qe(...t){t=ES(t);const e="THREE."+t.shift();{const n=t[0];n&&n.isStackTrace?console.warn(n.getError(e)):console.warn(e,...t)}}function bt(...t){t=ES(t);const e="THREE."+t.shift();{const n=t[0];n&&n.isStackTrace?console.error(n.getError(e)):console.error(e,...t)}}function al(...t){const e=t.join(" ");e in Av||(Av[e]=!0,Qe(...t))}function AA(t,e,n){return new Promise(function(i,a){function s(){switch(t.clientWaitSync(e,t.SYNC_FLUSH_COMMANDS_BIT,0)){case t.WAIT_FAILED:a();break;case t.TIMEOUT_EXPIRED:setTimeout(s,n);break;default:i()}}setTimeout(s,n)})}const wA={[bp]:yp,[Sp]:Tp,[Mp]:Ap,[_l]:Ep,[yp]:bp,[Tp]:Sp,[Ap]:Mp,[Ep]:_l};class rr{addEventListener(e,n){this._listeners===void 0&&(this._listeners={});const i=this._listeners;i[e]===void 0&&(i[e]=[]),i[e].indexOf(n)===-1&&i[e].push(n)}hasEventListener(e,n){const i=this._listeners;return i===void 0?!1:i[e]!==void 0&&i[e].indexOf(n)!==-1}removeEventListener(e,n){const i=this._listeners;if(i===void 0)return;const a=i[e];if(a!==void 0){const s=a.indexOf(n);s!==-1&&a.splice(s,1)}}dispatchEvent(e){const n=this._listeners;if(n===void 0)return;const i=n[e.type];if(i!==void 0){e.target=this;const a=i.slice(0);for(let s=0,l=a.length;s<l;s++)a[s].call(this,e);e.target=null}}}const Tn=["00","01","02","03","04","05","06","07","08","09","0a","0b","0c","0d","0e","0f","10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f","20","21","22","23","24","25","26","27","28","29","2a","2b","2c","2d","2e","2f","30","31","32","33","34","35","36","37","38","39","3a","3b","3c","3d","3e","3f","40","41","42","43","44","45","46","47","48","49","4a","4b","4c","4d","4e","4f","50","51","52","53","54","55","56","57","58","59","5a","5b","5c","5d","5e","5f","60","61","62","63","64","65","66","67","68","69","6a","6b","6c","6d","6e","6f","70","71","72","73","74","75","76","77","78","79","7a","7b","7c","7d","7e","7f","80","81","82","83","84","85","86","87","88","89","8a","8b","8c","8d","8e","8f","90","91","92","93","94","95","96","97","98","99","9a","9b","9c","9d","9e","9f","a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","aa","ab","ac","ad","ae","af","b0","b1","b2","b3","b4","b5","b6","b7","b8","b9","ba","bb","bc","bd","be","bf","c0","c1","c2","c3","c4","c5","c6","c7","c8","c9","ca","cb","cc","cd","ce","cf","d0","d1","d2","d3","d4","d5","d6","d7","d8","d9","da","db","dc","dd","de","df","e0","e1","e2","e3","e4","e5","e6","e7","e8","e9","ea","eb","ec","ed","ee","ef","f0","f1","f2","f3","f4","f5","f6","f7","f8","f9","fa","fb","fc","fd","fe","ff"],Du=Math.PI/180,sm=180/Math.PI;function lc(){const t=Math.random()*4294967295|0,e=Math.random()*4294967295|0,n=Math.random()*4294967295|0,i=Math.random()*4294967295|0;return(Tn[t&255]+Tn[t>>8&255]+Tn[t>>16&255]+Tn[t>>24&255]+"-"+Tn[e&255]+Tn[e>>8&255]+"-"+Tn[e>>16&15|64]+Tn[e>>24&255]+"-"+Tn[n&63|128]+Tn[n>>8&255]+"-"+Tn[n>>16&255]+Tn[n>>24&255]+Tn[i&255]+Tn[i>>8&255]+Tn[i>>16&255]+Tn[i>>24&255]).toLowerCase()}function pt(t,e,n){return Math.max(e,Math.min(n,t))}function NA(t,e){return(t%e+e)%e}function Lh(t,e,n){return(1-n)*t+n*e}function Vl(t,e){switch(e.constructor){case Float32Array:return t;case Uint32Array:return t/4294967295;case Uint16Array:return t/65535;case Uint8Array:return t/255;case Int32Array:return Math.max(t/2147483647,-1);case Int16Array:return Math.max(t/32767,-1);case Int8Array:return Math.max(t/127,-1);default:throw new Error("THREE.MathUtils: Invalid component type.")}}function In(t,e){switch(e.constructor){case Float32Array:return t;case Uint32Array:return Math.round(t*4294967295);case Uint16Array:return Math.round(t*65535);case Uint8Array:return Math.round(t*255);case Int32Array:return Math.round(t*2147483647);case Int16Array:return Math.round(t*32767);case Int8Array:return Math.round(t*127);default:throw new Error("THREE.MathUtils: Invalid component type.")}}const Dg=class Dg{constructor(e=0,n=0){this.x=e,this.y=n}get width(){return this.x}set width(e){this.x=e}get height(){return this.y}set height(e){this.y=e}set(e,n){return this.x=e,this.y=n,this}setScalar(e){return this.x=e,this.y=e,this}setX(e){return this.x=e,this}setY(e){return this.y=e,this}setComponent(e,n){switch(e){case 0:this.x=n;break;case 1:this.y=n;break;default:throw new Error("THREE.Vector2: index is out of range: "+e)}return this}getComponent(e){switch(e){case 0:return this.x;case 1:return this.y;default:throw new Error("THREE.Vector2: index is out of range: "+e)}}clone(){return new this.constructor(this.x,this.y)}copy(e){return this.x=e.x,this.y=e.y,this}add(e){return this.x+=e.x,this.y+=e.y,this}addScalar(e){return this.x+=e,this.y+=e,this}addVectors(e,n){return this.x=e.x+n.x,this.y=e.y+n.y,this}addScaledVector(e,n){return this.x+=e.x*n,this.y+=e.y*n,this}sub(e){return this.x-=e.x,this.y-=e.y,this}subScalar(e){return this.x-=e,this.y-=e,this}subVectors(e,n){return this.x=e.x-n.x,this.y=e.y-n.y,this}multiply(e){return this.x*=e.x,this.y*=e.y,this}multiplyScalar(e){return this.x*=e,this.y*=e,this}divide(e){return this.x/=e.x,this.y/=e.y,this}divideScalar(e){return this.multiplyScalar(1/e)}applyMatrix3(e){const n=this.x,i=this.y,a=e.elements;return this.x=a[0]*n+a[3]*i+a[6],this.y=a[1]*n+a[4]*i+a[7],this}min(e){return this.x=Math.min(this.x,e.x),this.y=Math.min(this.y,e.y),this}max(e){return this.x=Math.max(this.x,e.x),this.y=Math.max(this.y,e.y),this}clamp(e,n){return this.x=pt(this.x,e.x,n.x),this.y=pt(this.y,e.y,n.y),this}clampScalar(e,n){return this.x=pt(this.x,e,n),this.y=pt(this.y,e,n),this}clampLength(e,n){const i=this.length();return this.divideScalar(i||1).multiplyScalar(pt(i,e,n))}floor(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this}ceil(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this}round(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this}roundToZero(){return this.x=Math.trunc(this.x),this.y=Math.trunc(this.y),this}negate(){return this.x=-this.x,this.y=-this.y,this}dot(e){return this.x*e.x+this.y*e.y}cross(e){return this.x*e.y-this.y*e.x}lengthSq(){return this.x*this.x+this.y*this.y}length(){return Math.sqrt(this.x*this.x+this.y*this.y)}manhattanLength(){return Math.abs(this.x)+Math.abs(this.y)}normalize(){return this.divideScalar(this.length()||1)}angle(){return Math.atan2(-this.y,-this.x)+Math.PI}angleTo(e){const n=Math.sqrt(this.lengthSq()*e.lengthSq());if(n===0)return Math.PI/2;const i=this.dot(e)/n;return Math.acos(pt(i,-1,1))}distanceTo(e){return Math.sqrt(this.distanceToSquared(e))}distanceToSquared(e){const n=this.x-e.x,i=this.y-e.y;return n*n+i*i}manhattanDistanceTo(e){return Math.abs(this.x-e.x)+Math.abs(this.y-e.y)}setLength(e){return this.normalize().multiplyScalar(e)}lerp(e,n){return this.x+=(e.x-this.x)*n,this.y+=(e.y-this.y)*n,this}lerpVectors(e,n,i){return this.x=e.x+(n.x-e.x)*i,this.y=e.y+(n.y-e.y)*i,this}equals(e){return e.x===this.x&&e.y===this.y}fromArray(e,n=0){return this.x=e[n],this.y=e[n+1],this}toArray(e=[],n=0){return e[n]=this.x,e[n+1]=this.y,e}fromBufferAttribute(e,n){return this.x=e.getX(n),this.y=e.getY(n),this}rotateAround(e,n){const i=Math.cos(n),a=Math.sin(n),s=this.x-e.x,l=this.y-e.y;return this.x=s*i-l*a+e.x,this.y=s*a+l*i+e.y,this}random(){return this.x=Math.random(),this.y=Math.random(),this}*[Symbol.iterator](){yield this.x,yield this.y}};Dg.prototype.isVector2=!0;let Mt=Dg;class Ll{constructor(e=0,n=0,i=0,a=1){this.isQuaternion=!0,this._x=e,this._y=n,this._z=i,this._w=a}static slerpFlat(e,n,i,a,s,l,o){let c=i[a+0],u=i[a+1],h=i[a+2],g=i[a+3],d=s[l+0],p=s[l+1],x=s[l+2],_=s[l+3];if(g!==_||c!==d||u!==p||h!==x){let m=c*d+u*p+h*x+g*_;m<0&&(d=-d,p=-p,x=-x,_=-_,m=-m);let f=1-o;if(m<.9995){const v=Math.acos(m),y=Math.sin(v);f=Math.sin(f*v)/y,o=Math.sin(o*v)/y,c=c*f+d*o,u=u*f+p*o,h=h*f+x*o,g=g*f+_*o}else{c=c*f+d*o,u=u*f+p*o,h=h*f+x*o,g=g*f+_*o;const v=1/Math.sqrt(c*c+u*u+h*h+g*g);c*=v,u*=v,h*=v,g*=v}}e[n]=c,e[n+1]=u,e[n+2]=h,e[n+3]=g}static multiplyQuaternionsFlat(e,n,i,a,s,l){const o=i[a],c=i[a+1],u=i[a+2],h=i[a+3],g=s[l],d=s[l+1],p=s[l+2],x=s[l+3];return e[n]=o*x+h*g+c*p-u*d,e[n+1]=c*x+h*d+u*g-o*p,e[n+2]=u*x+h*p+o*d-c*g,e[n+3]=h*x-o*g-c*d-u*p,e}get x(){return this._x}set x(e){this._x=e,this._onChangeCallback()}get y(){return this._y}set y(e){this._y=e,this._onChangeCallback()}get z(){return this._z}set z(e){this._z=e,this._onChangeCallback()}get w(){return this._w}set w(e){this._w=e,this._onChangeCallback()}set(e,n,i,a){return this._x=e,this._y=n,this._z=i,this._w=a,this._onChangeCallback(),this}clone(){return new this.constructor(this._x,this._y,this._z,this._w)}copy(e){return this._x=e.x,this._y=e.y,this._z=e.z,this._w=e.w,this._onChangeCallback(),this}setFromEuler(e,n=!0){const i=e._x,a=e._y,s=e._z,l=e._order,o=Math.cos,c=Math.sin,u=o(i/2),h=o(a/2),g=o(s/2),d=c(i/2),p=c(a/2),x=c(s/2);switch(l){case"XYZ":this._x=d*h*g+u*p*x,this._y=u*p*g-d*h*x,this._z=u*h*x+d*p*g,this._w=u*h*g-d*p*x;break;case"YXZ":this._x=d*h*g+u*p*x,this._y=u*p*g-d*h*x,this._z=u*h*x-d*p*g,this._w=u*h*g+d*p*x;break;case"ZXY":this._x=d*h*g-u*p*x,this._y=u*p*g+d*h*x,this._z=u*h*x+d*p*g,this._w=u*h*g-d*p*x;break;case"ZYX":this._x=d*h*g-u*p*x,this._y=u*p*g+d*h*x,this._z=u*h*x-d*p*g,this._w=u*h*g+d*p*x;break;case"YZX":this._x=d*h*g+u*p*x,this._y=u*p*g+d*h*x,this._z=u*h*x-d*p*g,this._w=u*h*g-d*p*x;break;case"XZY":this._x=d*h*g-u*p*x,this._y=u*p*g-d*h*x,this._z=u*h*x+d*p*g,this._w=u*h*g+d*p*x;break;default:Qe("Quaternion: .setFromEuler() encountered an unknown order: "+l)}return n===!0&&this._onChangeCallback(),this}setFromAxisAngle(e,n){const i=n/2,a=Math.sin(i);return this._x=e.x*a,this._y=e.y*a,this._z=e.z*a,this._w=Math.cos(i),this._onChangeCallback(),this}setFromRotationMatrix(e){const n=e.elements,i=n[0],a=n[4],s=n[8],l=n[1],o=n[5],c=n[9],u=n[2],h=n[6],g=n[10],d=i+o+g;if(d>0){const p=.5/Math.sqrt(d+1);this._w=.25/p,this._x=(h-c)*p,this._y=(s-u)*p,this._z=(l-a)*p}else if(i>o&&i>g){const p=2*Math.sqrt(1+i-o-g);this._w=(h-c)/p,this._x=.25*p,this._y=(a+l)/p,this._z=(s+u)/p}else if(o>g){const p=2*Math.sqrt(1+o-i-g);this._w=(s-u)/p,this._x=(a+l)/p,this._y=.25*p,this._z=(c+h)/p}else{const p=2*Math.sqrt(1+g-i-o);this._w=(l-a)/p,this._x=(s+u)/p,this._y=(c+h)/p,this._z=.25*p}return this._onChangeCallback(),this}setFromUnitVectors(e,n){let i=e.dot(n)+1;return i<1e-8?(i=0,Math.abs(e.x)>Math.abs(e.z)?(this._x=-e.y,this._y=e.x,this._z=0,this._w=i):(this._x=0,this._y=-e.z,this._z=e.y,this._w=i)):(this._x=e.y*n.z-e.z*n.y,this._y=e.z*n.x-e.x*n.z,this._z=e.x*n.y-e.y*n.x,this._w=i),this.normalize()}angleTo(e){return 2*Math.acos(Math.abs(pt(this.dot(e),-1,1)))}rotateTowards(e,n){const i=this.angleTo(e);if(i===0)return this;const a=Math.min(1,n/i);return this.slerp(e,a),this}identity(){return this.set(0,0,0,1)}invert(){return this.conjugate()}conjugate(){return this._x*=-1,this._y*=-1,this._z*=-1,this._onChangeCallback(),this}dot(e){return this._x*e._x+this._y*e._y+this._z*e._z+this._w*e._w}lengthSq(){return this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w}length(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)}normalize(){let e=this.length();return e===0?(this._x=0,this._y=0,this._z=0,this._w=1):(e=1/e,this._x=this._x*e,this._y=this._y*e,this._z=this._z*e,this._w=this._w*e),this._onChangeCallback(),this}multiply(e){return this.multiplyQuaternions(this,e)}premultiply(e){return this.multiplyQuaternions(e,this)}multiplyQuaternions(e,n){const i=e._x,a=e._y,s=e._z,l=e._w,o=n._x,c=n._y,u=n._z,h=n._w;return this._x=i*h+l*o+a*u-s*c,this._y=a*h+l*c+s*o-i*u,this._z=s*h+l*u+i*c-a*o,this._w=l*h-i*o-a*c-s*u,this._onChangeCallback(),this}slerp(e,n){let i=e._x,a=e._y,s=e._z,l=e._w,o=this.dot(e);o<0&&(i=-i,a=-a,s=-s,l=-l,o=-o);let c=1-n;if(o<.9995){const u=Math.acos(o),h=Math.sin(u);c=Math.sin(c*u)/h,n=Math.sin(n*u)/h,this._x=this._x*c+i*n,this._y=this._y*c+a*n,this._z=this._z*c+s*n,this._w=this._w*c+l*n,this._onChangeCallback()}else this._x=this._x*c+i*n,this._y=this._y*c+a*n,this._z=this._z*c+s*n,this._w=this._w*c+l*n,this.normalize();return this}slerpQuaternions(e,n,i){return this.copy(e).slerp(n,i)}random(){const e=2*Math.PI*Math.random(),n=2*Math.PI*Math.random(),i=Math.random(),a=Math.sqrt(1-i),s=Math.sqrt(i);return this.set(a*Math.sin(e),a*Math.cos(e),s*Math.sin(n),s*Math.cos(n))}equals(e){return e._x===this._x&&e._y===this._y&&e._z===this._z&&e._w===this._w}fromArray(e,n=0){return this._x=e[n],this._y=e[n+1],this._z=e[n+2],this._w=e[n+3],this._onChangeCallback(),this}toArray(e=[],n=0){return e[n]=this._x,e[n+1]=this._y,e[n+2]=this._z,e[n+3]=this._w,e}fromBufferAttribute(e,n){return this._x=e.getX(n),this._y=e.getY(n),this._z=e.getZ(n),this._w=e.getW(n),this._onChangeCallback(),this}toJSON(){return this.toArray()}_onChange(e){return this._onChangeCallback=e,this}_onChangeCallback(){}*[Symbol.iterator](){yield this._x,yield this._y,yield this._z,yield this._w}}const Lg=class Lg{constructor(e=0,n=0,i=0){this.x=e,this.y=n,this.z=i}set(e,n,i){return i===void 0&&(i=this.z),this.x=e,this.y=n,this.z=i,this}setScalar(e){return this.x=e,this.y=e,this.z=e,this}setX(e){return this.x=e,this}setY(e){return this.y=e,this}setZ(e){return this.z=e,this}setComponent(e,n){switch(e){case 0:this.x=n;break;case 1:this.y=n;break;case 2:this.z=n;break;default:throw new Error("THREE.Vector3: index is out of range: "+e)}return this}getComponent(e){switch(e){case 0:return this.x;case 1:return this.y;case 2:return this.z;default:throw new Error("THREE.Vector3: index is out of range: "+e)}}clone(){return new this.constructor(this.x,this.y,this.z)}copy(e){return this.x=e.x,this.y=e.y,this.z=e.z,this}add(e){return this.x+=e.x,this.y+=e.y,this.z+=e.z,this}addScalar(e){return this.x+=e,this.y+=e,this.z+=e,this}addVectors(e,n){return this.x=e.x+n.x,this.y=e.y+n.y,this.z=e.z+n.z,this}addScaledVector(e,n){return this.x+=e.x*n,this.y+=e.y*n,this.z+=e.z*n,this}sub(e){return this.x-=e.x,this.y-=e.y,this.z-=e.z,this}subScalar(e){return this.x-=e,this.y-=e,this.z-=e,this}subVectors(e,n){return this.x=e.x-n.x,this.y=e.y-n.y,this.z=e.z-n.z,this}multiply(e){return this.x*=e.x,this.y*=e.y,this.z*=e.z,this}multiplyScalar(e){return this.x*=e,this.y*=e,this.z*=e,this}multiplyVectors(e,n){return this.x=e.x*n.x,this.y=e.y*n.y,this.z=e.z*n.z,this}applyEuler(e){return this.applyQuaternion(Nv.setFromEuler(e))}applyAxisAngle(e,n){return this.applyQuaternion(Nv.setFromAxisAngle(e,n))}applyMatrix3(e){const n=this.x,i=this.y,a=this.z,s=e.elements;return this.x=s[0]*n+s[3]*i+s[6]*a,this.y=s[1]*n+s[4]*i+s[7]*a,this.z=s[2]*n+s[5]*i+s[8]*a,this}applyNormalMatrix(e){return this.applyMatrix3(e).normalize()}applyMatrix4(e){const n=this.x,i=this.y,a=this.z,s=e.elements,l=1/(s[3]*n+s[7]*i+s[11]*a+s[15]);return this.x=(s[0]*n+s[4]*i+s[8]*a+s[12])*l,this.y=(s[1]*n+s[5]*i+s[9]*a+s[13])*l,this.z=(s[2]*n+s[6]*i+s[10]*a+s[14])*l,this}applyQuaternion(e){const n=this.x,i=this.y,a=this.z,s=e.x,l=e.y,o=e.z,c=e.w,u=2*(l*a-o*i),h=2*(o*n-s*a),g=2*(s*i-l*n);return this.x=n+c*u+l*g-o*h,this.y=i+c*h+o*u-s*g,this.z=a+c*g+s*h-l*u,this}project(e){return this.applyMatrix4(e.matrixWorldInverse).applyMatrix4(e.projectionMatrix)}unproject(e){return this.applyMatrix4(e.projectionMatrixInverse).applyMatrix4(e.matrixWorld)}transformDirection(e){const n=this.x,i=this.y,a=this.z,s=e.elements;return this.x=s[0]*n+s[4]*i+s[8]*a,this.y=s[1]*n+s[5]*i+s[9]*a,this.z=s[2]*n+s[6]*i+s[10]*a,this.normalize()}divide(e){return this.x/=e.x,this.y/=e.y,this.z/=e.z,this}divideScalar(e){return this.multiplyScalar(1/e)}min(e){return this.x=Math.min(this.x,e.x),this.y=Math.min(this.y,e.y),this.z=Math.min(this.z,e.z),this}max(e){return this.x=Math.max(this.x,e.x),this.y=Math.max(this.y,e.y),this.z=Math.max(this.z,e.z),this}clamp(e,n){return this.x=pt(this.x,e.x,n.x),this.y=pt(this.y,e.y,n.y),this.z=pt(this.z,e.z,n.z),this}clampScalar(e,n){return this.x=pt(this.x,e,n),this.y=pt(this.y,e,n),this.z=pt(this.z,e,n),this}clampLength(e,n){const i=this.length();return this.divideScalar(i||1).multiplyScalar(pt(i,e,n))}floor(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this}ceil(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this}round(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this}roundToZero(){return this.x=Math.trunc(this.x),this.y=Math.trunc(this.y),this.z=Math.trunc(this.z),this}negate(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this}dot(e){return this.x*e.x+this.y*e.y+this.z*e.z}lengthSq(){return this.x*this.x+this.y*this.y+this.z*this.z}length(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)}manhattanLength(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)}normalize(){return this.divideScalar(this.length()||1)}setLength(e){return this.normalize().multiplyScalar(e)}lerp(e,n){return this.x+=(e.x-this.x)*n,this.y+=(e.y-this.y)*n,this.z+=(e.z-this.z)*n,this}lerpVectors(e,n,i){return this.x=e.x+(n.x-e.x)*i,this.y=e.y+(n.y-e.y)*i,this.z=e.z+(n.z-e.z)*i,this}cross(e){return this.crossVectors(this,e)}crossVectors(e,n){const i=e.x,a=e.y,s=e.z,l=n.x,o=n.y,c=n.z;return this.x=a*c-s*o,this.y=s*l-i*c,this.z=i*o-a*l,this}projectOnVector(e){const n=e.lengthSq();if(n===0)return this.set(0,0,0);const i=e.dot(this)/n;return this.copy(e).multiplyScalar(i)}projectOnPlane(e){return Uh.copy(this).projectOnVector(e),this.sub(Uh)}reflect(e){return this.sub(Uh.copy(e).multiplyScalar(2*this.dot(e)))}angleTo(e){const n=Math.sqrt(this.lengthSq()*e.lengthSq());if(n===0)return Math.PI/2;const i=this.dot(e)/n;return Math.acos(pt(i,-1,1))}distanceTo(e){return Math.sqrt(this.distanceToSquared(e))}distanceToSquared(e){const n=this.x-e.x,i=this.y-e.y,a=this.z-e.z;return n*n+i*i+a*a}manhattanDistanceTo(e){return Math.abs(this.x-e.x)+Math.abs(this.y-e.y)+Math.abs(this.z-e.z)}setFromSpherical(e){return this.setFromSphericalCoords(e.radius,e.phi,e.theta)}setFromSphericalCoords(e,n,i){const a=Math.sin(n)*e;return this.x=a*Math.sin(i),this.y=Math.cos(n)*e,this.z=a*Math.cos(i),this}setFromCylindrical(e){return this.setFromCylindricalCoords(e.radius,e.theta,e.y)}setFromCylindricalCoords(e,n,i){return this.x=e*Math.sin(n),this.y=i,this.z=e*Math.cos(n),this}setFromMatrixPosition(e){const n=e.elements;return this.x=n[12],this.y=n[13],this.z=n[14],this}setFromMatrixScale(e){const n=this.setFromMatrixColumn(e,0).length(),i=this.setFromMatrixColumn(e,1).length(),a=this.setFromMatrixColumn(e,2).length();return this.x=n,this.y=i,this.z=a,this}setFromMatrixColumn(e,n){return this.fromArray(e.elements,n*4)}setFromMatrix3Column(e,n){return this.fromArray(e.elements,n*3)}setFromEuler(e){return this.x=e._x,this.y=e._y,this.z=e._z,this}setFromColor(e){return this.x=e.r,this.y=e.g,this.z=e.b,this}equals(e){return e.x===this.x&&e.y===this.y&&e.z===this.z}fromArray(e,n=0){return this.x=e[n],this.y=e[n+1],this.z=e[n+2],this}toArray(e=[],n=0){return e[n]=this.x,e[n+1]=this.y,e[n+2]=this.z,e}fromBufferAttribute(e,n){return this.x=e.getX(n),this.y=e.getY(n),this.z=e.getZ(n),this}random(){return this.x=Math.random(),this.y=Math.random(),this.z=Math.random(),this}randomDirection(){const e=Math.random()*Math.PI*2,n=Math.random()*2-1,i=Math.sqrt(1-n*n);return this.x=i*Math.cos(e),this.y=n,this.z=i*Math.sin(e),this}*[Symbol.iterator](){yield this.x,yield this.y,yield this.z}};Lg.prototype.isVector3=!0;let Z=Lg;const Uh=new Z,Nv=new Ll,Ug=class Ug{constructor(e,n,i,a,s,l,o,c,u){this.elements=[1,0,0,0,1,0,0,0,1],e!==void 0&&this.set(e,n,i,a,s,l,o,c,u)}set(e,n,i,a,s,l,o,c,u){const h=this.elements;return h[0]=e,h[1]=a,h[2]=o,h[3]=n,h[4]=s,h[5]=c,h[6]=i,h[7]=l,h[8]=u,this}identity(){return this.set(1,0,0,0,1,0,0,0,1),this}copy(e){const n=this.elements,i=e.elements;return n[0]=i[0],n[1]=i[1],n[2]=i[2],n[3]=i[3],n[4]=i[4],n[5]=i[5],n[6]=i[6],n[7]=i[7],n[8]=i[8],this}extractBasis(e,n,i){return e.setFromMatrix3Column(this,0),n.setFromMatrix3Column(this,1),i.setFromMatrix3Column(this,2),this}setFromMatrix4(e){const n=e.elements;return this.set(n[0],n[4],n[8],n[1],n[5],n[9],n[2],n[6],n[10]),this}multiply(e){return this.multiplyMatrices(this,e)}premultiply(e){return this.multiplyMatrices(e,this)}multiplyMatrices(e,n){const i=e.elements,a=n.elements,s=this.elements,l=i[0],o=i[3],c=i[6],u=i[1],h=i[4],g=i[7],d=i[2],p=i[5],x=i[8],_=a[0],m=a[3],f=a[6],v=a[1],y=a[4],b=a[7],T=a[2],M=a[5],A=a[8];return s[0]=l*_+o*v+c*T,s[3]=l*m+o*y+c*M,s[6]=l*f+o*b+c*A,s[1]=u*_+h*v+g*T,s[4]=u*m+h*y+g*M,s[7]=u*f+h*b+g*A,s[2]=d*_+p*v+x*T,s[5]=d*m+p*y+x*M,s[8]=d*f+p*b+x*A,this}multiplyScalar(e){const n=this.elements;return n[0]*=e,n[3]*=e,n[6]*=e,n[1]*=e,n[4]*=e,n[7]*=e,n[2]*=e,n[5]*=e,n[8]*=e,this}determinant(){const e=this.elements,n=e[0],i=e[1],a=e[2],s=e[3],l=e[4],o=e[5],c=e[6],u=e[7],h=e[8];return n*l*h-n*o*u-i*s*h+i*o*c+a*s*u-a*l*c}invert(){const e=this.elements,n=e[0],i=e[1],a=e[2],s=e[3],l=e[4],o=e[5],c=e[6],u=e[7],h=e[8],g=h*l-o*u,d=o*c-h*s,p=u*s-l*c,x=n*g+i*d+a*p;if(x===0)return this.set(0,0,0,0,0,0,0,0,0);const _=1/x;return e[0]=g*_,e[1]=(a*u-h*i)*_,e[2]=(o*i-a*l)*_,e[3]=d*_,e[4]=(h*n-a*c)*_,e[5]=(a*s-o*n)*_,e[6]=p*_,e[7]=(i*c-u*n)*_,e[8]=(l*n-i*s)*_,this}transpose(){let e;const n=this.elements;return e=n[1],n[1]=n[3],n[3]=e,e=n[2],n[2]=n[6],n[6]=e,e=n[5],n[5]=n[7],n[7]=e,this}getNormalMatrix(e){return this.setFromMatrix4(e).invert().transpose()}transposeIntoArray(e){const n=this.elements;return e[0]=n[0],e[1]=n[3],e[2]=n[6],e[3]=n[1],e[4]=n[4],e[5]=n[7],e[6]=n[2],e[7]=n[5],e[8]=n[8],this}setUvTransform(e,n,i,a,s,l,o){const c=Math.cos(s),u=Math.sin(s);return this.set(i*c,i*u,-i*(c*l+u*o)+l+e,-a*u,a*c,-a*(-u*l+c*o)+o+n,0,0,1),this}scale(e,n){return al("Matrix3: .scale() is deprecated. Use .makeScale() instead."),this.premultiply(Oh.makeScale(e,n)),this}rotate(e){return al("Matrix3: .rotate() is deprecated. Use .makeRotation() instead."),this.premultiply(Oh.makeRotation(-e)),this}translate(e,n){return al("Matrix3: .translate() is deprecated. Use .makeTranslation() instead."),this.premultiply(Oh.makeTranslation(e,n)),this}makeTranslation(e,n){return e.isVector2?this.set(1,0,e.x,0,1,e.y,0,0,1):this.set(1,0,e,0,1,n,0,0,1),this}makeRotation(e){const n=Math.cos(e),i=Math.sin(e);return this.set(n,-i,0,i,n,0,0,0,1),this}makeScale(e,n){return this.set(e,0,0,0,n,0,0,0,1),this}equals(e){const n=this.elements,i=e.elements;for(let a=0;a<9;a++)if(n[a]!==i[a])return!1;return!0}fromArray(e,n=0){for(let i=0;i<9;i++)this.elements[i]=e[i+n];return this}toArray(e=[],n=0){const i=this.elements;return e[n]=i[0],e[n+1]=i[1],e[n+2]=i[2],e[n+3]=i[3],e[n+4]=i[4],e[n+5]=i[5],e[n+6]=i[6],e[n+7]=i[7],e[n+8]=i[8],e}clone(){return new this.constructor().fromArray(this.elements)}};Ug.prototype.isMatrix3=!0;let tt=Ug;const Oh=new tt,Cv=new tt().set(.4123908,.3575843,.1804808,.212639,.7151687,.0721923,.0193308,.1191948,.9505322),Rv=new tt().set(3.2409699,-1.5373832,-.4986108,-.9692436,1.8759675,.0415551,.0556301,-.203977,1.0569715);function CA(){const t={enabled:!0,workingColorSpace:dd,spaces:{},convert:function(a,s,l){return this.enabled===!1||s===l||!s||!l||(this.spaces[s].transfer===Et&&(a.r=Ea(a.r),a.g=Ea(a.g),a.b=Ea(a.b)),this.spaces[s].primaries!==this.spaces[l].primaries&&(a.applyMatrix3(this.spaces[s].toXYZ),a.applyMatrix3(this.spaces[l].fromXYZ)),this.spaces[l].transfer===Et&&(a.r=sl(a.r),a.g=sl(a.g),a.b=sl(a.b))),a},workingToColorSpace:function(a,s){return this.convert(a,this.workingColorSpace,s)},colorSpaceToWorking:function(a,s){return this.convert(a,s,this.workingColorSpace)},getPrimaries:function(a){return this.spaces[a].primaries},getTransfer:function(a){return a===Qa?hd:this.spaces[a].transfer},getToneMappingMode:function(a){return this.spaces[a].outputColorSpaceConfig.toneMappingMode||"standard"},getLuminanceCoefficients:function(a,s=this.workingColorSpace){return a.fromArray(this.spaces[s].luminanceCoefficients)},define:function(a){Object.assign(this.spaces,a)},_getMatrix:function(a,s,l){return a.copy(this.spaces[s].toXYZ).multiply(this.spaces[l].fromXYZ)},_getDrawingBufferColorSpace:function(a){return this.spaces[a].outputColorSpaceConfig.drawingBufferColorSpace},_getUnpackColorSpace:function(a=this.workingColorSpace){return this.spaces[a].workingColorSpaceConfig.unpackColorSpace},fromWorkingColorSpace:function(a,s){return al("ColorManagement: .fromWorkingColorSpace() has been renamed to .workingToColorSpace()."),t.workingToColorSpace(a,s)},toWorkingColorSpace:function(a,s){return al("ColorManagement: .toWorkingColorSpace() has been renamed to .colorSpaceToWorking()."),t.colorSpaceToWorking(a,s)}},e=[.64,.33,.3,.6,.15,.06],n=[.2126,.7152,.0722],i=[.3127,.329];return t.define({[dd]:{primaries:e,whitePoint:i,transfer:hd,toXYZ:Cv,fromXYZ:Rv,luminanceCoefficients:n,workingColorSpaceConfig:{unpackColorSpace:di},outputColorSpaceConfig:{drawingBufferColorSpace:di}},[di]:{primaries:e,whitePoint:i,transfer:Et,toXYZ:Cv,fromXYZ:Rv,luminanceCoefficients:n,outputColorSpaceConfig:{drawingBufferColorSpace:di}}}),t}const ft=CA();function Ea(t){return t<.04045?t*.0773993808:Math.pow(t*.9478672986+.0521327014,2.4)}function sl(t){return t<.0031308?t*12.92:1.055*Math.pow(t,.41666)-.055}let gr;class RA{static getDataURL(e,n="image/png"){if(/^data:/i.test(e.src)||typeof HTMLCanvasElement>"u")return e.src;let i;if(e instanceof HTMLCanvasElement)i=e;else{gr===void 0&&(gr=pd("canvas")),gr.width=e.width,gr.height=e.height;const a=gr.getContext("2d");e instanceof ImageData?a.putImageData(e,0,0):a.drawImage(e,0,0,e.width,e.height),i=gr}return i.toDataURL(n)}static sRGBToLinear(e){if(typeof HTMLImageElement<"u"&&e instanceof HTMLImageElement||typeof HTMLCanvasElement<"u"&&e instanceof HTMLCanvasElement||typeof ImageBitmap<"u"&&e instanceof ImageBitmap){const n=pd("canvas");n.width=e.width,n.height=e.height;const i=n.getContext("2d");i.drawImage(e,0,0,e.width,e.height);const a=i.getImageData(0,0,e.width,e.height),s=a.data;for(let l=0;l<s.length;l++)s[l]=Ea(s[l]/255)*255;return i.putImageData(a,0,0),n}else if(e.data){const n=e.data.slice(0);for(let i=0;i<n.length;i++)n instanceof Uint8Array||n instanceof Uint8ClampedArray?n[i]=Math.floor(Ea(n[i]/255)*255):n[i]=Ea(n[i]);return{data:n,width:e.width,height:e.height}}else return Qe("ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied."),e}}let DA=0;class Tg{constructor(e=null){this.isSource=!0,Object.defineProperty(this,"id",{value:DA++}),this.uuid=lc(),this.data=e,this.dataReady=!0,this.version=0}getSize(e){const n=this.data;return typeof HTMLVideoElement<"u"&&n instanceof HTMLVideoElement?e.set(n.videoWidth,n.videoHeight,0):typeof VideoFrame<"u"&&n instanceof VideoFrame?e.set(n.displayWidth,n.displayHeight,0):n!==null?e.set(n.width,n.height,n.depth||0):e.set(0,0,0),e}set needsUpdate(e){e===!0&&this.version++}toJSON(e){const n=e===void 0||typeof e=="string";if(!n&&e.images[this.uuid]!==void 0)return e.images[this.uuid];const i={uuid:this.uuid,url:""},a=this.data;if(a!==null){let s;if(Array.isArray(a)){s=[];for(let l=0,o=a.length;l<o;l++)a[l].isDataTexture?s.push(Ph(a[l].image)):s.push(Ph(a[l]))}else s=Ph(a);i.url=s}return n||(e.images[this.uuid]=i),i}}function Ph(t){return typeof HTMLImageElement<"u"&&t instanceof HTMLImageElement||typeof HTMLCanvasElement<"u"&&t instanceof HTMLCanvasElement||typeof ImageBitmap<"u"&&t instanceof ImageBitmap?RA.getDataURL(t):t.data?{data:Array.from(t.data),width:t.width,height:t.height,type:t.data.constructor.name}:(Qe("Texture: Unable to serialize Texture."),{})}let LA=0;const zh=new Z;class Un extends rr{constructor(e=Un.DEFAULT_IMAGE,n=Un.DEFAULT_MAPPING,i=_a,a=_a,s=wn,l=ks,o=Ui,c=vi,u=Un.DEFAULT_ANISOTROPY,h=Qa){super(),this.isTexture=!0,Object.defineProperty(this,"id",{value:LA++}),this.uuid=lc(),this.name="",this.source=new Tg(e),this.mipmaps=[],this.mapping=n,this.channel=0,this.wrapS=i,this.wrapT=a,this.magFilter=s,this.minFilter=l,this.anisotropy=u,this.format=o,this.internalFormat=null,this.type=c,this.offset=new Mt(0,0),this.repeat=new Mt(1,1),this.center=new Mt(0,0),this.rotation=0,this.matrixAutoUpdate=!0,this.matrix=new tt,this.generateMipmaps=!0,this.premultiplyAlpha=!1,this.flipY=!0,this.unpackAlignment=4,this.colorSpace=h,this.userData={},this.updateRanges=[],this.version=0,this.onUpdate=null,this.renderTarget=null,this.isRenderTargetTexture=!1,this.isArrayTexture=!!(e&&e.depth&&e.depth>1),this.pmremVersion=0,this.normalized=!1}get width(){return this.source.getSize(zh).x}get height(){return this.source.getSize(zh).y}get depth(){return this.source.getSize(zh).z}get image(){return this.source.data}set image(e){this.source.data=e}updateMatrix(){this.matrix.setUvTransform(this.offset.x,this.offset.y,this.repeat.x,this.repeat.y,this.rotation,this.center.x,this.center.y)}addUpdateRange(e,n){this.updateRanges.push({start:e,count:n})}clearUpdateRanges(){this.updateRanges.length=0}clone(){return new this.constructor().copy(this)}copy(e){return this.name=e.name,this.source=e.source,this.mipmaps=e.mipmaps.slice(0),this.mapping=e.mapping,this.channel=e.channel,this.wrapS=e.wrapS,this.wrapT=e.wrapT,this.magFilter=e.magFilter,this.minFilter=e.minFilter,this.anisotropy=e.anisotropy,this.format=e.format,this.internalFormat=e.internalFormat,this.type=e.type,this.normalized=e.normalized,this.offset.copy(e.offset),this.repeat.copy(e.repeat),this.center.copy(e.center),this.rotation=e.rotation,this.matrixAutoUpdate=e.matrixAutoUpdate,this.matrix.copy(e.matrix),this.generateMipmaps=e.generateMipmaps,this.premultiplyAlpha=e.premultiplyAlpha,this.flipY=e.flipY,this.unpackAlignment=e.unpackAlignment,this.colorSpace=e.colorSpace,this.renderTarget=e.renderTarget,this.isRenderTargetTexture=e.isRenderTargetTexture,this.isArrayTexture=e.isArrayTexture,this.userData=JSON.parse(JSON.stringify(e.userData)),this.needsUpdate=!0,this}setValues(e){for(const n in e){const i=e[n];if(i===void 0){Qe(`Texture.setValues(): parameter '${n}' has value of undefined.`);continue}const a=this[n];if(a===void 0){Qe(`Texture.setValues(): property '${n}' does not exist.`);continue}a&&i&&a.isVector2&&i.isVector2||a&&i&&a.isVector3&&i.isVector3||a&&i&&a.isMatrix3&&i.isMatrix3?a.copy(i):this[n]=i}}toJSON(e){const n=e===void 0||typeof e=="string";if(!n&&e.textures[this.uuid]!==void 0)return e.textures[this.uuid];const i={metadata:{version:4.7,type:"Texture",generator:"Texture.toJSON"},uuid:this.uuid,name:this.name,image:this.source.toJSON(e).uuid,mapping:this.mapping,channel:this.channel,repeat:[this.repeat.x,this.repeat.y],offset:[this.offset.x,this.offset.y],center:[this.center.x,this.center.y],rotation:this.rotation,wrap:[this.wrapS,this.wrapT],format:this.format,internalFormat:this.internalFormat,type:this.type,normalized:this.normalized,colorSpace:this.colorSpace,minFilter:this.minFilter,magFilter:this.magFilter,anisotropy:this.anisotropy,flipY:this.flipY,generateMipmaps:this.generateMipmaps,premultiplyAlpha:this.premultiplyAlpha,unpackAlignment:this.unpackAlignment};return Object.keys(this.userData).length>0&&(i.userData=this.userData),n||(e.textures[this.uuid]=i),i}dispose(){this.dispatchEvent({type:"dispose"})}transformUv(e){if(this.mapping!==xS)return e;if(e.applyMatrix3(this.matrix),e.x<0||e.x>1)switch(this.wrapS){case wp:e.x=e.x-Math.floor(e.x);break;case _a:e.x=e.x<0?0:1;break;case Np:Math.abs(Math.floor(e.x)%2)===1?e.x=Math.ceil(e.x)-e.x:e.x=e.x-Math.floor(e.x);break}if(e.y<0||e.y>1)switch(this.wrapT){case wp:e.y=e.y-Math.floor(e.y);break;case _a:e.y=e.y<0?0:1;break;case Np:Math.abs(Math.floor(e.y)%2)===1?e.y=Math.ceil(e.y)-e.y:e.y=e.y-Math.floor(e.y);break}return this.flipY&&(e.y=1-e.y),e}set needsUpdate(e){e===!0&&(this.version++,this.source.needsUpdate=!0)}set needsPMREMUpdate(e){e===!0&&this.pmremVersion++}}Un.DEFAULT_IMAGE=null;Un.DEFAULT_MAPPING=xS;Un.DEFAULT_ANISOTROPY=1;const Og=class Og{constructor(e=0,n=0,i=0,a=1){this.x=e,this.y=n,this.z=i,this.w=a}get width(){return this.z}set width(e){this.z=e}get height(){return this.w}set height(e){this.w=e}set(e,n,i,a){return this.x=e,this.y=n,this.z=i,this.w=a,this}setScalar(e){return this.x=e,this.y=e,this.z=e,this.w=e,this}setX(e){return this.x=e,this}setY(e){return this.y=e,this}setZ(e){return this.z=e,this}setW(e){return this.w=e,this}setComponent(e,n){switch(e){case 0:this.x=n;break;case 1:this.y=n;break;case 2:this.z=n;break;case 3:this.w=n;break;default:throw new Error("THREE.Vector4: index is out of range: "+e)}return this}getComponent(e){switch(e){case 0:return this.x;case 1:return this.y;case 2:return this.z;case 3:return this.w;default:throw new Error("THREE.Vector4: index is out of range: "+e)}}clone(){return new this.constructor(this.x,this.y,this.z,this.w)}copy(e){return this.x=e.x,this.y=e.y,this.z=e.z,this.w=e.w!==void 0?e.w:1,this}add(e){return this.x+=e.x,this.y+=e.y,this.z+=e.z,this.w+=e.w,this}addScalar(e){return this.x+=e,this.y+=e,this.z+=e,this.w+=e,this}addVectors(e,n){return this.x=e.x+n.x,this.y=e.y+n.y,this.z=e.z+n.z,this.w=e.w+n.w,this}addScaledVector(e,n){return this.x+=e.x*n,this.y+=e.y*n,this.z+=e.z*n,this.w+=e.w*n,this}sub(e){return this.x-=e.x,this.y-=e.y,this.z-=e.z,this.w-=e.w,this}subScalar(e){return this.x-=e,this.y-=e,this.z-=e,this.w-=e,this}subVectors(e,n){return this.x=e.x-n.x,this.y=e.y-n.y,this.z=e.z-n.z,this.w=e.w-n.w,this}multiply(e){return this.x*=e.x,this.y*=e.y,this.z*=e.z,this.w*=e.w,this}multiplyScalar(e){return this.x*=e,this.y*=e,this.z*=e,this.w*=e,this}applyMatrix4(e){const n=this.x,i=this.y,a=this.z,s=this.w,l=e.elements;return this.x=l[0]*n+l[4]*i+l[8]*a+l[12]*s,this.y=l[1]*n+l[5]*i+l[9]*a+l[13]*s,this.z=l[2]*n+l[6]*i+l[10]*a+l[14]*s,this.w=l[3]*n+l[7]*i+l[11]*a+l[15]*s,this}divide(e){return this.x/=e.x,this.y/=e.y,this.z/=e.z,this.w/=e.w,this}divideScalar(e){return this.multiplyScalar(1/e)}setAxisAngleFromQuaternion(e){this.w=2*Math.acos(e.w);const n=Math.sqrt(1-e.w*e.w);return n<1e-4?(this.x=1,this.y=0,this.z=0):(this.x=e.x/n,this.y=e.y/n,this.z=e.z/n),this}setAxisAngleFromRotationMatrix(e){let n,i,a,s;const c=e.elements,u=c[0],h=c[4],g=c[8],d=c[1],p=c[5],x=c[9],_=c[2],m=c[6],f=c[10];if(Math.abs(h-d)<.01&&Math.abs(g-_)<.01&&Math.abs(x-m)<.01){if(Math.abs(h+d)<.1&&Math.abs(g+_)<.1&&Math.abs(x+m)<.1&&Math.abs(u+p+f-3)<.1)return this.set(1,0,0,0),this;n=Math.PI;const y=(u+1)/2,b=(p+1)/2,T=(f+1)/2,M=(h+d)/4,A=(g+_)/4,S=(x+m)/4;return y>b&&y>T?y<.01?(i=0,a=.707106781,s=.707106781):(i=Math.sqrt(y),a=M/i,s=A/i):b>T?b<.01?(i=.707106781,a=0,s=.707106781):(a=Math.sqrt(b),i=M/a,s=S/a):T<.01?(i=.707106781,a=.707106781,s=0):(s=Math.sqrt(T),i=A/s,a=S/s),this.set(i,a,s,n),this}let v=Math.sqrt((m-x)*(m-x)+(g-_)*(g-_)+(d-h)*(d-h));return Math.abs(v)<.001&&(v=1),this.x=(m-x)/v,this.y=(g-_)/v,this.z=(d-h)/v,this.w=Math.acos((u+p+f-1)/2),this}setFromMatrixPosition(e){const n=e.elements;return this.x=n[12],this.y=n[13],this.z=n[14],this.w=n[15],this}min(e){return this.x=Math.min(this.x,e.x),this.y=Math.min(this.y,e.y),this.z=Math.min(this.z,e.z),this.w=Math.min(this.w,e.w),this}max(e){return this.x=Math.max(this.x,e.x),this.y=Math.max(this.y,e.y),this.z=Math.max(this.z,e.z),this.w=Math.max(this.w,e.w),this}clamp(e,n){return this.x=pt(this.x,e.x,n.x),this.y=pt(this.y,e.y,n.y),this.z=pt(this.z,e.z,n.z),this.w=pt(this.w,e.w,n.w),this}clampScalar(e,n){return this.x=pt(this.x,e,n),this.y=pt(this.y,e,n),this.z=pt(this.z,e,n),this.w=pt(this.w,e,n),this}clampLength(e,n){const i=this.length();return this.divideScalar(i||1).multiplyScalar(pt(i,e,n))}floor(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this.w=Math.floor(this.w),this}ceil(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this.w=Math.ceil(this.w),this}round(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this.w=Math.round(this.w),this}roundToZero(){return this.x=Math.trunc(this.x),this.y=Math.trunc(this.y),this.z=Math.trunc(this.z),this.w=Math.trunc(this.w),this}negate(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this.w=-this.w,this}dot(e){return this.x*e.x+this.y*e.y+this.z*e.z+this.w*e.w}lengthSq(){return this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w}length(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w)}manhattanLength(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)+Math.abs(this.w)}normalize(){return this.divideScalar(this.length()||1)}setLength(e){return this.normalize().multiplyScalar(e)}lerp(e,n){return this.x+=(e.x-this.x)*n,this.y+=(e.y-this.y)*n,this.z+=(e.z-this.z)*n,this.w+=(e.w-this.w)*n,this}lerpVectors(e,n,i){return this.x=e.x+(n.x-e.x)*i,this.y=e.y+(n.y-e.y)*i,this.z=e.z+(n.z-e.z)*i,this.w=e.w+(n.w-e.w)*i,this}equals(e){return e.x===this.x&&e.y===this.y&&e.z===this.z&&e.w===this.w}fromArray(e,n=0){return this.x=e[n],this.y=e[n+1],this.z=e[n+2],this.w=e[n+3],this}toArray(e=[],n=0){return e[n]=this.x,e[n+1]=this.y,e[n+2]=this.z,e[n+3]=this.w,e}fromBufferAttribute(e,n){return this.x=e.getX(n),this.y=e.getY(n),this.z=e.getZ(n),this.w=e.getW(n),this}random(){return this.x=Math.random(),this.y=Math.random(),this.z=Math.random(),this.w=Math.random(),this}*[Symbol.iterator](){yield this.x,yield this.y,yield this.z,yield this.w}};Og.prototype.isVector4=!0;let Yt=Og;class UA extends rr{constructor(e=1,n=1,i={}){super(),i=Object.assign({generateMipmaps:!1,internalFormat:null,minFilter:wn,depthBuffer:!0,stencilBuffer:!1,resolveDepthBuffer:!0,resolveStencilBuffer:!0,depthTexture:null,samples:0,count:1,depth:1,multiview:!1,useArrayDepthTexture:!1},i),this.isRenderTarget=!0,this.width=e,this.height=n,this.depth=i.depth,this.scissor=new Yt(0,0,e,n),this.scissorTest=!1,this.viewport=new Yt(0,0,e,n),this.textures=[];const a={width:e,height:n,depth:i.depth},s=new Un(a),l=i.count;for(let o=0;o<l;o++)this.textures[o]=s.clone(),this.textures[o].isRenderTargetTexture=!0,this.textures[o].renderTarget=this;this._setTextureOptions(i),this.depthBuffer=i.depthBuffer,this.stencilBuffer=i.stencilBuffer,this.resolveDepthBuffer=i.resolveDepthBuffer,this.resolveStencilBuffer=i.resolveStencilBuffer,this._depthTexture=null,this.depthTexture=i.depthTexture,this.samples=i.samples,this.multiview=i.multiview,this.useArrayDepthTexture=i.useArrayDepthTexture}_setTextureOptions(e={}){const n={minFilter:wn,generateMipmaps:!1,flipY:!1,internalFormat:null};e.mapping!==void 0&&(n.mapping=e.mapping),e.wrapS!==void 0&&(n.wrapS=e.wrapS),e.wrapT!==void 0&&(n.wrapT=e.wrapT),e.wrapR!==void 0&&(n.wrapR=e.wrapR),e.magFilter!==void 0&&(n.magFilter=e.magFilter),e.minFilter!==void 0&&(n.minFilter=e.minFilter),e.format!==void 0&&(n.format=e.format),e.type!==void 0&&(n.type=e.type),e.anisotropy!==void 0&&(n.anisotropy=e.anisotropy),e.colorSpace!==void 0&&(n.colorSpace=e.colorSpace),e.flipY!==void 0&&(n.flipY=e.flipY),e.generateMipmaps!==void 0&&(n.generateMipmaps=e.generateMipmaps),e.internalFormat!==void 0&&(n.internalFormat=e.internalFormat);for(let i=0;i<this.textures.length;i++)this.textures[i].setValues(n)}get texture(){return this.textures[0]}set texture(e){this.textures[0]=e}set depthTexture(e){this._depthTexture!==null&&(this._depthTexture.renderTarget=null),e!==null&&(e.renderTarget=this),this._depthTexture=e}get depthTexture(){return this._depthTexture}setSize(e,n,i=1){if(this.width!==e||this.height!==n||this.depth!==i){this.width=e,this.height=n,this.depth=i;for(let a=0,s=this.textures.length;a<s;a++)this.textures[a].image.width=e,this.textures[a].image.height=n,this.textures[a].image.depth=i,this.textures[a].isData3DTexture!==!0&&(this.textures[a].isArrayTexture=this.textures[a].image.depth>1);this.dispose()}this.viewport.set(0,0,e,n),this.scissor.set(0,0,e,n)}clone(){return new this.constructor().copy(this)}copy(e){this.width=e.width,this.height=e.height,this.depth=e.depth,this.scissor.copy(e.scissor),this.scissorTest=e.scissorTest,this.viewport.copy(e.viewport),this.textures.length=0;for(let n=0,i=e.textures.length;n<i;n++){this.textures[n]=e.textures[n].clone(),this.textures[n].isRenderTargetTexture=!0,this.textures[n].renderTarget=this;const a=Object.assign({},e.textures[n].image);this.textures[n].source=new Tg(a)}return this.depthBuffer=e.depthBuffer,this.stencilBuffer=e.stencilBuffer,this.resolveDepthBuffer=e.resolveDepthBuffer,this.resolveStencilBuffer=e.resolveStencilBuffer,e.depthTexture!==null&&(this.depthTexture=e.depthTexture.clone()),this.samples=e.samples,this.multiview=e.multiview,this.useArrayDepthTexture=e.useArrayDepthTexture,this}dispose(){this.dispatchEvent({type:"dispose"})}}class Ki extends UA{constructor(e=1,n=1,i={}){super(e,n,i),this.isWebGLRenderTarget=!0}}class TS extends Un{constructor(e=null,n=1,i=1,a=1){super(null),this.isDataArrayTexture=!0,this.image={data:e,width:n,height:i,depth:a},this.magFilter=Sn,this.minFilter=Sn,this.wrapR=_a,this.generateMipmaps=!1,this.flipY=!1,this.unpackAlignment=1,this.layerUpdates=new Set}addLayerUpdate(e){this.layerUpdates.add(e)}clearLayerUpdates(){this.layerUpdates.clear()}}class OA extends Un{constructor(e=null,n=1,i=1,a=1){super(null),this.isData3DTexture=!0,this.image={data:e,width:n,height:i,depth:a},this.magFilter=Sn,this.minFilter=Sn,this.wrapR=_a,this.generateMipmaps=!1,this.flipY=!1,this.unpackAlignment=1}}const bd=class bd{constructor(e,n,i,a,s,l,o,c,u,h,g,d,p,x,_,m){this.elements=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],e!==void 0&&this.set(e,n,i,a,s,l,o,c,u,h,g,d,p,x,_,m)}set(e,n,i,a,s,l,o,c,u,h,g,d,p,x,_,m){const f=this.elements;return f[0]=e,f[4]=n,f[8]=i,f[12]=a,f[1]=s,f[5]=l,f[9]=o,f[13]=c,f[2]=u,f[6]=h,f[10]=g,f[14]=d,f[3]=p,f[7]=x,f[11]=_,f[15]=m,this}identity(){return this.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1),this}clone(){return new bd().fromArray(this.elements)}copy(e){const n=this.elements,i=e.elements;return n[0]=i[0],n[1]=i[1],n[2]=i[2],n[3]=i[3],n[4]=i[4],n[5]=i[5],n[6]=i[6],n[7]=i[7],n[8]=i[8],n[9]=i[9],n[10]=i[10],n[11]=i[11],n[12]=i[12],n[13]=i[13],n[14]=i[14],n[15]=i[15],this}copyPosition(e){const n=this.elements,i=e.elements;return n[12]=i[12],n[13]=i[13],n[14]=i[14],this}setFromMatrix3(e){const n=e.elements;return this.set(n[0],n[3],n[6],0,n[1],n[4],n[7],0,n[2],n[5],n[8],0,0,0,0,1),this}extractBasis(e,n,i){return this.determinantAffine()===0?(e.set(1,0,0),n.set(0,1,0),i.set(0,0,1),this):(e.setFromMatrixColumn(this,0),n.setFromMatrixColumn(this,1),i.setFromMatrixColumn(this,2),this)}makeBasis(e,n,i){return this.set(e.x,n.x,i.x,0,e.y,n.y,i.y,0,e.z,n.z,i.z,0,0,0,0,1),this}extractRotation(e){if(e.determinantAffine()===0)return this.identity();const n=this.elements,i=e.elements,a=1/xr.setFromMatrixColumn(e,0).length(),s=1/xr.setFromMatrixColumn(e,1).length(),l=1/xr.setFromMatrixColumn(e,2).length();return n[0]=i[0]*a,n[1]=i[1]*a,n[2]=i[2]*a,n[3]=0,n[4]=i[4]*s,n[5]=i[5]*s,n[6]=i[6]*s,n[7]=0,n[8]=i[8]*l,n[9]=i[9]*l,n[10]=i[10]*l,n[11]=0,n[12]=0,n[13]=0,n[14]=0,n[15]=1,this}makeRotationFromEuler(e){const n=this.elements,i=e.x,a=e.y,s=e.z,l=Math.cos(i),o=Math.sin(i),c=Math.cos(a),u=Math.sin(a),h=Math.cos(s),g=Math.sin(s);if(e.order==="XYZ"){const d=l*h,p=l*g,x=o*h,_=o*g;n[0]=c*h,n[4]=-c*g,n[8]=u,n[1]=p+x*u,n[5]=d-_*u,n[9]=-o*c,n[2]=_-d*u,n[6]=x+p*u,n[10]=l*c}else if(e.order==="YXZ"){const d=c*h,p=c*g,x=u*h,_=u*g;n[0]=d+_*o,n[4]=x*o-p,n[8]=l*u,n[1]=l*g,n[5]=l*h,n[9]=-o,n[2]=p*o-x,n[6]=_+d*o,n[10]=l*c}else if(e.order==="ZXY"){const d=c*h,p=c*g,x=u*h,_=u*g;n[0]=d-_*o,n[4]=-l*g,n[8]=x+p*o,n[1]=p+x*o,n[5]=l*h,n[9]=_-d*o,n[2]=-l*u,n[6]=o,n[10]=l*c}else if(e.order==="ZYX"){const d=l*h,p=l*g,x=o*h,_=o*g;n[0]=c*h,n[4]=x*u-p,n[8]=d*u+_,n[1]=c*g,n[5]=_*u+d,n[9]=p*u-x,n[2]=-u,n[6]=o*c,n[10]=l*c}else if(e.order==="YZX"){const d=l*c,p=l*u,x=o*c,_=o*u;n[0]=c*h,n[4]=_-d*g,n[8]=x*g+p,n[1]=g,n[5]=l*h,n[9]=-o*h,n[2]=-u*h,n[6]=p*g+x,n[10]=d-_*g}else if(e.order==="XZY"){const d=l*c,p=l*u,x=o*c,_=o*u;n[0]=c*h,n[4]=-g,n[8]=u*h,n[1]=d*g+_,n[5]=l*h,n[9]=p*g-x,n[2]=x*g-p,n[6]=o*h,n[10]=_*g+d}return n[3]=0,n[7]=0,n[11]=0,n[12]=0,n[13]=0,n[14]=0,n[15]=1,this}makeRotationFromQuaternion(e){return this.compose(PA,e,zA)}lookAt(e,n,i){const a=this.elements;return Kn.subVectors(e,n),Kn.lengthSq()===0&&(Kn.z=1),Kn.normalize(),Ia.crossVectors(i,Kn),Ia.lengthSq()===0&&(Math.abs(i.z)===1?Kn.x+=1e-4:Kn.z+=1e-4,Kn.normalize(),Ia.crossVectors(i,Kn)),Ia.normalize(),Cc.crossVectors(Kn,Ia),a[0]=Ia.x,a[4]=Cc.x,a[8]=Kn.x,a[1]=Ia.y,a[5]=Cc.y,a[9]=Kn.y,a[2]=Ia.z,a[6]=Cc.z,a[10]=Kn.z,this}multiply(e){return this.multiplyMatrices(this,e)}premultiply(e){return this.multiplyMatrices(e,this)}multiplyMatrices(e,n){const i=e.elements,a=n.elements,s=this.elements,l=i[0],o=i[4],c=i[8],u=i[12],h=i[1],g=i[5],d=i[9],p=i[13],x=i[2],_=i[6],m=i[10],f=i[14],v=i[3],y=i[7],b=i[11],T=i[15],M=a[0],A=a[4],S=a[8],N=a[12],D=a[1],U=a[5],I=a[9],Y=a[13],k=a[2],B=a[6],z=a[10],j=a[14],X=a[3],q=a[7],ve=a[11],he=a[15];return s[0]=l*M+o*D+c*k+u*X,s[4]=l*A+o*U+c*B+u*q,s[8]=l*S+o*I+c*z+u*ve,s[12]=l*N+o*Y+c*j+u*he,s[1]=h*M+g*D+d*k+p*X,s[5]=h*A+g*U+d*B+p*q,s[9]=h*S+g*I+d*z+p*ve,s[13]=h*N+g*Y+d*j+p*he,s[2]=x*M+_*D+m*k+f*X,s[6]=x*A+_*U+m*B+f*q,s[10]=x*S+_*I+m*z+f*ve,s[14]=x*N+_*Y+m*j+f*he,s[3]=v*M+y*D+b*k+T*X,s[7]=v*A+y*U+b*B+T*q,s[11]=v*S+y*I+b*z+T*ve,s[15]=v*N+y*Y+b*j+T*he,this}multiplyScalar(e){const n=this.elements;return n[0]*=e,n[4]*=e,n[8]*=e,n[12]*=e,n[1]*=e,n[5]*=e,n[9]*=e,n[13]*=e,n[2]*=e,n[6]*=e,n[10]*=e,n[14]*=e,n[3]*=e,n[7]*=e,n[11]*=e,n[15]*=e,this}determinant(){const e=this.elements,n=e[0],i=e[4],a=e[8],s=e[12],l=e[1],o=e[5],c=e[9],u=e[13],h=e[2],g=e[6],d=e[10],p=e[14],x=e[3],_=e[7],m=e[11],f=e[15],v=c*p-u*d,y=o*p-u*g,b=o*d-c*g,T=l*p-u*h,M=l*d-c*h,A=l*g-o*h;return n*(_*v-m*y+f*b)-i*(x*v-m*T+f*M)+a*(x*y-_*T+f*A)-s*(x*b-_*M+m*A)}determinantAffine(){const e=this.elements,n=e[0],i=e[4],a=e[8],s=e[1],l=e[5],o=e[9],c=e[2],u=e[6],h=e[10];return n*(l*h-o*u)-i*(s*h-o*c)+a*(s*u-l*c)}transpose(){const e=this.elements;let n;return n=e[1],e[1]=e[4],e[4]=n,n=e[2],e[2]=e[8],e[8]=n,n=e[6],e[6]=e[9],e[9]=n,n=e[3],e[3]=e[12],e[12]=n,n=e[7],e[7]=e[13],e[13]=n,n=e[11],e[11]=e[14],e[14]=n,this}setPosition(e,n,i){const a=this.elements;return e.isVector3?(a[12]=e.x,a[13]=e.y,a[14]=e.z):(a[12]=e,a[13]=n,a[14]=i),this}invert(){const e=this.elements,n=e[0],i=e[1],a=e[2],s=e[3],l=e[4],o=e[5],c=e[6],u=e[7],h=e[8],g=e[9],d=e[10],p=e[11],x=e[12],_=e[13],m=e[14],f=e[15],v=n*o-i*l,y=n*c-a*l,b=n*u-s*l,T=i*c-a*o,M=i*u-s*o,A=a*u-s*c,S=h*_-g*x,N=h*m-d*x,D=h*f-p*x,U=g*m-d*_,I=g*f-p*_,Y=d*f-p*m,k=v*Y-y*I+b*U+T*D-M*N+A*S;if(k===0)return this.set(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);const B=1/k;return e[0]=(o*Y-c*I+u*U)*B,e[1]=(a*I-i*Y-s*U)*B,e[2]=(_*A-m*M+f*T)*B,e[3]=(d*M-g*A-p*T)*B,e[4]=(c*D-l*Y-u*N)*B,e[5]=(n*Y-a*D+s*N)*B,e[6]=(m*b-x*A-f*y)*B,e[7]=(h*A-d*b+p*y)*B,e[8]=(l*I-o*D+u*S)*B,e[9]=(i*D-n*I-s*S)*B,e[10]=(x*M-_*b+f*v)*B,e[11]=(g*b-h*M-p*v)*B,e[12]=(o*N-l*U-c*S)*B,e[13]=(n*U-i*N+a*S)*B,e[14]=(_*y-x*T-m*v)*B,e[15]=(h*T-g*y+d*v)*B,this}scale(e){const n=this.elements,i=e.x,a=e.y,s=e.z;return n[0]*=i,n[4]*=a,n[8]*=s,n[1]*=i,n[5]*=a,n[9]*=s,n[2]*=i,n[6]*=a,n[10]*=s,n[3]*=i,n[7]*=a,n[11]*=s,this}getMaxScaleOnAxis(){const e=this.elements,n=e[0]*e[0]+e[1]*e[1]+e[2]*e[2],i=e[4]*e[4]+e[5]*e[5]+e[6]*e[6],a=e[8]*e[8]+e[9]*e[9]+e[10]*e[10];return Math.sqrt(Math.max(n,i,a))}makeTranslation(e,n,i){return e.isVector3?this.set(1,0,0,e.x,0,1,0,e.y,0,0,1,e.z,0,0,0,1):this.set(1,0,0,e,0,1,0,n,0,0,1,i,0,0,0,1),this}makeRotationX(e){const n=Math.cos(e),i=Math.sin(e);return this.set(1,0,0,0,0,n,-i,0,0,i,n,0,0,0,0,1),this}makeRotationY(e){const n=Math.cos(e),i=Math.sin(e);return this.set(n,0,i,0,0,1,0,0,-i,0,n,0,0,0,0,1),this}makeRotationZ(e){const n=Math.cos(e),i=Math.sin(e);return this.set(n,-i,0,0,i,n,0,0,0,0,1,0,0,0,0,1),this}makeRotationAxis(e,n){const i=Math.cos(n),a=Math.sin(n),s=1-i,l=e.x,o=e.y,c=e.z,u=s*l,h=s*o;return this.set(u*l+i,u*o-a*c,u*c+a*o,0,u*o+a*c,h*o+i,h*c-a*l,0,u*c-a*o,h*c+a*l,s*c*c+i,0,0,0,0,1),this}makeScale(e,n,i){return this.set(e,0,0,0,0,n,0,0,0,0,i,0,0,0,0,1),this}makeShear(e,n,i,a,s,l){return this.set(1,i,s,0,e,1,l,0,n,a,1,0,0,0,0,1),this}compose(e,n,i){const a=this.elements,s=n._x,l=n._y,o=n._z,c=n._w,u=s+s,h=l+l,g=o+o,d=s*u,p=s*h,x=s*g,_=l*h,m=l*g,f=o*g,v=c*u,y=c*h,b=c*g,T=i.x,M=i.y,A=i.z;return a[0]=(1-(_+f))*T,a[1]=(p+b)*T,a[2]=(x-y)*T,a[3]=0,a[4]=(p-b)*M,a[5]=(1-(d+f))*M,a[6]=(m+v)*M,a[7]=0,a[8]=(x+y)*A,a[9]=(m-v)*A,a[10]=(1-(d+_))*A,a[11]=0,a[12]=e.x,a[13]=e.y,a[14]=e.z,a[15]=1,this}decompose(e,n,i){const a=this.elements;e.x=a[12],e.y=a[13],e.z=a[14];const s=this.determinantAffine();if(s===0)return i.set(1,1,1),n.identity(),this;let l=xr.set(a[0],a[1],a[2]).length();const o=xr.set(a[4],a[5],a[6]).length(),c=xr.set(a[8],a[9],a[10]).length();s<0&&(l=-l),wi.copy(this);const u=1/l,h=1/o,g=1/c;return wi.elements[0]*=u,wi.elements[1]*=u,wi.elements[2]*=u,wi.elements[4]*=h,wi.elements[5]*=h,wi.elements[6]*=h,wi.elements[8]*=g,wi.elements[9]*=g,wi.elements[10]*=g,n.setFromRotationMatrix(wi),i.x=l,i.y=o,i.z=c,this}makePerspective(e,n,i,a,s,l,o=qi,c=!1){const u=this.elements,h=2*s/(n-e),g=2*s/(i-a),d=(n+e)/(n-e),p=(i+a)/(i-a);let x,_;if(c)x=s/(l-s),_=l*s/(l-s);else if(o===qi)x=-(l+s)/(l-s),_=-2*l*s/(l-s);else if(o===fd)x=-l/(l-s),_=-l*s/(l-s);else throw new Error("THREE.Matrix4.makePerspective(): Invalid coordinate system: "+o);return u[0]=h,u[4]=0,u[8]=d,u[12]=0,u[1]=0,u[5]=g,u[9]=p,u[13]=0,u[2]=0,u[6]=0,u[10]=x,u[14]=_,u[3]=0,u[7]=0,u[11]=-1,u[15]=0,this}makeOrthographic(e,n,i,a,s,l,o=qi,c=!1){const u=this.elements,h=2/(n-e),g=2/(i-a),d=-(n+e)/(n-e),p=-(i+a)/(i-a);let x,_;if(c)x=1/(l-s),_=l/(l-s);else if(o===qi)x=-2/(l-s),_=-(l+s)/(l-s);else if(o===fd)x=-1/(l-s),_=-s/(l-s);else throw new Error("THREE.Matrix4.makeOrthographic(): Invalid coordinate system: "+o);return u[0]=h,u[4]=0,u[8]=0,u[12]=d,u[1]=0,u[5]=g,u[9]=0,u[13]=p,u[2]=0,u[6]=0,u[10]=x,u[14]=_,u[3]=0,u[7]=0,u[11]=0,u[15]=1,this}equals(e){const n=this.elements,i=e.elements;for(let a=0;a<16;a++)if(n[a]!==i[a])return!1;return!0}fromArray(e,n=0){for(let i=0;i<16;i++)this.elements[i]=e[i+n];return this}toArray(e=[],n=0){const i=this.elements;return e[n]=i[0],e[n+1]=i[1],e[n+2]=i[2],e[n+3]=i[3],e[n+4]=i[4],e[n+5]=i[5],e[n+6]=i[6],e[n+7]=i[7],e[n+8]=i[8],e[n+9]=i[9],e[n+10]=i[10],e[n+11]=i[11],e[n+12]=i[12],e[n+13]=i[13],e[n+14]=i[14],e[n+15]=i[15],e}};bd.prototype.isMatrix4=!0;let zt=bd;const xr=new Z,wi=new zt,PA=new Z(0,0,0),zA=new Z(1,1,1),Ia=new Z,Cc=new Z,Kn=new Z,Dv=new zt,Lv=new Ll;class Qs{constructor(e=0,n=0,i=0,a=Qs.DEFAULT_ORDER){this.isEuler=!0,this._x=e,this._y=n,this._z=i,this._order=a}get x(){return this._x}set x(e){this._x=e,this._onChangeCallback()}get y(){return this._y}set y(e){this._y=e,this._onChangeCallback()}get z(){return this._z}set z(e){this._z=e,this._onChangeCallback()}get order(){return this._order}set order(e){this._order=e,this._onChangeCallback()}set(e,n,i,a=this._order){return this._x=e,this._y=n,this._z=i,this._order=a,this._onChangeCallback(),this}clone(){return new this.constructor(this._x,this._y,this._z,this._order)}copy(e){return this._x=e._x,this._y=e._y,this._z=e._z,this._order=e._order,this._onChangeCallback(),this}setFromRotationMatrix(e,n=this._order,i=!0){const a=e.elements,s=a[0],l=a[4],o=a[8],c=a[1],u=a[5],h=a[9],g=a[2],d=a[6],p=a[10];switch(n){case"XYZ":this._y=Math.asin(pt(o,-1,1)),Math.abs(o)<.9999999?(this._x=Math.atan2(-h,p),this._z=Math.atan2(-l,s)):(this._x=Math.atan2(d,u),this._z=0);break;case"YXZ":this._x=Math.asin(-pt(h,-1,1)),Math.abs(h)<.9999999?(this._y=Math.atan2(o,p),this._z=Math.atan2(c,u)):(this._y=Math.atan2(-g,s),this._z=0);break;case"ZXY":this._x=Math.asin(pt(d,-1,1)),Math.abs(d)<.9999999?(this._y=Math.atan2(-g,p),this._z=Math.atan2(-l,u)):(this._y=0,this._z=Math.atan2(c,s));break;case"ZYX":this._y=Math.asin(-pt(g,-1,1)),Math.abs(g)<.9999999?(this._x=Math.atan2(d,p),this._z=Math.atan2(c,s)):(this._x=0,this._z=Math.atan2(-l,u));break;case"YZX":this._z=Math.asin(pt(c,-1,1)),Math.abs(c)<.9999999?(this._x=Math.atan2(-h,u),this._y=Math.atan2(-g,s)):(this._x=0,this._y=Math.atan2(o,p));break;case"XZY":this._z=Math.asin(-pt(l,-1,1)),Math.abs(l)<.9999999?(this._x=Math.atan2(d,u),this._y=Math.atan2(o,s)):(this._x=Math.atan2(-h,p),this._y=0);break;default:Qe("Euler: .setFromRotationMatrix() encountered an unknown order: "+n)}return this._order=n,i===!0&&this._onChangeCallback(),this}setFromQuaternion(e,n,i){return Dv.makeRotationFromQuaternion(e),this.setFromRotationMatrix(Dv,n,i)}setFromVector3(e,n=this._order){return this.set(e.x,e.y,e.z,n)}reorder(e){return Lv.setFromEuler(this),this.setFromQuaternion(Lv,e)}equals(e){return e._x===this._x&&e._y===this._y&&e._z===this._z&&e._order===this._order}fromArray(e){return this._x=e[0],this._y=e[1],this._z=e[2],e[3]!==void 0&&(this._order=e[3]),this._onChangeCallback(),this}toArray(e=[],n=0){return e[n]=this._x,e[n+1]=this._y,e[n+2]=this._z,e[n+3]=this._order,e}_onChange(e){return this._onChangeCallback=e,this}_onChangeCallback(){}*[Symbol.iterator](){yield this._x,yield this._y,yield this._z,yield this._order}}Qs.DEFAULT_ORDER="XYZ";class AS{constructor(){this.mask=1}set(e){this.mask=(1<<e|0)>>>0}enable(e){this.mask|=1<<e|0}enableAll(){this.mask=-1}toggle(e){this.mask^=1<<e|0}disable(e){this.mask&=~(1<<e|0)}disableAll(){this.mask=0}test(e){return(this.mask&e.mask)!==0}isEnabled(e){return(this.mask&(1<<e|0))!==0}}let jA=0;const Uv=new Z,vr=new Ll,ra=new zt,Rc=new Z,Wl=new Z,kA=new Z,IA=new Ll,Ov=new Z(1,0,0),Pv=new Z(0,1,0),zv=new Z(0,0,1),jv={type:"added"},BA={type:"removed"},_r={type:"childadded",child:null},jh={type:"childremoved",child:null};class On extends rr{constructor(){super(),this.isObject3D=!0,Object.defineProperty(this,"id",{value:jA++}),this.uuid=lc(),this.name="",this.type="Object3D",this.parent=null,this.children=[],this.up=On.DEFAULT_UP.clone();const e=new Z,n=new Qs,i=new Ll,a=new Z(1,1,1);function s(){i.setFromEuler(n,!1)}function l(){n.setFromQuaternion(i,void 0,!1)}n._onChange(s),i._onChange(l),Object.defineProperties(this,{position:{configurable:!0,enumerable:!0,value:e},rotation:{configurable:!0,enumerable:!0,value:n},quaternion:{configurable:!0,enumerable:!0,value:i},scale:{configurable:!0,enumerable:!0,value:a},modelViewMatrix:{value:new zt},normalMatrix:{value:new tt}}),this.matrix=new zt,this.matrixWorld=new zt,this.matrixAutoUpdate=On.DEFAULT_MATRIX_AUTO_UPDATE,this.matrixWorldAutoUpdate=On.DEFAULT_MATRIX_WORLD_AUTO_UPDATE,this.matrixWorldNeedsUpdate=!1,this.layers=new AS,this.visible=!0,this.castShadow=!1,this.receiveShadow=!1,this.frustumCulled=!0,this.renderOrder=0,this.animations=[],this.customDepthMaterial=void 0,this.customDistanceMaterial=void 0,this.static=!1,this.userData={},this.pivot=null}onBeforeShadow(){}onAfterShadow(){}onBeforeRender(){}onAfterRender(){}applyMatrix4(e){this.matrixAutoUpdate&&this.updateMatrix(),this.matrix.premultiply(e),this.matrix.decompose(this.position,this.quaternion,this.scale)}applyQuaternion(e){return this.quaternion.premultiply(e),this}setRotationFromAxisAngle(e,n){this.quaternion.setFromAxisAngle(e,n)}setRotationFromEuler(e){this.quaternion.setFromEuler(e,!0)}setRotationFromMatrix(e){this.quaternion.setFromRotationMatrix(e)}setRotationFromQuaternion(e){this.quaternion.copy(e)}rotateOnAxis(e,n){return vr.setFromAxisAngle(e,n),this.quaternion.multiply(vr),this}rotateOnWorldAxis(e,n){return vr.setFromAxisAngle(e,n),this.quaternion.premultiply(vr),this}rotateX(e){return this.rotateOnAxis(Ov,e)}rotateY(e){return this.rotateOnAxis(Pv,e)}rotateZ(e){return this.rotateOnAxis(zv,e)}translateOnAxis(e,n){return Uv.copy(e).applyQuaternion(this.quaternion),this.position.add(Uv.multiplyScalar(n)),this}translateX(e){return this.translateOnAxis(Ov,e)}translateY(e){return this.translateOnAxis(Pv,e)}translateZ(e){return this.translateOnAxis(zv,e)}localToWorld(e){return this.updateWorldMatrix(!0,!1),e.applyMatrix4(this.matrixWorld)}worldToLocal(e){return this.updateWorldMatrix(!0,!1),e.applyMatrix4(ra.copy(this.matrixWorld).invert())}lookAt(e,n,i){e.isVector3?Rc.copy(e):Rc.set(e,n,i);const a=this.parent;this.updateWorldMatrix(!0,!1),Wl.setFromMatrixPosition(this.matrixWorld),this.isCamera||this.isLight?ra.lookAt(Wl,Rc,this.up):ra.lookAt(Rc,Wl,this.up),this.quaternion.setFromRotationMatrix(ra),a&&(ra.extractRotation(a.matrixWorld),vr.setFromRotationMatrix(ra),this.quaternion.premultiply(vr.invert()))}add(e){if(arguments.length>1){for(let n=0;n<arguments.length;n++)this.add(arguments[n]);return this}return e===this?(bt("Object3D.add: object can't be added as a child of itself.",e),this):(e&&e.isObject3D?(e.removeFromParent(),e.parent=this,this.children.push(e),e.dispatchEvent(jv),_r.child=e,this.dispatchEvent(_r),_r.child=null):bt("Object3D.add: object not an instance of THREE.Object3D.",e),this)}remove(e){if(arguments.length>1){for(let i=0;i<arguments.length;i++)this.remove(arguments[i]);return this}const n=this.children.indexOf(e);return n!==-1&&(e.parent=null,this.children.splice(n,1),e.dispatchEvent(BA),jh.child=e,this.dispatchEvent(jh),jh.child=null),this}removeFromParent(){const e=this.parent;return e!==null&&e.remove(this),this}clear(){return this.remove(...this.children)}attach(e){return this.updateWorldMatrix(!0,!1),ra.copy(this.matrixWorld).invert(),e.parent!==null&&(e.parent.updateWorldMatrix(!0,!1),ra.multiply(e.parent.matrixWorld)),e.applyMatrix4(ra),e.removeFromParent(),e.parent=this,this.children.push(e),e.updateWorldMatrix(!1,!0),e.dispatchEvent(jv),_r.child=e,this.dispatchEvent(_r),_r.child=null,this}getObjectById(e){return this.getObjectByProperty("id",e)}getObjectByName(e){return this.getObjectByProperty("name",e)}getObjectByProperty(e,n){if(this[e]===n)return this;for(let i=0,a=this.children.length;i<a;i++){const l=this.children[i].getObjectByProperty(e,n);if(l!==void 0)return l}}getObjectsByProperty(e,n,i=[]){this[e]===n&&i.push(this);const a=this.children;for(let s=0,l=a.length;s<l;s++)a[s].getObjectsByProperty(e,n,i);return i}getWorldPosition(e){return this.updateWorldMatrix(!0,!1),e.setFromMatrixPosition(this.matrixWorld)}getWorldQuaternion(e){return this.updateWorldMatrix(!0,!1),this.matrixWorld.decompose(Wl,e,kA),e}getWorldScale(e){return this.updateWorldMatrix(!0,!1),this.matrixWorld.decompose(Wl,IA,e),e}getWorldDirection(e){this.updateWorldMatrix(!0,!1);const n=this.matrixWorld.elements;return e.set(n[8],n[9],n[10]).normalize()}raycast(){}traverse(e){e(this);const n=this.children;for(let i=0,a=n.length;i<a;i++)n[i].traverse(e)}traverseVisible(e){if(this.visible===!1)return;e(this);const n=this.children;for(let i=0,a=n.length;i<a;i++)n[i].traverseVisible(e)}traverseAncestors(e){const n=this.parent;n!==null&&(e(n),n.traverseAncestors(e))}updateMatrix(){this.matrix.compose(this.position,this.quaternion,this.scale);const e=this.pivot;if(e!==null){const n=e.x,i=e.y,a=e.z,s=this.matrix.elements;s[12]+=n-s[0]*n-s[4]*i-s[8]*a,s[13]+=i-s[1]*n-s[5]*i-s[9]*a,s[14]+=a-s[2]*n-s[6]*i-s[10]*a}this.matrixWorldNeedsUpdate=!0}updateMatrixWorld(e){this.matrixAutoUpdate&&this.updateMatrix(),(this.matrixWorldNeedsUpdate||e)&&(this.matrixWorldAutoUpdate===!0&&(this.parent===null?this.matrixWorld.copy(this.matrix):this.matrixWorld.multiplyMatrices(this.parent.matrixWorld,this.matrix)),this.matrixWorldNeedsUpdate=!1,e=!0);const n=this.children;for(let i=0,a=n.length;i<a;i++)n[i].updateMatrixWorld(e)}updateWorldMatrix(e,n,i=!1){const a=this.parent;if(e===!0&&a!==null&&a.updateWorldMatrix(!0,!1),this.matrixAutoUpdate&&this.updateMatrix(),(this.matrixWorldNeedsUpdate||i)&&(this.matrixWorldAutoUpdate===!0&&(this.parent===null?this.matrixWorld.copy(this.matrix):this.matrixWorld.multiplyMatrices(this.parent.matrixWorld,this.matrix)),this.matrixWorldNeedsUpdate=!1,i=!0),n===!0){const s=this.children;for(let l=0,o=s.length;l<o;l++)s[l].updateWorldMatrix(!1,!0,i)}}toJSON(e){const n=e===void 0||typeof e=="string",i={};n&&(e={geometries:{},materials:{},textures:{},images:{},shapes:{},skeletons:{},animations:{},nodes:{}},i.metadata={version:4.7,type:"Object",generator:"Object3D.toJSON"});const a={};a.uuid=this.uuid,a.type=this.type,this.name!==""&&(a.name=this.name),this.castShadow===!0&&(a.castShadow=!0),this.receiveShadow===!0&&(a.receiveShadow=!0),this.visible===!1&&(a.visible=!1),this.frustumCulled===!1&&(a.frustumCulled=!1),this.renderOrder!==0&&(a.renderOrder=this.renderOrder),this.static!==!1&&(a.static=this.static),Object.keys(this.userData).length>0&&(a.userData=this.userData),a.layers=this.layers.mask,a.matrix=this.matrix.toArray(),a.up=this.up.toArray(),this.pivot!==null&&(a.pivot=this.pivot.toArray()),this.matrixAutoUpdate===!1&&(a.matrixAutoUpdate=!1),this.morphTargetDictionary!==void 0&&(a.morphTargetDictionary=Object.assign({},this.morphTargetDictionary)),this.morphTargetInfluences!==void 0&&(a.morphTargetInfluences=this.morphTargetInfluences.slice()),this.isInstancedMesh&&(a.type="InstancedMesh",a.count=this.count,a.instanceMatrix=this.instanceMatrix.toJSON(),this.instanceColor!==null&&(a.instanceColor=this.instanceColor.toJSON())),this.isBatchedMesh&&(a.type="BatchedMesh",a.perObjectFrustumCulled=this.perObjectFrustumCulled,a.sortObjects=this.sortObjects,a.drawRanges=this._drawRanges,a.reservedRanges=this._reservedRanges,a.geometryInfo=this._geometryInfo.map(o=>({...o,boundingBox:o.boundingBox?o.boundingBox.toJSON():void 0,boundingSphere:o.boundingSphere?o.boundingSphere.toJSON():void 0})),a.instanceInfo=this._instanceInfo.map(o=>({...o})),a.availableInstanceIds=this._availableInstanceIds.slice(),a.availableGeometryIds=this._availableGeometryIds.slice(),a.nextIndexStart=this._nextIndexStart,a.nextVertexStart=this._nextVertexStart,a.geometryCount=this._geometryCount,a.maxInstanceCount=this._maxInstanceCount,a.maxVertexCount=this._maxVertexCount,a.maxIndexCount=this._maxIndexCount,a.geometryInitialized=this._geometryInitialized,a.matricesTexture=this._matricesTexture.toJSON(e),a.indirectTexture=this._indirectTexture.toJSON(e),this._colorsTexture!==null&&(a.colorsTexture=this._colorsTexture.toJSON(e)),this.boundingSphere!==null&&(a.boundingSphere=this.boundingSphere.toJSON()),this.boundingBox!==null&&(a.boundingBox=this.boundingBox.toJSON()));function s(o,c){return o[c.uuid]===void 0&&(o[c.uuid]=c.toJSON(e)),c.uuid}if(this.isScene)this.background&&(this.background.isColor?a.background=this.background.toJSON():this.background.isTexture&&(a.background=this.background.toJSON(e).uuid)),this.environment&&this.environment.isTexture&&this.environment.isRenderTargetTexture!==!0&&(a.environment=this.environment.toJSON(e).uuid);else if(this.isMesh||this.isLine||this.isPoints){a.geometry=s(e.geometries,this.geometry);const o=this.geometry.parameters;if(o!==void 0&&o.shapes!==void 0){const c=o.shapes;if(Array.isArray(c))for(let u=0,h=c.length;u<h;u++){const g=c[u];s(e.shapes,g)}else s(e.shapes,c)}}if(this.isSkinnedMesh&&(a.bindMode=this.bindMode,a.bindMatrix=this.bindMatrix.toArray(),this.skeleton!==void 0&&(s(e.skeletons,this.skeleton),a.skeleton=this.skeleton.uuid)),this.material!==void 0)if(Array.isArray(this.material)){const o=[];for(let c=0,u=this.material.length;c<u;c++)o.push(s(e.materials,this.material[c]));a.material=o}else a.material=s(e.materials,this.material);if(this.children.length>0){a.children=[];for(let o=0;o<this.children.length;o++)a.children.push(this.children[o].toJSON(e).object)}if(this.animations.length>0){a.animations=[];for(let o=0;o<this.animations.length;o++){const c=this.animations[o];a.animations.push(s(e.animations,c))}}if(n){const o=l(e.geometries),c=l(e.materials),u=l(e.textures),h=l(e.images),g=l(e.shapes),d=l(e.skeletons),p=l(e.animations),x=l(e.nodes);o.length>0&&(i.geometries=o),c.length>0&&(i.materials=c),u.length>0&&(i.textures=u),h.length>0&&(i.images=h),g.length>0&&(i.shapes=g),d.length>0&&(i.skeletons=d),p.length>0&&(i.animations=p),x.length>0&&(i.nodes=x)}return i.object=a,i;function l(o){const c=[];for(const u in o){const h=o[u];delete h.metadata,c.push(h)}return c}}clone(e){return new this.constructor().copy(this,e)}copy(e,n=!0){if(this.name=e.name,this.up.copy(e.up),this.position.copy(e.position),this.rotation.order=e.rotation.order,this.quaternion.copy(e.quaternion),this.scale.copy(e.scale),this.pivot=e.pivot!==null?e.pivot.clone():null,this.matrix.copy(e.matrix),this.matrixWorld.copy(e.matrixWorld),this.matrixAutoUpdate=e.matrixAutoUpdate,this.matrixWorldAutoUpdate=e.matrixWorldAutoUpdate,this.matrixWorldNeedsUpdate=e.matrixWorldNeedsUpdate,this.layers.mask=e.layers.mask,this.visible=e.visible,this.castShadow=e.castShadow,this.receiveShadow=e.receiveShadow,this.frustumCulled=e.frustumCulled,this.renderOrder=e.renderOrder,this.static=e.static,this.animations=e.animations.slice(),this.userData=JSON.parse(JSON.stringify(e.userData)),n===!0)for(let i=0;i<e.children.length;i++){const a=e.children[i];this.add(a.clone())}return this}}On.DEFAULT_UP=new Z(0,1,0);On.DEFAULT_MATRIX_AUTO_UPDATE=!0;On.DEFAULT_MATRIX_WORLD_AUTO_UPDATE=!0;class Dc extends On{constructor(){super(),this.isGroup=!0,this.type="Group"}}const FA={type:"move"};class kh{constructor(){this._targetRay=null,this._grip=null,this._hand=null}getHandSpace(){return this._hand===null&&(this._hand=new Dc,this._hand.matrixAutoUpdate=!1,this._hand.visible=!1,this._hand.joints={},this._hand.inputState={pinching:!1}),this._hand}getTargetRaySpace(){return this._targetRay===null&&(this._targetRay=new Dc,this._targetRay.matrixAutoUpdate=!1,this._targetRay.visible=!1,this._targetRay.hasLinearVelocity=!1,this._targetRay.linearVelocity=new Z,this._targetRay.hasAngularVelocity=!1,this._targetRay.angularVelocity=new Z),this._targetRay}getGripSpace(){return this._grip===null&&(this._grip=new Dc,this._grip.matrixAutoUpdate=!1,this._grip.visible=!1,this._grip.hasLinearVelocity=!1,this._grip.linearVelocity=new Z,this._grip.hasAngularVelocity=!1,this._grip.angularVelocity=new Z,this._grip.eventsEnabled=!1),this._grip}dispatchEvent(e){return this._targetRay!==null&&this._targetRay.dispatchEvent(e),this._grip!==null&&this._grip.dispatchEvent(e),this._hand!==null&&this._hand.dispatchEvent(e),this}connect(e){if(e&&e.hand){const n=this._hand;if(n)for(const i of e.hand.values())this._getHandJoint(n,i)}return this.dispatchEvent({type:"connected",data:e}),this}disconnect(e){return this.dispatchEvent({type:"disconnected",data:e}),this._targetRay!==null&&(this._targetRay.visible=!1),this._grip!==null&&(this._grip.visible=!1),this._hand!==null&&(this._hand.visible=!1),this}update(e,n,i){let a=null,s=null,l=null;const o=this._targetRay,c=this._grip,u=this._hand;if(e&&n.session.visibilityState!=="visible-blurred"){if(u&&e.hand){l=!0;for(const _ of e.hand.values()){const m=n.getJointPose(_,i),f=this._getHandJoint(u,_);m!==null&&(f.matrix.fromArray(m.transform.matrix),f.matrix.decompose(f.position,f.rotation,f.scale),f.matrixWorldNeedsUpdate=!0,f.jointRadius=m.radius),f.visible=m!==null}const h=u.joints["index-finger-tip"],g=u.joints["thumb-tip"],d=h.position.distanceTo(g.position),p=.02,x=.005;u.inputState.pinching&&d>p+x?(u.inputState.pinching=!1,this.dispatchEvent({type:"pinchend",handedness:e.handedness,target:this})):!u.inputState.pinching&&d<=p-x&&(u.inputState.pinching=!0,this.dispatchEvent({type:"pinchstart",handedness:e.handedness,target:this}))}else c!==null&&e.gripSpace&&(s=n.getPose(e.gripSpace,i),s!==null&&(c.matrix.fromArray(s.transform.matrix),c.matrix.decompose(c.position,c.rotation,c.scale),c.matrixWorldNeedsUpdate=!0,s.linearVelocity?(c.hasLinearVelocity=!0,c.linearVelocity.copy(s.linearVelocity)):c.hasLinearVelocity=!1,s.angularVelocity?(c.hasAngularVelocity=!0,c.angularVelocity.copy(s.angularVelocity)):c.hasAngularVelocity=!1,c.eventsEnabled&&c.dispatchEvent({type:"gripUpdated",data:e,target:this})));o!==null&&(a=n.getPose(e.targetRaySpace,i),a===null&&s!==null&&(a=s),a!==null&&(o.matrix.fromArray(a.transform.matrix),o.matrix.decompose(o.position,o.rotation,o.scale),o.matrixWorldNeedsUpdate=!0,a.linearVelocity?(o.hasLinearVelocity=!0,o.linearVelocity.copy(a.linearVelocity)):o.hasLinearVelocity=!1,a.angularVelocity?(o.hasAngularVelocity=!0,o.angularVelocity.copy(a.angularVelocity)):o.hasAngularVelocity=!1,this.dispatchEvent(FA)))}return o!==null&&(o.visible=a!==null),c!==null&&(c.visible=s!==null),u!==null&&(u.visible=l!==null),this}_getHandJoint(e,n){if(e.joints[n.jointName]===void 0){const i=new Dc;i.matrixAutoUpdate=!1,i.visible=!1,e.joints[n.jointName]=i,e.add(i)}return e.joints[n.jointName]}}const wS={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074},Ba={h:0,s:0,l:0},Lc={h:0,s:0,l:0};function Ih(t,e,n){return n<0&&(n+=1),n>1&&(n-=1),n<1/6?t+(e-t)*6*n:n<1/2?e:n<2/3?t+(e-t)*6*(2/3-n):t}class gt{constructor(e,n,i){return this.isColor=!0,this.r=1,this.g=1,this.b=1,this.set(e,n,i)}set(e,n,i){if(n===void 0&&i===void 0){const a=e;a&&a.isColor?this.copy(a):typeof a=="number"?this.setHex(a):typeof a=="string"&&this.setStyle(a)}else this.setRGB(e,n,i);return this}setScalar(e){return this.r=e,this.g=e,this.b=e,this}setHex(e,n=di){return e=Math.floor(e),this.r=(e>>16&255)/255,this.g=(e>>8&255)/255,this.b=(e&255)/255,ft.colorSpaceToWorking(this,n),this}setRGB(e,n,i,a=ft.workingColorSpace){return this.r=e,this.g=n,this.b=i,ft.colorSpaceToWorking(this,a),this}setHSL(e,n,i,a=ft.workingColorSpace){if(e=NA(e,1),n=pt(n,0,1),i=pt(i,0,1),n===0)this.r=this.g=this.b=i;else{const s=i<=.5?i*(1+n):i+n-i*n,l=2*i-s;this.r=Ih(l,s,e+1/3),this.g=Ih(l,s,e),this.b=Ih(l,s,e-1/3)}return ft.colorSpaceToWorking(this,a),this}setStyle(e,n=di){function i(s){s!==void 0&&parseFloat(s)<1&&Qe("Color: Alpha component of "+e+" will be ignored.")}let a;if(a=/^(\w+)\(([^\)]*)\)/.exec(e)){let s;const l=a[1],o=a[2];switch(l){case"rgb":case"rgba":if(s=/^\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(o))return i(s[4]),this.setRGB(Math.min(255,parseInt(s[1],10))/255,Math.min(255,parseInt(s[2],10))/255,Math.min(255,parseInt(s[3],10))/255,n);if(s=/^\s*(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(o))return i(s[4]),this.setRGB(Math.min(100,parseInt(s[1],10))/100,Math.min(100,parseInt(s[2],10))/100,Math.min(100,parseInt(s[3],10))/100,n);break;case"hsl":case"hsla":if(s=/^\s*(\d*\.?\d+)\s*,\s*(\d*\.?\d+)\%\s*,\s*(\d*\.?\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(o))return i(s[4]),this.setHSL(parseFloat(s[1])/360,parseFloat(s[2])/100,parseFloat(s[3])/100,n);break;default:Qe("Color: Unknown color model "+e)}}else if(a=/^\#([A-Fa-f\d]+)$/.exec(e)){const s=a[1],l=s.length;if(l===3)return this.setRGB(parseInt(s.charAt(0),16)/15,parseInt(s.charAt(1),16)/15,parseInt(s.charAt(2),16)/15,n);if(l===6)return this.setHex(parseInt(s,16),n);Qe("Color: Invalid hex color "+e)}else if(e&&e.length>0)return this.setColorName(e,n);return this}setColorName(e,n=di){const i=wS[e.toLowerCase()];return i!==void 0?this.setHex(i,n):Qe("Color: Unknown color "+e),this}clone(){return new this.constructor(this.r,this.g,this.b)}copy(e){return this.r=e.r,this.g=e.g,this.b=e.b,this}copySRGBToLinear(e){return this.r=Ea(e.r),this.g=Ea(e.g),this.b=Ea(e.b),this}copyLinearToSRGB(e){return this.r=sl(e.r),this.g=sl(e.g),this.b=sl(e.b),this}convertSRGBToLinear(){return this.copySRGBToLinear(this),this}convertLinearToSRGB(){return this.copyLinearToSRGB(this),this}getHex(e=di){return ft.workingToColorSpace(An.copy(this),e),Math.round(pt(An.r*255,0,255))*65536+Math.round(pt(An.g*255,0,255))*256+Math.round(pt(An.b*255,0,255))}getHexString(e=di){return("000000"+this.getHex(e).toString(16)).slice(-6)}getHSL(e,n=ft.workingColorSpace){ft.workingToColorSpace(An.copy(this),n);const i=An.r,a=An.g,s=An.b,l=Math.max(i,a,s),o=Math.min(i,a,s);let c,u;const h=(o+l)/2;if(o===l)c=0,u=0;else{const g=l-o;switch(u=h<=.5?g/(l+o):g/(2-l-o),l){case i:c=(a-s)/g+(a<s?6:0);break;case a:c=(s-i)/g+2;break;case s:c=(i-a)/g+4;break}c/=6}return e.h=c,e.s=u,e.l=h,e}getRGB(e,n=ft.workingColorSpace){return ft.workingToColorSpace(An.copy(this),n),e.r=An.r,e.g=An.g,e.b=An.b,e}getStyle(e=di){ft.workingToColorSpace(An.copy(this),e);const n=An.r,i=An.g,a=An.b;return e!==di?`color(${e} ${n.toFixed(3)} ${i.toFixed(3)} ${a.toFixed(3)})`:`rgb(${Math.round(n*255)},${Math.round(i*255)},${Math.round(a*255)})`}offsetHSL(e,n,i){return this.getHSL(Ba),this.setHSL(Ba.h+e,Ba.s+n,Ba.l+i)}add(e){return this.r+=e.r,this.g+=e.g,this.b+=e.b,this}addColors(e,n){return this.r=e.r+n.r,this.g=e.g+n.g,this.b=e.b+n.b,this}addScalar(e){return this.r+=e,this.g+=e,this.b+=e,this}sub(e){return this.r=Math.max(0,this.r-e.r),this.g=Math.max(0,this.g-e.g),this.b=Math.max(0,this.b-e.b),this}multiply(e){return this.r*=e.r,this.g*=e.g,this.b*=e.b,this}multiplyScalar(e){return this.r*=e,this.g*=e,this.b*=e,this}lerp(e,n){return this.r+=(e.r-this.r)*n,this.g+=(e.g-this.g)*n,this.b+=(e.b-this.b)*n,this}lerpColors(e,n,i){return this.r=e.r+(n.r-e.r)*i,this.g=e.g+(n.g-e.g)*i,this.b=e.b+(n.b-e.b)*i,this}lerpHSL(e,n){this.getHSL(Ba),e.getHSL(Lc);const i=Lh(Ba.h,Lc.h,n),a=Lh(Ba.s,Lc.s,n),s=Lh(Ba.l,Lc.l,n);return this.setHSL(i,a,s),this}setFromVector3(e){return this.r=e.x,this.g=e.y,this.b=e.z,this}applyMatrix3(e){const n=this.r,i=this.g,a=this.b,s=e.elements;return this.r=s[0]*n+s[3]*i+s[6]*a,this.g=s[1]*n+s[4]*i+s[7]*a,this.b=s[2]*n+s[5]*i+s[8]*a,this}equals(e){return e.r===this.r&&e.g===this.g&&e.b===this.b}fromArray(e,n=0){return this.r=e[n],this.g=e[n+1],this.b=e[n+2],this}toArray(e=[],n=0){return e[n]=this.r,e[n+1]=this.g,e[n+2]=this.b,e}fromBufferAttribute(e,n){return this.r=e.getX(n),this.g=e.getY(n),this.b=e.getZ(n),this}toJSON(){return this.getHex()}*[Symbol.iterator](){yield this.r,yield this.g,yield this.b}}const An=new gt;gt.NAMES=wS;class HA extends On{constructor(){super(),this.isScene=!0,this.type="Scene",this.background=null,this.environment=null,this.fog=null,this.backgroundBlurriness=0,this.backgroundIntensity=1,this.backgroundRotation=new Qs,this.environmentIntensity=1,this.environmentRotation=new Qs,this.overrideMaterial=null,typeof __THREE_DEVTOOLS__<"u"&&__THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe",{detail:this}))}copy(e,n){return super.copy(e,n),e.background!==null&&(this.background=e.background.clone()),e.environment!==null&&(this.environment=e.environment.clone()),e.fog!==null&&(this.fog=e.fog.clone()),this.backgroundBlurriness=e.backgroundBlurriness,this.backgroundIntensity=e.backgroundIntensity,this.backgroundRotation.copy(e.backgroundRotation),this.environmentIntensity=e.environmentIntensity,this.environmentRotation.copy(e.environmentRotation),e.overrideMaterial!==null&&(this.overrideMaterial=e.overrideMaterial.clone()),this.matrixAutoUpdate=e.matrixAutoUpdate,this}toJSON(e){const n=super.toJSON(e);return this.fog!==null&&(n.object.fog=this.fog.toJSON()),this.backgroundBlurriness>0&&(n.object.backgroundBlurriness=this.backgroundBlurriness),this.backgroundIntensity!==1&&(n.object.backgroundIntensity=this.backgroundIntensity),n.object.backgroundRotation=this.backgroundRotation.toArray(),this.environmentIntensity!==1&&(n.object.environmentIntensity=this.environmentIntensity),n.object.environmentRotation=this.environmentRotation.toArray(),n}}const Ni=new Z,la=new Z,Bh=new Z,oa=new Z,br=new Z,yr=new Z,kv=new Z,Fh=new Z,Hh=new Z,Gh=new Z,Vh=new Yt,Wh=new Yt,Xh=new Yt;class _i{constructor(e=new Z,n=new Z,i=new Z){this.a=e,this.b=n,this.c=i}static getNormal(e,n,i,a){a.subVectors(i,n),Ni.subVectors(e,n),a.cross(Ni);const s=a.lengthSq();return s>0?a.multiplyScalar(1/Math.sqrt(s)):a.set(0,0,0)}static getBarycoord(e,n,i,a,s){Ni.subVectors(a,n),la.subVectors(i,n),Bh.subVectors(e,n);const l=Ni.dot(Ni),o=Ni.dot(la),c=Ni.dot(Bh),u=la.dot(la),h=la.dot(Bh),g=l*u-o*o;if(g===0)return s.set(0,0,0),null;const d=1/g,p=(u*c-o*h)*d,x=(l*h-o*c)*d;return s.set(1-p-x,x,p)}static containsPoint(e,n,i,a){return this.getBarycoord(e,n,i,a,oa)===null?!1:oa.x>=0&&oa.y>=0&&oa.x+oa.y<=1}static getInterpolation(e,n,i,a,s,l,o,c){return this.getBarycoord(e,n,i,a,oa)===null?(c.x=0,c.y=0,"z"in c&&(c.z=0),"w"in c&&(c.w=0),null):(c.setScalar(0),c.addScaledVector(s,oa.x),c.addScaledVector(l,oa.y),c.addScaledVector(o,oa.z),c)}static getInterpolatedAttribute(e,n,i,a,s,l){return Vh.setScalar(0),Wh.setScalar(0),Xh.setScalar(0),Vh.fromBufferAttribute(e,n),Wh.fromBufferAttribute(e,i),Xh.fromBufferAttribute(e,a),l.setScalar(0),l.addScaledVector(Vh,s.x),l.addScaledVector(Wh,s.y),l.addScaledVector(Xh,s.z),l}static isFrontFacing(e,n,i,a){return Ni.subVectors(i,n),la.subVectors(e,n),Ni.cross(la).dot(a)<0}set(e,n,i){return this.a.copy(e),this.b.copy(n),this.c.copy(i),this}setFromPointsAndIndices(e,n,i,a){return this.a.copy(e[n]),this.b.copy(e[i]),this.c.copy(e[a]),this}setFromAttributeAndIndices(e,n,i,a){return this.a.fromBufferAttribute(e,n),this.b.fromBufferAttribute(e,i),this.c.fromBufferAttribute(e,a),this}clone(){return new this.constructor().copy(this)}copy(e){return this.a.copy(e.a),this.b.copy(e.b),this.c.copy(e.c),this}getArea(){return Ni.subVectors(this.c,this.b),la.subVectors(this.a,this.b),Ni.cross(la).length()*.5}getMidpoint(e){return e.addVectors(this.a,this.b).add(this.c).multiplyScalar(1/3)}getNormal(e){return _i.getNormal(this.a,this.b,this.c,e)}getPlane(e){return e.setFromCoplanarPoints(this.a,this.b,this.c)}getBarycoord(e,n){return _i.getBarycoord(e,this.a,this.b,this.c,n)}getInterpolation(e,n,i,a,s){return _i.getInterpolation(e,this.a,this.b,this.c,n,i,a,s)}containsPoint(e){return _i.containsPoint(e,this.a,this.b,this.c)}isFrontFacing(e){return _i.isFrontFacing(this.a,this.b,this.c,e)}intersectsBox(e){return e.intersectsTriangle(this)}closestPointToPoint(e,n){const i=this.a,a=this.b,s=this.c;let l,o;br.subVectors(a,i),yr.subVectors(s,i),Fh.subVectors(e,i);const c=br.dot(Fh),u=yr.dot(Fh);if(c<=0&&u<=0)return n.copy(i);Hh.subVectors(e,a);const h=br.dot(Hh),g=yr.dot(Hh);if(h>=0&&g<=h)return n.copy(a);const d=c*g-h*u;if(d<=0&&c>=0&&h<=0)return l=c/(c-h),n.copy(i).addScaledVector(br,l);Gh.subVectors(e,s);const p=br.dot(Gh),x=yr.dot(Gh);if(x>=0&&p<=x)return n.copy(s);const _=p*u-c*x;if(_<=0&&u>=0&&x<=0)return o=u/(u-x),n.copy(i).addScaledVector(yr,o);const m=h*x-p*g;if(m<=0&&g-h>=0&&p-x>=0)return kv.subVectors(s,a),o=(g-h)/(g-h+(p-x)),n.copy(a).addScaledVector(kv,o);const f=1/(m+_+d);return l=_*f,o=d*f,n.copy(i).addScaledVector(br,l).addScaledVector(yr,o)}equals(e){return e.a.equals(this.a)&&e.b.equals(this.b)&&e.c.equals(this.c)}}class lr{constructor(e=new Z(1/0,1/0,1/0),n=new Z(-1/0,-1/0,-1/0)){this.isBox3=!0,this.min=e,this.max=n}set(e,n){return this.min.copy(e),this.max.copy(n),this}setFromArray(e){this.makeEmpty();for(let n=0,i=e.length;n<i;n+=3)this.expandByPoint(Ci.fromArray(e,n));return this}setFromBufferAttribute(e){this.makeEmpty();for(let n=0,i=e.count;n<i;n++)this.expandByPoint(Ci.fromBufferAttribute(e,n));return this}setFromPoints(e){this.makeEmpty();for(let n=0,i=e.length;n<i;n++)this.expandByPoint(e[n]);return this}setFromCenterAndSize(e,n){const i=Ci.copy(n).multiplyScalar(.5);return this.min.copy(e).sub(i),this.max.copy(e).add(i),this}setFromObject(e,n=!1){return this.makeEmpty(),this.expandByObject(e,n)}clone(){return new this.constructor().copy(this)}copy(e){return this.min.copy(e.min),this.max.copy(e.max),this}makeEmpty(){return this.min.x=this.min.y=this.min.z=1/0,this.max.x=this.max.y=this.max.z=-1/0,this}isEmpty(){return this.max.x<this.min.x||this.max.y<this.min.y||this.max.z<this.min.z}getCenter(e){return this.isEmpty()?e.set(0,0,0):e.addVectors(this.min,this.max).multiplyScalar(.5)}getSize(e){return this.isEmpty()?e.set(0,0,0):e.subVectors(this.max,this.min)}expandByPoint(e){return this.min.min(e),this.max.max(e),this}expandByVector(e){return this.min.sub(e),this.max.add(e),this}expandByScalar(e){return this.min.addScalar(-e),this.max.addScalar(e),this}expandByObject(e,n=!1){e.updateWorldMatrix(!1,!1);const i=e.geometry;if(i!==void 0){const s=i.getAttribute("position");if(n===!0&&s!==void 0&&e.isInstancedMesh!==!0)for(let l=0,o=s.count;l<o;l++)e.isMesh===!0?e.getVertexPosition(l,Ci):Ci.fromBufferAttribute(s,l),Ci.applyMatrix4(e.matrixWorld),this.expandByPoint(Ci);else e.boundingBox!==void 0?(e.boundingBox===null&&e.computeBoundingBox(),Uc.copy(e.boundingBox)):(i.boundingBox===null&&i.computeBoundingBox(),Uc.copy(i.boundingBox)),Uc.applyMatrix4(e.matrixWorld),this.union(Uc)}const a=e.children;for(let s=0,l=a.length;s<l;s++)this.expandByObject(a[s],n);return this}containsPoint(e){return e.x>=this.min.x&&e.x<=this.max.x&&e.y>=this.min.y&&e.y<=this.max.y&&e.z>=this.min.z&&e.z<=this.max.z}containsBox(e){return this.min.x<=e.min.x&&e.max.x<=this.max.x&&this.min.y<=e.min.y&&e.max.y<=this.max.y&&this.min.z<=e.min.z&&e.max.z<=this.max.z}getParameter(e,n){return n.set((e.x-this.min.x)/(this.max.x-this.min.x),(e.y-this.min.y)/(this.max.y-this.min.y),(e.z-this.min.z)/(this.max.z-this.min.z))}intersectsBox(e){return e.max.x>=this.min.x&&e.min.x<=this.max.x&&e.max.y>=this.min.y&&e.min.y<=this.max.y&&e.max.z>=this.min.z&&e.min.z<=this.max.z}intersectsSphere(e){return this.clampPoint(e.center,Ci),Ci.distanceToSquared(e.center)<=e.radius*e.radius}intersectsPlane(e){let n,i;return e.normal.x>0?(n=e.normal.x*this.min.x,i=e.normal.x*this.max.x):(n=e.normal.x*this.max.x,i=e.normal.x*this.min.x),e.normal.y>0?(n+=e.normal.y*this.min.y,i+=e.normal.y*this.max.y):(n+=e.normal.y*this.max.y,i+=e.normal.y*this.min.y),e.normal.z>0?(n+=e.normal.z*this.min.z,i+=e.normal.z*this.max.z):(n+=e.normal.z*this.max.z,i+=e.normal.z*this.min.z),n<=-e.constant&&i>=-e.constant}intersectsTriangle(e){if(this.isEmpty())return!1;this.getCenter(Xl),Oc.subVectors(this.max,Xl),Sr.subVectors(e.a,Xl),Mr.subVectors(e.b,Xl),Er.subVectors(e.c,Xl),Fa.subVectors(Mr,Sr),Ha.subVectors(Er,Mr),As.subVectors(Sr,Er);let n=[0,-Fa.z,Fa.y,0,-Ha.z,Ha.y,0,-As.z,As.y,Fa.z,0,-Fa.x,Ha.z,0,-Ha.x,As.z,0,-As.x,-Fa.y,Fa.x,0,-Ha.y,Ha.x,0,-As.y,As.x,0];return!qh(n,Sr,Mr,Er,Oc)||(n=[1,0,0,0,1,0,0,0,1],!qh(n,Sr,Mr,Er,Oc))?!1:(Pc.crossVectors(Fa,Ha),n=[Pc.x,Pc.y,Pc.z],qh(n,Sr,Mr,Er,Oc))}clampPoint(e,n){return n.copy(e).clamp(this.min,this.max)}distanceToPoint(e){return this.clampPoint(e,Ci).distanceTo(e)}getBoundingSphere(e){return this.isEmpty()?e.makeEmpty():(this.getCenter(e.center),e.radius=this.getSize(Ci).length()*.5),e}intersect(e){return this.min.max(e.min),this.max.min(e.max),this.isEmpty()&&this.makeEmpty(),this}union(e){return this.min.min(e.min),this.max.max(e.max),this}applyMatrix4(e){return this.isEmpty()?this:(ca[0].set(this.min.x,this.min.y,this.min.z).applyMatrix4(e),ca[1].set(this.min.x,this.min.y,this.max.z).applyMatrix4(e),ca[2].set(this.min.x,this.max.y,this.min.z).applyMatrix4(e),ca[3].set(this.min.x,this.max.y,this.max.z).applyMatrix4(e),ca[4].set(this.max.x,this.min.y,this.min.z).applyMatrix4(e),ca[5].set(this.max.x,this.min.y,this.max.z).applyMatrix4(e),ca[6].set(this.max.x,this.max.y,this.min.z).applyMatrix4(e),ca[7].set(this.max.x,this.max.y,this.max.z).applyMatrix4(e),this.setFromPoints(ca),this)}translate(e){return this.min.add(e),this.max.add(e),this}equals(e){return e.min.equals(this.min)&&e.max.equals(this.max)}toJSON(){return{min:this.min.toArray(),max:this.max.toArray()}}fromJSON(e){return this.min.fromArray(e.min),this.max.fromArray(e.max),this}}const ca=[new Z,new Z,new Z,new Z,new Z,new Z,new Z,new Z],Ci=new Z,Uc=new lr,Sr=new Z,Mr=new Z,Er=new Z,Fa=new Z,Ha=new Z,As=new Z,Xl=new Z,Oc=new Z,Pc=new Z,ws=new Z;function qh(t,e,n,i,a){for(let s=0,l=t.length-3;s<=l;s+=3){ws.fromArray(t,s);const o=a.x*Math.abs(ws.x)+a.y*Math.abs(ws.y)+a.z*Math.abs(ws.z),c=e.dot(ws),u=n.dot(ws),h=i.dot(ws);if(Math.max(-Math.max(c,u,h),Math.min(c,u,h))>o)return!1}return!0}const en=new Z,zc=new Mt;let GA=0;class on extends rr{constructor(e,n,i=!1){if(super(),Array.isArray(e))throw new TypeError("THREE.BufferAttribute: array should be a Typed Array.");this.isBufferAttribute=!0,Object.defineProperty(this,"id",{value:GA++}),this.name="",this.array=e,this.itemSize=n,this.count=e!==void 0?e.length/n:0,this.normalized=i,this.usage=Ev,this.updateRanges=[],this.gpuType=Li,this.version=0}onUploadCallback(){}set needsUpdate(e){e===!0&&this.version++}setUsage(e){return this.usage=e,this}addUpdateRange(e,n){this.updateRanges.push({start:e,count:n})}clearUpdateRanges(){this.updateRanges.length=0}copy(e){return this.name=e.name,this.array=new e.array.constructor(e.array),this.itemSize=e.itemSize,this.count=e.count,this.normalized=e.normalized,this.usage=e.usage,this.gpuType=e.gpuType,this}copyAt(e,n,i){e*=this.itemSize,i*=n.itemSize;for(let a=0,s=this.itemSize;a<s;a++)this.array[e+a]=n.array[i+a];return this}copyArray(e){return this.array.set(e),this}applyMatrix3(e){if(this.itemSize===2)for(let n=0,i=this.count;n<i;n++)zc.fromBufferAttribute(this,n),zc.applyMatrix3(e),this.setXY(n,zc.x,zc.y);else if(this.itemSize===3)for(let n=0,i=this.count;n<i;n++)en.fromBufferAttribute(this,n),en.applyMatrix3(e),this.setXYZ(n,en.x,en.y,en.z);return this}applyMatrix4(e){for(let n=0,i=this.count;n<i;n++)en.fromBufferAttribute(this,n),en.applyMatrix4(e),this.setXYZ(n,en.x,en.y,en.z);return this}applyNormalMatrix(e){for(let n=0,i=this.count;n<i;n++)en.fromBufferAttribute(this,n),en.applyNormalMatrix(e),this.setXYZ(n,en.x,en.y,en.z);return this}transformDirection(e){for(let n=0,i=this.count;n<i;n++)en.fromBufferAttribute(this,n),en.transformDirection(e),this.setXYZ(n,en.x,en.y,en.z);return this}set(e,n=0){return this.array.set(e,n),this}getComponent(e,n){let i=this.array[e*this.itemSize+n];return this.normalized&&(i=Vl(i,this.array)),i}setComponent(e,n,i){return this.normalized&&(i=In(i,this.array)),this.array[e*this.itemSize+n]=i,this}getX(e){let n=this.array[e*this.itemSize];return this.normalized&&(n=Vl(n,this.array)),n}setX(e,n){return this.normalized&&(n=In(n,this.array)),this.array[e*this.itemSize]=n,this}getY(e){let n=this.array[e*this.itemSize+1];return this.normalized&&(n=Vl(n,this.array)),n}setY(e,n){return this.normalized&&(n=In(n,this.array)),this.array[e*this.itemSize+1]=n,this}getZ(e){let n=this.array[e*this.itemSize+2];return this.normalized&&(n=Vl(n,this.array)),n}setZ(e,n){return this.normalized&&(n=In(n,this.array)),this.array[e*this.itemSize+2]=n,this}getW(e){let n=this.array[e*this.itemSize+3];return this.normalized&&(n=Vl(n,this.array)),n}setW(e,n){return this.normalized&&(n=In(n,this.array)),this.array[e*this.itemSize+3]=n,this}setXY(e,n,i){return e*=this.itemSize,this.normalized&&(n=In(n,this.array),i=In(i,this.array)),this.array[e+0]=n,this.array[e+1]=i,this}setXYZ(e,n,i,a){return e*=this.itemSize,this.normalized&&(n=In(n,this.array),i=In(i,this.array),a=In(a,this.array)),this.array[e+0]=n,this.array[e+1]=i,this.array[e+2]=a,this}setXYZW(e,n,i,a,s){return e*=this.itemSize,this.normalized&&(n=In(n,this.array),i=In(i,this.array),a=In(a,this.array),s=In(s,this.array)),this.array[e+0]=n,this.array[e+1]=i,this.array[e+2]=a,this.array[e+3]=s,this}onUpload(e){return this.onUploadCallback=e,this}clone(){return new this.constructor(this.array,this.itemSize).copy(this)}toJSON(){const e={itemSize:this.itemSize,type:this.array.constructor.name,array:Array.from(this.array),normalized:this.normalized};return this.name!==""&&(e.name=this.name),this.usage!==Ev&&(e.usage=this.usage),e}dispose(){this.dispatchEvent({type:"dispose"})}}class NS extends on{constructor(e,n,i){super(new Uint16Array(e),n,i)}}class CS extends on{constructor(e,n,i){super(new Uint32Array(e),n,i)}}class Si extends on{constructor(e,n,i){super(new Float32Array(e),n,i)}}const VA=new lr,ql=new Z,Yh=new Z;class or{constructor(e=new Z,n=-1){this.isSphere=!0,this.center=e,this.radius=n}set(e,n){return this.center.copy(e),this.radius=n,this}setFromPoints(e,n){const i=this.center;n!==void 0?i.copy(n):VA.setFromPoints(e).getCenter(i);let a=0;for(let s=0,l=e.length;s<l;s++)a=Math.max(a,i.distanceToSquared(e[s]));return this.radius=Math.sqrt(a),this}copy(e){return this.center.copy(e.center),this.radius=e.radius,this}isEmpty(){return this.radius<0}makeEmpty(){return this.center.set(0,0,0),this.radius=-1,this}containsPoint(e){return e.distanceToSquared(this.center)<=this.radius*this.radius}distanceToPoint(e){return e.distanceTo(this.center)-this.radius}intersectsSphere(e){const n=this.radius+e.radius;return e.center.distanceToSquared(this.center)<=n*n}intersectsBox(e){return e.intersectsSphere(this)}intersectsPlane(e){return Math.abs(e.distanceToPoint(this.center))<=this.radius}clampPoint(e,n){const i=this.center.distanceToSquared(e);return n.copy(e),i>this.radius*this.radius&&(n.sub(this.center).normalize(),n.multiplyScalar(this.radius).add(this.center)),n}getBoundingBox(e){return this.isEmpty()?(e.makeEmpty(),e):(e.set(this.center,this.center),e.expandByScalar(this.radius),e)}applyMatrix4(e){return this.center.applyMatrix4(e),this.radius=this.radius*e.getMaxScaleOnAxis(),this}translate(e){return this.center.add(e),this}expandByPoint(e){if(this.isEmpty())return this.center.copy(e),this.radius=0,this;ql.subVectors(e,this.center);const n=ql.lengthSq();if(n>this.radius*this.radius){const i=Math.sqrt(n),a=(i-this.radius)*.5;this.center.addScaledVector(ql,a/i),this.radius+=a}return this}union(e){return e.isEmpty()?this:this.isEmpty()?(this.copy(e),this):(this.center.equals(e.center)===!0?this.radius=Math.max(this.radius,e.radius):(Yh.subVectors(e.center,this.center).setLength(e.radius),this.expandByPoint(ql.copy(e.center).add(Yh)),this.expandByPoint(ql.copy(e.center).sub(Yh))),this)}equals(e){return e.center.equals(this.center)&&e.radius===this.radius}clone(){return new this.constructor().copy(this)}toJSON(){return{radius:this.radius,center:this.center.toArray()}}fromJSON(e){return this.radius=e.radius,this.center.fromArray(e.center),this}}let WA=0;const ci=new zt,$h=new On,Tr=new Z,Zn=new lr,Yl=new lr,fn=new Z;class qn extends rr{constructor(){super(),this.isBufferGeometry=!0,Object.defineProperty(this,"id",{value:WA++}),this.uuid=lc(),this.name="",this.type="BufferGeometry",this.index=null,this.indirect=null,this.indirectOffset=0,this.attributes={},this.morphAttributes={},this.morphTargetsRelative=!1,this.groups=[],this.boundingBox=null,this.boundingSphere=null,this.drawRange={start:0,count:1/0},this.userData={},this._transformed=!1}getIndex(){return this.index}setIndex(e){return Array.isArray(e)?this.index=new(EA(e)?CS:NS)(e,1):this.index=e,this}setIndirect(e,n=0){return this.indirect=e,this.indirectOffset=n,this}getIndirect(){return this.indirect}getAttribute(e){return this.attributes[e]}setAttribute(e,n){return this.attributes[e]=n,this}deleteAttribute(e){return delete this.attributes[e],this}hasAttribute(e){return this.attributes[e]!==void 0}addGroup(e,n,i=0){this.groups.push({start:e,count:n,materialIndex:i})}clearGroups(){this.groups=[]}setDrawRange(e,n){this.drawRange.start=e,this.drawRange.count=n}applyMatrix4(e){const n=this.attributes.position;n!==void 0&&(n.applyMatrix4(e),n.needsUpdate=!0);const i=this.attributes.normal;if(i!==void 0){const s=new tt().getNormalMatrix(e);i.applyNormalMatrix(s),i.needsUpdate=!0}const a=this.attributes.tangent;return a!==void 0&&(a.transformDirection(e),a.needsUpdate=!0),this.boundingBox!==null&&this.computeBoundingBox(),this.boundingSphere!==null&&this.computeBoundingSphere(),this._transformed=!0,this}applyQuaternion(e){return ci.makeRotationFromQuaternion(e),this.applyMatrix4(ci),this}rotateX(e){return ci.makeRotationX(e),this.applyMatrix4(ci),this}rotateY(e){return ci.makeRotationY(e),this.applyMatrix4(ci),this}rotateZ(e){return ci.makeRotationZ(e),this.applyMatrix4(ci),this}translate(e,n,i){return ci.makeTranslation(e,n,i),this.applyMatrix4(ci),this}scale(e,n,i){return ci.makeScale(e,n,i),this.applyMatrix4(ci),this}lookAt(e){return $h.lookAt(e),$h.updateMatrix(),this.applyMatrix4($h.matrix),this}center(){return this.computeBoundingBox(),this.boundingBox.getCenter(Tr).negate(),this.translate(Tr.x,Tr.y,Tr.z),this}setFromPoints(e){const n=this.getAttribute("position");if(n===void 0){const i=[];for(let a=0,s=e.length;a<s;a++){const l=e[a];i.push(l.x,l.y,l.z||0)}this.setAttribute("position",new Si(i,3))}else{const i=Math.min(e.length,n.count);for(let a=0;a<i;a++){const s=e[a];n.setXYZ(a,s.x,s.y,s.z||0)}e.length>n.count&&Qe("BufferGeometry: Buffer size too small for points data. Use .dispose() and create a new geometry."),n.needsUpdate=!0}return this}computeBoundingBox(){this.boundingBox===null&&(this.boundingBox=new lr);const e=this.attributes.position,n=this.morphAttributes.position;if(e&&e.isGLBufferAttribute){bt("BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box.",this),this.boundingBox.set(new Z(-1/0,-1/0,-1/0),new Z(1/0,1/0,1/0));return}if(e!==void 0){if(this.boundingBox.setFromBufferAttribute(e),n)for(let i=0,a=n.length;i<a;i++){const s=n[i];Zn.setFromBufferAttribute(s),this.morphTargetsRelative?(fn.addVectors(this.boundingBox.min,Zn.min),this.boundingBox.expandByPoint(fn),fn.addVectors(this.boundingBox.max,Zn.max),this.boundingBox.expandByPoint(fn)):(this.boundingBox.expandByPoint(Zn.min),this.boundingBox.expandByPoint(Zn.max))}}else this.boundingBox.makeEmpty();(isNaN(this.boundingBox.min.x)||isNaN(this.boundingBox.min.y)||isNaN(this.boundingBox.min.z))&&bt('BufferGeometry.computeBoundingBox(): Computed min/max have NaN values. The "position" attribute is likely to have NaN values.',this)}computeBoundingSphere(){this.boundingSphere===null&&(this.boundingSphere=new or);const e=this.attributes.position,n=this.morphAttributes.position;if(e&&e.isGLBufferAttribute){bt("BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere.",this),this.boundingSphere.set(new Z,1/0);return}if(e){const i=this.boundingSphere.center;if(Zn.setFromBufferAttribute(e),n)for(let s=0,l=n.length;s<l;s++){const o=n[s];Yl.setFromBufferAttribute(o),this.morphTargetsRelative?(fn.addVectors(Zn.min,Yl.min),Zn.expandByPoint(fn),fn.addVectors(Zn.max,Yl.max),Zn.expandByPoint(fn)):(Zn.expandByPoint(Yl.min),Zn.expandByPoint(Yl.max))}Zn.getCenter(i);let a=0;for(let s=0,l=e.count;s<l;s++)fn.fromBufferAttribute(e,s),a=Math.max(a,i.distanceToSquared(fn));if(n)for(let s=0,l=n.length;s<l;s++){const o=n[s],c=this.morphTargetsRelative;for(let u=0,h=o.count;u<h;u++)fn.fromBufferAttribute(o,u),c&&(Tr.fromBufferAttribute(e,u),fn.add(Tr)),a=Math.max(a,i.distanceToSquared(fn))}this.boundingSphere.radius=Math.sqrt(a),isNaN(this.boundingSphere.radius)&&bt('BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.',this)}}computeTangents(){const e=this.index,n=this.attributes;if(e===null||n.position===void 0||n.normal===void 0||n.uv===void 0){bt("BufferGeometry: .computeTangents() failed. Missing required attributes (index, position, normal or uv)");return}const i=n.position,a=n.normal,s=n.uv;let l=this.getAttribute("tangent");(l===void 0||l.count!==i.count)&&(l=new on(new Float32Array(4*i.count),4),this.setAttribute("tangent",l));const o=[],c=[];for(let S=0;S<i.count;S++)o[S]=new Z,c[S]=new Z;const u=new Z,h=new Z,g=new Z,d=new Mt,p=new Mt,x=new Mt,_=new Z,m=new Z;function f(S,N,D){u.fromBufferAttribute(i,S),h.fromBufferAttribute(i,N),g.fromBufferAttribute(i,D),d.fromBufferAttribute(s,S),p.fromBufferAttribute(s,N),x.fromBufferAttribute(s,D),h.sub(u),g.sub(u),p.sub(d),x.sub(d);const U=1/(p.x*x.y-x.x*p.y);isFinite(U)&&(_.copy(h).multiplyScalar(x.y).addScaledVector(g,-p.y).multiplyScalar(U),m.copy(g).multiplyScalar(p.x).addScaledVector(h,-x.x).multiplyScalar(U),o[S].add(_),o[N].add(_),o[D].add(_),c[S].add(m),c[N].add(m),c[D].add(m))}let v=this.groups;v.length===0&&(v=[{start:0,count:e.count}]);for(let S=0,N=v.length;S<N;++S){const D=v[S],U=D.start,I=D.count;for(let Y=U,k=U+I;Y<k;Y+=3)f(e.getX(Y+0),e.getX(Y+1),e.getX(Y+2))}const y=new Z,b=new Z,T=new Z,M=new Z;function A(S){T.fromBufferAttribute(a,S),M.copy(T);const N=o[S];y.copy(N),y.sub(T.multiplyScalar(T.dot(N))).normalize(),b.crossVectors(M,N);const U=b.dot(c[S])<0?-1:1;l.setXYZW(S,y.x,y.y,y.z,U)}for(let S=0,N=v.length;S<N;++S){const D=v[S],U=D.start,I=D.count;for(let Y=U,k=U+I;Y<k;Y+=3)A(e.getX(Y+0)),A(e.getX(Y+1)),A(e.getX(Y+2))}this._transformed=!0}computeVertexNormals(){const e=this.index,n=this.getAttribute("position");if(n!==void 0){let i=this.getAttribute("normal");if(i===void 0||i.count!==n.count)i=new on(new Float32Array(n.count*3),3),this.setAttribute("normal",i);else for(let d=0,p=i.count;d<p;d++)i.setXYZ(d,0,0,0);const a=new Z,s=new Z,l=new Z,o=new Z,c=new Z,u=new Z,h=new Z,g=new Z;if(e)for(let d=0,p=e.count;d<p;d+=3){const x=e.getX(d+0),_=e.getX(d+1),m=e.getX(d+2);a.fromBufferAttribute(n,x),s.fromBufferAttribute(n,_),l.fromBufferAttribute(n,m),h.subVectors(l,s),g.subVectors(a,s),h.cross(g),o.fromBufferAttribute(i,x),c.fromBufferAttribute(i,_),u.fromBufferAttribute(i,m),o.add(h),c.add(h),u.add(h),i.setXYZ(x,o.x,o.y,o.z),i.setXYZ(_,c.x,c.y,c.z),i.setXYZ(m,u.x,u.y,u.z)}else for(let d=0,p=n.count;d<p;d+=3)a.fromBufferAttribute(n,d+0),s.fromBufferAttribute(n,d+1),l.fromBufferAttribute(n,d+2),h.subVectors(l,s),g.subVectors(a,s),h.cross(g),i.setXYZ(d+0,h.x,h.y,h.z),i.setXYZ(d+1,h.x,h.y,h.z),i.setXYZ(d+2,h.x,h.y,h.z);this.normalizeNormals(),i.needsUpdate=!0}}normalizeNormals(){const e=this.attributes.normal;for(let n=0,i=e.count;n<i;n++)fn.fromBufferAttribute(e,n),fn.normalize(),e.setXYZ(n,fn.x,fn.y,fn.z)}toNonIndexed(){function e(o,c){const u=o.array,h=o.itemSize,g=o.normalized,d=new u.constructor(c.length*h);let p=0,x=0;for(let _=0,m=c.length;_<m;_++){o.isInterleavedBufferAttribute?p=c[_]*o.data.stride+o.offset:p=c[_]*h;for(let f=0;f<h;f++)d[x++]=u[p++]}return new on(d,h,g)}if(this.index===null)return Qe("BufferGeometry.toNonIndexed(): BufferGeometry is already non-indexed."),this;const n=new qn,i=this.index.array,a=this.attributes;for(const o in a){const c=a[o],u=e(c,i);n.setAttribute(o,u)}const s=this.morphAttributes;for(const o in s){const c=[],u=s[o];for(let h=0,g=u.length;h<g;h++){const d=u[h],p=e(d,i);c.push(p)}n.morphAttributes[o]=c}n.morphTargetsRelative=this.morphTargetsRelative;const l=this.groups;for(let o=0,c=l.length;o<c;o++){const u=l[o];n.addGroup(u.start,u.count,u.materialIndex)}return n}toJSON(){const e={metadata:{version:4.7,type:"BufferGeometry",generator:"BufferGeometry.toJSON"}};if(e.uuid=this.uuid,e.type=this.parameters!==void 0&&this._transformed===!0?"BufferGeometry":this.type,this.name!==""&&(e.name=this.name),Object.keys(this.userData).length>0&&(e.userData=this.userData),this.parameters!==void 0&&this._transformed!==!0){const c=this.parameters;for(const u in c)c[u]!==void 0&&(e[u]=c[u]);return e}e.data={attributes:{}};const n=this.index;n!==null&&(e.data.index={type:n.array.constructor.name,array:Array.prototype.slice.call(n.array)});const i=this.attributes;for(const c in i){const u=i[c];e.data.attributes[c]=u.toJSON(e.data)}const a={};let s=!1;for(const c in this.morphAttributes){const u=this.morphAttributes[c],h=[];for(let g=0,d=u.length;g<d;g++){const p=u[g];h.push(p.toJSON(e.data))}h.length>0&&(a[c]=h,s=!0)}s&&(e.data.morphAttributes=a,e.data.morphTargetsRelative=this.morphTargetsRelative);const l=this.groups;l.length>0&&(e.data.groups=JSON.parse(JSON.stringify(l)));const o=this.boundingSphere;return o!==null&&(e.data.boundingSphere=o.toJSON()),e}clone(){return new this.constructor().copy(this)}copy(e){this.index=null,this.attributes={},this.morphAttributes={},this.groups=[],this.boundingBox=null,this.boundingSphere=null;const n={};this.name=e.name;const i=e.index;i!==null&&this.setIndex(i.clone());const a=e.attributes;for(const u in a){const h=a[u];this.setAttribute(u,h.clone(n))}const s=e.morphAttributes;for(const u in s){const h=[],g=s[u];for(let d=0,p=g.length;d<p;d++)h.push(g[d].clone(n));this.morphAttributes[u]=h}this.morphTargetsRelative=e.morphTargetsRelative;const l=e.groups;for(let u=0,h=l.length;u<h;u++){const g=l[u];this.addGroup(g.start,g.count,g.materialIndex)}const o=e.boundingBox;o!==null&&(this.boundingBox=o.clone());const c=e.boundingSphere;return c!==null&&(this.boundingSphere=c.clone()),this.drawRange.start=e.drawRange.start,this.drawRange.count=e.drawRange.count,this.userData=e.userData,this._transformed=e._transformed,this}dispose(){this.dispatchEvent({type:"dispose"})}}let XA=0;class Ul extends rr{constructor(){super(),this.isMaterial=!0,Object.defineProperty(this,"id",{value:XA++}),this.uuid=lc(),this.name="",this.type="Material",this.blending=il,this.side=ys,this.vertexColors=!1,this.opacity=1,this.transparent=!1,this.alphaHash=!1,this.blendSrc=vp,this.blendDst=_p,this.blendEquation=zs,this.blendSrcAlpha=null,this.blendDstAlpha=null,this.blendEquationAlpha=null,this.blendColor=new gt(0,0,0),this.blendAlpha=0,this.depthFunc=_l,this.depthTest=!0,this.depthWrite=!0,this.stencilWriteMask=255,this.stencilFunc=Mv,this.stencilRef=0,this.stencilFuncMask=255,this.stencilFail=mr,this.stencilZFail=mr,this.stencilZPass=mr,this.stencilWrite=!1,this.clippingPlanes=null,this.clipIntersection=!1,this.clipShadows=!1,this.shadowSide=null,this.colorWrite=!0,this.precision=null,this.polygonOffset=!1,this.polygonOffsetFactor=0,this.polygonOffsetUnits=0,this.dithering=!1,this.alphaToCoverage=!1,this.premultipliedAlpha=!1,this.forceSinglePass=!1,this.allowOverride=!0,this.visible=!0,this.toneMapped=!0,this.userData={},this.version=0,this._alphaTest=0}get alphaTest(){return this._alphaTest}set alphaTest(e){this._alphaTest>0!=e>0&&this.version++,this._alphaTest=e}onBeforeRender(){}onBeforeCompile(){}customProgramCacheKey(){return this.onBeforeCompile.toString()}setValues(e){if(e!==void 0)for(const n in e){const i=e[n];if(i===void 0){Qe(`Material: parameter '${n}' has value of undefined.`);continue}const a=this[n];if(a===void 0){Qe(`Material: '${n}' is not a property of THREE.${this.type}.`);continue}a&&a.isColor?a.set(i):a&&a.isVector2&&i&&i.isVector2||a&&a.isEuler&&i&&i.isEuler||a&&a.isVector3&&i&&i.isVector3?a.copy(i):this[n]=i}}toJSON(e){const n=e===void 0||typeof e=="string";n&&(e={textures:{},images:{}});const i={metadata:{version:4.7,type:"Material",generator:"Material.toJSON"}};i.uuid=this.uuid,i.type=this.type,this.name!==""&&(i.name=this.name),this.color&&this.color.isColor&&(i.color=this.color.getHex()),this.roughness!==void 0&&(i.roughness=this.roughness),this.metalness!==void 0&&(i.metalness=this.metalness),this.sheen!==void 0&&(i.sheen=this.sheen),this.sheenColor&&this.sheenColor.isColor&&(i.sheenColor=this.sheenColor.getHex()),this.sheenRoughness!==void 0&&(i.sheenRoughness=this.sheenRoughness),this.emissive&&this.emissive.isColor&&(i.emissive=this.emissive.getHex()),this.emissiveIntensity!==void 0&&this.emissiveIntensity!==1&&(i.emissiveIntensity=this.emissiveIntensity),this.specular&&this.specular.isColor&&(i.specular=this.specular.getHex()),this.specularIntensity!==void 0&&(i.specularIntensity=this.specularIntensity),this.specularColor&&this.specularColor.isColor&&(i.specularColor=this.specularColor.getHex()),this.shininess!==void 0&&(i.shininess=this.shininess),this.clearcoat!==void 0&&(i.clearcoat=this.clearcoat),this.clearcoatRoughness!==void 0&&(i.clearcoatRoughness=this.clearcoatRoughness),this.clearcoatMap&&this.clearcoatMap.isTexture&&(i.clearcoatMap=this.clearcoatMap.toJSON(e).uuid),this.clearcoatRoughnessMap&&this.clearcoatRoughnessMap.isTexture&&(i.clearcoatRoughnessMap=this.clearcoatRoughnessMap.toJSON(e).uuid),this.clearcoatNormalMap&&this.clearcoatNormalMap.isTexture&&(i.clearcoatNormalMap=this.clearcoatNormalMap.toJSON(e).uuid,i.clearcoatNormalScale=this.clearcoatNormalScale.toArray()),this.sheenColorMap&&this.sheenColorMap.isTexture&&(i.sheenColorMap=this.sheenColorMap.toJSON(e).uuid),this.sheenRoughnessMap&&this.sheenRoughnessMap.isTexture&&(i.sheenRoughnessMap=this.sheenRoughnessMap.toJSON(e).uuid),this.dispersion!==void 0&&(i.dispersion=this.dispersion),this.iridescence!==void 0&&(i.iridescence=this.iridescence),this.iridescenceIOR!==void 0&&(i.iridescenceIOR=this.iridescenceIOR),this.iridescenceThicknessRange!==void 0&&(i.iridescenceThicknessRange=this.iridescenceThicknessRange),this.iridescenceMap&&this.iridescenceMap.isTexture&&(i.iridescenceMap=this.iridescenceMap.toJSON(e).uuid),this.iridescenceThicknessMap&&this.iridescenceThicknessMap.isTexture&&(i.iridescenceThicknessMap=this.iridescenceThicknessMap.toJSON(e).uuid),this.anisotropy!==void 0&&(i.anisotropy=this.anisotropy),this.anisotropyRotation!==void 0&&(i.anisotropyRotation=this.anisotropyRotation),this.anisotropyMap&&this.anisotropyMap.isTexture&&(i.anisotropyMap=this.anisotropyMap.toJSON(e).uuid),this.map&&this.map.isTexture&&(i.map=this.map.toJSON(e).uuid),this.matcap&&this.matcap.isTexture&&(i.matcap=this.matcap.toJSON(e).uuid),this.alphaMap&&this.alphaMap.isTexture&&(i.alphaMap=this.alphaMap.toJSON(e).uuid),this.lightMap&&this.lightMap.isTexture&&(i.lightMap=this.lightMap.toJSON(e).uuid,i.lightMapIntensity=this.lightMapIntensity),this.aoMap&&this.aoMap.isTexture&&(i.aoMap=this.aoMap.toJSON(e).uuid,i.aoMapIntensity=this.aoMapIntensity),this.bumpMap&&this.bumpMap.isTexture&&(i.bumpMap=this.bumpMap.toJSON(e).uuid,i.bumpScale=this.bumpScale),this.normalMap&&this.normalMap.isTexture&&(i.normalMap=this.normalMap.toJSON(e).uuid,i.normalMapType=this.normalMapType,i.normalScale=this.normalScale.toArray()),this.displacementMap&&this.displacementMap.isTexture&&(i.displacementMap=this.displacementMap.toJSON(e).uuid,i.displacementScale=this.displacementScale,i.displacementBias=this.displacementBias),this.roughnessMap&&this.roughnessMap.isTexture&&(i.roughnessMap=this.roughnessMap.toJSON(e).uuid),this.metalnessMap&&this.metalnessMap.isTexture&&(i.metalnessMap=this.metalnessMap.toJSON(e).uuid),this.emissiveMap&&this.emissiveMap.isTexture&&(i.emissiveMap=this.emissiveMap.toJSON(e).uuid),this.specularMap&&this.specularMap.isTexture&&(i.specularMap=this.specularMap.toJSON(e).uuid),this.specularIntensityMap&&this.specularIntensityMap.isTexture&&(i.specularIntensityMap=this.specularIntensityMap.toJSON(e).uuid),this.specularColorMap&&this.specularColorMap.isTexture&&(i.specularColorMap=this.specularColorMap.toJSON(e).uuid),this.envMap&&this.envMap.isTexture&&(i.envMap=this.envMap.toJSON(e).uuid,this.combine!==void 0&&(i.combine=this.combine)),this.envMapRotation!==void 0&&(i.envMapRotation=this.envMapRotation.toArray()),this.envMapIntensity!==void 0&&(i.envMapIntensity=this.envMapIntensity),this.reflectivity!==void 0&&(i.reflectivity=this.reflectivity),this.refractionRatio!==void 0&&(i.refractionRatio=this.refractionRatio),this.gradientMap&&this.gradientMap.isTexture&&(i.gradientMap=this.gradientMap.toJSON(e).uuid),this.transmission!==void 0&&(i.transmission=this.transmission),this.transmissionMap&&this.transmissionMap.isTexture&&(i.transmissionMap=this.transmissionMap.toJSON(e).uuid),this.thickness!==void 0&&(i.thickness=this.thickness),this.thicknessMap&&this.thicknessMap.isTexture&&(i.thicknessMap=this.thicknessMap.toJSON(e).uuid),this.attenuationDistance!==void 0&&this.attenuationDistance!==1/0&&(i.attenuationDistance=this.attenuationDistance),this.attenuationColor!==void 0&&(i.attenuationColor=this.attenuationColor.getHex()),this.size!==void 0&&(i.size=this.size),this.shadowSide!==null&&(i.shadowSide=this.shadowSide),this.sizeAttenuation!==void 0&&(i.sizeAttenuation=this.sizeAttenuation),this.blending!==il&&(i.blending=this.blending),this.side!==ys&&(i.side=this.side),this.vertexColors===!0&&(i.vertexColors=!0),this.opacity<1&&(i.opacity=this.opacity),this.transparent===!0&&(i.transparent=!0),this.blendSrc!==vp&&(i.blendSrc=this.blendSrc),this.blendDst!==_p&&(i.blendDst=this.blendDst),this.blendEquation!==zs&&(i.blendEquation=this.blendEquation),this.blendSrcAlpha!==null&&(i.blendSrcAlpha=this.blendSrcAlpha),this.blendDstAlpha!==null&&(i.blendDstAlpha=this.blendDstAlpha),this.blendEquationAlpha!==null&&(i.blendEquationAlpha=this.blendEquationAlpha),this.blendColor&&this.blendColor.isColor&&(i.blendColor=this.blendColor.getHex()),this.blendAlpha!==0&&(i.blendAlpha=this.blendAlpha),this.depthFunc!==_l&&(i.depthFunc=this.depthFunc),this.depthTest===!1&&(i.depthTest=this.depthTest),this.depthWrite===!1&&(i.depthWrite=this.depthWrite),this.colorWrite===!1&&(i.colorWrite=this.colorWrite),this.stencilWriteMask!==255&&(i.stencilWriteMask=this.stencilWriteMask),this.stencilFunc!==Mv&&(i.stencilFunc=this.stencilFunc),this.stencilRef!==0&&(i.stencilRef=this.stencilRef),this.stencilFuncMask!==255&&(i.stencilFuncMask=this.stencilFuncMask),this.stencilFail!==mr&&(i.stencilFail=this.stencilFail),this.stencilZFail!==mr&&(i.stencilZFail=this.stencilZFail),this.stencilZPass!==mr&&(i.stencilZPass=this.stencilZPass),this.stencilWrite===!0&&(i.stencilWrite=this.stencilWrite),this.rotation!==void 0&&this.rotation!==0&&(i.rotation=this.rotation),this.polygonOffset===!0&&(i.polygonOffset=!0),this.polygonOffsetFactor!==0&&(i.polygonOffsetFactor=this.polygonOffsetFactor),this.polygonOffsetUnits!==0&&(i.polygonOffsetUnits=this.polygonOffsetUnits),this.linewidth!==void 0&&this.linewidth!==1&&(i.linewidth=this.linewidth),this.dashSize!==void 0&&(i.dashSize=this.dashSize),this.gapSize!==void 0&&(i.gapSize=this.gapSize),this.scale!==void 0&&(i.scale=this.scale),this.dithering===!0&&(i.dithering=!0),this.alphaTest>0&&(i.alphaTest=this.alphaTest),this.alphaHash===!0&&(i.alphaHash=!0),this.alphaToCoverage===!0&&(i.alphaToCoverage=!0),this.premultipliedAlpha===!0&&(i.premultipliedAlpha=!0),this.forceSinglePass===!0&&(i.forceSinglePass=!0),this.allowOverride===!1&&(i.allowOverride=!1),this.wireframe===!0&&(i.wireframe=!0),this.wireframeLinewidth>1&&(i.wireframeLinewidth=this.wireframeLinewidth),this.wireframeLinecap!=="round"&&(i.wireframeLinecap=this.wireframeLinecap),this.wireframeLinejoin!=="round"&&(i.wireframeLinejoin=this.wireframeLinejoin),this.flatShading===!0&&(i.flatShading=!0),this.visible===!1&&(i.visible=!1),this.toneMapped===!1&&(i.toneMapped=!1),this.fog===!1&&(i.fog=!1),Object.keys(this.userData).length>0&&(i.userData=this.userData);function a(s){const l=[];for(const o in s){const c=s[o];delete c.metadata,l.push(c)}return l}if(n){const s=a(e.textures),l=a(e.images);s.length>0&&(i.textures=s),l.length>0&&(i.images=l)}return i}fromJSON(e,n){if(e.uuid!==void 0&&(this.uuid=e.uuid),e.name!==void 0&&(this.name=e.name),e.color!==void 0&&this.color!==void 0&&this.color.setHex(e.color),e.roughness!==void 0&&(this.roughness=e.roughness),e.metalness!==void 0&&(this.metalness=e.metalness),e.sheen!==void 0&&(this.sheen=e.sheen),e.sheenColor!==void 0&&(this.sheenColor=new gt().setHex(e.sheenColor)),e.sheenRoughness!==void 0&&(this.sheenRoughness=e.sheenRoughness),e.emissive!==void 0&&this.emissive!==void 0&&this.emissive.setHex(e.emissive),e.specular!==void 0&&this.specular!==void 0&&this.specular.setHex(e.specular),e.specularIntensity!==void 0&&(this.specularIntensity=e.specularIntensity),e.specularColor!==void 0&&this.specularColor!==void 0&&this.specularColor.setHex(e.specularColor),e.shininess!==void 0&&(this.shininess=e.shininess),e.clearcoat!==void 0&&(this.clearcoat=e.clearcoat),e.clearcoatRoughness!==void 0&&(this.clearcoatRoughness=e.clearcoatRoughness),e.dispersion!==void 0&&(this.dispersion=e.dispersion),e.iridescence!==void 0&&(this.iridescence=e.iridescence),e.iridescenceIOR!==void 0&&(this.iridescenceIOR=e.iridescenceIOR),e.iridescenceThicknessRange!==void 0&&(this.iridescenceThicknessRange=e.iridescenceThicknessRange),e.transmission!==void 0&&(this.transmission=e.transmission),e.thickness!==void 0&&(this.thickness=e.thickness),e.attenuationDistance!==void 0&&(this.attenuationDistance=e.attenuationDistance),e.attenuationColor!==void 0&&this.attenuationColor!==void 0&&this.attenuationColor.setHex(e.attenuationColor),e.anisotropy!==void 0&&(this.anisotropy=e.anisotropy),e.anisotropyRotation!==void 0&&(this.anisotropyRotation=e.anisotropyRotation),e.fog!==void 0&&(this.fog=e.fog),e.flatShading!==void 0&&(this.flatShading=e.flatShading),e.blending!==void 0&&(this.blending=e.blending),e.combine!==void 0&&(this.combine=e.combine),e.side!==void 0&&(this.side=e.side),e.shadowSide!==void 0&&(this.shadowSide=e.shadowSide),e.opacity!==void 0&&(this.opacity=e.opacity),e.transparent!==void 0&&(this.transparent=e.transparent),e.alphaTest!==void 0&&(this.alphaTest=e.alphaTest),e.alphaHash!==void 0&&(this.alphaHash=e.alphaHash),e.depthFunc!==void 0&&(this.depthFunc=e.depthFunc),e.depthTest!==void 0&&(this.depthTest=e.depthTest),e.depthWrite!==void 0&&(this.depthWrite=e.depthWrite),e.colorWrite!==void 0&&(this.colorWrite=e.colorWrite),e.blendSrc!==void 0&&(this.blendSrc=e.blendSrc),e.blendDst!==void 0&&(this.blendDst=e.blendDst),e.blendEquation!==void 0&&(this.blendEquation=e.blendEquation),e.blendSrcAlpha!==void 0&&(this.blendSrcAlpha=e.blendSrcAlpha),e.blendDstAlpha!==void 0&&(this.blendDstAlpha=e.blendDstAlpha),e.blendEquationAlpha!==void 0&&(this.blendEquationAlpha=e.blendEquationAlpha),e.blendColor!==void 0&&this.blendColor!==void 0&&this.blendColor.setHex(e.blendColor),e.blendAlpha!==void 0&&(this.blendAlpha=e.blendAlpha),e.stencilWriteMask!==void 0&&(this.stencilWriteMask=e.stencilWriteMask),e.stencilFunc!==void 0&&(this.stencilFunc=e.stencilFunc),e.stencilRef!==void 0&&(this.stencilRef=e.stencilRef),e.stencilFuncMask!==void 0&&(this.stencilFuncMask=e.stencilFuncMask),e.stencilFail!==void 0&&(this.stencilFail=e.stencilFail),e.stencilZFail!==void 0&&(this.stencilZFail=e.stencilZFail),e.stencilZPass!==void 0&&(this.stencilZPass=e.stencilZPass),e.stencilWrite!==void 0&&(this.stencilWrite=e.stencilWrite),e.wireframe!==void 0&&(this.wireframe=e.wireframe),e.wireframeLinewidth!==void 0&&(this.wireframeLinewidth=e.wireframeLinewidth),e.wireframeLinecap!==void 0&&(this.wireframeLinecap=e.wireframeLinecap),e.wireframeLinejoin!==void 0&&(this.wireframeLinejoin=e.wireframeLinejoin),e.rotation!==void 0&&(this.rotation=e.rotation),e.linewidth!==void 0&&(this.linewidth=e.linewidth),e.dashSize!==void 0&&(this.dashSize=e.dashSize),e.gapSize!==void 0&&(this.gapSize=e.gapSize),e.scale!==void 0&&(this.scale=e.scale),e.polygonOffset!==void 0&&(this.polygonOffset=e.polygonOffset),e.polygonOffsetFactor!==void 0&&(this.polygonOffsetFactor=e.polygonOffsetFactor),e.polygonOffsetUnits!==void 0&&(this.polygonOffsetUnits=e.polygonOffsetUnits),e.dithering!==void 0&&(this.dithering=e.dithering),e.alphaToCoverage!==void 0&&(this.alphaToCoverage=e.alphaToCoverage),e.premultipliedAlpha!==void 0&&(this.premultipliedAlpha=e.premultipliedAlpha),e.forceSinglePass!==void 0&&(this.forceSinglePass=e.forceSinglePass),e.allowOverride!==void 0&&(this.allowOverride=e.allowOverride),e.visible!==void 0&&(this.visible=e.visible),e.toneMapped!==void 0&&(this.toneMapped=e.toneMapped),e.userData!==void 0&&(this.userData=e.userData),e.vertexColors!==void 0&&(typeof e.vertexColors=="number"?this.vertexColors=e.vertexColors>0:this.vertexColors=e.vertexColors),e.size!==void 0&&(this.size=e.size),e.sizeAttenuation!==void 0&&(this.sizeAttenuation=e.sizeAttenuation),e.map!==void 0&&(this.map=n[e.map]||null),e.matcap!==void 0&&(this.matcap=n[e.matcap]||null),e.alphaMap!==void 0&&(this.alphaMap=n[e.alphaMap]||null),e.bumpMap!==void 0&&(this.bumpMap=n[e.bumpMap]||null),e.bumpScale!==void 0&&(this.bumpScale=e.bumpScale),e.normalMap!==void 0&&(this.normalMap=n[e.normalMap]||null),e.normalMapType!==void 0&&(this.normalMapType=e.normalMapType),e.normalScale!==void 0){let i=e.normalScale;Array.isArray(i)===!1&&(i=[i,i]),this.normalScale=new Mt().fromArray(i)}return e.displacementMap!==void 0&&(this.displacementMap=n[e.displacementMap]||null),e.displacementScale!==void 0&&(this.displacementScale=e.displacementScale),e.displacementBias!==void 0&&(this.displacementBias=e.displacementBias),e.roughnessMap!==void 0&&(this.roughnessMap=n[e.roughnessMap]||null),e.metalnessMap!==void 0&&(this.metalnessMap=n[e.metalnessMap]||null),e.emissiveMap!==void 0&&(this.emissiveMap=n[e.emissiveMap]||null),e.emissiveIntensity!==void 0&&(this.emissiveIntensity=e.emissiveIntensity),e.specularMap!==void 0&&(this.specularMap=n[e.specularMap]||null),e.specularIntensityMap!==void 0&&(this.specularIntensityMap=n[e.specularIntensityMap]||null),e.specularColorMap!==void 0&&(this.specularColorMap=n[e.specularColorMap]||null),e.envMap!==void 0&&(this.envMap=n[e.envMap]||null),e.envMapRotation!==void 0&&this.envMapRotation.fromArray(e.envMapRotation),e.envMapIntensity!==void 0&&(this.envMapIntensity=e.envMapIntensity),e.reflectivity!==void 0&&(this.reflectivity=e.reflectivity),e.refractionRatio!==void 0&&(this.refractionRatio=e.refractionRatio),e.lightMap!==void 0&&(this.lightMap=n[e.lightMap]||null),e.lightMapIntensity!==void 0&&(this.lightMapIntensity=e.lightMapIntensity),e.aoMap!==void 0&&(this.aoMap=n[e.aoMap]||null),e.aoMapIntensity!==void 0&&(this.aoMapIntensity=e.aoMapIntensity),e.gradientMap!==void 0&&(this.gradientMap=n[e.gradientMap]||null),e.clearcoatMap!==void 0&&(this.clearcoatMap=n[e.clearcoatMap]||null),e.clearcoatRoughnessMap!==void 0&&(this.clearcoatRoughnessMap=n[e.clearcoatRoughnessMap]||null),e.clearcoatNormalMap!==void 0&&(this.clearcoatNormalMap=n[e.clearcoatNormalMap]||null),e.clearcoatNormalScale!==void 0&&(this.clearcoatNormalScale=new Mt().fromArray(e.clearcoatNormalScale)),e.iridescenceMap!==void 0&&(this.iridescenceMap=n[e.iridescenceMap]||null),e.iridescenceThicknessMap!==void 0&&(this.iridescenceThicknessMap=n[e.iridescenceThicknessMap]||null),e.transmissionMap!==void 0&&(this.transmissionMap=n[e.transmissionMap]||null),e.thicknessMap!==void 0&&(this.thicknessMap=n[e.thicknessMap]||null),e.anisotropyMap!==void 0&&(this.anisotropyMap=n[e.anisotropyMap]||null),e.sheenColorMap!==void 0&&(this.sheenColorMap=n[e.sheenColorMap]||null),e.sheenRoughnessMap!==void 0&&(this.sheenRoughnessMap=n[e.sheenRoughnessMap]||null),this}clone(){return new this.constructor().copy(this)}copy(e){this.name=e.name,this.blending=e.blending,this.side=e.side,this.vertexColors=e.vertexColors,this.opacity=e.opacity,this.transparent=e.transparent,this.blendSrc=e.blendSrc,this.blendDst=e.blendDst,this.blendEquation=e.blendEquation,this.blendSrcAlpha=e.blendSrcAlpha,this.blendDstAlpha=e.blendDstAlpha,this.blendEquationAlpha=e.blendEquationAlpha,this.blendColor.copy(e.blendColor),this.blendAlpha=e.blendAlpha,this.depthFunc=e.depthFunc,this.depthTest=e.depthTest,this.depthWrite=e.depthWrite,this.stencilWriteMask=e.stencilWriteMask,this.stencilFunc=e.stencilFunc,this.stencilRef=e.stencilRef,this.stencilFuncMask=e.stencilFuncMask,this.stencilFail=e.stencilFail,this.stencilZFail=e.stencilZFail,this.stencilZPass=e.stencilZPass,this.stencilWrite=e.stencilWrite;const n=e.clippingPlanes;let i=null;if(n!==null){const a=n.length;i=new Array(a);for(let s=0;s!==a;++s)i[s]=n[s].clone()}return this.clippingPlanes=i,this.clipIntersection=e.clipIntersection,this.clipShadows=e.clipShadows,this.shadowSide=e.shadowSide,this.colorWrite=e.colorWrite,this.precision=e.precision,this.polygonOffset=e.polygonOffset,this.polygonOffsetFactor=e.polygonOffsetFactor,this.polygonOffsetUnits=e.polygonOffsetUnits,this.dithering=e.dithering,this.alphaTest=e.alphaTest,this.alphaHash=e.alphaHash,this.alphaToCoverage=e.alphaToCoverage,this.premultipliedAlpha=e.premultipliedAlpha,this.forceSinglePass=e.forceSinglePass,this.allowOverride=e.allowOverride,this.visible=e.visible,this.toneMapped=e.toneMapped,this.userData=JSON.parse(JSON.stringify(e.userData)),this}dispose(){this.dispatchEvent({type:"dispose"})}set needsUpdate(e){e===!0&&this.version++}}const ua=new Z,Kh=new Z,jc=new Z,Ga=new Z,Zh=new Z,kc=new Z,Qh=new Z;class Ag{constructor(e=new Z,n=new Z(0,0,-1)){this.origin=e,this.direction=n}set(e,n){return this.origin.copy(e),this.direction.copy(n),this}copy(e){return this.origin.copy(e.origin),this.direction.copy(e.direction),this}at(e,n){return n.copy(this.origin).addScaledVector(this.direction,e)}lookAt(e){return this.direction.copy(e).sub(this.origin).normalize(),this}recast(e){return this.origin.copy(this.at(e,ua)),this}closestPointToPoint(e,n){n.subVectors(e,this.origin);const i=n.dot(this.direction);return i<0?n.copy(this.origin):n.copy(this.origin).addScaledVector(this.direction,i)}distanceToPoint(e){return Math.sqrt(this.distanceSqToPoint(e))}distanceSqToPoint(e){const n=ua.subVectors(e,this.origin).dot(this.direction);return n<0?this.origin.distanceToSquared(e):(ua.copy(this.origin).addScaledVector(this.direction,n),ua.distanceToSquared(e))}distanceSqToSegment(e,n,i,a){Kh.copy(e).add(n).multiplyScalar(.5),jc.copy(n).sub(e).normalize(),Ga.copy(this.origin).sub(Kh);const s=e.distanceTo(n)*.5,l=-this.direction.dot(jc),o=Ga.dot(this.direction),c=-Ga.dot(jc),u=Ga.lengthSq(),h=Math.abs(1-l*l);let g,d,p,x;if(h>0)if(g=l*c-o,d=l*o-c,x=s*h,g>=0)if(d>=-x)if(d<=x){const _=1/h;g*=_,d*=_,p=g*(g+l*d+2*o)+d*(l*g+d+2*c)+u}else d=s,g=Math.max(0,-(l*d+o)),p=-g*g+d*(d+2*c)+u;else d=-s,g=Math.max(0,-(l*d+o)),p=-g*g+d*(d+2*c)+u;else d<=-x?(g=Math.max(0,-(-l*s+o)),d=g>0?-s:Math.min(Math.max(-s,-c),s),p=-g*g+d*(d+2*c)+u):d<=x?(g=0,d=Math.min(Math.max(-s,-c),s),p=d*(d+2*c)+u):(g=Math.max(0,-(l*s+o)),d=g>0?s:Math.min(Math.max(-s,-c),s),p=-g*g+d*(d+2*c)+u);else d=l>0?-s:s,g=Math.max(0,-(l*d+o)),p=-g*g+d*(d+2*c)+u;return i&&i.copy(this.origin).addScaledVector(this.direction,g),a&&a.copy(Kh).addScaledVector(jc,d),p}intersectSphere(e,n){ua.subVectors(e.center,this.origin);const i=ua.dot(this.direction),a=ua.dot(ua)-i*i,s=e.radius*e.radius;if(a>s)return null;const l=Math.sqrt(s-a),o=i-l,c=i+l;return c<0?null:o<0?this.at(c,n):this.at(o,n)}intersectsSphere(e){return e.radius<0?!1:this.distanceSqToPoint(e.center)<=e.radius*e.radius}distanceToPlane(e){const n=e.normal.dot(this.direction);if(n===0)return e.distanceToPoint(this.origin)===0?0:null;const i=-(this.origin.dot(e.normal)+e.constant)/n;return i>=0?i:null}intersectPlane(e,n){const i=this.distanceToPlane(e);return i===null?null:this.at(i,n)}intersectsPlane(e){const n=e.distanceToPoint(this.origin);return n===0||e.normal.dot(this.direction)*n<0}intersectBox(e,n){let i,a,s,l,o,c;const u=1/this.direction.x,h=1/this.direction.y,g=1/this.direction.z,d=this.origin;return u>=0?(i=(e.min.x-d.x)*u,a=(e.max.x-d.x)*u):(i=(e.max.x-d.x)*u,a=(e.min.x-d.x)*u),h>=0?(s=(e.min.y-d.y)*h,l=(e.max.y-d.y)*h):(s=(e.max.y-d.y)*h,l=(e.min.y-d.y)*h),i>l||s>a||((s>i||isNaN(i))&&(i=s),(l<a||isNaN(a))&&(a=l),g>=0?(o=(e.min.z-d.z)*g,c=(e.max.z-d.z)*g):(o=(e.max.z-d.z)*g,c=(e.min.z-d.z)*g),i>c||o>a)||((o>i||i!==i)&&(i=o),(c<a||a!==a)&&(a=c),a<0)?null:this.at(i>=0?i:a,n)}intersectsBox(e){return this.intersectBox(e,ua)!==null}intersectTriangle(e,n,i,a,s){Zh.subVectors(n,e),kc.subVectors(i,e),Qh.crossVectors(Zh,kc);let l=this.direction.dot(Qh),o;if(l>0){if(a)return null;o=1}else if(l<0)o=-1,l=-l;else return null;Ga.subVectors(this.origin,e);const c=o*this.direction.dot(kc.crossVectors(Ga,kc));if(c<0)return null;const u=o*this.direction.dot(Zh.cross(Ga));if(u<0||c+u>l)return null;const h=-o*Ga.dot(Qh);return h<0?null:this.at(h/l,s)}applyMatrix4(e){return this.origin.applyMatrix4(e),this.direction.transformDirection(e),this}equals(e){return e.origin.equals(this.origin)&&e.direction.equals(this.direction)}clone(){return new this.constructor().copy(this)}}class RS extends Ul{constructor(e){super(),this.isMeshBasicMaterial=!0,this.type="MeshBasicMaterial",this.color=new gt(16777215),this.map=null,this.lightMap=null,this.lightMapIntensity=1,this.aoMap=null,this.aoMapIntensity=1,this.specularMap=null,this.alphaMap=null,this.envMap=null,this.envMapRotation=new Qs,this.combine=cS,this.reflectivity=1,this.refractionRatio=.98,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.fog=!0,this.setValues(e)}copy(e){return super.copy(e),this.color.copy(e.color),this.map=e.map,this.lightMap=e.lightMap,this.lightMapIntensity=e.lightMapIntensity,this.aoMap=e.aoMap,this.aoMapIntensity=e.aoMapIntensity,this.specularMap=e.specularMap,this.alphaMap=e.alphaMap,this.envMap=e.envMap,this.envMapRotation.copy(e.envMapRotation),this.combine=e.combine,this.reflectivity=e.reflectivity,this.refractionRatio=e.refractionRatio,this.wireframe=e.wireframe,this.wireframeLinewidth=e.wireframeLinewidth,this.wireframeLinecap=e.wireframeLinecap,this.wireframeLinejoin=e.wireframeLinejoin,this.fog=e.fog,this}}const Iv=new zt,Ns=new Ag,Ic=new or,Bv=new Z,Bc=new Z,Fc=new Z,Hc=new Z,Jh=new Z,Gc=new Z,Fv=new Z,Vc=new Z;class Ei extends On{constructor(e=new qn,n=new RS){super(),this.isMesh=!0,this.type="Mesh",this.geometry=e,this.material=n,this.morphTargetDictionary=void 0,this.morphTargetInfluences=void 0,this.count=1,this.updateMorphTargets()}copy(e,n){return super.copy(e,n),e.morphTargetInfluences!==void 0&&(this.morphTargetInfluences=e.morphTargetInfluences.slice()),e.morphTargetDictionary!==void 0&&(this.morphTargetDictionary=Object.assign({},e.morphTargetDictionary)),this.material=Array.isArray(e.material)?e.material.slice():e.material,this.geometry=e.geometry,this}updateMorphTargets(){const n=this.geometry.morphAttributes,i=Object.keys(n);if(i.length>0){const a=n[i[0]];if(a!==void 0){this.morphTargetInfluences=[],this.morphTargetDictionary={};for(let s=0,l=a.length;s<l;s++){const o=a[s].name||String(s);this.morphTargetInfluences.push(0),this.morphTargetDictionary[o]=s}}}}getVertexPosition(e,n){const i=this.geometry,a=i.attributes.position,s=i.morphAttributes.position,l=i.morphTargetsRelative;n.fromBufferAttribute(a,e);const o=this.morphTargetInfluences;if(s&&o){Gc.set(0,0,0);for(let c=0,u=s.length;c<u;c++){const h=o[c],g=s[c];h!==0&&(Jh.fromBufferAttribute(g,e),l?Gc.addScaledVector(Jh,h):Gc.addScaledVector(Jh.sub(n),h))}n.add(Gc)}return n}raycast(e,n){const i=this.geometry,a=this.material,s=this.matrixWorld;a!==void 0&&(i.boundingSphere===null&&i.computeBoundingSphere(),Ic.copy(i.boundingSphere),Ic.applyMatrix4(s),Ns.copy(e.ray).recast(e.near),!(Ic.containsPoint(Ns.origin)===!1&&(Ns.intersectSphere(Ic,Bv)===null||Ns.origin.distanceToSquared(Bv)>(e.far-e.near)**2))&&(Iv.copy(s).invert(),Ns.copy(e.ray).applyMatrix4(Iv),!(i.boundingBox!==null&&Ns.intersectsBox(i.boundingBox)===!1)&&this._computeIntersections(e,n,Ns)))}_computeIntersections(e,n,i){let a;const s=this.geometry,l=this.material,o=s.index,c=s.attributes.position,u=s.attributes.uv,h=s.attributes.uv1,g=s.attributes.normal,d=s.groups,p=s.drawRange;if(o!==null)if(Array.isArray(l))for(let x=0,_=d.length;x<_;x++){const m=d[x],f=l[m.materialIndex],v=Math.max(m.start,p.start),y=Math.min(o.count,Math.min(m.start+m.count,p.start+p.count));for(let b=v,T=y;b<T;b+=3){const M=o.getX(b),A=o.getX(b+1),S=o.getX(b+2);a=Wc(this,f,e,i,u,h,g,M,A,S),a&&(a.faceIndex=Math.floor(b/3),a.face.materialIndex=m.materialIndex,n.push(a))}}else{const x=Math.max(0,p.start),_=Math.min(o.count,p.start+p.count);for(let m=x,f=_;m<f;m+=3){const v=o.getX(m),y=o.getX(m+1),b=o.getX(m+2);a=Wc(this,l,e,i,u,h,g,v,y,b),a&&(a.faceIndex=Math.floor(m/3),n.push(a))}}else if(c!==void 0)if(Array.isArray(l))for(let x=0,_=d.length;x<_;x++){const m=d[x],f=l[m.materialIndex],v=Math.max(m.start,p.start),y=Math.min(c.count,Math.min(m.start+m.count,p.start+p.count));for(let b=v,T=y;b<T;b+=3){const M=b,A=b+1,S=b+2;a=Wc(this,f,e,i,u,h,g,M,A,S),a&&(a.faceIndex=Math.floor(b/3),a.face.materialIndex=m.materialIndex,n.push(a))}}else{const x=Math.max(0,p.start),_=Math.min(c.count,p.start+p.count);for(let m=x,f=_;m<f;m+=3){const v=m,y=m+1,b=m+2;a=Wc(this,l,e,i,u,h,g,v,y,b),a&&(a.faceIndex=Math.floor(m/3),n.push(a))}}}}function qA(t,e,n,i,a,s,l,o){let c;if(e.side===Wn?c=i.intersectTriangle(l,s,a,!0,o):c=i.intersectTriangle(a,s,l,e.side===ys,o),c===null)return null;Vc.copy(o),Vc.applyMatrix4(t.matrixWorld);const u=n.ray.origin.distanceTo(Vc);return u<n.near||u>n.far?null:{distance:u,point:Vc.clone(),object:t}}function Wc(t,e,n,i,a,s,l,o,c,u){t.getVertexPosition(o,Bc),t.getVertexPosition(c,Fc),t.getVertexPosition(u,Hc);const h=qA(t,e,n,i,Bc,Fc,Hc,Fv);if(h){const g=new Z;_i.getBarycoord(Fv,Bc,Fc,Hc,g),a&&(h.uv=_i.getInterpolatedAttribute(a,o,c,u,g,new Mt)),s&&(h.uv1=_i.getInterpolatedAttribute(s,o,c,u,g,new Mt)),l&&(h.normal=_i.getInterpolatedAttribute(l,o,c,u,g,new Z),h.normal.dot(i.direction)>0&&h.normal.multiplyScalar(-1));const d={a:o,b:c,c:u,normal:new Z,materialIndex:0};_i.getNormal(Bc,Fc,Hc,d.normal),h.face=d,h.barycoord=g}return h}class DS extends Un{constructor(e=null,n=1,i=1,a,s,l,o,c,u=Sn,h=Sn,g,d){super(null,l,o,c,u,h,a,s,g,d),this.isDataTexture=!0,this.image={data:e,width:n,height:i},this.generateMipmaps=!1,this.flipY=!1,this.unpackAlignment=1}}class $r extends on{constructor(e,n,i,a=1){super(e,n,i),this.isInstancedBufferAttribute=!0,this.meshPerAttribute=a}copy(e){return super.copy(e),this.meshPerAttribute=e.meshPerAttribute,this}toJSON(){const e=super.toJSON();return e.meshPerAttribute=this.meshPerAttribute,e.isInstancedBufferAttribute=!0,e}}const Ar=new zt,Hv=new zt,Xc=[],Gv=new lr,YA=new zt,$l=new Ei,Kl=new or;class Vv extends Ei{constructor(e,n,i){super(e,n),this.isInstancedMesh=!0,this.instanceMatrix=new $r(new Float32Array(i*16),16),this.instanceColor=null,this.morphTexture=null,this.count=i,this.boundingBox=null,this.boundingSphere=null;for(let a=0;a<i;a++)this.setMatrixAt(a,YA)}computeBoundingBox(){const e=this.geometry,n=this.count;this.boundingBox===null&&(this.boundingBox=new lr),e.boundingBox===null&&e.computeBoundingBox(),this.boundingBox.makeEmpty();for(let i=0;i<n;i++)this.getMatrixAt(i,Ar),Gv.copy(e.boundingBox).applyMatrix4(Ar),this.boundingBox.union(Gv)}computeBoundingSphere(){const e=this.geometry,n=this.count;this.boundingSphere===null&&(this.boundingSphere=new or),e.boundingSphere===null&&e.computeBoundingSphere(),this.boundingSphere.makeEmpty();for(let i=0;i<n;i++)this.getMatrixAt(i,Ar),Kl.copy(e.boundingSphere).applyMatrix4(Ar),this.boundingSphere.union(Kl)}copy(e,n){return super.copy(e,n),this.instanceMatrix.copy(e.instanceMatrix),e.morphTexture!==null&&(this.morphTexture=e.morphTexture.clone()),e.instanceColor!==null&&(this.instanceColor=e.instanceColor.clone()),this.count=e.count,e.boundingBox!==null&&(this.boundingBox=e.boundingBox.clone()),e.boundingSphere!==null&&(this.boundingSphere=e.boundingSphere.clone()),this}getColorAt(e,n){return this.instanceColor===null?n.setRGB(1,1,1):n.fromArray(this.instanceColor.array,e*3)}getMatrixAt(e,n){return n.fromArray(this.instanceMatrix.array,e*16)}getMorphAt(e,n){const i=n.morphTargetInfluences,a=this.morphTexture.source.data.data,s=i.length+1,l=e*s+1;for(let o=0;o<i.length;o++)i[o]=a[l+o]}raycast(e,n){const i=this.matrixWorld,a=this.count;if($l.geometry=this.geometry,$l.material=this.material,$l.material!==void 0&&(this.boundingSphere===null&&this.computeBoundingSphere(),Kl.copy(this.boundingSphere),Kl.applyMatrix4(i),e.ray.intersectsSphere(Kl)!==!1))for(let s=0;s<a;s++){this.getMatrixAt(s,Ar),Hv.multiplyMatrices(i,Ar),$l.matrixWorld=Hv,$l.raycast(e,Xc);for(let l=0,o=Xc.length;l<o;l++){const c=Xc[l];c.instanceId=s,c.object=this,n.push(c)}Xc.length=0}}setColorAt(e,n){return this.instanceColor===null&&(this.instanceColor=new $r(new Float32Array(this.instanceMatrix.count*3).fill(1),3)),n.toArray(this.instanceColor.array,e*3),this}setMatrixAt(e,n){return n.toArray(this.instanceMatrix.array,e*16),this}setMorphAt(e,n){const i=n.morphTargetInfluences,a=i.length+1;this.morphTexture===null&&(this.morphTexture=new DS(new Float32Array(a*this.count),a,this.count,_g,Li));const s=this.morphTexture.source.data.data;let l=0;for(let u=0;u<i.length;u++)l+=i[u];const o=this.geometry.morphTargetsRelative?1:1-l,c=a*e;return s[c]=o,s.set(i,c+1),this}updateMorphTargets(){}dispose(){this.dispatchEvent({type:"dispose"}),this.morphTexture!==null&&(this.morphTexture.dispose(),this.morphTexture=null)}}const ef=new Z,$A=new Z,KA=new tt;class Ps{constructor(e=new Z(1,0,0),n=0){this.isPlane=!0,this.normal=e,this.constant=n}set(e,n){return this.normal.copy(e),this.constant=n,this}setComponents(e,n,i,a){return this.normal.set(e,n,i),this.constant=a,this}setFromNormalAndCoplanarPoint(e,n){return this.normal.copy(e),this.constant=-n.dot(this.normal),this}setFromCoplanarPoints(e,n,i){const a=ef.subVectors(i,n).cross($A.subVectors(e,n)).normalize();return this.setFromNormalAndCoplanarPoint(a,e),this}copy(e){return this.normal.copy(e.normal),this.constant=e.constant,this}normalize(){const e=1/this.normal.length();return this.normal.multiplyScalar(e),this.constant*=e,this}negate(){return this.constant*=-1,this.normal.negate(),this}distanceToPoint(e){return this.normal.dot(e)+this.constant}distanceToSphere(e){return this.distanceToPoint(e.center)-e.radius}projectPoint(e,n){return n.copy(e).addScaledVector(this.normal,-this.distanceToPoint(e))}intersectLine(e,n,i=!0){const a=e.delta(ef),s=this.normal.dot(a);if(s===0)return this.distanceToPoint(e.start)===0?n.copy(e.start):null;const l=-(e.start.dot(this.normal)+this.constant)/s;return i===!0&&(l<0||l>1)?null:n.copy(e.start).addScaledVector(a,l)}intersectsLine(e){const n=this.distanceToPoint(e.start),i=this.distanceToPoint(e.end);return n<0&&i>0||i<0&&n>0}intersectsBox(e){return e.intersectsPlane(this)}intersectsSphere(e){return e.intersectsPlane(this)}coplanarPoint(e){return e.copy(this.normal).multiplyScalar(-this.constant)}applyMatrix4(e,n){const i=n||KA.getNormalMatrix(e),a=this.coplanarPoint(ef).applyMatrix4(e),s=this.normal.applyMatrix3(i).normalize();return this.constant=-a.dot(s),this}translate(e){return this.constant-=e.dot(this.normal),this}equals(e){return e.normal.equals(this.normal)&&e.constant===this.constant}clone(){return new this.constructor().copy(this)}}const Cs=new or,ZA=new Mt(.5,.5),qc=new Z;class LS{constructor(e=new Ps,n=new Ps,i=new Ps,a=new Ps,s=new Ps,l=new Ps){this.planes=[e,n,i,a,s,l]}set(e,n,i,a,s,l){const o=this.planes;return o[0].copy(e),o[1].copy(n),o[2].copy(i),o[3].copy(a),o[4].copy(s),o[5].copy(l),this}copy(e){const n=this.planes;for(let i=0;i<6;i++)n[i].copy(e.planes[i]);return this}setFromProjectionMatrix(e,n=qi,i=!1){const a=this.planes,s=e.elements,l=s[0],o=s[1],c=s[2],u=s[3],h=s[4],g=s[5],d=s[6],p=s[7],x=s[8],_=s[9],m=s[10],f=s[11],v=s[12],y=s[13],b=s[14],T=s[15];if(a[0].setComponents(u-l,p-h,f-x,T-v).normalize(),a[1].setComponents(u+l,p+h,f+x,T+v).normalize(),a[2].setComponents(u+o,p+g,f+_,T+y).normalize(),a[3].setComponents(u-o,p-g,f-_,T-y).normalize(),i)a[4].setComponents(c,d,m,b).normalize(),a[5].setComponents(u-c,p-d,f-m,T-b).normalize();else if(a[4].setComponents(u-c,p-d,f-m,T-b).normalize(),n===qi)a[5].setComponents(u+c,p+d,f+m,T+b).normalize();else if(n===fd)a[5].setComponents(c,d,m,b).normalize();else throw new Error("THREE.Frustum.setFromProjectionMatrix(): Invalid coordinate system: "+n);return this}intersectsObject(e){if(e.boundingSphere!==void 0)e.boundingSphere===null&&e.computeBoundingSphere(),Cs.copy(e.boundingSphere).applyMatrix4(e.matrixWorld);else{const n=e.geometry;n.boundingSphere===null&&n.computeBoundingSphere(),Cs.copy(n.boundingSphere).applyMatrix4(e.matrixWorld)}return this.intersectsSphere(Cs)}intersectsSprite(e){Cs.center.set(0,0,0);const n=ZA.distanceTo(e.center);return Cs.radius=.7071067811865476+n,Cs.applyMatrix4(e.matrixWorld),this.intersectsSphere(Cs)}intersectsSphere(e){const n=this.planes,i=e.center,a=-e.radius;for(let s=0;s<6;s++)if(n[s].distanceToPoint(i)<a)return!1;return!0}intersectsBox(e){const n=this.planes;for(let i=0;i<6;i++){const a=n[i];if(qc.x=a.normal.x>0?e.max.x:e.min.x,qc.y=a.normal.y>0?e.max.y:e.min.y,qc.z=a.normal.z>0?e.max.z:e.min.z,a.distanceToPoint(qc)<0)return!1}return!0}containsPoint(e){const n=this.planes;for(let i=0;i<6;i++)if(n[i].distanceToPoint(e)<0)return!1;return!0}clone(){return new this.constructor().copy(this)}}class US extends Ul{constructor(e){super(),this.isLineBasicMaterial=!0,this.type="LineBasicMaterial",this.color=new gt(16777215),this.map=null,this.linewidth=1,this.linecap="round",this.linejoin="round",this.fog=!0,this.setValues(e)}copy(e){return super.copy(e),this.color.copy(e.color),this.map=e.map,this.linewidth=e.linewidth,this.linecap=e.linecap,this.linejoin=e.linejoin,this.fog=e.fog,this}}const md=new Z,gd=new Z,Wv=new zt,Zl=new Ag,Yc=new or,tf=new Z,Xv=new Z;class QA extends On{constructor(e=new qn,n=new US){super(),this.isLine=!0,this.type="Line",this.geometry=e,this.material=n,this.morphTargetDictionary=void 0,this.morphTargetInfluences=void 0,this.updateMorphTargets()}copy(e,n){return super.copy(e,n),this.material=Array.isArray(e.material)?e.material.slice():e.material,this.geometry=e.geometry,this}computeLineDistances(){const e=this.geometry;if(e.index===null){const n=e.attributes.position,i=[0];for(let a=1,s=n.count;a<s;a++)md.fromBufferAttribute(n,a-1),gd.fromBufferAttribute(n,a),i[a]=i[a-1],i[a]+=md.distanceTo(gd);e.setAttribute("lineDistance",new Si(i,1))}else Qe("Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.");return this}raycast(e,n){const i=this.geometry,a=this.matrixWorld,s=e.params.Line.threshold,l=i.drawRange;if(i.boundingSphere===null&&i.computeBoundingSphere(),Yc.copy(i.boundingSphere),Yc.applyMatrix4(a),Yc.radius+=s,e.ray.intersectsSphere(Yc)===!1)return;Wv.copy(a).invert(),Zl.copy(e.ray).applyMatrix4(Wv);const o=s/((this.scale.x+this.scale.y+this.scale.z)/3),c=o*o,u=this.isLineSegments?2:1,h=i.index,d=i.attributes.position;if(h!==null){const p=Math.max(0,l.start),x=Math.min(h.count,l.start+l.count);for(let _=p,m=x-1;_<m;_+=u){const f=h.getX(_),v=h.getX(_+1),y=$c(this,e,Zl,c,f,v,_);y&&n.push(y)}if(this.isLineLoop){const _=h.getX(x-1),m=h.getX(p),f=$c(this,e,Zl,c,_,m,x-1);f&&n.push(f)}}else{const p=Math.max(0,l.start),x=Math.min(d.count,l.start+l.count);for(let _=p,m=x-1;_<m;_+=u){const f=$c(this,e,Zl,c,_,_+1,_);f&&n.push(f)}if(this.isLineLoop){const _=$c(this,e,Zl,c,x-1,p,x-1);_&&n.push(_)}}}updateMorphTargets(){const n=this.geometry.morphAttributes,i=Object.keys(n);if(i.length>0){const a=n[i[0]];if(a!==void 0){this.morphTargetInfluences=[],this.morphTargetDictionary={};for(let s=0,l=a.length;s<l;s++){const o=a[s].name||String(s);this.morphTargetInfluences.push(0),this.morphTargetDictionary[o]=s}}}}}function $c(t,e,n,i,a,s,l){const o=t.geometry.attributes.position;if(md.fromBufferAttribute(o,a),gd.fromBufferAttribute(o,s),n.distanceSqToSegment(md,gd,tf,Xv)>i)return;tf.applyMatrix4(t.matrixWorld);const u=e.ray.origin.distanceTo(tf);if(!(u<e.near||u>e.far))return{distance:u,point:Xv.clone().applyMatrix4(t.matrixWorld),index:l,face:null,faceIndex:null,barycoord:null,object:t}}const qv=new Z,Yv=new Z;class nf extends QA{constructor(e,n){super(e,n),this.isLineSegments=!0,this.type="LineSegments"}computeLineDistances(){const e=this.geometry;if(e.index===null){const n=e.attributes.position,i=[];for(let a=0,s=n.count;a<s;a+=2)qv.fromBufferAttribute(n,a),Yv.fromBufferAttribute(n,a+1),i[a]=a===0?0:i[a-1],i[a+1]=i[a]+qv.distanceTo(Yv);e.setAttribute("lineDistance",new Si(i,1))}else Qe("LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.");return this}}class JA extends Ul{constructor(e){super(),this.isPointsMaterial=!0,this.type="PointsMaterial",this.color=new gt(16777215),this.map=null,this.alphaMap=null,this.size=1,this.sizeAttenuation=!0,this.fog=!0,this.setValues(e)}copy(e){return super.copy(e),this.color.copy(e.color),this.map=e.map,this.alphaMap=e.alphaMap,this.size=e.size,this.sizeAttenuation=e.sizeAttenuation,this.fog=e.fog,this}}const $v=new zt,rm=new Ag,Kc=new or,Zc=new Z;class ew extends On{constructor(e=new qn,n=new JA){super(),this.isPoints=!0,this.type="Points",this.geometry=e,this.material=n,this.morphTargetDictionary=void 0,this.morphTargetInfluences=void 0,this.updateMorphTargets()}copy(e,n){return super.copy(e,n),this.material=Array.isArray(e.material)?e.material.slice():e.material,this.geometry=e.geometry,this}raycast(e,n){const i=this.geometry,a=this.matrixWorld,s=e.params.Points.threshold,l=i.drawRange;if(i.boundingSphere===null&&i.computeBoundingSphere(),Kc.copy(i.boundingSphere),Kc.applyMatrix4(a),Kc.radius+=s,e.ray.intersectsSphere(Kc)===!1)return;$v.copy(a).invert(),rm.copy(e.ray).applyMatrix4($v);const o=s/((this.scale.x+this.scale.y+this.scale.z)/3),c=o*o,u=i.index,g=i.attributes.position;if(u!==null){const d=Math.max(0,l.start),p=Math.min(u.count,l.start+l.count);for(let x=d,_=p;x<_;x++){const m=u.getX(x);Zc.fromBufferAttribute(g,m),Kv(Zc,m,c,a,e,n,this)}}else{const d=Math.max(0,l.start),p=Math.min(g.count,l.start+l.count);for(let x=d,_=p;x<_;x++)Zc.fromBufferAttribute(g,x),Kv(Zc,x,c,a,e,n,this)}}updateMorphTargets(){const n=this.geometry.morphAttributes,i=Object.keys(n);if(i.length>0){const a=n[i[0]];if(a!==void 0){this.morphTargetInfluences=[],this.morphTargetDictionary={};for(let s=0,l=a.length;s<l;s++){const o=a[s].name||String(s);this.morphTargetInfluences.push(0),this.morphTargetDictionary[o]=s}}}}}function Kv(t,e,n,i,a,s,l){const o=rm.distanceSqToPoint(t);if(o<n){const c=new Z;rm.closestPointToPoint(t,c),c.applyMatrix4(i);const u=a.ray.origin.distanceTo(c);if(u<a.near||u>a.far)return;s.push({distance:u,distanceToRay:Math.sqrt(o),point:c,index:e,face:null,faceIndex:null,barycoord:null,object:l})}}class OS extends Un{constructor(e=[],n=Ks,i,a,s,l,o,c,u,h){super(e,n,i,a,s,l,o,c,u,h),this.isCubeTexture=!0,this.flipY=!1}get images(){return this.image}set images(e){this.image=e}}class yl extends Un{constructor(e,n,i=Zi,a,s,l,o=Sn,c=Sn,u,h=Da,g=1){if(h!==Da&&h!==Is)throw new Error("THREE.DepthTexture: format must be either THREE.DepthFormat or THREE.DepthStencilFormat");const d={width:e,height:n,depth:g};super(d,a,s,l,o,c,h,i,u),this.isDepthTexture=!0,this.flipY=!1,this.generateMipmaps=!1,this.compareFunction=null}copy(e){return super.copy(e),this.source=new Tg(Object.assign({},e.image)),this.compareFunction=e.compareFunction,this}toJSON(e){const n=super.toJSON(e);return this.compareFunction!==null&&(n.compareFunction=this.compareFunction),n}}class tw extends yl{constructor(e,n=Zi,i=Ks,a,s,l=Sn,o=Sn,c,u=Da){const h={width:e,height:e,depth:1},g=[h,h,h,h,h,h];super(e,e,n,i,a,s,l,o,c,u),this.image=g,this.isCubeDepthTexture=!0,this.isCubeTexture=!0}get images(){return this.image}set images(e){this.image=e}}class PS extends Un{constructor(e=null){super(),this.sourceTexture=e,this.isExternalTexture=!0}copy(e){return super.copy(e),this.sourceTexture=e.sourceTexture,this}}class Js extends qn{constructor(e=1,n=1,i=1,a=1,s=1,l=1){super(),this.type="BoxGeometry",this.parameters={width:e,height:n,depth:i,widthSegments:a,heightSegments:s,depthSegments:l};const o=this;a=Math.floor(a),s=Math.floor(s),l=Math.floor(l);const c=[],u=[],h=[],g=[];let d=0,p=0;x("z","y","x",-1,-1,i,n,e,l,s,0),x("z","y","x",1,-1,i,n,-e,l,s,1),x("x","z","y",1,1,e,i,n,a,l,2),x("x","z","y",1,-1,e,i,-n,a,l,3),x("x","y","z",1,-1,e,n,i,a,s,4),x("x","y","z",-1,-1,e,n,-i,a,s,5),this.setIndex(c),this.setAttribute("position",new Si(u,3)),this.setAttribute("normal",new Si(h,3)),this.setAttribute("uv",new Si(g,2));function x(_,m,f,v,y,b,T,M,A,S,N){const D=b/A,U=T/S,I=b/2,Y=T/2,k=M/2,B=A+1,z=S+1;let j=0,X=0;const q=new Z;for(let ve=0;ve<z;ve++){const he=ve*U-Y;for(let Ae=0;Ae<B;Ae++){const $=Ae*D-I;q[_]=$*v,q[m]=he*y,q[f]=k,u.push(q.x,q.y,q.z),q[_]=0,q[m]=0,q[f]=M>0?1:-1,h.push(q.x,q.y,q.z),g.push(Ae/A),g.push(1-ve/S),j+=1}}for(let ve=0;ve<S;ve++)for(let he=0;he<A;he++){const Ae=d+he+B*ve,$=d+he+B*(ve+1),pe=d+(he+1)+B*(ve+1),we=d+(he+1)+B*ve;c.push(Ae,$,we),c.push($,pe,we),X+=6}o.addGroup(p,X,N),p+=X,d+=j}}copy(e){return super.copy(e),this.parameters=Object.assign({},e.parameters),this}static fromJSON(e){return new Js(e.width,e.height,e.depth,e.widthSegments,e.heightSegments,e.depthSegments)}}const Qc=new Z,Jc=new Z,af=new Z,eu=new _i;class nw extends qn{constructor(e=null,n=1){if(super(),this.type="EdgesGeometry",this.parameters={geometry:e,thresholdAngle:n},e!==null){const a=Math.pow(10,4),s=Math.cos(Du*n),l=e.getIndex(),o=e.getAttribute("position"),c=l?l.count:o.count,u=[0,0,0],h=["a","b","c"],g=new Array(3),d={},p=[];for(let x=0;x<c;x+=3){l?(u[0]=l.getX(x),u[1]=l.getX(x+1),u[2]=l.getX(x+2)):(u[0]=x,u[1]=x+1,u[2]=x+2);const{a:_,b:m,c:f}=eu;if(_.fromBufferAttribute(o,u[0]),m.fromBufferAttribute(o,u[1]),f.fromBufferAttribute(o,u[2]),eu.getNormal(af),g[0]=`${Math.round(_.x*a)},${Math.round(_.y*a)},${Math.round(_.z*a)}`,g[1]=`${Math.round(m.x*a)},${Math.round(m.y*a)},${Math.round(m.z*a)}`,g[2]=`${Math.round(f.x*a)},${Math.round(f.y*a)},${Math.round(f.z*a)}`,!(g[0]===g[1]||g[1]===g[2]||g[2]===g[0]))for(let v=0;v<3;v++){const y=(v+1)%3,b=g[v],T=g[y],M=eu[h[v]],A=eu[h[y]],S=`${b}_${T}`,N=`${T}_${b}`;N in d&&d[N]?(af.dot(d[N].normal)<=s&&(p.push(M.x,M.y,M.z),p.push(A.x,A.y,A.z)),d[N]=null):S in d||(d[S]={index0:u[v],index1:u[y],normal:af.clone()})}}for(const x in d)if(d[x]){const{index0:_,index1:m}=d[x];Qc.fromBufferAttribute(o,_),Jc.fromBufferAttribute(o,m),p.push(Qc.x,Qc.y,Qc.z),p.push(Jc.x,Jc.y,Jc.z)}this.setAttribute("position",new Si(p,3))}}copy(e){return super.copy(e),this.parameters=Object.assign({},e.parameters),this}}class Hd extends qn{constructor(e=1,n=1,i=1,a=1){super(),this.type="PlaneGeometry",this.parameters={width:e,height:n,widthSegments:i,heightSegments:a};const s=e/2,l=n/2,o=Math.floor(i),c=Math.floor(a),u=o+1,h=c+1,g=e/o,d=n/c,p=[],x=[],_=[],m=[];for(let f=0;f<h;f++){const v=f*d-l;for(let y=0;y<u;y++){const b=y*g-s;x.push(b,-v,0),_.push(0,0,1),m.push(y/o),m.push(1-f/c)}}for(let f=0;f<c;f++)for(let v=0;v<o;v++){const y=v+u*f,b=v+u*(f+1),T=v+1+u*(f+1),M=v+1+u*f;p.push(y,b,M),p.push(b,T,M)}this.setIndex(p),this.setAttribute("position",new Si(x,3)),this.setAttribute("normal",new Si(_,3)),this.setAttribute("uv",new Si(m,2))}copy(e){return super.copy(e),this.parameters=Object.assign({},e.parameters),this}static fromJSON(e){return new Hd(e.width,e.height,e.widthSegments,e.heightSegments)}}function Sl(t){const e={};for(const n in t){e[n]={};for(const i in t[n]){const a=t[n][i];if(Zv(a))a.isRenderTargetTexture?(Qe("UniformsUtils: Textures of render targets cannot be cloned via cloneUniforms() or mergeUniforms()."),e[n][i]=null):e[n][i]=a.clone();else if(Array.isArray(a))if(Zv(a[0])){const s=[];for(let l=0,o=a.length;l<o;l++)s[l]=a[l].clone();e[n][i]=s}else e[n][i]=a.slice();else e[n][i]=a}}return e}function Nn(t){const e={};for(let n=0;n<t.length;n++){const i=Sl(t[n]);for(const a in i)e[a]=i[a]}return e}function Zv(t){return t&&(t.isColor||t.isMatrix3||t.isMatrix4||t.isVector2||t.isVector3||t.isVector4||t.isTexture||t.isQuaternion)}function iw(t){const e=[];for(let n=0;n<t.length;n++)e.push(t[n].clone());return e}function zS(t){const e=t.getRenderTarget();return e===null?t.outputColorSpace:e.isXRRenderTarget===!0?e.texture.colorSpace:ft.workingColorSpace}const aw={clone:Sl,merge:Nn};var sw=`void main() { + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); +}`,rw=`void main() { + gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 ); +}`;class Gn extends Ul{constructor(e){super(),this.isShaderMaterial=!0,this.type="ShaderMaterial",this.defines={},this.uniforms={},this.uniformsGroups=[],this.vertexShader=sw,this.fragmentShader=rw,this.linewidth=1,this.wireframe=!1,this.wireframeLinewidth=1,this.fog=!1,this.lights=!1,this.clipping=!1,this.forceSinglePass=!0,this.extensions={clipCullDistance:!1,multiDraw:!1},this.defaultAttributeValues={color:[1,1,1],uv:[0,0],uv1:[0,0]},this.index0AttributeName=void 0,this.uniformsNeedUpdate=!1,this.glslVersion=null,e!==void 0&&this.setValues(e)}copy(e){return super.copy(e),this.fragmentShader=e.fragmentShader,this.vertexShader=e.vertexShader,this.uniforms=Sl(e.uniforms),this.uniformsGroups=iw(e.uniformsGroups),this.defines=Object.assign({},e.defines),this.wireframe=e.wireframe,this.wireframeLinewidth=e.wireframeLinewidth,this.fog=e.fog,this.lights=e.lights,this.clipping=e.clipping,this.extensions=Object.assign({},e.extensions),this.glslVersion=e.glslVersion,this.defaultAttributeValues=Object.assign({},e.defaultAttributeValues),this.index0AttributeName=e.index0AttributeName,this.uniformsNeedUpdate=e.uniformsNeedUpdate,this}toJSON(e){const n=super.toJSON(e);n.glslVersion=this.glslVersion,n.uniforms={};for(const a in this.uniforms){const l=this.uniforms[a].value;l&&l.isTexture?n.uniforms[a]={type:"t",value:l.toJSON(e).uuid}:l&&l.isColor?n.uniforms[a]={type:"c",value:l.getHex()}:l&&l.isVector2?n.uniforms[a]={type:"v2",value:l.toArray()}:l&&l.isVector3?n.uniforms[a]={type:"v3",value:l.toArray()}:l&&l.isVector4?n.uniforms[a]={type:"v4",value:l.toArray()}:l&&l.isMatrix3?n.uniforms[a]={type:"m3",value:l.toArray()}:l&&l.isMatrix4?n.uniforms[a]={type:"m4",value:l.toArray()}:n.uniforms[a]={value:l}}Object.keys(this.defines).length>0&&(n.defines=this.defines),n.vertexShader=this.vertexShader,n.fragmentShader=this.fragmentShader,n.lights=this.lights,n.clipping=this.clipping;const i={};for(const a in this.extensions)this.extensions[a]===!0&&(i[a]=!0);return Object.keys(i).length>0&&(n.extensions=i),n}fromJSON(e,n){if(super.fromJSON(e,n),e.uniforms!==void 0)for(const i in e.uniforms){const a=e.uniforms[i];switch(this.uniforms[i]={},a.type){case"t":this.uniforms[i].value=n[a.value]||null;break;case"c":this.uniforms[i].value=new gt().setHex(a.value);break;case"v2":this.uniforms[i].value=new Mt().fromArray(a.value);break;case"v3":this.uniforms[i].value=new Z().fromArray(a.value);break;case"v4":this.uniforms[i].value=new Yt().fromArray(a.value);break;case"m3":this.uniforms[i].value=new tt().fromArray(a.value);break;case"m4":this.uniforms[i].value=new zt().fromArray(a.value);break;default:this.uniforms[i].value=a.value}}if(e.defines!==void 0&&(this.defines=e.defines),e.vertexShader!==void 0&&(this.vertexShader=e.vertexShader),e.fragmentShader!==void 0&&(this.fragmentShader=e.fragmentShader),e.glslVersion!==void 0&&(this.glslVersion=e.glslVersion),e.extensions!==void 0)for(const i in e.extensions)this.extensions[i]=e.extensions[i];return e.lights!==void 0&&(this.lights=e.lights),e.clipping!==void 0&&(this.clipping=e.clipping),this}}class lw extends Gn{constructor(e){super(e),this.isRawShaderMaterial=!0,this.type="RawShaderMaterial"}}class ow extends Ul{constructor(e){super(),this.isMeshDepthMaterial=!0,this.type="MeshDepthMaterial",this.depthPacking=gA,this.map=null,this.alphaMap=null,this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.wireframe=!1,this.wireframeLinewidth=1,this.setValues(e)}copy(e){return super.copy(e),this.depthPacking=e.depthPacking,this.map=e.map,this.alphaMap=e.alphaMap,this.displacementMap=e.displacementMap,this.displacementScale=e.displacementScale,this.displacementBias=e.displacementBias,this.wireframe=e.wireframe,this.wireframeLinewidth=e.wireframeLinewidth,this}}class cw extends Ul{constructor(e){super(),this.isMeshDistanceMaterial=!0,this.type="MeshDistanceMaterial",this.map=null,this.alphaMap=null,this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.setValues(e)}copy(e){return super.copy(e),this.map=e.map,this.alphaMap=e.alphaMap,this.displacementMap=e.displacementMap,this.displacementScale=e.displacementScale,this.displacementBias=e.displacementBias,this}}const tu=new Z,nu=new Ll,ji=new Z;class jS extends On{constructor(){super(),this.isCamera=!0,this.type="Camera",this.matrixWorldInverse=new zt,this.projectionMatrix=new zt,this.projectionMatrixInverse=new zt,this.coordinateSystem=qi,this._reversedDepth=!1}get reversedDepth(){return this._reversedDepth}copy(e,n){return super.copy(e,n),this.matrixWorldInverse.copy(e.matrixWorldInverse),this.projectionMatrix.copy(e.projectionMatrix),this.projectionMatrixInverse.copy(e.projectionMatrixInverse),this.coordinateSystem=e.coordinateSystem,this}getWorldDirection(e){return super.getWorldDirection(e).negate()}updateMatrixWorld(e){super.updateMatrixWorld(e),this.matrixWorld.decompose(tu,nu,ji),ji.x===1&&ji.y===1&&ji.z===1?this.matrixWorldInverse.copy(this.matrixWorld).invert():this.matrixWorldInverse.compose(tu,nu,ji.set(1,1,1)).invert()}updateWorldMatrix(e,n,i=!1){super.updateWorldMatrix(e,n,i),this.matrixWorld.decompose(tu,nu,ji),ji.x===1&&ji.y===1&&ji.z===1?this.matrixWorldInverse.copy(this.matrixWorld).invert():this.matrixWorldInverse.compose(tu,nu,ji.set(1,1,1)).invert()}clone(){return new this.constructor().copy(this)}}const Va=new Z,Qv=new Mt,Jv=new Mt;class Di extends jS{constructor(e=50,n=1,i=.1,a=2e3){super(),this.isPerspectiveCamera=!0,this.type="PerspectiveCamera",this.fov=e,this.zoom=1,this.near=i,this.far=a,this.focus=10,this.aspect=n,this.view=null,this.filmGauge=35,this.filmOffset=0,this.updateProjectionMatrix()}copy(e,n){return super.copy(e,n),this.fov=e.fov,this.zoom=e.zoom,this.near=e.near,this.far=e.far,this.focus=e.focus,this.aspect=e.aspect,this.view=e.view===null?null:Object.assign({},e.view),this.filmGauge=e.filmGauge,this.filmOffset=e.filmOffset,this}setFocalLength(e){const n=.5*this.getFilmHeight()/e;this.fov=sm*2*Math.atan(n),this.updateProjectionMatrix()}getFocalLength(){const e=Math.tan(Du*.5*this.fov);return .5*this.getFilmHeight()/e}getEffectiveFOV(){return sm*2*Math.atan(Math.tan(Du*.5*this.fov)/this.zoom)}getFilmWidth(){return this.filmGauge*Math.min(this.aspect,1)}getFilmHeight(){return this.filmGauge/Math.max(this.aspect,1)}getViewBounds(e,n,i){Va.set(-1,-1,.5).applyMatrix4(this.projectionMatrixInverse),n.set(Va.x,Va.y).multiplyScalar(-e/Va.z),Va.set(1,1,.5).applyMatrix4(this.projectionMatrixInverse),i.set(Va.x,Va.y).multiplyScalar(-e/Va.z)}getViewSize(e,n){return this.getViewBounds(e,Qv,Jv),n.subVectors(Jv,Qv)}setViewOffset(e,n,i,a,s,l){this.aspect=e/n,this.view===null&&(this.view={enabled:!0,fullWidth:1,fullHeight:1,offsetX:0,offsetY:0,width:1,height:1}),this.view.enabled=!0,this.view.fullWidth=e,this.view.fullHeight=n,this.view.offsetX=i,this.view.offsetY=a,this.view.width=s,this.view.height=l,this.updateProjectionMatrix()}clearViewOffset(){this.view!==null&&(this.view.enabled=!1),this.updateProjectionMatrix()}updateProjectionMatrix(){const e=this.near;let n=e*Math.tan(Du*.5*this.fov)/this.zoom,i=2*n,a=this.aspect*i,s=-.5*a;const l=this.view;if(this.view!==null&&this.view.enabled){const c=l.fullWidth,u=l.fullHeight;s+=l.offsetX*a/c,n-=l.offsetY*i/u,a*=l.width/c,i*=l.height/u}const o=this.filmOffset;o!==0&&(s+=e*o/this.getFilmWidth()),this.projectionMatrix.makePerspective(s,s+a,n,n-i,e,this.far,this.coordinateSystem,this.reversedDepth),this.projectionMatrixInverse.copy(this.projectionMatrix).invert()}toJSON(e){const n=super.toJSON(e);return n.object.fov=this.fov,n.object.zoom=this.zoom,n.object.near=this.near,n.object.far=this.far,n.object.focus=this.focus,n.object.aspect=this.aspect,this.view!==null&&(n.object.view=Object.assign({},this.view)),n.object.filmGauge=this.filmGauge,n.object.filmOffset=this.filmOffset,n}}class wg extends jS{constructor(e=-1,n=1,i=1,a=-1,s=.1,l=2e3){super(),this.isOrthographicCamera=!0,this.type="OrthographicCamera",this.zoom=1,this.view=null,this.left=e,this.right=n,this.top=i,this.bottom=a,this.near=s,this.far=l,this.updateProjectionMatrix()}copy(e,n){return super.copy(e,n),this.left=e.left,this.right=e.right,this.top=e.top,this.bottom=e.bottom,this.near=e.near,this.far=e.far,this.zoom=e.zoom,this.view=e.view===null?null:Object.assign({},e.view),this}setViewOffset(e,n,i,a,s,l){this.view===null&&(this.view={enabled:!0,fullWidth:1,fullHeight:1,offsetX:0,offsetY:0,width:1,height:1}),this.view.enabled=!0,this.view.fullWidth=e,this.view.fullHeight=n,this.view.offsetX=i,this.view.offsetY=a,this.view.width=s,this.view.height=l,this.updateProjectionMatrix()}clearViewOffset(){this.view!==null&&(this.view.enabled=!1),this.updateProjectionMatrix()}updateProjectionMatrix(){const e=(this.right-this.left)/(2*this.zoom),n=(this.top-this.bottom)/(2*this.zoom),i=(this.right+this.left)/2,a=(this.top+this.bottom)/2;let s=i-e,l=i+e,o=a+n,c=a-n;if(this.view!==null&&this.view.enabled){const u=(this.right-this.left)/this.view.fullWidth/this.zoom,h=(this.top-this.bottom)/this.view.fullHeight/this.zoom;s+=u*this.view.offsetX,l=s+u*this.view.width,o-=h*this.view.offsetY,c=o-h*this.view.height}this.projectionMatrix.makeOrthographic(s,l,o,c,this.near,this.far,this.coordinateSystem,this.reversedDepth),this.projectionMatrixInverse.copy(this.projectionMatrix).invert()}toJSON(e){const n=super.toJSON(e);return n.object.zoom=this.zoom,n.object.left=this.left,n.object.right=this.right,n.object.top=this.top,n.object.bottom=this.bottom,n.object.near=this.near,n.object.far=this.far,this.view!==null&&(n.object.view=Object.assign({},this.view)),n}}const wr=-90,Nr=1;class uw extends On{constructor(e,n,i){super(),this.type="CubeCamera",this.renderTarget=i,this.coordinateSystem=null,this.activeMipmapLevel=0;const a=new Di(wr,Nr,e,n);a.layers=this.layers,this.add(a);const s=new Di(wr,Nr,e,n);s.layers=this.layers,this.add(s);const l=new Di(wr,Nr,e,n);l.layers=this.layers,this.add(l);const o=new Di(wr,Nr,e,n);o.layers=this.layers,this.add(o);const c=new Di(wr,Nr,e,n);c.layers=this.layers,this.add(c);const u=new Di(wr,Nr,e,n);u.layers=this.layers,this.add(u)}updateCoordinateSystem(){const e=this.coordinateSystem,n=this.children.concat(),[i,a,s,l,o,c]=n;for(const u of n)this.remove(u);if(e===qi)i.up.set(0,1,0),i.lookAt(1,0,0),a.up.set(0,1,0),a.lookAt(-1,0,0),s.up.set(0,0,-1),s.lookAt(0,1,0),l.up.set(0,0,1),l.lookAt(0,-1,0),o.up.set(0,1,0),o.lookAt(0,0,1),c.up.set(0,1,0),c.lookAt(0,0,-1);else if(e===fd)i.up.set(0,-1,0),i.lookAt(-1,0,0),a.up.set(0,-1,0),a.lookAt(1,0,0),s.up.set(0,0,1),s.lookAt(0,1,0),l.up.set(0,0,-1),l.lookAt(0,-1,0),o.up.set(0,-1,0),o.lookAt(0,0,1),c.up.set(0,-1,0),c.lookAt(0,0,-1);else throw new Error("THREE.CubeCamera.updateCoordinateSystem(): Invalid coordinate system: "+e);for(const u of n)this.add(u),u.updateMatrixWorld()}update(e,n){this.parent===null&&this.updateMatrixWorld();const{renderTarget:i,activeMipmapLevel:a}=this;this.coordinateSystem!==e.coordinateSystem&&(this.coordinateSystem=e.coordinateSystem,this.updateCoordinateSystem());const[s,l,o,c,u,h]=this.children,g=e.getRenderTarget(),d=e.getActiveCubeFace(),p=e.getActiveMipmapLevel(),x=e.xr.enabled;e.xr.enabled=!1;const _=i.texture.generateMipmaps;i.texture.generateMipmaps=!1;let m=!1;e.isWebGLRenderer===!0?m=e.state.buffers.depth.getReversed():m=e.reversedDepthBuffer,e.setRenderTarget(i,0,a),m&&e.autoClear===!1&&e.clearDepth(),e.render(n,s),e.setRenderTarget(i,1,a),m&&e.autoClear===!1&&e.clearDepth(),e.render(n,l),e.setRenderTarget(i,2,a),m&&e.autoClear===!1&&e.clearDepth(),e.render(n,o),e.setRenderTarget(i,3,a),m&&e.autoClear===!1&&e.clearDepth(),e.render(n,c),e.setRenderTarget(i,4,a),m&&e.autoClear===!1&&e.clearDepth(),e.render(n,u),i.texture.generateMipmaps=_,e.setRenderTarget(i,5,a),m&&e.autoClear===!1&&e.clearDepth(),e.render(n,h),e.setRenderTarget(g,d,p),e.xr.enabled=x,i.texture.needsPMREMUpdate=!0}}class dw extends Di{constructor(e=[]){super(),this.isArrayCamera=!0,this.isMultiViewCamera=!1,this.cameras=e}}const Pg=class Pg{constructor(e,n,i,a){this.elements=[1,0,0,1],e!==void 0&&this.set(e,n,i,a)}identity(){return this.set(1,0,0,1),this}fromArray(e,n=0){for(let i=0;i<4;i++)this.elements[i]=e[i+n];return this}set(e,n,i,a){const s=this.elements;return s[0]=e,s[2]=n,s[1]=i,s[3]=a,this}};Pg.prototype.isMatrix2=!0;let e0=Pg;function t0(t,e,n,i){const a=hw(i);switch(n){case SS:return t*e;case _g:return t*e/a.components*a.byteLength;case bg:return t*e/a.components*a.byteLength;case Zs:return t*e*2/a.components*a.byteLength;case yg:return t*e*2/a.components*a.byteLength;case MS:return t*e*3/a.components*a.byteLength;case Ui:return t*e*4/a.components*a.byteLength;case Sg:return t*e*4/a.components*a.byteLength;case wu:case Nu:return Math.floor((t+3)/4)*Math.floor((e+3)/4)*8;case Cu:case Ru:return Math.floor((t+3)/4)*Math.floor((e+3)/4)*16;case Rp:case Lp:return Math.max(t,16)*Math.max(e,8)/4;case Cp:case Dp:return Math.max(t,8)*Math.max(e,8)/2;case Up:case Op:case zp:case jp:return Math.floor((t+3)/4)*Math.floor((e+3)/4)*8;case Pp:case cd:case kp:return Math.floor((t+3)/4)*Math.floor((e+3)/4)*16;case Ip:return Math.floor((t+3)/4)*Math.floor((e+3)/4)*16;case Bp:return Math.floor((t+4)/5)*Math.floor((e+3)/4)*16;case Fp:return Math.floor((t+4)/5)*Math.floor((e+4)/5)*16;case Hp:return Math.floor((t+5)/6)*Math.floor((e+4)/5)*16;case Gp:return Math.floor((t+5)/6)*Math.floor((e+5)/6)*16;case Vp:return Math.floor((t+7)/8)*Math.floor((e+4)/5)*16;case Wp:return Math.floor((t+7)/8)*Math.floor((e+5)/6)*16;case Xp:return Math.floor((t+7)/8)*Math.floor((e+7)/8)*16;case qp:return Math.floor((t+9)/10)*Math.floor((e+4)/5)*16;case Yp:return Math.floor((t+9)/10)*Math.floor((e+5)/6)*16;case $p:return Math.floor((t+9)/10)*Math.floor((e+7)/8)*16;case Kp:return Math.floor((t+9)/10)*Math.floor((e+9)/10)*16;case Zp:return Math.floor((t+11)/12)*Math.floor((e+9)/10)*16;case Qp:return Math.floor((t+11)/12)*Math.floor((e+11)/12)*16;case Jp:case em:case tm:return Math.ceil(t/4)*Math.ceil(e/4)*16;case nm:case im:return Math.ceil(t/4)*Math.ceil(e/4)*8;case ud:case am:return Math.ceil(t/4)*Math.ceil(e/4)*16}throw new Error(`Unable to determine texture byte length for ${n} format.`)}function hw(t){switch(t){case vi:case vS:return{byteLength:1,components:1};case Wo:case _S:case Ra:return{byteLength:2,components:1};case xg:case vg:return{byteLength:2,components:4};case Zi:case gg:case Li:return{byteLength:4,components:1};case bS:case yS:return{byteLength:4,components:3}}throw new Error(`THREE.TextureUtils: Unknown texture type ${t}.`)}typeof __THREE_DEVTOOLS__<"u"&&__THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("register",{detail:{revision:mg}}));typeof window<"u"&&(window.__THREE__?Qe("WARNING: Multiple instances of Three.js being imported."):window.__THREE__=mg);/** + * @license + * Copyright 2010-2026 Three.js Authors + * SPDX-License-Identifier: MIT + */function kS(){let t=null,e=!1,n=null,i=null;function a(s,l){n(s,l),i=t.requestAnimationFrame(a)}return{start:function(){e!==!0&&n!==null&&t!==null&&(i=t.requestAnimationFrame(a),e=!0)},stop:function(){t!==null&&t.cancelAnimationFrame(i),e=!1},setAnimationLoop:function(s){n=s},setContext:function(s){t=s}}}function fw(t){const e=new WeakMap;function n(o,c){const u=o.array,h=o.usage,g=u.byteLength,d=t.createBuffer();t.bindBuffer(c,d),t.bufferData(c,u,h),o.onUploadCallback();let p;if(u instanceof Float32Array)p=t.FLOAT;else if(typeof Float16Array<"u"&&u instanceof Float16Array)p=t.HALF_FLOAT;else if(u instanceof Uint16Array)o.isFloat16BufferAttribute?p=t.HALF_FLOAT:p=t.UNSIGNED_SHORT;else if(u instanceof Int16Array)p=t.SHORT;else if(u instanceof Uint32Array)p=t.UNSIGNED_INT;else if(u instanceof Int32Array)p=t.INT;else if(u instanceof Int8Array)p=t.BYTE;else if(u instanceof Uint8Array)p=t.UNSIGNED_BYTE;else if(u instanceof Uint8ClampedArray)p=t.UNSIGNED_BYTE;else throw new Error("THREE.WebGLAttributes: Unsupported buffer data format: "+u);return{buffer:d,type:p,bytesPerElement:u.BYTES_PER_ELEMENT,version:o.version,size:g}}function i(o,c,u){const h=c.array,g=c.updateRanges;if(t.bindBuffer(u,o),g.length===0)t.bufferSubData(u,0,h);else{g.sort((p,x)=>p.start-x.start);let d=0;for(let p=1;p<g.length;p++){const x=g[d],_=g[p];_.start<=x.start+x.count+1?x.count=Math.max(x.count,_.start+_.count-x.start):(++d,g[d]=_)}g.length=d+1;for(let p=0,x=g.length;p<x;p++){const _=g[p];t.bufferSubData(u,_.start*h.BYTES_PER_ELEMENT,h,_.start,_.count)}c.clearUpdateRanges()}c.onUploadCallback()}function a(o){return o.isInterleavedBufferAttribute&&(o=o.data),e.get(o)}function s(o){o.isInterleavedBufferAttribute&&(o=o.data);const c=e.get(o);c&&(t.deleteBuffer(c.buffer),e.delete(o))}function l(o,c){if(o.isInterleavedBufferAttribute&&(o=o.data),o.isGLBufferAttribute){const h=e.get(o);(!h||h.version<o.version)&&e.set(o,{buffer:o.buffer,type:o.type,bytesPerElement:o.elementSize,version:o.version});return}const u=e.get(o);if(u===void 0)e.set(o,n(o,c));else if(u.version<o.version){if(u.size!==o.array.byteLength)throw new Error("THREE.WebGLAttributes: The size of the buffer attribute's array buffer does not match the original size. Resizing buffer attributes is not supported.");i(u.buffer,o,c),u.version=o.version}}return{get:a,remove:s,update:l}}var pw=`#ifdef USE_ALPHAHASH + if ( diffuseColor.a < getAlphaHashThreshold( vPosition ) ) discard; +#endif`,mw=`#ifdef USE_ALPHAHASH + const float ALPHA_HASH_SCALE = 0.05; + float hash2D( vec2 value ) { + return fract( 1.0e4 * sin( 17.0 * value.x + 0.1 * value.y ) * ( 0.1 + abs( sin( 13.0 * value.y + value.x ) ) ) ); + } + float hash3D( vec3 value ) { + return hash2D( vec2( hash2D( value.xy ), value.z ) ); + } + float getAlphaHashThreshold( vec3 position ) { + float maxDeriv = max( + length( dFdx( position.xyz ) ), + length( dFdy( position.xyz ) ) + ); + float pixScale = 1.0 / ( ALPHA_HASH_SCALE * maxDeriv ); + vec2 pixScales = vec2( + exp2( floor( log2( pixScale ) ) ), + exp2( ceil( log2( pixScale ) ) ) + ); + vec2 alpha = vec2( + hash3D( floor( pixScales.x * position.xyz ) ), + hash3D( floor( pixScales.y * position.xyz ) ) + ); + float lerpFactor = fract( log2( pixScale ) ); + float x = ( 1.0 - lerpFactor ) * alpha.x + lerpFactor * alpha.y; + float a = min( lerpFactor, 1.0 - lerpFactor ); + vec3 cases = vec3( + x * x / ( 2.0 * a * ( 1.0 - a ) ), + ( x - 0.5 * a ) / ( 1.0 - a ), + 1.0 - ( ( 1.0 - x ) * ( 1.0 - x ) / ( 2.0 * a * ( 1.0 - a ) ) ) + ); + float threshold = ( x < ( 1.0 - a ) ) + ? ( ( x < a ) ? cases.x : cases.y ) + : cases.z; + return clamp( threshold , 1.0e-6, 1.0 ); + } +#endif`,gw=`#ifdef USE_ALPHAMAP + diffuseColor.a *= texture2D( alphaMap, vAlphaMapUv ).g; +#endif`,xw=`#ifdef USE_ALPHAMAP + uniform sampler2D alphaMap; +#endif`,vw=`#ifdef USE_ALPHATEST + #ifdef ALPHA_TO_COVERAGE + diffuseColor.a = smoothstep( alphaTest, alphaTest + fwidth( diffuseColor.a ), diffuseColor.a ); + if ( diffuseColor.a == 0.0 ) discard; + #else + if ( diffuseColor.a < alphaTest ) discard; + #endif +#endif`,_w=`#ifdef USE_ALPHATEST + uniform float alphaTest; +#endif`,bw=`#ifdef USE_AOMAP + float ambientOcclusion = ( texture2D( aoMap, vAoMapUv ).r - 1.0 ) * aoMapIntensity + 1.0; + reflectedLight.indirectDiffuse *= ambientOcclusion; + #if defined( USE_CLEARCOAT ) + clearcoatSpecularIndirect *= ambientOcclusion; + #endif + #if defined( USE_SHEEN ) + sheenSpecularIndirect *= ambientOcclusion; + #endif + #if defined( USE_ENVMAP ) && defined( STANDARD ) + float dotNV = saturate( dot( geometryNormal, geometryViewDir ) ); + reflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.roughness ); + #endif +#endif`,yw=`#ifdef USE_AOMAP + uniform sampler2D aoMap; + uniform float aoMapIntensity; +#endif`,Sw=`#ifdef USE_BATCHING + #if ! defined( GL_ANGLE_multi_draw ) + #define gl_DrawID _gl_DrawID + uniform int _gl_DrawID; + #endif + uniform highp sampler2D batchingTexture; + uniform highp usampler2D batchingIdTexture; + mat4 getBatchingMatrix( const in float i ) { + int size = textureSize( batchingTexture, 0 ).x; + int j = int( i ) * 4; + int x = j % size; + int y = j / size; + vec4 v1 = texelFetch( batchingTexture, ivec2( x, y ), 0 ); + vec4 v2 = texelFetch( batchingTexture, ivec2( x + 1, y ), 0 ); + vec4 v3 = texelFetch( batchingTexture, ivec2( x + 2, y ), 0 ); + vec4 v4 = texelFetch( batchingTexture, ivec2( x + 3, y ), 0 ); + return mat4( v1, v2, v3, v4 ); + } + float getIndirectIndex( const in int i ) { + int size = textureSize( batchingIdTexture, 0 ).x; + int x = i % size; + int y = i / size; + return float( texelFetch( batchingIdTexture, ivec2( x, y ), 0 ).r ); + } +#endif +#ifdef USE_BATCHING_COLOR + uniform sampler2D batchingColorTexture; + vec4 getBatchingColor( const in float i ) { + int size = textureSize( batchingColorTexture, 0 ).x; + int j = int( i ); + int x = j % size; + int y = j / size; + return texelFetch( batchingColorTexture, ivec2( x, y ), 0 ); + } +#endif`,Mw=`#ifdef USE_BATCHING + mat4 batchingMatrix = getBatchingMatrix( getIndirectIndex( gl_DrawID ) ); +#endif`,Ew=`vec3 transformed = vec3( position ); +#ifdef USE_ALPHAHASH + vPosition = vec3( position ); +#endif`,Tw=`vec3 objectNormal = vec3( normal ); +#ifdef USE_TANGENT + vec3 objectTangent = vec3( tangent.xyz ); +#endif`,Aw=`float G_BlinnPhong_Implicit( ) { + return 0.25; +} +float D_BlinnPhong( const in float shininess, const in float dotNH ) { + return RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess ); +} +vec3 BRDF_BlinnPhong( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float shininess ) { + vec3 halfDir = normalize( lightDir + viewDir ); + float dotNH = saturate( dot( normal, halfDir ) ); + float dotVH = saturate( dot( viewDir, halfDir ) ); + vec3 F = F_Schlick( specularColor, 1.0, dotVH ); + float G = G_BlinnPhong_Implicit( ); + float D = D_BlinnPhong( shininess, dotNH ); + return F * ( G * D ); +} // validated`,ww=`#ifdef USE_IRIDESCENCE + const mat3 XYZ_TO_REC709 = mat3( + 3.2404542, -0.9692660, 0.0556434, + -1.5371385, 1.8760108, -0.2040259, + -0.4985314, 0.0415560, 1.0572252 + ); + vec3 Fresnel0ToIor( vec3 fresnel0 ) { + vec3 sqrtF0 = sqrt( fresnel0 ); + return ( vec3( 1.0 ) + sqrtF0 ) / ( vec3( 1.0 ) - sqrtF0 ); + } + vec3 IorToFresnel0( vec3 transmittedIor, float incidentIor ) { + return pow2( ( transmittedIor - vec3( incidentIor ) ) / ( transmittedIor + vec3( incidentIor ) ) ); + } + float IorToFresnel0( float transmittedIor, float incidentIor ) { + return pow2( ( transmittedIor - incidentIor ) / ( transmittedIor + incidentIor )); + } + vec3 evalSensitivity( float OPD, vec3 shift ) { + float phase = 2.0 * PI * OPD * 1.0e-9; + vec3 val = vec3( 5.4856e-13, 4.4201e-13, 5.2481e-13 ); + vec3 pos = vec3( 1.6810e+06, 1.7953e+06, 2.2084e+06 ); + vec3 var = vec3( 4.3278e+09, 9.3046e+09, 6.6121e+09 ); + vec3 xyz = val * sqrt( 2.0 * PI * var ) * cos( pos * phase + shift ) * exp( - pow2( phase ) * var ); + xyz.x += 9.7470e-14 * sqrt( 2.0 * PI * 4.5282e+09 ) * cos( 2.2399e+06 * phase + shift[ 0 ] ) * exp( - 4.5282e+09 * pow2( phase ) ); + xyz /= 1.0685e-7; + vec3 rgb = XYZ_TO_REC709 * xyz; + return rgb; + } + vec3 evalIridescence( float outsideIOR, float eta2, float cosTheta1, float thinFilmThickness, vec3 baseF0 ) { + vec3 I; + float iridescenceIOR = mix( outsideIOR, eta2, smoothstep( 0.0, 0.03, thinFilmThickness ) ); + float sinTheta2Sq = pow2( outsideIOR / iridescenceIOR ) * ( 1.0 - pow2( cosTheta1 ) ); + float cosTheta2Sq = 1.0 - sinTheta2Sq; + if ( cosTheta2Sq < 0.0 ) { + return vec3( 1.0 ); + } + float cosTheta2 = sqrt( cosTheta2Sq ); + float R0 = IorToFresnel0( iridescenceIOR, outsideIOR ); + float R12 = F_Schlick( R0, 1.0, cosTheta1 ); + float T121 = 1.0 - R12; + float phi12 = 0.0; + if ( iridescenceIOR < outsideIOR ) phi12 = PI; + float phi21 = PI - phi12; + vec3 baseIOR = Fresnel0ToIor( clamp( baseF0, 0.0, 0.9999 ) ); vec3 R1 = IorToFresnel0( baseIOR, iridescenceIOR ); + vec3 R23 = F_Schlick( R1, 1.0, cosTheta2 ); + vec3 phi23 = vec3( 0.0 ); + if ( baseIOR[ 0 ] < iridescenceIOR ) phi23[ 0 ] = PI; + if ( baseIOR[ 1 ] < iridescenceIOR ) phi23[ 1 ] = PI; + if ( baseIOR[ 2 ] < iridescenceIOR ) phi23[ 2 ] = PI; + float OPD = 2.0 * iridescenceIOR * thinFilmThickness * cosTheta2; + vec3 phi = vec3( phi21 ) + phi23; + vec3 R123 = clamp( R12 * R23, 1e-5, 0.9999 ); + vec3 r123 = sqrt( R123 ); + vec3 Rs = pow2( T121 ) * R23 / ( vec3( 1.0 ) - R123 ); + vec3 C0 = R12 + Rs; + I = C0; + vec3 Cm = Rs - T121; + for ( int m = 1; m <= 2; ++ m ) { + Cm *= r123; + vec3 Sm = 2.0 * evalSensitivity( float( m ) * OPD, float( m ) * phi ); + I += Cm * Sm; + } + return max( I, vec3( 0.0 ) ); + } +#endif`,Nw=`#ifdef USE_BUMPMAP + uniform sampler2D bumpMap; + uniform float bumpScale; + vec2 dHdxy_fwd() { + vec2 dSTdx = dFdx( vBumpMapUv ); + vec2 dSTdy = dFdy( vBumpMapUv ); + float Hll = bumpScale * texture2D( bumpMap, vBumpMapUv ).x; + float dBx = bumpScale * texture2D( bumpMap, vBumpMapUv + dSTdx ).x - Hll; + float dBy = bumpScale * texture2D( bumpMap, vBumpMapUv + dSTdy ).x - Hll; + return vec2( dBx, dBy ); + } + vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy, float faceDirection ) { + vec3 vSigmaX = normalize( dFdx( surf_pos.xyz ) ); + vec3 vSigmaY = normalize( dFdy( surf_pos.xyz ) ); + vec3 vN = surf_norm; + vec3 R1 = cross( vSigmaY, vN ); + vec3 R2 = cross( vN, vSigmaX ); + float fDet = dot( vSigmaX, R1 ) * faceDirection; + vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 ); + return normalize( abs( fDet ) * surf_norm - vGrad ); + } +#endif`,Cw=`#if NUM_CLIPPING_PLANES > 0 + vec4 plane; + #ifdef ALPHA_TO_COVERAGE + float distanceToPlane, distanceGradient; + float clipOpacity = 1.0; + #pragma unroll_loop_start + for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) { + plane = clippingPlanes[ i ]; + distanceToPlane = - dot( vClipPosition, plane.xyz ) + plane.w; + distanceGradient = fwidth( distanceToPlane ) / 2.0; + clipOpacity *= smoothstep( - distanceGradient, distanceGradient, distanceToPlane ); + if ( clipOpacity == 0.0 ) discard; + } + #pragma unroll_loop_end + #if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES + float unionClipOpacity = 1.0; + #pragma unroll_loop_start + for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) { + plane = clippingPlanes[ i ]; + distanceToPlane = - dot( vClipPosition, plane.xyz ) + plane.w; + distanceGradient = fwidth( distanceToPlane ) / 2.0; + unionClipOpacity *= 1.0 - smoothstep( - distanceGradient, distanceGradient, distanceToPlane ); + } + #pragma unroll_loop_end + clipOpacity *= 1.0 - unionClipOpacity; + #endif + diffuseColor.a *= clipOpacity; + if ( diffuseColor.a == 0.0 ) discard; + #else + #pragma unroll_loop_start + for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) { + plane = clippingPlanes[ i ]; + if ( dot( vClipPosition, plane.xyz ) > plane.w ) discard; + } + #pragma unroll_loop_end + #if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES + bool clipped = true; + #pragma unroll_loop_start + for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) { + plane = clippingPlanes[ i ]; + clipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped; + } + #pragma unroll_loop_end + if ( clipped ) discard; + #endif + #endif +#endif`,Rw=`#if NUM_CLIPPING_PLANES > 0 + varying vec3 vClipPosition; + uniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ]; +#endif`,Dw=`#if NUM_CLIPPING_PLANES > 0 + varying vec3 vClipPosition; +#endif`,Lw=`#if NUM_CLIPPING_PLANES > 0 + vClipPosition = - mvPosition.xyz; +#endif`,Uw=`#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA ) + diffuseColor *= vColor; +#endif`,Ow=`#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA ) + varying vec4 vColor; +#endif`,Pw=`#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA ) || defined( USE_INSTANCING_COLOR ) || defined( USE_BATCHING_COLOR ) + varying vec4 vColor; +#endif`,zw=`#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA ) || defined( USE_INSTANCING_COLOR ) || defined( USE_BATCHING_COLOR ) + vColor = vec4( 1.0 ); +#endif +#ifdef USE_COLOR_ALPHA + vColor *= color; +#elif defined( USE_COLOR ) + vColor.rgb *= color; +#endif +#ifdef USE_INSTANCING_COLOR + vColor.rgb *= instanceColor.rgb; +#endif +#ifdef USE_BATCHING_COLOR + vColor *= getBatchingColor( getIndirectIndex( gl_DrawID ) ); +#endif`,jw=`#define PI 3.141592653589793 +#define PI2 6.283185307179586 +#define PI_HALF 1.5707963267948966 +#define RECIPROCAL_PI 0.3183098861837907 +#define RECIPROCAL_PI2 0.15915494309189535 +#define EPSILON 1e-6 +#ifndef saturate +#define saturate( a ) clamp( a, 0.0, 1.0 ) +#endif +#define whiteComplement( a ) ( 1.0 - saturate( a ) ) +float pow2( const in float x ) { return x*x; } +vec3 pow2( const in vec3 x ) { return x*x; } +float pow3( const in float x ) { return x*x*x; } +float pow4( const in float x ) { float x2 = x*x; return x2*x2; } +float max3( const in vec3 v ) { return max( max( v.x, v.y ), v.z ); } +float average( const in vec3 v ) { return dot( v, vec3( 0.3333333 ) ); } +highp float rand( const in vec2 uv ) { + const highp float a = 12.9898, b = 78.233, c = 43758.5453; + highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI ); + return fract( sin( sn ) * c ); +} +#ifdef HIGH_PRECISION + float precisionSafeLength( vec3 v ) { return length( v ); } +#else + float precisionSafeLength( vec3 v ) { + float maxComponent = max3( abs( v ) ); + return length( v / maxComponent ) * maxComponent; + } +#endif +struct IncidentLight { + vec3 color; + vec3 direction; + bool visible; +}; +struct ReflectedLight { + vec3 directDiffuse; + vec3 directSpecular; + vec3 indirectDiffuse; + vec3 indirectSpecular; +}; +#ifdef USE_ALPHAHASH + varying vec3 vPosition; +#endif +vec3 transformDirection( in vec3 dir, in mat4 matrix ) { + return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); +} +#define inverseTransformDirection transformDirectionByInverseViewMatrix +vec3 transformNormalByInverseViewMatrix( in vec3 normal, in mat4 viewMatrix ) { + return normalize( ( vec4( normal, 0.0 ) * viewMatrix ).xyz ); +} +vec3 transformDirectionByInverseViewMatrix( in vec3 dir, in mat4 viewMatrix ) { + return normalize( ( vec4( dir, 0.0 ) * viewMatrix ).xyz ); +} +bool isPerspectiveMatrix( mat4 m ) { + return m[ 2 ][ 3 ] == - 1.0; +} +vec2 equirectUv( in vec3 dir ) { + float u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5; + float v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5; + return vec2( u, v ); +} +vec3 BRDF_Lambert( const in vec3 diffuseColor ) { + return RECIPROCAL_PI * diffuseColor; +} +vec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) { + float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH ); + return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel ); +} +float F_Schlick( const in float f0, const in float f90, const in float dotVH ) { + float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH ); + return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel ); +} // validated`,kw=`#ifdef ENVMAP_TYPE_CUBE_UV + #define cubeUV_minMipLevel 4.0 + #define cubeUV_minTileSize 16.0 + float getFace( vec3 direction ) { + vec3 absDirection = abs( direction ); + float face = - 1.0; + if ( absDirection.x > absDirection.z ) { + if ( absDirection.x > absDirection.y ) + face = direction.x > 0.0 ? 0.0 : 3.0; + else + face = direction.y > 0.0 ? 1.0 : 4.0; + } else { + if ( absDirection.z > absDirection.y ) + face = direction.z > 0.0 ? 2.0 : 5.0; + else + face = direction.y > 0.0 ? 1.0 : 4.0; + } + return face; + } + vec2 getUV( vec3 direction, float face ) { + vec2 uv; + if ( face == 0.0 ) { + uv = vec2( direction.z, direction.y ) / abs( direction.x ); + } else if ( face == 1.0 ) { + uv = vec2( - direction.x, - direction.z ) / abs( direction.y ); + } else if ( face == 2.0 ) { + uv = vec2( - direction.x, direction.y ) / abs( direction.z ); + } else if ( face == 3.0 ) { + uv = vec2( - direction.z, direction.y ) / abs( direction.x ); + } else if ( face == 4.0 ) { + uv = vec2( - direction.x, direction.z ) / abs( direction.y ); + } else { + uv = vec2( direction.x, direction.y ) / abs( direction.z ); + } + return 0.5 * ( uv + 1.0 ); + } + vec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) { + float face = getFace( direction ); + float filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 ); + mipInt = max( mipInt, cubeUV_minMipLevel ); + float faceSize = exp2( mipInt ); + highp vec2 uv = getUV( direction, face ) * ( faceSize - 2.0 ) + 1.0; + if ( face > 2.0 ) { + uv.y += faceSize; + face -= 3.0; + } + uv.x += face * faceSize; + uv.x += filterInt * 3.0 * cubeUV_minTileSize; + uv.y += 4.0 * ( exp2( CUBEUV_MAX_MIP ) - faceSize ); + uv.x *= CUBEUV_TEXEL_WIDTH; + uv.y *= CUBEUV_TEXEL_HEIGHT; + #ifdef texture2DGradEXT + return texture2DGradEXT( envMap, uv, vec2( 0.0 ), vec2( 0.0 ) ).rgb; + #else + return texture2D( envMap, uv ).rgb; + #endif + } + #define cubeUV_r0 1.0 + #define cubeUV_m0 - 2.0 + #define cubeUV_r1 0.8 + #define cubeUV_m1 - 1.0 + #define cubeUV_r4 0.4 + #define cubeUV_m4 2.0 + #define cubeUV_r5 0.305 + #define cubeUV_m5 3.0 + #define cubeUV_r6 0.21 + #define cubeUV_m6 4.0 + float roughnessToMip( float roughness ) { + float mip = 0.0; + if ( roughness >= cubeUV_r1 ) { + mip = ( cubeUV_r0 - roughness ) * ( cubeUV_m1 - cubeUV_m0 ) / ( cubeUV_r0 - cubeUV_r1 ) + cubeUV_m0; + } else if ( roughness >= cubeUV_r4 ) { + mip = ( cubeUV_r1 - roughness ) * ( cubeUV_m4 - cubeUV_m1 ) / ( cubeUV_r1 - cubeUV_r4 ) + cubeUV_m1; + } else if ( roughness >= cubeUV_r5 ) { + mip = ( cubeUV_r4 - roughness ) * ( cubeUV_m5 - cubeUV_m4 ) / ( cubeUV_r4 - cubeUV_r5 ) + cubeUV_m4; + } else if ( roughness >= cubeUV_r6 ) { + mip = ( cubeUV_r5 - roughness ) * ( cubeUV_m6 - cubeUV_m5 ) / ( cubeUV_r5 - cubeUV_r6 ) + cubeUV_m5; + } else { + mip = - 2.0 * log2( 1.16 * roughness ); } + return mip; + } + vec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) { + float mip = clamp( roughnessToMip( roughness ), cubeUV_m0, CUBEUV_MAX_MIP ); + float mipF = fract( mip ); + float mipInt = floor( mip ); + vec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt ); + if ( mipF == 0.0 ) { + return vec4( color0, 1.0 ); + } else { + vec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 ); + return vec4( mix( color0, color1, mipF ), 1.0 ); + } + } +#endif`,Iw=`vec3 transformedNormal = objectNormal; +#ifdef USE_TANGENT + vec3 transformedTangent = objectTangent; +#endif +#ifdef USE_BATCHING + mat3 bm = mat3( batchingMatrix ); + transformedNormal /= vec3( dot( bm[ 0 ], bm[ 0 ] ), dot( bm[ 1 ], bm[ 1 ] ), dot( bm[ 2 ], bm[ 2 ] ) ); + transformedNormal = bm * transformedNormal; + #ifdef USE_TANGENT + transformedTangent = bm * transformedTangent; + #endif +#endif +#ifdef USE_INSTANCING + mat3 im = mat3( instanceMatrix ); + transformedNormal /= vec3( dot( im[ 0 ], im[ 0 ] ), dot( im[ 1 ], im[ 1 ] ), dot( im[ 2 ], im[ 2 ] ) ); + transformedNormal = im * transformedNormal; + #ifdef USE_TANGENT + transformedTangent = im * transformedTangent; + #endif +#endif +transformedNormal = normalMatrix * transformedNormal; +#ifdef FLIP_SIDED + transformedNormal = - transformedNormal; +#endif +#ifdef USE_TANGENT + transformedTangent = ( modelViewMatrix * vec4( transformedTangent, 0.0 ) ).xyz; +#endif`,Bw=`#ifdef USE_DISPLACEMENTMAP + uniform sampler2D displacementMap; + uniform float displacementScale; + uniform float displacementBias; +#endif`,Fw=`#ifdef USE_DISPLACEMENTMAP + transformed += normalize( objectNormal ) * ( texture2D( displacementMap, vDisplacementMapUv ).x * displacementScale + displacementBias ); +#endif`,Hw=`#ifdef USE_EMISSIVEMAP + vec4 emissiveColor = texture2D( emissiveMap, vEmissiveMapUv ); + #ifdef DECODE_VIDEO_TEXTURE_EMISSIVE + emissiveColor = sRGBTransferEOTF( emissiveColor ); + #endif + totalEmissiveRadiance *= emissiveColor.rgb; +#endif`,Gw=`#ifdef USE_EMISSIVEMAP + uniform sampler2D emissiveMap; +#endif`,Vw="gl_FragColor = linearToOutputTexel( gl_FragColor );",Ww=`vec4 LinearTransferOETF( in vec4 value ) { + return value; +} +vec4 sRGBTransferEOTF( in vec4 value ) { + return vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.a ); +} +vec4 sRGBTransferOETF( in vec4 value ) { + return vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a ); +}`,Xw=`#ifdef USE_ENVMAP + #ifdef ENV_WORLDPOS + vec3 cameraToFrag; + if ( isOrthographic ) { + cameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); + } else { + cameraToFrag = normalize( vWorldPosition - cameraPosition ); + } + vec3 worldNormal = transformNormalByInverseViewMatrix( normal, viewMatrix ); + #ifdef ENVMAP_MODE_REFLECTION + vec3 reflectVec = reflect( cameraToFrag, worldNormal ); + #else + vec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio ); + #endif + #else + vec3 reflectVec = vReflect; + #endif + #ifdef ENVMAP_TYPE_CUBE + vec4 envColor = textureCube( envMap, envMapRotation * reflectVec ); + #ifdef ENVMAP_BLENDING_MULTIPLY + outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity ); + #elif defined( ENVMAP_BLENDING_MIX ) + outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity ); + #elif defined( ENVMAP_BLENDING_ADD ) + outgoingLight += envColor.xyz * specularStrength * reflectivity; + #endif + #endif +#endif`,qw=`#ifdef USE_ENVMAP + uniform float envMapIntensity; + uniform mat3 envMapRotation; + #ifdef ENVMAP_TYPE_CUBE + uniform samplerCube envMap; + #else + uniform sampler2D envMap; + #endif +#endif`,Yw=`#ifdef USE_ENVMAP + uniform float reflectivity; + #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( LAMBERT ) + #define ENV_WORLDPOS + #endif + #ifdef ENV_WORLDPOS + varying vec3 vWorldPosition; + uniform float refractionRatio; + #else + varying vec3 vReflect; + #endif +#endif`,$w=`#ifdef USE_ENVMAP + #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( LAMBERT ) + #define ENV_WORLDPOS + #endif + #ifdef ENV_WORLDPOS + + varying vec3 vWorldPosition; + #else + varying vec3 vReflect; + uniform float refractionRatio; + #endif +#endif`,Kw=`#ifdef USE_ENVMAP + #ifdef ENV_WORLDPOS + vWorldPosition = worldPosition.xyz; + #else + vec3 cameraToVertex; + if ( isOrthographic ) { + cameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); + } else { + cameraToVertex = normalize( worldPosition.xyz - cameraPosition ); + } + vec3 worldNormal = transformNormalByInverseViewMatrix( transformedNormal, viewMatrix ); + #ifdef ENVMAP_MODE_REFLECTION + vReflect = reflect( cameraToVertex, worldNormal ); + #else + vReflect = refract( cameraToVertex, worldNormal, refractionRatio ); + #endif + #endif +#endif`,Zw=`#ifdef USE_FOG + vFogDepth = - mvPosition.z; +#endif`,Qw=`#ifdef USE_FOG + varying float vFogDepth; +#endif`,Jw=`#ifdef USE_FOG + #ifdef FOG_EXP2 + float fogFactor = 1.0 - exp( - fogDensity * fogDensity * vFogDepth * vFogDepth ); + #else + float fogFactor = smoothstep( fogNear, fogFar, vFogDepth ); + #endif + gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor ); +#endif`,eN=`#ifdef USE_FOG + uniform vec3 fogColor; + varying float vFogDepth; + #ifdef FOG_EXP2 + uniform float fogDensity; + #else + uniform float fogNear; + uniform float fogFar; + #endif +#endif`,tN=`#ifdef USE_GRADIENTMAP + uniform sampler2D gradientMap; +#endif +vec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) { + float dotNL = dot( normal, lightDirection ); + vec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 ); + #ifdef USE_GRADIENTMAP + return vec3( texture2D( gradientMap, coord ).r ); + #else + vec2 fw = fwidth( coord ) * 0.5; + return mix( vec3( 0.7 ), vec3( 1.0 ), smoothstep( 0.7 - fw.x, 0.7 + fw.x, coord.x ) ); + #endif +}`,nN=`#ifdef USE_LIGHTMAP + uniform sampler2D lightMap; + uniform float lightMapIntensity; +#endif`,iN=`LambertMaterial material; +material.diffuseColor = diffuseColor.rgb; +material.specularStrength = specularStrength;`,aN=`varying vec3 vViewPosition; +struct LambertMaterial { + vec3 diffuseColor; + float specularStrength; +}; +void RE_Direct_Lambert( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in LambertMaterial material, inout ReflectedLight reflectedLight ) { + float dotNL = saturate( dot( geometryNormal, directLight.direction ) ); + vec3 irradiance = dotNL * directLight.color; + reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); +} +void RE_IndirectDiffuse_Lambert( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in LambertMaterial material, inout ReflectedLight reflectedLight ) { + reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); +} +#define RE_Direct RE_Direct_Lambert +#define RE_IndirectDiffuse RE_IndirectDiffuse_Lambert`,sN=`uniform bool receiveShadow; +uniform vec3 ambientLightColor; +#if defined( USE_LIGHT_PROBES ) + uniform vec3 lightProbe[ 9 ]; +#endif +vec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) { + float x = normal.x, y = normal.y, z = normal.z; + vec3 result = shCoefficients[ 0 ] * 0.886227; + result += shCoefficients[ 1 ] * 2.0 * 0.511664 * y; + result += shCoefficients[ 2 ] * 2.0 * 0.511664 * z; + result += shCoefficients[ 3 ] * 2.0 * 0.511664 * x; + result += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y; + result += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z; + result += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 ); + result += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z; + result += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y ); + return result; +} +vec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in vec3 normal ) { + vec3 worldNormal = transformNormalByInverseViewMatrix( normal, viewMatrix ); + vec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe ); + return irradiance; +} +vec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) { + vec3 irradiance = ambientLightColor; + return irradiance; +} +float getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) { + float distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 ); + if ( cutoffDistance > 0.0 ) { + distanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) ); + } + return distanceFalloff; +} +float getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) { + return smoothstep( coneCosine, penumbraCosine, angleCosine ); +} +#if NUM_DIR_LIGHTS > 0 + struct DirectionalLight { + vec3 direction; + vec3 color; + }; + uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ]; + void getDirectionalLightInfo( const in DirectionalLight directionalLight, out IncidentLight light ) { + light.color = directionalLight.color; + light.direction = directionalLight.direction; + light.visible = true; + } +#endif +#if NUM_POINT_LIGHTS > 0 + struct PointLight { + vec3 position; + vec3 color; + float distance; + float decay; + }; + uniform PointLight pointLights[ NUM_POINT_LIGHTS ]; + void getPointLightInfo( const in PointLight pointLight, const in vec3 geometryPosition, out IncidentLight light ) { + vec3 lVector = pointLight.position - geometryPosition; + light.direction = normalize( lVector ); + float lightDistance = length( lVector ); + light.color = pointLight.color; + light.color *= getDistanceAttenuation( lightDistance, pointLight.distance, pointLight.decay ); + light.visible = ( light.color != vec3( 0.0 ) ); + } +#endif +#if NUM_SPOT_LIGHTS > 0 + struct SpotLight { + vec3 position; + vec3 direction; + vec3 color; + float distance; + float decay; + float coneCos; + float penumbraCos; + }; + uniform SpotLight spotLights[ NUM_SPOT_LIGHTS ]; + void getSpotLightInfo( const in SpotLight spotLight, const in vec3 geometryPosition, out IncidentLight light ) { + vec3 lVector = spotLight.position - geometryPosition; + light.direction = normalize( lVector ); + float angleCos = dot( light.direction, spotLight.direction ); + float spotAttenuation = getSpotAttenuation( spotLight.coneCos, spotLight.penumbraCos, angleCos ); + if ( spotAttenuation > 0.0 ) { + float lightDistance = length( lVector ); + light.color = spotLight.color * spotAttenuation; + light.color *= getDistanceAttenuation( lightDistance, spotLight.distance, spotLight.decay ); + light.visible = ( light.color != vec3( 0.0 ) ); + } else { + light.color = vec3( 0.0 ); + light.visible = false; + } + } +#endif +#if NUM_RECT_AREA_LIGHTS > 0 + struct RectAreaLight { + vec3 color; + vec3 position; + vec3 halfWidth; + vec3 halfHeight; + }; + uniform sampler2D ltc_1; uniform sampler2D ltc_2; + uniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ]; +#endif +#if NUM_HEMI_LIGHTS > 0 + struct HemisphereLight { + vec3 direction; + vec3 skyColor; + vec3 groundColor; + }; + uniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ]; + vec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in vec3 normal ) { + float dotNL = dot( normal, hemiLight.direction ); + float hemiDiffuseWeight = 0.5 * dotNL + 0.5; + vec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight ); + return irradiance; + } +#endif +#include <lightprobes_pars_fragment>`,rN=`#ifdef USE_ENVMAP + vec3 getIBLIrradiance( const in vec3 normal ) { + #ifdef ENVMAP_TYPE_CUBE_UV + vec3 worldNormal = transformNormalByInverseViewMatrix( normal, viewMatrix ); + vec4 envMapColor = textureCubeUV( envMap, envMapRotation * worldNormal, 1.0 ); + return PI * envMapColor.rgb * envMapIntensity; + #else + return vec3( 0.0 ); + #endif + } + vec3 getIBLRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness ) { + #ifdef ENVMAP_TYPE_CUBE_UV + vec3 reflectVec = reflect( - viewDir, normal ); + reflectVec = normalize( mix( reflectVec, normal, pow4( roughness ) ) ); + reflectVec = transformDirectionByInverseViewMatrix( reflectVec, viewMatrix ); + vec4 envMapColor = textureCubeUV( envMap, envMapRotation * reflectVec, roughness ); + return envMapColor.rgb * envMapIntensity; + #else + return vec3( 0.0 ); + #endif + } + #ifdef USE_ANISOTROPY + vec3 getIBLAnisotropyRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness, const in vec3 bitangent, const in float anisotropy ) { + #ifdef ENVMAP_TYPE_CUBE_UV + vec3 bentNormal = cross( bitangent, viewDir ); + bentNormal = normalize( cross( bentNormal, bitangent ) ); + bentNormal = normalize( mix( bentNormal, normal, pow2( pow2( 1.0 - anisotropy * ( 1.0 - roughness ) ) ) ) ); + return getIBLRadiance( viewDir, bentNormal, roughness ); + #else + return vec3( 0.0 ); + #endif + } + #endif +#endif`,lN=`ToonMaterial material; +material.diffuseColor = diffuseColor.rgb;`,oN=`varying vec3 vViewPosition; +struct ToonMaterial { + vec3 diffuseColor; +}; +void RE_Direct_Toon( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { + vec3 irradiance = getGradientIrradiance( geometryNormal, directLight.direction ) * directLight.color; + reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); +} +void RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { + reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); +} +#define RE_Direct RE_Direct_Toon +#define RE_IndirectDiffuse RE_IndirectDiffuse_Toon`,cN=`BlinnPhongMaterial material; +material.diffuseColor = diffuseColor.rgb; +material.specularColor = specular; +material.specularShininess = shininess; +material.specularStrength = specularStrength;`,uN=`varying vec3 vViewPosition; +struct BlinnPhongMaterial { + vec3 diffuseColor; + vec3 specularColor; + float specularShininess; + float specularStrength; +}; +void RE_Direct_BlinnPhong( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { + float dotNL = saturate( dot( geometryNormal, directLight.direction ) ); + vec3 irradiance = dotNL * directLight.color; + reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); + reflectedLight.directSpecular += irradiance * BRDF_BlinnPhong( directLight.direction, geometryViewDir, geometryNormal, material.specularColor, material.specularShininess ) * material.specularStrength; +} +void RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { + reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); +} +#define RE_Direct RE_Direct_BlinnPhong +#define RE_IndirectDiffuse RE_IndirectDiffuse_BlinnPhong`,dN=`PhysicalMaterial material; +material.diffuseColor = diffuseColor.rgb; +material.diffuseContribution = diffuseColor.rgb * ( 1.0 - metalnessFactor ); +material.metalness = metalnessFactor; +vec3 dxy = max( abs( dFdx( nonPerturbedNormal ) ), abs( dFdy( nonPerturbedNormal ) ) ); +float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z ); +material.roughness = max( roughnessFactor, 0.0525 );material.roughness += geometryRoughness; +material.roughness = min( material.roughness, 1.0 ); +#ifdef IOR + material.ior = ior; + #ifdef USE_SPECULAR + float specularIntensityFactor = specularIntensity; + vec3 specularColorFactor = specularColor; + #ifdef USE_SPECULAR_COLORMAP + specularColorFactor *= texture2D( specularColorMap, vSpecularColorMapUv ).rgb; + #endif + #ifdef USE_SPECULAR_INTENSITYMAP + specularIntensityFactor *= texture2D( specularIntensityMap, vSpecularIntensityMapUv ).a; + #endif + material.specularF90 = mix( specularIntensityFactor, 1.0, metalnessFactor ); + #else + float specularIntensityFactor = 1.0; + vec3 specularColorFactor = vec3( 1.0 ); + material.specularF90 = 1.0; + #endif + material.specularColor = min( pow2( ( material.ior - 1.0 ) / ( material.ior + 1.0 ) ) * specularColorFactor, vec3( 1.0 ) ) * specularIntensityFactor; + material.specularColorBlended = mix( material.specularColor, diffuseColor.rgb, metalnessFactor ); +#else + material.specularColor = vec3( 0.04 ); + material.specularColorBlended = mix( material.specularColor, diffuseColor.rgb, metalnessFactor ); + material.specularF90 = 1.0; +#endif +#ifdef USE_CLEARCOAT + material.clearcoat = clearcoat; + material.clearcoatRoughness = clearcoatRoughness; + material.clearcoatF0 = vec3( 0.04 ); + material.clearcoatF90 = 1.0; + #ifdef USE_CLEARCOATMAP + material.clearcoat *= texture2D( clearcoatMap, vClearcoatMapUv ).x; + #endif + #ifdef USE_CLEARCOAT_ROUGHNESSMAP + material.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vClearcoatRoughnessMapUv ).y; + #endif + material.clearcoat = saturate( material.clearcoat ); material.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 ); + material.clearcoatRoughness += geometryRoughness; + material.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 ); +#endif +#ifdef USE_DISPERSION + material.dispersion = dispersion; +#endif +#ifdef USE_IRIDESCENCE + material.iridescence = iridescence; + material.iridescenceIOR = iridescenceIOR; + #ifdef USE_IRIDESCENCEMAP + material.iridescence *= texture2D( iridescenceMap, vIridescenceMapUv ).r; + #endif + #ifdef USE_IRIDESCENCE_THICKNESSMAP + material.iridescenceThickness = (iridescenceThicknessMaximum - iridescenceThicknessMinimum) * texture2D( iridescenceThicknessMap, vIridescenceThicknessMapUv ).g + iridescenceThicknessMinimum; + #else + material.iridescenceThickness = iridescenceThicknessMaximum; + #endif +#endif +#ifdef USE_SHEEN + material.sheenColor = sheenColor; + #ifdef USE_SHEEN_COLORMAP + material.sheenColor *= texture2D( sheenColorMap, vSheenColorMapUv ).rgb; + #endif + material.sheenRoughness = clamp( sheenRoughness, 0.0001, 1.0 ); + #ifdef USE_SHEEN_ROUGHNESSMAP + material.sheenRoughness *= texture2D( sheenRoughnessMap, vSheenRoughnessMapUv ).a; + #endif +#endif +#ifdef USE_ANISOTROPY + #ifdef USE_ANISOTROPYMAP + mat2 anisotropyMat = mat2( anisotropyVector.x, anisotropyVector.y, - anisotropyVector.y, anisotropyVector.x ); + vec3 anisotropyPolar = texture2D( anisotropyMap, vAnisotropyMapUv ).rgb; + vec2 anisotropyV = anisotropyMat * normalize( 2.0 * anisotropyPolar.rg - vec2( 1.0 ) ) * anisotropyPolar.b; + #else + vec2 anisotropyV = anisotropyVector; + #endif + material.anisotropy = length( anisotropyV ); + if( material.anisotropy == 0.0 ) { + anisotropyV = vec2( 1.0, 0.0 ); + } else { + anisotropyV /= material.anisotropy; + material.anisotropy = saturate( material.anisotropy ); + } + material.alphaT = mix( pow2( material.roughness ), 1.0, pow2( material.anisotropy ) ); + material.anisotropyT = tbn[ 0 ] * anisotropyV.x + tbn[ 1 ] * anisotropyV.y; + material.anisotropyB = tbn[ 1 ] * anisotropyV.x - tbn[ 0 ] * anisotropyV.y; +#endif`,hN=`uniform sampler2D dfgLUT; +struct PhysicalMaterial { + vec3 diffuseColor; + vec3 diffuseContribution; + vec3 specularColor; + vec3 specularColorBlended; + float roughness; + float metalness; + float specularF90; + float dispersion; + #ifdef USE_CLEARCOAT + float clearcoat; + float clearcoatRoughness; + vec3 clearcoatF0; + float clearcoatF90; + #endif + #ifdef USE_IRIDESCENCE + float iridescence; + float iridescenceIOR; + float iridescenceThickness; + vec3 iridescenceFresnel; + vec3 iridescenceF0; + vec3 iridescenceFresnelDielectric; + vec3 iridescenceFresnelMetallic; + #endif + #ifdef USE_SHEEN + vec3 sheenColor; + float sheenRoughness; + #endif + #ifdef IOR + float ior; + #endif + #ifdef USE_TRANSMISSION + float transmission; + float transmissionAlpha; + float thickness; + float attenuationDistance; + vec3 attenuationColor; + #endif + #ifdef USE_ANISOTROPY + float anisotropy; + float alphaT; + vec3 anisotropyT; + vec3 anisotropyB; + #endif +}; +vec3 clearcoatSpecularDirect = vec3( 0.0 ); +vec3 clearcoatSpecularIndirect = vec3( 0.0 ); +vec3 sheenSpecularDirect = vec3( 0.0 ); +vec3 sheenSpecularIndirect = vec3(0.0 ); +vec3 Schlick_to_F0( const in vec3 f, const in float f90, const in float dotVH ) { + float x = clamp( 1.0 - dotVH, 0.0, 1.0 ); + float x2 = x * x; + float x5 = clamp( x * x2 * x2, 0.0, 0.9999 ); + return ( f - vec3( f90 ) * x5 ) / ( 1.0 - x5 ); +} +float V_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) { + float a2 = pow2( alpha ); + float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) ); + float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) ); + return 0.5 / max( gv + gl, EPSILON ); +} +float D_GGX( const in float alpha, const in float dotNH ) { + float a2 = pow2( alpha ); + float denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0; + return RECIPROCAL_PI * a2 / pow2( denom ); +} +#ifdef USE_ANISOTROPY + float V_GGX_SmithCorrelated_Anisotropic( const in float alphaT, const in float alphaB, const in float dotTV, const in float dotBV, const in float dotTL, const in float dotBL, const in float dotNV, const in float dotNL ) { + float gv = dotNL * length( vec3( alphaT * dotTV, alphaB * dotBV, dotNV ) ); + float gl = dotNV * length( vec3( alphaT * dotTL, alphaB * dotBL, dotNL ) ); + return 0.5 / max( gv + gl, EPSILON ); + } + float D_GGX_Anisotropic( const in float alphaT, const in float alphaB, const in float dotNH, const in float dotTH, const in float dotBH ) { + float a2 = alphaT * alphaB; + highp vec3 v = vec3( alphaB * dotTH, alphaT * dotBH, a2 * dotNH ); + highp float v2 = dot( v, v ); + float w2 = a2 / v2; + return RECIPROCAL_PI * a2 * pow2 ( w2 ); + } +#endif +#ifdef USE_CLEARCOAT + vec3 BRDF_GGX_Clearcoat( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material) { + vec3 f0 = material.clearcoatF0; + float f90 = material.clearcoatF90; + float roughness = material.clearcoatRoughness; + float alpha = pow2( roughness ); + vec3 halfDir = normalize( lightDir + viewDir ); + float dotNL = saturate( dot( normal, lightDir ) ); + float dotNV = saturate( dot( normal, viewDir ) ); + float dotNH = saturate( dot( normal, halfDir ) ); + float dotVH = saturate( dot( viewDir, halfDir ) ); + vec3 F = F_Schlick( f0, f90, dotVH ); + float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV ); + float D = D_GGX( alpha, dotNH ); + return F * ( V * D ); + } +#endif +vec3 BRDF_GGX( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material ) { + vec3 f0 = material.specularColorBlended; + float f90 = material.specularF90; + float roughness = material.roughness; + float alpha = pow2( roughness ); + vec3 halfDir = normalize( lightDir + viewDir ); + float dotNL = saturate( dot( normal, lightDir ) ); + float dotNV = saturate( dot( normal, viewDir ) ); + float dotNH = saturate( dot( normal, halfDir ) ); + float dotVH = saturate( dot( viewDir, halfDir ) ); + vec3 F = F_Schlick( f0, f90, dotVH ); + #ifdef USE_IRIDESCENCE + F = mix( F, material.iridescenceFresnel, material.iridescence ); + #endif + #ifdef USE_ANISOTROPY + float dotTL = dot( material.anisotropyT, lightDir ); + float dotTV = dot( material.anisotropyT, viewDir ); + float dotTH = dot( material.anisotropyT, halfDir ); + float dotBL = dot( material.anisotropyB, lightDir ); + float dotBV = dot( material.anisotropyB, viewDir ); + float dotBH = dot( material.anisotropyB, halfDir ); + float V = V_GGX_SmithCorrelated_Anisotropic( material.alphaT, alpha, dotTV, dotBV, dotTL, dotBL, dotNV, dotNL ); + float D = D_GGX_Anisotropic( material.alphaT, alpha, dotNH, dotTH, dotBH ); + #else + float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV ); + float D = D_GGX( alpha, dotNH ); + #endif + return F * ( V * D ); +} +vec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) { + const float LUT_SIZE = 64.0; + const float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE; + const float LUT_BIAS = 0.5 / LUT_SIZE; + float dotNV = saturate( dot( N, V ) ); + vec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) ); + uv = uv * LUT_SCALE + LUT_BIAS; + return uv; +} +float LTC_ClippedSphereFormFactor( const in vec3 f ) { + float l = length( f ); + return max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 ); +} +vec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) { + float x = dot( v1, v2 ); + float y = abs( x ); + float a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y; + float b = 3.4175940 + ( 4.1616724 + y ) * y; + float v = a / b; + float theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v; + return cross( v1, v2 ) * theta_sintheta; +} +vec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) { + vec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ]; + vec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ]; + vec3 lightNormal = cross( v1, v2 ); + if( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 ); + vec3 T1, T2; + T1 = normalize( V - N * dot( V, N ) ); + T2 = - cross( N, T1 ); + mat3 mat = mInv * transpose( mat3( T1, T2, N ) ); + vec3 coords[ 4 ]; + coords[ 0 ] = mat * ( rectCoords[ 0 ] - P ); + coords[ 1 ] = mat * ( rectCoords[ 1 ] - P ); + coords[ 2 ] = mat * ( rectCoords[ 2 ] - P ); + coords[ 3 ] = mat * ( rectCoords[ 3 ] - P ); + coords[ 0 ] = normalize( coords[ 0 ] ); + coords[ 1 ] = normalize( coords[ 1 ] ); + coords[ 2 ] = normalize( coords[ 2 ] ); + coords[ 3 ] = normalize( coords[ 3 ] ); + vec3 vectorFormFactor = vec3( 0.0 ); + vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] ); + vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] ); + vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] ); + vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] ); + float result = LTC_ClippedSphereFormFactor( vectorFormFactor ); + return vec3( result ); +} +#if defined( USE_SHEEN ) +float D_Charlie( float roughness, float dotNH ) { + float alpha = pow2( roughness ); + float invAlpha = 1.0 / alpha; + float cos2h = dotNH * dotNH; + float sin2h = max( 1.0 - cos2h, 0.0078125 ); + return ( 2.0 + invAlpha ) * pow( sin2h, invAlpha * 0.5 ) / ( 2.0 * PI ); +} +float V_Neubelt( float dotNV, float dotNL ) { + return saturate( 1.0 / ( 4.0 * ( dotNL + dotNV - dotNL * dotNV ) ) ); +} +vec3 BRDF_Sheen( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, vec3 sheenColor, const in float sheenRoughness ) { + vec3 halfDir = normalize( lightDir + viewDir ); + float dotNL = saturate( dot( normal, lightDir ) ); + float dotNV = saturate( dot( normal, viewDir ) ); + float dotNH = saturate( dot( normal, halfDir ) ); + float D = D_Charlie( sheenRoughness, dotNH ); + float V = V_Neubelt( dotNV, dotNL ); + return sheenColor * ( D * V ); +} +#endif +float IBLSheenBRDF( const in vec3 normal, const in vec3 viewDir, const in float roughness ) { + float dotNV = saturate( dot( normal, viewDir ) ); + float r2 = roughness * roughness; + float rInv = 1.0 / ( roughness + 0.1 ); + float a = -1.9362 + 1.0678 * roughness + 0.4573 * r2 - 0.8469 * rInv; + float b = -0.6014 + 0.5538 * roughness - 0.4670 * r2 - 0.1255 * rInv; + float DG = exp( a * dotNV + b ); + return saturate( DG ); +} +vec3 EnvironmentBRDF( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness ) { + float dotNV = saturate( dot( normal, viewDir ) ); + vec2 fab = texture2D( dfgLUT, vec2( roughness, dotNV ) ).rg; + return specularColor * fab.x + specularF90 * fab.y; +} +#ifdef USE_IRIDESCENCE +void computeMultiscatteringIridescence( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float iridescence, const in vec3 iridescenceF0, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) { +#else +void computeMultiscattering( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) { +#endif + float dotNV = saturate( dot( normal, viewDir ) ); + vec2 fab = texture2D( dfgLUT, vec2( roughness, dotNV ) ).rg; + #ifdef USE_IRIDESCENCE + vec3 Fr = mix( specularColor, iridescenceF0, iridescence ); + #else + vec3 Fr = specularColor; + #endif + vec3 FssEss = Fr * fab.x + specularF90 * fab.y; + float Ess = fab.x + fab.y; + float Ems = 1.0 - Ess; + vec3 Favg = Fr + ( 1.0 - Fr ) * 0.047619; vec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg ); + singleScatter += FssEss; + multiScatter += Fms * Ems; +} +vec3 BRDF_GGX_Multiscatter( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material ) { + vec3 singleScatter = BRDF_GGX( lightDir, viewDir, normal, material ); + float dotNL = saturate( dot( normal, lightDir ) ); + float dotNV = saturate( dot( normal, viewDir ) ); + vec2 dfgV = texture2D( dfgLUT, vec2( material.roughness, dotNV ) ).rg; + vec2 dfgL = texture2D( dfgLUT, vec2( material.roughness, dotNL ) ).rg; + vec3 FssEss_V = material.specularColorBlended * dfgV.x + material.specularF90 * dfgV.y; + vec3 FssEss_L = material.specularColorBlended * dfgL.x + material.specularF90 * dfgL.y; + float Ess_V = dfgV.x + dfgV.y; + float Ess_L = dfgL.x + dfgL.y; + float Ems_V = 1.0 - Ess_V; + float Ems_L = 1.0 - Ess_L; + vec3 Favg = material.specularColorBlended + ( 1.0 - material.specularColorBlended ) * 0.047619; + vec3 Fms = FssEss_V * FssEss_L * Favg / ( 1.0 - Ems_V * Ems_L * Favg + EPSILON ); + float compensationFactor = Ems_V * Ems_L; + vec3 multiScatter = Fms * compensationFactor; + return singleScatter + multiScatter; +} +#if NUM_RECT_AREA_LIGHTS > 0 + void RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { + vec3 normal = geometryNormal; + vec3 viewDir = geometryViewDir; + vec3 position = geometryPosition; + vec3 lightPos = rectAreaLight.position; + vec3 halfWidth = rectAreaLight.halfWidth; + vec3 halfHeight = rectAreaLight.halfHeight; + vec3 lightColor = rectAreaLight.color; + float roughness = material.roughness; + vec3 rectCoords[ 4 ]; + rectCoords[ 0 ] = lightPos + halfWidth - halfHeight; rectCoords[ 1 ] = lightPos - halfWidth - halfHeight; + rectCoords[ 2 ] = lightPos - halfWidth + halfHeight; + rectCoords[ 3 ] = lightPos + halfWidth + halfHeight; + vec2 uv = LTC_Uv( normal, viewDir, roughness ); + vec4 t1 = texture2D( ltc_1, uv ); + vec4 t2 = texture2D( ltc_2, uv ); + mat3 mInv = mat3( + vec3( t1.x, 0, t1.y ), + vec3( 0, 1, 0 ), + vec3( t1.z, 0, t1.w ) + ); + vec3 fresnel = ( material.specularColorBlended * t2.x + ( material.specularF90 - material.specularColorBlended ) * t2.y ); + reflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords ); + reflectedLight.directDiffuse += lightColor * material.diffuseContribution * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords ); + #ifdef USE_CLEARCOAT + vec3 Ncc = geometryClearcoatNormal; + vec2 uvClearcoat = LTC_Uv( Ncc, viewDir, material.clearcoatRoughness ); + vec4 t1Clearcoat = texture2D( ltc_1, uvClearcoat ); + vec4 t2Clearcoat = texture2D( ltc_2, uvClearcoat ); + mat3 mInvClearcoat = mat3( + vec3( t1Clearcoat.x, 0, t1Clearcoat.y ), + vec3( 0, 1, 0 ), + vec3( t1Clearcoat.z, 0, t1Clearcoat.w ) + ); + vec3 fresnelClearcoat = material.clearcoatF0 * t2Clearcoat.x + ( material.clearcoatF90 - material.clearcoatF0 ) * t2Clearcoat.y; + clearcoatSpecularDirect += lightColor * fresnelClearcoat * LTC_Evaluate( Ncc, viewDir, position, mInvClearcoat, rectCoords ); + #endif + } +#endif +void RE_Direct_Physical( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { + float dotNL = saturate( dot( geometryNormal, directLight.direction ) ); + vec3 irradiance = dotNL * directLight.color; + #ifdef USE_CLEARCOAT + float dotNLcc = saturate( dot( geometryClearcoatNormal, directLight.direction ) ); + vec3 ccIrradiance = dotNLcc * directLight.color; + clearcoatSpecularDirect += ccIrradiance * BRDF_GGX_Clearcoat( directLight.direction, geometryViewDir, geometryClearcoatNormal, material ); + #endif + #ifdef USE_SHEEN + + sheenSpecularDirect += irradiance * BRDF_Sheen( directLight.direction, geometryViewDir, geometryNormal, material.sheenColor, material.sheenRoughness ); + + float sheenAlbedoV = IBLSheenBRDF( geometryNormal, geometryViewDir, material.sheenRoughness ); + float sheenAlbedoL = IBLSheenBRDF( geometryNormal, directLight.direction, material.sheenRoughness ); + + float sheenEnergyComp = 1.0 - max3( material.sheenColor ) * max( sheenAlbedoV, sheenAlbedoL ); + + irradiance *= sheenEnergyComp; + + #endif + reflectedLight.directSpecular += irradiance * BRDF_GGX_Multiscatter( directLight.direction, geometryViewDir, geometryNormal, material ); + reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseContribution ); +} +void RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { + vec3 diffuse = irradiance * BRDF_Lambert( material.diffuseContribution ); + #ifdef USE_SHEEN + float sheenAlbedo = IBLSheenBRDF( geometryNormal, geometryViewDir, material.sheenRoughness ); + float sheenEnergyComp = 1.0 - max3( material.sheenColor ) * sheenAlbedo; + diffuse *= sheenEnergyComp; + #endif + reflectedLight.indirectDiffuse += diffuse; +} +void RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearcoatRadiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) { + #ifdef USE_CLEARCOAT + clearcoatSpecularIndirect += clearcoatRadiance * EnvironmentBRDF( geometryClearcoatNormal, geometryViewDir, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness ); + #endif + #ifdef USE_SHEEN + sheenSpecularIndirect += irradiance * material.sheenColor * IBLSheenBRDF( geometryNormal, geometryViewDir, material.sheenRoughness ) * RECIPROCAL_PI; + #endif + vec3 singleScatteringDielectric = vec3( 0.0 ); + vec3 multiScatteringDielectric = vec3( 0.0 ); + vec3 singleScatteringMetallic = vec3( 0.0 ); + vec3 multiScatteringMetallic = vec3( 0.0 ); + #ifdef USE_IRIDESCENCE + computeMultiscatteringIridescence( geometryNormal, geometryViewDir, material.specularColor, material.specularF90, material.iridescence, material.iridescenceFresnelDielectric, material.roughness, singleScatteringDielectric, multiScatteringDielectric ); + computeMultiscatteringIridescence( geometryNormal, geometryViewDir, material.diffuseColor, material.specularF90, material.iridescence, material.iridescenceFresnelMetallic, material.roughness, singleScatteringMetallic, multiScatteringMetallic ); + #else + computeMultiscattering( geometryNormal, geometryViewDir, material.specularColor, material.specularF90, material.roughness, singleScatteringDielectric, multiScatteringDielectric ); + computeMultiscattering( geometryNormal, geometryViewDir, material.diffuseColor, material.specularF90, material.roughness, singleScatteringMetallic, multiScatteringMetallic ); + #endif + vec3 singleScattering = mix( singleScatteringDielectric, singleScatteringMetallic, material.metalness ); + vec3 multiScattering = mix( multiScatteringDielectric, multiScatteringMetallic, material.metalness ); + vec3 totalScatteringDielectric = singleScatteringDielectric + multiScatteringDielectric; + vec3 diffuse = material.diffuseContribution * ( 1.0 - totalScatteringDielectric ); + vec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI; + vec3 indirectSpecular = radiance * singleScattering; + indirectSpecular += multiScattering * cosineWeightedIrradiance; + vec3 indirectDiffuse = diffuse * cosineWeightedIrradiance; + #ifdef USE_SHEEN + float sheenAlbedo = IBLSheenBRDF( geometryNormal, geometryViewDir, material.sheenRoughness ); + float sheenEnergyComp = 1.0 - max3( material.sheenColor ) * sheenAlbedo; + indirectSpecular *= sheenEnergyComp; + indirectDiffuse *= sheenEnergyComp; + #endif + reflectedLight.indirectSpecular += indirectSpecular; + reflectedLight.indirectDiffuse += indirectDiffuse; +} +#define RE_Direct RE_Direct_Physical +#define RE_Direct_RectArea RE_Direct_RectArea_Physical +#define RE_IndirectDiffuse RE_IndirectDiffuse_Physical +#define RE_IndirectSpecular RE_IndirectSpecular_Physical +float computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) { + return saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion ); +}`,fN=` +vec3 geometryPosition = - vViewPosition; +vec3 geometryNormal = normal; +vec3 geometryViewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition ); +vec3 geometryClearcoatNormal = vec3( 0.0 ); +#ifdef USE_CLEARCOAT + geometryClearcoatNormal = clearcoatNormal; +#endif +#ifdef USE_IRIDESCENCE + float dotNVi = saturate( dot( normal, geometryViewDir ) ); + if ( material.iridescenceThickness == 0.0 ) { + material.iridescence = 0.0; + } else { + material.iridescence = saturate( material.iridescence ); + } + if ( material.iridescence > 0.0 ) { + material.iridescenceFresnelDielectric = evalIridescence( 1.0, material.iridescenceIOR, dotNVi, material.iridescenceThickness, material.specularColor ); + material.iridescenceFresnelMetallic = evalIridescence( 1.0, material.iridescenceIOR, dotNVi, material.iridescenceThickness, material.diffuseColor ); + material.iridescenceFresnel = mix( material.iridescenceFresnelDielectric, material.iridescenceFresnelMetallic, material.metalness ); + material.iridescenceF0 = Schlick_to_F0( material.iridescenceFresnel, 1.0, dotNVi ); + } +#endif +IncidentLight directLight; +#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct ) + PointLight pointLight; + #if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0 + PointLightShadow pointLightShadow; + #endif + #pragma unroll_loop_start + for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { + pointLight = pointLights[ i ]; + getPointLightInfo( pointLight, geometryPosition, directLight ); + #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS ) && ( defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_BASIC ) ) + pointLightShadow = pointLightShadows[ i ]; + directLight.color *= ( directLight.visible && receiveShadow ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowIntensity, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0; + #endif + RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); + } + #pragma unroll_loop_end +#endif +#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct ) + SpotLight spotLight; + vec4 spotColor; + vec3 spotLightCoord; + bool inSpotLightMap; + #if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0 + SpotLightShadow spotLightShadow; + #endif + #pragma unroll_loop_start + for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { + spotLight = spotLights[ i ]; + getSpotLightInfo( spotLight, geometryPosition, directLight ); + #if ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS ) + #define SPOT_LIGHT_MAP_INDEX UNROLLED_LOOP_INDEX + #elif ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) + #define SPOT_LIGHT_MAP_INDEX NUM_SPOT_LIGHT_MAPS + #else + #define SPOT_LIGHT_MAP_INDEX ( UNROLLED_LOOP_INDEX - NUM_SPOT_LIGHT_SHADOWS + NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS ) + #endif + #if ( SPOT_LIGHT_MAP_INDEX < NUM_SPOT_LIGHT_MAPS ) + spotLightCoord = vSpotLightCoord[ i ].xyz / vSpotLightCoord[ i ].w; + inSpotLightMap = all( lessThan( abs( spotLightCoord * 2. - 1. ), vec3( 1.0 ) ) ); + spotColor = texture2D( spotLightMap[ SPOT_LIGHT_MAP_INDEX ], spotLightCoord.xy ); + directLight.color = inSpotLightMap ? directLight.color * spotColor.rgb : directLight.color; + #endif + #undef SPOT_LIGHT_MAP_INDEX + #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) + spotLightShadow = spotLightShadows[ i ]; + directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowIntensity, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotLightCoord[ i ] ) : 1.0; + #endif + RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); + } + #pragma unroll_loop_end +#endif +#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct ) + DirectionalLight directionalLight; + #if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0 + DirectionalLightShadow directionalLightShadow; + #endif + #pragma unroll_loop_start + for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { + directionalLight = directionalLights[ i ]; + getDirectionalLightInfo( directionalLight, directLight ); + #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS ) + directionalLightShadow = directionalLightShadows[ i ]; + directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowIntensity, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; + #endif + RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); + } + #pragma unroll_loop_end +#endif +#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea ) + RectAreaLight rectAreaLight; + #pragma unroll_loop_start + for ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) { + rectAreaLight = rectAreaLights[ i ]; + RE_Direct_RectArea( rectAreaLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); + } + #pragma unroll_loop_end +#endif +#if defined( RE_IndirectDiffuse ) + vec3 iblIrradiance = vec3( 0.0 ); + vec3 irradiance = getAmbientLightIrradiance( ambientLightColor ); + #if defined( USE_LIGHT_PROBES ) + irradiance += getLightProbeIrradiance( lightProbe, geometryNormal ); + #endif + #if ( NUM_HEMI_LIGHTS > 0 ) + #pragma unroll_loop_start + for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { + irradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometryNormal ); + } + #pragma unroll_loop_end + #endif + #ifdef USE_LIGHT_PROBES_GRID + vec3 probeWorldPos = ( ( vec4( geometryPosition, 1.0 ) - viewMatrix[ 3 ] ) * viewMatrix ).xyz; + vec3 probeWorldNormal = transformNormalByInverseViewMatrix( geometryNormal, viewMatrix ); + irradiance += getLightProbeGridIrradiance( probeWorldPos, probeWorldNormal ); + #endif +#endif +#if defined( RE_IndirectSpecular ) + vec3 radiance = vec3( 0.0 ); + vec3 clearcoatRadiance = vec3( 0.0 ); +#endif`,pN=`#if defined( RE_IndirectDiffuse ) + #ifdef USE_LIGHTMAP + vec4 lightMapTexel = texture2D( lightMap, vLightMapUv ); + vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity; + irradiance += lightMapIrradiance; + #endif + #if defined( USE_ENVMAP ) && defined( ENVMAP_TYPE_CUBE_UV ) + #if defined( STANDARD ) || defined( LAMBERT ) || defined( PHONG ) + iblIrradiance += getIBLIrradiance( geometryNormal ); + #endif + #endif +#endif +#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular ) + #ifdef USE_ANISOTROPY + radiance += getIBLAnisotropyRadiance( geometryViewDir, geometryNormal, material.roughness, material.anisotropyB, material.anisotropy ); + #else + radiance += getIBLRadiance( geometryViewDir, geometryNormal, material.roughness ); + #endif + #ifdef USE_CLEARCOAT + clearcoatRadiance += getIBLRadiance( geometryViewDir, geometryClearcoatNormal, material.clearcoatRoughness ); + #endif +#endif`,mN=`#if defined( RE_IndirectDiffuse ) + #if defined( LAMBERT ) || defined( PHONG ) + irradiance += iblIrradiance; + #endif + RE_IndirectDiffuse( irradiance, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); +#endif +#if defined( RE_IndirectSpecular ) + RE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); +#endif`,gN=`#ifdef USE_LIGHT_PROBES_GRID +uniform highp sampler3D probesSH; +uniform vec3 probesMin; +uniform vec3 probesMax; +uniform vec3 probesResolution; +vec3 getLightProbeGridIrradiance( vec3 worldPos, vec3 worldNormal ) { + vec3 res = probesResolution; + vec3 gridRange = probesMax - probesMin; + vec3 resMinusOne = res - 1.0; + vec3 probeSpacing = gridRange / resMinusOne; + vec3 samplePos = worldPos + worldNormal * probeSpacing * 0.5; + vec3 uvw = clamp( ( samplePos - probesMin ) / gridRange, 0.0, 1.0 ); + uvw = uvw * resMinusOne / res + 0.5 / res; + float nz = res.z; + float paddedSlices = nz + 2.0; + float atlasDepth = 7.0 * paddedSlices; + float uvZBase = uvw.z * nz + 1.0; + vec4 s0 = texture( probesSH, vec3( uvw.xy, ( uvZBase ) / atlasDepth ) ); + vec4 s1 = texture( probesSH, vec3( uvw.xy, ( uvZBase + paddedSlices ) / atlasDepth ) ); + vec4 s2 = texture( probesSH, vec3( uvw.xy, ( uvZBase + 2.0 * paddedSlices ) / atlasDepth ) ); + vec4 s3 = texture( probesSH, vec3( uvw.xy, ( uvZBase + 3.0 * paddedSlices ) / atlasDepth ) ); + vec4 s4 = texture( probesSH, vec3( uvw.xy, ( uvZBase + 4.0 * paddedSlices ) / atlasDepth ) ); + vec4 s5 = texture( probesSH, vec3( uvw.xy, ( uvZBase + 5.0 * paddedSlices ) / atlasDepth ) ); + vec4 s6 = texture( probesSH, vec3( uvw.xy, ( uvZBase + 6.0 * paddedSlices ) / atlasDepth ) ); + vec3 c0 = s0.xyz; + vec3 c1 = vec3( s0.w, s1.xy ); + vec3 c2 = vec3( s1.zw, s2.x ); + vec3 c3 = s2.yzw; + vec3 c4 = s3.xyz; + vec3 c5 = vec3( s3.w, s4.xy ); + vec3 c6 = vec3( s4.zw, s5.x ); + vec3 c7 = s5.yzw; + vec3 c8 = s6.xyz; + float x = worldNormal.x, y = worldNormal.y, z = worldNormal.z; + vec3 result = c0 * 0.886227; + result += c1 * 2.0 * 0.511664 * y; + result += c2 * 2.0 * 0.511664 * z; + result += c3 * 2.0 * 0.511664 * x; + result += c4 * 2.0 * 0.429043 * x * y; + result += c5 * 2.0 * 0.429043 * y * z; + result += c6 * ( 0.743125 * z * z - 0.247708 ); + result += c7 * 2.0 * 0.429043 * x * z; + result += c8 * 0.429043 * ( x * x - y * y ); + return max( result, vec3( 0.0 ) ); +} +#endif`,xN=`#if defined( USE_LOGARITHMIC_DEPTH_BUFFER ) + gl_FragDepth = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5; +#endif`,vN=`#if defined( USE_LOGARITHMIC_DEPTH_BUFFER ) + uniform float logDepthBufFC; + varying float vFragDepth; + varying float vIsPerspective; +#endif`,_N=`#ifdef USE_LOGARITHMIC_DEPTH_BUFFER + varying float vFragDepth; + varying float vIsPerspective; +#endif`,bN=`#ifdef USE_LOGARITHMIC_DEPTH_BUFFER + vFragDepth = 1.0 + gl_Position.w; + vIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) ); +#endif`,yN=`#ifdef USE_MAP + vec4 sampledDiffuseColor = texture2D( map, vMapUv ); + #ifdef DECODE_VIDEO_TEXTURE + sampledDiffuseColor = sRGBTransferEOTF( sampledDiffuseColor ); + #endif + diffuseColor *= sampledDiffuseColor; +#endif`,SN=`#ifdef USE_MAP + uniform sampler2D map; +#endif`,MN=`#if defined( USE_MAP ) || defined( USE_ALPHAMAP ) + #if defined( USE_POINTS_UV ) + vec2 uv = vUv; + #else + vec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy; + #endif +#endif +#ifdef USE_MAP + diffuseColor *= texture2D( map, uv ); +#endif +#ifdef USE_ALPHAMAP + diffuseColor.a *= texture2D( alphaMap, uv ).g; +#endif`,EN=`#if defined( USE_POINTS_UV ) + varying vec2 vUv; +#else + #if defined( USE_MAP ) || defined( USE_ALPHAMAP ) + uniform mat3 uvTransform; + #endif +#endif +#ifdef USE_MAP + uniform sampler2D map; +#endif +#ifdef USE_ALPHAMAP + uniform sampler2D alphaMap; +#endif`,TN=`float metalnessFactor = metalness; +#ifdef USE_METALNESSMAP + vec4 texelMetalness = texture2D( metalnessMap, vMetalnessMapUv ); + metalnessFactor *= texelMetalness.b; +#endif`,AN=`#ifdef USE_METALNESSMAP + uniform sampler2D metalnessMap; +#endif`,wN=`#ifdef USE_INSTANCING_MORPH + float morphTargetInfluences[ MORPHTARGETS_COUNT ]; + float morphTargetBaseInfluence = texelFetch( morphTexture, ivec2( 0, gl_InstanceID ), 0 ).r; + for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { + morphTargetInfluences[i] = texelFetch( morphTexture, ivec2( i + 1, gl_InstanceID ), 0 ).r; + } +#endif`,NN=`#if defined( USE_MORPHCOLORS ) + vColor *= morphTargetBaseInfluence; + for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { + #if defined( USE_COLOR_ALPHA ) + if ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ) * morphTargetInfluences[ i ]; + #elif defined( USE_COLOR ) + if ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ).rgb * morphTargetInfluences[ i ]; + #endif + } +#endif`,CN=`#ifdef USE_MORPHNORMALS + objectNormal *= morphTargetBaseInfluence; + for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { + if ( morphTargetInfluences[ i ] != 0.0 ) objectNormal += getMorph( gl_VertexID, i, 1 ).xyz * morphTargetInfluences[ i ]; + } +#endif`,RN=`#ifdef USE_MORPHTARGETS + #ifndef USE_INSTANCING_MORPH + uniform float morphTargetBaseInfluence; + uniform float morphTargetInfluences[ MORPHTARGETS_COUNT ]; + #endif + uniform sampler2DArray morphTargetsTexture; + uniform ivec2 morphTargetsTextureSize; + vec4 getMorph( const in int vertexIndex, const in int morphTargetIndex, const in int offset ) { + int texelIndex = vertexIndex * MORPHTARGETS_TEXTURE_STRIDE + offset; + int y = texelIndex / morphTargetsTextureSize.x; + int x = texelIndex - y * morphTargetsTextureSize.x; + ivec3 morphUV = ivec3( x, y, morphTargetIndex ); + return texelFetch( morphTargetsTexture, morphUV, 0 ); + } +#endif`,DN=`#ifdef USE_MORPHTARGETS + transformed *= morphTargetBaseInfluence; + for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { + if ( morphTargetInfluences[ i ] != 0.0 ) transformed += getMorph( gl_VertexID, i, 0 ).xyz * morphTargetInfluences[ i ]; + } +#endif`,LN=`float faceDirection = gl_FrontFacing ? 1.0 : - 1.0; +#ifdef FLAT_SHADED + vec3 fdx = dFdx( vViewPosition ); + vec3 fdy = dFdy( vViewPosition ); + vec3 normal = normalize( cross( fdx, fdy ) ); +#else + vec3 normal = normalize( vNormal ); + #ifdef DOUBLE_SIDED + normal *= faceDirection; + #endif +#endif +#if defined( USE_NORMALMAP_TANGENTSPACE ) || defined( USE_CLEARCOAT_NORMALMAP ) || defined( USE_ANISOTROPY ) + #ifdef USE_TANGENT + mat3 tbn = mat3( normalize( vTangent ), normalize( vBitangent ), normal ); + #else + mat3 tbn = getTangentFrame( - vViewPosition, normal, + #if defined( USE_NORMALMAP ) + vNormalMapUv + #elif defined( USE_CLEARCOAT_NORMALMAP ) + vClearcoatNormalMapUv + #else + vUv + #endif + ); + #endif + #ifdef DOUBLE_SIDED + tbn[0] *= faceDirection; + tbn[1] *= faceDirection; + #endif +#endif +#ifdef USE_CLEARCOAT_NORMALMAP + #ifdef USE_TANGENT + mat3 tbn2 = mat3( normalize( vTangent ), normalize( vBitangent ), normal ); + #else + mat3 tbn2 = getTangentFrame( - vViewPosition, normal, vClearcoatNormalMapUv ); + #endif + #ifdef DOUBLE_SIDED + tbn2[0] *= faceDirection; + tbn2[1] *= faceDirection; + #endif +#endif +vec3 nonPerturbedNormal = normal;`,UN=`#ifdef USE_NORMALMAP_OBJECTSPACE + normal = texture2D( normalMap, vNormalMapUv ).xyz * 2.0 - 1.0; + #ifdef FLIP_SIDED + normal = - normal; + #endif + #ifdef DOUBLE_SIDED + normal = normal * faceDirection; + #endif + normal = normalize( normalMatrix * normal ); +#elif defined( USE_NORMALMAP_TANGENTSPACE ) + vec3 mapN = texture2D( normalMap, vNormalMapUv ).xyz * 2.0 - 1.0; + #if defined( USE_PACKED_NORMALMAP ) + mapN = vec3( mapN.xy, sqrt( saturate( 1.0 - dot( mapN.xy, mapN.xy ) ) ) ); + #endif + mapN.xy *= normalScale; + normal = normalize( tbn * mapN ); +#elif defined( USE_BUMPMAP ) + normal = perturbNormalArb( - vViewPosition, normal, dHdxy_fwd(), faceDirection ); +#endif`,ON=`#ifndef FLAT_SHADED + varying vec3 vNormal; + #ifdef USE_TANGENT + varying vec3 vTangent; + varying vec3 vBitangent; + #endif +#endif`,PN=`#ifndef FLAT_SHADED + varying vec3 vNormal; + #ifdef USE_TANGENT + varying vec3 vTangent; + varying vec3 vBitangent; + #endif +#endif`,zN=`#ifndef FLAT_SHADED + vNormal = normalize( transformedNormal ); + #ifdef USE_TANGENT + vTangent = normalize( transformedTangent ); + vBitangent = normalize( cross( vNormal, vTangent ) * tangent.w ); + #ifdef FLIP_SIDED + vBitangent = - vBitangent; + #endif + #endif +#endif`,jN=`#ifdef USE_NORMALMAP + uniform sampler2D normalMap; + uniform vec2 normalScale; +#endif +#ifdef USE_NORMALMAP_OBJECTSPACE + uniform mat3 normalMatrix; +#endif +#if ! defined ( USE_TANGENT ) && ( defined ( USE_NORMALMAP_TANGENTSPACE ) || defined ( USE_CLEARCOAT_NORMALMAP ) || defined( USE_ANISOTROPY ) ) + mat3 getTangentFrame( vec3 eye_pos, vec3 surf_norm, vec2 uv ) { + vec3 q0 = dFdx( eye_pos.xyz ); + vec3 q1 = dFdy( eye_pos.xyz ); + vec2 st0 = dFdx( uv.st ); + vec2 st1 = dFdy( uv.st ); + vec3 N = surf_norm; + vec3 q1perp = cross( q1, N ); + vec3 q0perp = cross( N, q0 ); + vec3 T = q1perp * st0.x + q0perp * st1.x; + vec3 B = q1perp * st0.y + q0perp * st1.y; + float det = max( dot( T, T ), dot( B, B ) ); + float scale = ( det == 0.0 ) ? 0.0 : inversesqrt( det ); + return mat3( T * scale, B * scale, N ); + } +#endif`,kN=`#ifdef USE_CLEARCOAT + vec3 clearcoatNormal = nonPerturbedNormal; +#endif`,IN=`#ifdef USE_CLEARCOAT_NORMALMAP + vec3 clearcoatMapN = texture2D( clearcoatNormalMap, vClearcoatNormalMapUv ).xyz * 2.0 - 1.0; + clearcoatMapN.xy *= clearcoatNormalScale; + clearcoatNormal = normalize( tbn2 * clearcoatMapN ); +#endif`,BN=`#ifdef USE_CLEARCOATMAP + uniform sampler2D clearcoatMap; +#endif +#ifdef USE_CLEARCOAT_NORMALMAP + uniform sampler2D clearcoatNormalMap; + uniform vec2 clearcoatNormalScale; +#endif +#ifdef USE_CLEARCOAT_ROUGHNESSMAP + uniform sampler2D clearcoatRoughnessMap; +#endif`,FN=`#ifdef USE_IRIDESCENCEMAP + uniform sampler2D iridescenceMap; +#endif +#ifdef USE_IRIDESCENCE_THICKNESSMAP + uniform sampler2D iridescenceThicknessMap; +#endif`,HN=`#ifdef OPAQUE +diffuseColor.a = 1.0; +#endif +#ifdef USE_TRANSMISSION +diffuseColor.a *= material.transmissionAlpha; +#endif +gl_FragColor = vec4( outgoingLight, diffuseColor.a );`,GN=`vec3 packNormalToRGB( const in vec3 normal ) { + return normalize( normal ) * 0.5 + 0.5; +} +vec3 unpackRGBToNormal( const in vec3 rgb ) { + return 2.0 * rgb.xyz - 1.0; +} +const float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;const float ShiftRight8 = 1. / 256.; +const float Inv255 = 1. / 255.; +const vec4 PackFactors = vec4( 1.0, 256.0, 256.0 * 256.0, 256.0 * 256.0 * 256.0 ); +const vec2 UnpackFactors2 = vec2( UnpackDownscale, 1.0 / PackFactors.g ); +const vec3 UnpackFactors3 = vec3( UnpackDownscale / PackFactors.rg, 1.0 / PackFactors.b ); +const vec4 UnpackFactors4 = vec4( UnpackDownscale / PackFactors.rgb, 1.0 / PackFactors.a ); +vec4 packDepthToRGBA( const in float v ) { + if( v <= 0.0 ) + return vec4( 0., 0., 0., 0. ); + if( v >= 1.0 ) + return vec4( 1., 1., 1., 1. ); + float vuf; + float af = modf( v * PackFactors.a, vuf ); + float bf = modf( vuf * ShiftRight8, vuf ); + float gf = modf( vuf * ShiftRight8, vuf ); + return vec4( vuf * Inv255, gf * PackUpscale, bf * PackUpscale, af ); +} +vec3 packDepthToRGB( const in float v ) { + if( v <= 0.0 ) + return vec3( 0., 0., 0. ); + if( v >= 1.0 ) + return vec3( 1., 1., 1. ); + float vuf; + float bf = modf( v * PackFactors.b, vuf ); + float gf = modf( vuf * ShiftRight8, vuf ); + return vec3( vuf * Inv255, gf * PackUpscale, bf ); +} +vec2 packDepthToRG( const in float v ) { + if( v <= 0.0 ) + return vec2( 0., 0. ); + if( v >= 1.0 ) + return vec2( 1., 1. ); + float vuf; + float gf = modf( v * 256., vuf ); + return vec2( vuf * Inv255, gf ); +} +float unpackRGBAToDepth( const in vec4 v ) { + return dot( v, UnpackFactors4 ); +} +float unpackRGBToDepth( const in vec3 v ) { + return dot( v, UnpackFactors3 ); +} +float unpackRGToDepth( const in vec2 v ) { + return v.r * UnpackFactors2.r + v.g * UnpackFactors2.g; +} +vec4 pack2HalfToRGBA( const in vec2 v ) { + vec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ) ); + return vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w ); +} +vec2 unpackRGBATo2Half( const in vec4 v ) { + return vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) ); +} +float viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) { + return ( viewZ + near ) / ( near - far ); +} +float orthographicDepthToViewZ( const in float depth, const in float near, const in float far ) { + #ifdef USE_REVERSED_DEPTH_BUFFER + + return depth * ( far - near ) - far; + #else + return depth * ( near - far ) - near; + #endif +} +float viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) { + return ( ( near + viewZ ) * far ) / ( ( far - near ) * viewZ ); +} +float perspectiveDepthToViewZ( const in float depth, const in float near, const in float far ) { + + #ifdef USE_REVERSED_DEPTH_BUFFER + return ( near * far ) / ( ( near - far ) * depth - near ); + #else + return ( near * far ) / ( ( far - near ) * depth - far ); + #endif +}`,VN=`#ifdef PREMULTIPLIED_ALPHA + gl_FragColor.rgb *= gl_FragColor.a; +#endif`,WN=`vec4 mvPosition = vec4( transformed, 1.0 ); +#ifdef USE_BATCHING + mvPosition = batchingMatrix * mvPosition; +#endif +#ifdef USE_INSTANCING + mvPosition = instanceMatrix * mvPosition; +#endif +mvPosition = modelViewMatrix * mvPosition; +gl_Position = projectionMatrix * mvPosition;`,XN=`#ifdef DITHERING + gl_FragColor.rgb = dithering( gl_FragColor.rgb ); +#endif`,qN=`#ifdef DITHERING + vec3 dithering( vec3 color ) { + float grid_position = rand( gl_FragCoord.xy ); + vec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 ); + dither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position ); + return color + dither_shift_RGB; + } +#endif`,YN=`float roughnessFactor = roughness; +#ifdef USE_ROUGHNESSMAP + vec4 texelRoughness = texture2D( roughnessMap, vRoughnessMapUv ); + roughnessFactor *= texelRoughness.g; +#endif`,$N=`#ifdef USE_ROUGHNESSMAP + uniform sampler2D roughnessMap; +#endif`,KN=`#if NUM_SPOT_LIGHT_COORDS > 0 + varying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ]; +#endif +#if NUM_SPOT_LIGHT_MAPS > 0 + uniform sampler2D spotLightMap[ NUM_SPOT_LIGHT_MAPS ]; +#endif +#ifdef USE_SHADOWMAP + #if NUM_DIR_LIGHT_SHADOWS > 0 + #if defined( SHADOWMAP_TYPE_PCF ) + uniform sampler2DShadow directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ]; + #else + uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ]; + #endif + varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; + struct DirectionalLightShadow { + float shadowIntensity; + float shadowBias; + float shadowNormalBias; + float shadowRadius; + vec2 shadowMapSize; + }; + uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; + #endif + #if NUM_SPOT_LIGHT_SHADOWS > 0 + #if defined( SHADOWMAP_TYPE_PCF ) + uniform sampler2DShadow spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ]; + #else + uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ]; + #endif + struct SpotLightShadow { + float shadowIntensity; + float shadowBias; + float shadowNormalBias; + float shadowRadius; + vec2 shadowMapSize; + }; + uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; + #endif + #if NUM_POINT_LIGHT_SHADOWS > 0 + #if defined( SHADOWMAP_TYPE_PCF ) + uniform samplerCubeShadow pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ]; + #elif defined( SHADOWMAP_TYPE_BASIC ) + uniform samplerCube pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ]; + #endif + varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; + struct PointLightShadow { + float shadowIntensity; + float shadowBias; + float shadowNormalBias; + float shadowRadius; + vec2 shadowMapSize; + float shadowCameraNear; + float shadowCameraFar; + }; + uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; + #endif + #if defined( SHADOWMAP_TYPE_PCF ) + float interleavedGradientNoise( vec2 position ) { + return fract( 52.9829189 * fract( dot( position, vec2( 0.06711056, 0.00583715 ) ) ) ); + } + vec2 vogelDiskSample( int sampleIndex, int samplesCount, float phi ) { + const float goldenAngle = 2.399963229728653; + float r = sqrt( ( float( sampleIndex ) + 0.5 ) / float( samplesCount ) ); + float theta = float( sampleIndex ) * goldenAngle + phi; + return vec2( cos( theta ), sin( theta ) ) * r; + } + #endif + #if defined( SHADOWMAP_TYPE_PCF ) + float getShadow( sampler2DShadow shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord ) { + float shadow = 1.0; + shadowCoord.xyz /= shadowCoord.w; + shadowCoord.z += shadowBias; + bool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0; + bool frustumTest = inFrustum && shadowCoord.z <= 1.0; + if ( frustumTest ) { + vec2 texelSize = vec2( 1.0 ) / shadowMapSize; + float radius = shadowRadius * texelSize.x; + float phi = interleavedGradientNoise( gl_FragCoord.xy ) * PI2; + shadow = ( + texture( shadowMap, vec3( shadowCoord.xy + vogelDiskSample( 0, 5, phi ) * radius, shadowCoord.z ) ) + + texture( shadowMap, vec3( shadowCoord.xy + vogelDiskSample( 1, 5, phi ) * radius, shadowCoord.z ) ) + + texture( shadowMap, vec3( shadowCoord.xy + vogelDiskSample( 2, 5, phi ) * radius, shadowCoord.z ) ) + + texture( shadowMap, vec3( shadowCoord.xy + vogelDiskSample( 3, 5, phi ) * radius, shadowCoord.z ) ) + + texture( shadowMap, vec3( shadowCoord.xy + vogelDiskSample( 4, 5, phi ) * radius, shadowCoord.z ) ) + ) * 0.2; + } + return mix( 1.0, shadow, shadowIntensity ); + } + #elif defined( SHADOWMAP_TYPE_VSM ) + float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord ) { + float shadow = 1.0; + shadowCoord.xyz /= shadowCoord.w; + #ifdef USE_REVERSED_DEPTH_BUFFER + shadowCoord.z -= shadowBias; + #else + shadowCoord.z += shadowBias; + #endif + bool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0; + bool frustumTest = inFrustum && shadowCoord.z <= 1.0; + if ( frustumTest ) { + vec2 distribution = texture2D( shadowMap, shadowCoord.xy ).rg; + float mean = distribution.x; + float variance = distribution.y * distribution.y; + #ifdef USE_REVERSED_DEPTH_BUFFER + float hard_shadow = step( mean, shadowCoord.z ); + #else + float hard_shadow = step( shadowCoord.z, mean ); + #endif + + if ( hard_shadow == 1.0 ) { + shadow = 1.0; + } else { + variance = max( variance, 0.0000001 ); + float d = shadowCoord.z - mean; + float p_max = variance / ( variance + d * d ); + p_max = clamp( ( p_max - 0.3 ) / 0.65, 0.0, 1.0 ); + shadow = max( hard_shadow, p_max ); + } + } + return mix( 1.0, shadow, shadowIntensity ); + } + #else + float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord ) { + float shadow = 1.0; + shadowCoord.xyz /= shadowCoord.w; + #ifdef USE_REVERSED_DEPTH_BUFFER + shadowCoord.z -= shadowBias; + #else + shadowCoord.z += shadowBias; + #endif + bool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0; + bool frustumTest = inFrustum && shadowCoord.z <= 1.0; + if ( frustumTest ) { + float depth = texture2D( shadowMap, shadowCoord.xy ).r; + #ifdef USE_REVERSED_DEPTH_BUFFER + shadow = step( depth, shadowCoord.z ); + #else + shadow = step( shadowCoord.z, depth ); + #endif + } + return mix( 1.0, shadow, shadowIntensity ); + } + #endif + #if NUM_POINT_LIGHT_SHADOWS > 0 + #if defined( SHADOWMAP_TYPE_PCF ) + float getPointShadow( samplerCubeShadow shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) { + float shadow = 1.0; + vec3 lightToPosition = shadowCoord.xyz; + vec3 bd3D = normalize( lightToPosition ); + vec3 absVec = abs( lightToPosition ); + float viewSpaceZ = max( max( absVec.x, absVec.y ), absVec.z ); + if ( viewSpaceZ - shadowCameraFar <= 0.0 && viewSpaceZ - shadowCameraNear >= 0.0 ) { + #ifdef USE_REVERSED_DEPTH_BUFFER + float dp = ( shadowCameraNear * ( shadowCameraFar - viewSpaceZ ) ) / ( viewSpaceZ * ( shadowCameraFar - shadowCameraNear ) ); + dp -= shadowBias; + #else + float dp = ( shadowCameraFar * ( viewSpaceZ - shadowCameraNear ) ) / ( viewSpaceZ * ( shadowCameraFar - shadowCameraNear ) ); + dp += shadowBias; + #endif + float texelSize = shadowRadius / shadowMapSize.x; + vec3 absDir = abs( bd3D ); + vec3 tangent = absDir.x > absDir.z ? vec3( 0.0, 1.0, 0.0 ) : vec3( 1.0, 0.0, 0.0 ); + tangent = normalize( cross( bd3D, tangent ) ); + vec3 bitangent = cross( bd3D, tangent ); + float phi = interleavedGradientNoise( gl_FragCoord.xy ) * PI2; + vec2 sample0 = vogelDiskSample( 0, 5, phi ); + vec2 sample1 = vogelDiskSample( 1, 5, phi ); + vec2 sample2 = vogelDiskSample( 2, 5, phi ); + vec2 sample3 = vogelDiskSample( 3, 5, phi ); + vec2 sample4 = vogelDiskSample( 4, 5, phi ); + shadow = ( + texture( shadowMap, vec4( bd3D + ( tangent * sample0.x + bitangent * sample0.y ) * texelSize, dp ) ) + + texture( shadowMap, vec4( bd3D + ( tangent * sample1.x + bitangent * sample1.y ) * texelSize, dp ) ) + + texture( shadowMap, vec4( bd3D + ( tangent * sample2.x + bitangent * sample2.y ) * texelSize, dp ) ) + + texture( shadowMap, vec4( bd3D + ( tangent * sample3.x + bitangent * sample3.y ) * texelSize, dp ) ) + + texture( shadowMap, vec4( bd3D + ( tangent * sample4.x + bitangent * sample4.y ) * texelSize, dp ) ) + ) * 0.2; + } + return mix( 1.0, shadow, shadowIntensity ); + } + #elif defined( SHADOWMAP_TYPE_BASIC ) + float getPointShadow( samplerCube shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) { + float shadow = 1.0; + vec3 lightToPosition = shadowCoord.xyz; + vec3 absVec = abs( lightToPosition ); + float viewSpaceZ = max( max( absVec.x, absVec.y ), absVec.z ); + if ( viewSpaceZ - shadowCameraFar <= 0.0 && viewSpaceZ - shadowCameraNear >= 0.0 ) { + float dp = ( shadowCameraFar * ( viewSpaceZ - shadowCameraNear ) ) / ( viewSpaceZ * ( shadowCameraFar - shadowCameraNear ) ); + dp += shadowBias; + vec3 bd3D = normalize( lightToPosition ); + float depth = textureCube( shadowMap, bd3D ).r; + #ifdef USE_REVERSED_DEPTH_BUFFER + depth = 1.0 - depth; + #endif + shadow = step( dp, depth ); + } + return mix( 1.0, shadow, shadowIntensity ); + } + #endif + #endif +#endif`,ZN=`#if NUM_SPOT_LIGHT_COORDS > 0 + uniform mat4 spotLightMatrix[ NUM_SPOT_LIGHT_COORDS ]; + varying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ]; +#endif +#ifdef USE_SHADOWMAP + #if NUM_DIR_LIGHT_SHADOWS > 0 + uniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ]; + varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; + struct DirectionalLightShadow { + float shadowIntensity; + float shadowBias; + float shadowNormalBias; + float shadowRadius; + vec2 shadowMapSize; + }; + uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; + #endif + #if NUM_SPOT_LIGHT_SHADOWS > 0 + struct SpotLightShadow { + float shadowIntensity; + float shadowBias; + float shadowNormalBias; + float shadowRadius; + vec2 shadowMapSize; + }; + uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; + #endif + #if NUM_POINT_LIGHT_SHADOWS > 0 + uniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ]; + varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; + struct PointLightShadow { + float shadowIntensity; + float shadowBias; + float shadowNormalBias; + float shadowRadius; + vec2 shadowMapSize; + float shadowCameraNear; + float shadowCameraFar; + }; + uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; + #endif +#endif`,QN=`#if ( defined( USE_SHADOWMAP ) && ( NUM_DIR_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0 ) ) || ( NUM_SPOT_LIGHT_COORDS > 0 ) + #ifdef HAS_NORMAL + vec3 shadowWorldNormal = transformNormalByInverseViewMatrix( transformedNormal, viewMatrix ); + #else + vec3 shadowWorldNormal = vec3( 0.0 ); + #endif + vec4 shadowWorldPosition; +#endif +#if defined( USE_SHADOWMAP ) + #if NUM_DIR_LIGHT_SHADOWS > 0 + #pragma unroll_loop_start + for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { + shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 ); + vDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition; + } + #pragma unroll_loop_end + #endif + #if NUM_POINT_LIGHT_SHADOWS > 0 + #pragma unroll_loop_start + for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { + shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalBias, 0 ); + vPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition; + } + #pragma unroll_loop_end + #endif +#endif +#if NUM_SPOT_LIGHT_COORDS > 0 + #pragma unroll_loop_start + for ( int i = 0; i < NUM_SPOT_LIGHT_COORDS; i ++ ) { + shadowWorldPosition = worldPosition; + #if ( defined( USE_SHADOWMAP ) && UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) + shadowWorldPosition.xyz += shadowWorldNormal * spotLightShadows[ i ].shadowNormalBias; + #endif + vSpotLightCoord[ i ] = spotLightMatrix[ i ] * shadowWorldPosition; + } + #pragma unroll_loop_end +#endif`,JN=`float getShadowMask() { + float shadow = 1.0; + #ifdef USE_SHADOWMAP + #if NUM_DIR_LIGHT_SHADOWS > 0 + DirectionalLightShadow directionalLight; + #pragma unroll_loop_start + for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { + directionalLight = directionalLightShadows[ i ]; + shadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowIntensity, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; + } + #pragma unroll_loop_end + #endif + #if NUM_SPOT_LIGHT_SHADOWS > 0 + SpotLightShadow spotLight; + #pragma unroll_loop_start + for ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) { + spotLight = spotLightShadows[ i ]; + shadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowIntensity, spotLight.shadowBias, spotLight.shadowRadius, vSpotLightCoord[ i ] ) : 1.0; + } + #pragma unroll_loop_end + #endif + #if NUM_POINT_LIGHT_SHADOWS > 0 && ( defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_BASIC ) ) + PointLightShadow pointLight; + #pragma unroll_loop_start + for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { + pointLight = pointLightShadows[ i ]; + shadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowIntensity, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0; + } + #pragma unroll_loop_end + #endif + #endif + return shadow; +}`,eC=`#ifdef USE_SKINNING + mat4 boneMatX = getBoneMatrix( skinIndex.x ); + mat4 boneMatY = getBoneMatrix( skinIndex.y ); + mat4 boneMatZ = getBoneMatrix( skinIndex.z ); + mat4 boneMatW = getBoneMatrix( skinIndex.w ); +#endif`,tC=`#ifdef USE_SKINNING + uniform mat4 bindMatrix; + uniform mat4 bindMatrixInverse; + uniform highp sampler2D boneTexture; + mat4 getBoneMatrix( const in float i ) { + int size = textureSize( boneTexture, 0 ).x; + int j = int( i ) * 4; + int x = j % size; + int y = j / size; + vec4 v1 = texelFetch( boneTexture, ivec2( x, y ), 0 ); + vec4 v2 = texelFetch( boneTexture, ivec2( x + 1, y ), 0 ); + vec4 v3 = texelFetch( boneTexture, ivec2( x + 2, y ), 0 ); + vec4 v4 = texelFetch( boneTexture, ivec2( x + 3, y ), 0 ); + return mat4( v1, v2, v3, v4 ); + } +#endif`,nC=`#ifdef USE_SKINNING + vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 ); + vec4 skinned = vec4( 0.0 ); + skinned += boneMatX * skinVertex * skinWeight.x; + skinned += boneMatY * skinVertex * skinWeight.y; + skinned += boneMatZ * skinVertex * skinWeight.z; + skinned += boneMatW * skinVertex * skinWeight.w; + transformed = ( bindMatrixInverse * skinned ).xyz; +#endif`,iC=`#ifdef USE_SKINNING + mat4 skinMatrix = mat4( 0.0 ); + skinMatrix += skinWeight.x * boneMatX; + skinMatrix += skinWeight.y * boneMatY; + skinMatrix += skinWeight.z * boneMatZ; + skinMatrix += skinWeight.w * boneMatW; + skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix; + objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz; + #ifdef USE_TANGENT + objectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz; + #endif +#endif`,aC=`float specularStrength; +#ifdef USE_SPECULARMAP + vec4 texelSpecular = texture2D( specularMap, vSpecularMapUv ); + specularStrength = texelSpecular.r; +#else + specularStrength = 1.0; +#endif`,sC=`#ifdef USE_SPECULARMAP + uniform sampler2D specularMap; +#endif`,rC=`#if defined( TONE_MAPPING ) + gl_FragColor.rgb = toneMapping( gl_FragColor.rgb ); +#endif`,lC=`#ifndef saturate +#define saturate( a ) clamp( a, 0.0, 1.0 ) +#endif +uniform float toneMappingExposure; +vec3 LinearToneMapping( vec3 color ) { + return saturate( toneMappingExposure * color ); +} +vec3 ReinhardToneMapping( vec3 color ) { + color *= toneMappingExposure; + return saturate( color / ( vec3( 1.0 ) + color ) ); +} +vec3 CineonToneMapping( vec3 color ) { + color *= toneMappingExposure; + color = max( vec3( 0.0 ), color - 0.004 ); + return pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) ); +} +vec3 RRTAndODTFit( vec3 v ) { + vec3 a = v * ( v + 0.0245786 ) - 0.000090537; + vec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081; + return a / b; +} +vec3 ACESFilmicToneMapping( vec3 color ) { + const mat3 ACESInputMat = mat3( + vec3( 0.59719, 0.07600, 0.02840 ), vec3( 0.35458, 0.90834, 0.13383 ), + vec3( 0.04823, 0.01566, 0.83777 ) + ); + const mat3 ACESOutputMat = mat3( + vec3( 1.60475, -0.10208, -0.00327 ), vec3( -0.53108, 1.10813, -0.07276 ), + vec3( -0.07367, -0.00605, 1.07602 ) + ); + color *= toneMappingExposure / 0.6; + color = ACESInputMat * color; + color = RRTAndODTFit( color ); + color = ACESOutputMat * color; + return saturate( color ); +} +const mat3 LINEAR_REC2020_TO_LINEAR_SRGB = mat3( + vec3( 1.6605, - 0.1246, - 0.0182 ), + vec3( - 0.5876, 1.1329, - 0.1006 ), + vec3( - 0.0728, - 0.0083, 1.1187 ) +); +const mat3 LINEAR_SRGB_TO_LINEAR_REC2020 = mat3( + vec3( 0.6274, 0.0691, 0.0164 ), + vec3( 0.3293, 0.9195, 0.0880 ), + vec3( 0.0433, 0.0113, 0.8956 ) +); +vec3 agxDefaultContrastApprox( vec3 x ) { + vec3 x2 = x * x; + vec3 x4 = x2 * x2; + return + 15.5 * x4 * x2 + - 40.14 * x4 * x + + 31.96 * x4 + - 6.868 * x2 * x + + 0.4298 * x2 + + 0.1191 * x + - 0.00232; +} +vec3 AgXToneMapping( vec3 color ) { + const mat3 AgXInsetMatrix = mat3( + vec3( 0.856627153315983, 0.137318972929847, 0.11189821299995 ), + vec3( 0.0951212405381588, 0.761241990602591, 0.0767994186031903 ), + vec3( 0.0482516061458583, 0.101439036467562, 0.811302368396859 ) + ); + const mat3 AgXOutsetMatrix = mat3( + vec3( 1.1271005818144368, - 0.1413297634984383, - 0.14132976349843826 ), + vec3( - 0.11060664309660323, 1.157823702216272, - 0.11060664309660294 ), + vec3( - 0.016493938717834573, - 0.016493938717834257, 1.2519364065950405 ) + ); + const float AgxMinEv = - 12.47393; const float AgxMaxEv = 4.026069; + color *= toneMappingExposure; + color = LINEAR_SRGB_TO_LINEAR_REC2020 * color; + color = AgXInsetMatrix * color; + color = max( color, 1e-10 ); color = log2( color ); + color = ( color - AgxMinEv ) / ( AgxMaxEv - AgxMinEv ); + color = clamp( color, 0.0, 1.0 ); + color = agxDefaultContrastApprox( color ); + color = AgXOutsetMatrix * color; + color = pow( max( vec3( 0.0 ), color ), vec3( 2.2 ) ); + color = LINEAR_REC2020_TO_LINEAR_SRGB * color; + color = clamp( color, 0.0, 1.0 ); + return color; +} +vec3 NeutralToneMapping( vec3 color ) { + const float StartCompression = 0.8 - 0.04; + const float Desaturation = 0.15; + color *= toneMappingExposure; + float x = min( color.r, min( color.g, color.b ) ); + float offset = x < 0.08 ? x - 6.25 * x * x : 0.04; + color -= offset; + float peak = max( color.r, max( color.g, color.b ) ); + if ( peak < StartCompression ) return color; + float d = 1. - StartCompression; + float newPeak = 1. - d * d / ( peak + d - StartCompression ); + color *= newPeak / peak; + float g = 1. - 1. / ( Desaturation * ( peak - newPeak ) + 1. ); + return mix( color, vec3( newPeak ), g ); +} +vec3 CustomToneMapping( vec3 color ) { return color; }`,oC=`#ifdef USE_TRANSMISSION + material.transmission = transmission; + material.transmissionAlpha = 1.0; + material.thickness = thickness; + material.attenuationDistance = attenuationDistance; + material.attenuationColor = attenuationColor; + #ifdef USE_TRANSMISSIONMAP + material.transmission *= texture2D( transmissionMap, vTransmissionMapUv ).r; + #endif + #ifdef USE_THICKNESSMAP + material.thickness *= texture2D( thicknessMap, vThicknessMapUv ).g; + #endif + vec3 pos = vWorldPosition; + vec3 v = normalize( cameraPosition - pos ); + vec3 n = transformNormalByInverseViewMatrix( normal, viewMatrix ); + vec4 transmitted = getIBLVolumeRefraction( + n, v, material.roughness, material.diffuseContribution, material.specularColorBlended, material.specularF90, + pos, modelMatrix, viewMatrix, projectionMatrix, material.dispersion, material.ior, material.thickness, + material.attenuationColor, material.attenuationDistance ); + material.transmissionAlpha = mix( material.transmissionAlpha, transmitted.a, material.transmission ); + totalDiffuse = mix( totalDiffuse, transmitted.rgb, material.transmission ); +#endif`,cC=`#ifdef USE_TRANSMISSION + uniform float transmission; + uniform float thickness; + uniform float attenuationDistance; + uniform vec3 attenuationColor; + #ifdef USE_TRANSMISSIONMAP + uniform sampler2D transmissionMap; + #endif + #ifdef USE_THICKNESSMAP + uniform sampler2D thicknessMap; + #endif + uniform vec2 transmissionSamplerSize; + uniform sampler2D transmissionSamplerMap; + uniform mat4 modelMatrix; + uniform mat4 projectionMatrix; + varying vec3 vWorldPosition; + float w0( float a ) { + return ( 1.0 / 6.0 ) * ( a * ( a * ( - a + 3.0 ) - 3.0 ) + 1.0 ); + } + float w1( float a ) { + return ( 1.0 / 6.0 ) * ( a * a * ( 3.0 * a - 6.0 ) + 4.0 ); + } + float w2( float a ){ + return ( 1.0 / 6.0 ) * ( a * ( a * ( - 3.0 * a + 3.0 ) + 3.0 ) + 1.0 ); + } + float w3( float a ) { + return ( 1.0 / 6.0 ) * ( a * a * a ); + } + float g0( float a ) { + return w0( a ) + w1( a ); + } + float g1( float a ) { + return w2( a ) + w3( a ); + } + float h0( float a ) { + return - 1.0 + w1( a ) / ( w0( a ) + w1( a ) ); + } + float h1( float a ) { + return 1.0 + w3( a ) / ( w2( a ) + w3( a ) ); + } + vec4 bicubic( sampler2D tex, vec2 uv, vec4 texelSize, float lod ) { + uv = uv * texelSize.zw + 0.5; + vec2 iuv = floor( uv ); + vec2 fuv = fract( uv ); + float g0x = g0( fuv.x ); + float g1x = g1( fuv.x ); + float h0x = h0( fuv.x ); + float h1x = h1( fuv.x ); + float h0y = h0( fuv.y ); + float h1y = h1( fuv.y ); + vec2 p0 = ( vec2( iuv.x + h0x, iuv.y + h0y ) - 0.5 ) * texelSize.xy; + vec2 p1 = ( vec2( iuv.x + h1x, iuv.y + h0y ) - 0.5 ) * texelSize.xy; + vec2 p2 = ( vec2( iuv.x + h0x, iuv.y + h1y ) - 0.5 ) * texelSize.xy; + vec2 p3 = ( vec2( iuv.x + h1x, iuv.y + h1y ) - 0.5 ) * texelSize.xy; + return g0( fuv.y ) * ( g0x * textureLod( tex, p0, lod ) + g1x * textureLod( tex, p1, lod ) ) + + g1( fuv.y ) * ( g0x * textureLod( tex, p2, lod ) + g1x * textureLod( tex, p3, lod ) ); + } + vec4 textureBicubic( sampler2D sampler, vec2 uv, float lod ) { + vec2 fLodSize = vec2( textureSize( sampler, int( lod ) ) ); + vec2 cLodSize = vec2( textureSize( sampler, int( lod + 1.0 ) ) ); + vec2 fLodSizeInv = 1.0 / fLodSize; + vec2 cLodSizeInv = 1.0 / cLodSize; + vec4 fSample = bicubic( sampler, uv, vec4( fLodSizeInv, fLodSize ), floor( lod ) ); + vec4 cSample = bicubic( sampler, uv, vec4( cLodSizeInv, cLodSize ), ceil( lod ) ); + return mix( fSample, cSample, fract( lod ) ); + } + vec3 getVolumeTransmissionRay( const in vec3 n, const in vec3 v, const in float thickness, const in float ior, const in mat4 modelMatrix ) { + vec3 refractionVector = refract( - v, normalize( n ), 1.0 / ior ); + vec3 modelScale; + modelScale.x = length( vec3( modelMatrix[ 0 ].xyz ) ); + modelScale.y = length( vec3( modelMatrix[ 1 ].xyz ) ); + modelScale.z = length( vec3( modelMatrix[ 2 ].xyz ) ); + return normalize( refractionVector ) * thickness * modelScale; + } + float applyIorToRoughness( const in float roughness, const in float ior ) { + return roughness * clamp( ior * 2.0 - 2.0, 0.0, 1.0 ); + } + vec4 getTransmissionSample( const in vec2 fragCoord, const in float roughness, const in float ior ) { + float lod = log2( transmissionSamplerSize.x ) * applyIorToRoughness( roughness, ior ); + return textureBicubic( transmissionSamplerMap, fragCoord.xy, lod ); + } + vec3 volumeAttenuation( const in float transmissionDistance, const in vec3 attenuationColor, const in float attenuationDistance ) { + if ( isinf( attenuationDistance ) ) { + return vec3( 1.0 ); + } else { + vec3 attenuationCoefficient = -log( attenuationColor ) / attenuationDistance; + vec3 transmittance = exp( - attenuationCoefficient * transmissionDistance ); return transmittance; + } + } + vec4 getIBLVolumeRefraction( const in vec3 n, const in vec3 v, const in float roughness, const in vec3 diffuseColor, + const in vec3 specularColor, const in float specularF90, const in vec3 position, const in mat4 modelMatrix, + const in mat4 viewMatrix, const in mat4 projMatrix, const in float dispersion, const in float ior, const in float thickness, + const in vec3 attenuationColor, const in float attenuationDistance ) { + vec4 transmittedLight; + vec3 transmittance; + #ifdef USE_DISPERSION + float halfSpread = ( ior - 1.0 ) * 0.025 * dispersion; + vec3 iors = vec3( ior - halfSpread, ior, ior + halfSpread ); + for ( int i = 0; i < 3; i ++ ) { + vec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, iors[ i ], modelMatrix ); + vec3 refractedRayExit = position + transmissionRay; + vec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 ); + vec2 refractionCoords = ndcPos.xy / ndcPos.w; + refractionCoords += 1.0; + refractionCoords /= 2.0; + vec4 transmissionSample = getTransmissionSample( refractionCoords, roughness, iors[ i ] ); + transmittedLight[ i ] = transmissionSample[ i ]; + transmittedLight.a += transmissionSample.a; + transmittance[ i ] = diffuseColor[ i ] * volumeAttenuation( length( transmissionRay ), attenuationColor, attenuationDistance )[ i ]; + } + transmittedLight.a /= 3.0; + #else + vec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, ior, modelMatrix ); + vec3 refractedRayExit = position + transmissionRay; + vec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 ); + vec2 refractionCoords = ndcPos.xy / ndcPos.w; + refractionCoords += 1.0; + refractionCoords /= 2.0; + transmittedLight = getTransmissionSample( refractionCoords, roughness, ior ); + transmittance = diffuseColor * volumeAttenuation( length( transmissionRay ), attenuationColor, attenuationDistance ); + #endif + vec3 attenuatedColor = transmittance * transmittedLight.rgb; + vec3 F = EnvironmentBRDF( n, v, specularColor, specularF90, roughness ); + float transmittanceFactor = ( transmittance.r + transmittance.g + transmittance.b ) / 3.0; + return vec4( ( 1.0 - F ) * attenuatedColor, 1.0 - ( 1.0 - transmittedLight.a ) * transmittanceFactor ); + } +#endif`,uC=`#if defined( USE_UV ) || defined( USE_ANISOTROPY ) + varying vec2 vUv; +#endif +#ifdef USE_MAP + varying vec2 vMapUv; +#endif +#ifdef USE_ALPHAMAP + varying vec2 vAlphaMapUv; +#endif +#ifdef USE_LIGHTMAP + varying vec2 vLightMapUv; +#endif +#ifdef USE_AOMAP + varying vec2 vAoMapUv; +#endif +#ifdef USE_BUMPMAP + varying vec2 vBumpMapUv; +#endif +#ifdef USE_NORMALMAP + varying vec2 vNormalMapUv; +#endif +#ifdef USE_EMISSIVEMAP + varying vec2 vEmissiveMapUv; +#endif +#ifdef USE_METALNESSMAP + varying vec2 vMetalnessMapUv; +#endif +#ifdef USE_ROUGHNESSMAP + varying vec2 vRoughnessMapUv; +#endif +#ifdef USE_ANISOTROPYMAP + varying vec2 vAnisotropyMapUv; +#endif +#ifdef USE_CLEARCOATMAP + varying vec2 vClearcoatMapUv; +#endif +#ifdef USE_CLEARCOAT_NORMALMAP + varying vec2 vClearcoatNormalMapUv; +#endif +#ifdef USE_CLEARCOAT_ROUGHNESSMAP + varying vec2 vClearcoatRoughnessMapUv; +#endif +#ifdef USE_IRIDESCENCEMAP + varying vec2 vIridescenceMapUv; +#endif +#ifdef USE_IRIDESCENCE_THICKNESSMAP + varying vec2 vIridescenceThicknessMapUv; +#endif +#ifdef USE_SHEEN_COLORMAP + varying vec2 vSheenColorMapUv; +#endif +#ifdef USE_SHEEN_ROUGHNESSMAP + varying vec2 vSheenRoughnessMapUv; +#endif +#ifdef USE_SPECULARMAP + varying vec2 vSpecularMapUv; +#endif +#ifdef USE_SPECULAR_COLORMAP + varying vec2 vSpecularColorMapUv; +#endif +#ifdef USE_SPECULAR_INTENSITYMAP + varying vec2 vSpecularIntensityMapUv; +#endif +#ifdef USE_TRANSMISSIONMAP + uniform mat3 transmissionMapTransform; + varying vec2 vTransmissionMapUv; +#endif +#ifdef USE_THICKNESSMAP + uniform mat3 thicknessMapTransform; + varying vec2 vThicknessMapUv; +#endif`,dC=`#if defined( USE_UV ) || defined( USE_ANISOTROPY ) + varying vec2 vUv; +#endif +#ifdef USE_MAP + uniform mat3 mapTransform; + varying vec2 vMapUv; +#endif +#ifdef USE_ALPHAMAP + uniform mat3 alphaMapTransform; + varying vec2 vAlphaMapUv; +#endif +#ifdef USE_LIGHTMAP + uniform mat3 lightMapTransform; + varying vec2 vLightMapUv; +#endif +#ifdef USE_AOMAP + uniform mat3 aoMapTransform; + varying vec2 vAoMapUv; +#endif +#ifdef USE_BUMPMAP + uniform mat3 bumpMapTransform; + varying vec2 vBumpMapUv; +#endif +#ifdef USE_NORMALMAP + uniform mat3 normalMapTransform; + varying vec2 vNormalMapUv; +#endif +#ifdef USE_DISPLACEMENTMAP + uniform mat3 displacementMapTransform; + varying vec2 vDisplacementMapUv; +#endif +#ifdef USE_EMISSIVEMAP + uniform mat3 emissiveMapTransform; + varying vec2 vEmissiveMapUv; +#endif +#ifdef USE_METALNESSMAP + uniform mat3 metalnessMapTransform; + varying vec2 vMetalnessMapUv; +#endif +#ifdef USE_ROUGHNESSMAP + uniform mat3 roughnessMapTransform; + varying vec2 vRoughnessMapUv; +#endif +#ifdef USE_ANISOTROPYMAP + uniform mat3 anisotropyMapTransform; + varying vec2 vAnisotropyMapUv; +#endif +#ifdef USE_CLEARCOATMAP + uniform mat3 clearcoatMapTransform; + varying vec2 vClearcoatMapUv; +#endif +#ifdef USE_CLEARCOAT_NORMALMAP + uniform mat3 clearcoatNormalMapTransform; + varying vec2 vClearcoatNormalMapUv; +#endif +#ifdef USE_CLEARCOAT_ROUGHNESSMAP + uniform mat3 clearcoatRoughnessMapTransform; + varying vec2 vClearcoatRoughnessMapUv; +#endif +#ifdef USE_SHEEN_COLORMAP + uniform mat3 sheenColorMapTransform; + varying vec2 vSheenColorMapUv; +#endif +#ifdef USE_SHEEN_ROUGHNESSMAP + uniform mat3 sheenRoughnessMapTransform; + varying vec2 vSheenRoughnessMapUv; +#endif +#ifdef USE_IRIDESCENCEMAP + uniform mat3 iridescenceMapTransform; + varying vec2 vIridescenceMapUv; +#endif +#ifdef USE_IRIDESCENCE_THICKNESSMAP + uniform mat3 iridescenceThicknessMapTransform; + varying vec2 vIridescenceThicknessMapUv; +#endif +#ifdef USE_SPECULARMAP + uniform mat3 specularMapTransform; + varying vec2 vSpecularMapUv; +#endif +#ifdef USE_SPECULAR_COLORMAP + uniform mat3 specularColorMapTransform; + varying vec2 vSpecularColorMapUv; +#endif +#ifdef USE_SPECULAR_INTENSITYMAP + uniform mat3 specularIntensityMapTransform; + varying vec2 vSpecularIntensityMapUv; +#endif +#ifdef USE_TRANSMISSIONMAP + uniform mat3 transmissionMapTransform; + varying vec2 vTransmissionMapUv; +#endif +#ifdef USE_THICKNESSMAP + uniform mat3 thicknessMapTransform; + varying vec2 vThicknessMapUv; +#endif`,hC=`#if defined( USE_UV ) || defined( USE_ANISOTROPY ) + vUv = vec3( uv, 1 ).xy; +#endif +#ifdef USE_MAP + vMapUv = ( mapTransform * vec3( MAP_UV, 1 ) ).xy; +#endif +#ifdef USE_ALPHAMAP + vAlphaMapUv = ( alphaMapTransform * vec3( ALPHAMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_LIGHTMAP + vLightMapUv = ( lightMapTransform * vec3( LIGHTMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_AOMAP + vAoMapUv = ( aoMapTransform * vec3( AOMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_BUMPMAP + vBumpMapUv = ( bumpMapTransform * vec3( BUMPMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_NORMALMAP + vNormalMapUv = ( normalMapTransform * vec3( NORMALMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_DISPLACEMENTMAP + vDisplacementMapUv = ( displacementMapTransform * vec3( DISPLACEMENTMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_EMISSIVEMAP + vEmissiveMapUv = ( emissiveMapTransform * vec3( EMISSIVEMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_METALNESSMAP + vMetalnessMapUv = ( metalnessMapTransform * vec3( METALNESSMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_ROUGHNESSMAP + vRoughnessMapUv = ( roughnessMapTransform * vec3( ROUGHNESSMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_ANISOTROPYMAP + vAnisotropyMapUv = ( anisotropyMapTransform * vec3( ANISOTROPYMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_CLEARCOATMAP + vClearcoatMapUv = ( clearcoatMapTransform * vec3( CLEARCOATMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_CLEARCOAT_NORMALMAP + vClearcoatNormalMapUv = ( clearcoatNormalMapTransform * vec3( CLEARCOAT_NORMALMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_CLEARCOAT_ROUGHNESSMAP + vClearcoatRoughnessMapUv = ( clearcoatRoughnessMapTransform * vec3( CLEARCOAT_ROUGHNESSMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_IRIDESCENCEMAP + vIridescenceMapUv = ( iridescenceMapTransform * vec3( IRIDESCENCEMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_IRIDESCENCE_THICKNESSMAP + vIridescenceThicknessMapUv = ( iridescenceThicknessMapTransform * vec3( IRIDESCENCE_THICKNESSMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_SHEEN_COLORMAP + vSheenColorMapUv = ( sheenColorMapTransform * vec3( SHEEN_COLORMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_SHEEN_ROUGHNESSMAP + vSheenRoughnessMapUv = ( sheenRoughnessMapTransform * vec3( SHEEN_ROUGHNESSMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_SPECULARMAP + vSpecularMapUv = ( specularMapTransform * vec3( SPECULARMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_SPECULAR_COLORMAP + vSpecularColorMapUv = ( specularColorMapTransform * vec3( SPECULAR_COLORMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_SPECULAR_INTENSITYMAP + vSpecularIntensityMapUv = ( specularIntensityMapTransform * vec3( SPECULAR_INTENSITYMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_TRANSMISSIONMAP + vTransmissionMapUv = ( transmissionMapTransform * vec3( TRANSMISSIONMAP_UV, 1 ) ).xy; +#endif +#ifdef USE_THICKNESSMAP + vThicknessMapUv = ( thicknessMapTransform * vec3( THICKNESSMAP_UV, 1 ) ).xy; +#endif`,fC=`#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) || defined ( USE_TRANSMISSION ) || NUM_SPOT_LIGHT_COORDS > 0 + vec4 worldPosition = vec4( transformed, 1.0 ); + #ifdef USE_BATCHING + worldPosition = batchingMatrix * worldPosition; + #endif + #ifdef USE_INSTANCING + worldPosition = instanceMatrix * worldPosition; + #endif + worldPosition = modelMatrix * worldPosition; +#endif`;const pC=`varying vec2 vUv; +uniform mat3 uvTransform; +void main() { + vUv = ( uvTransform * vec3( uv, 1 ) ).xy; + gl_Position = vec4( position.xy, 1.0, 1.0 ); +}`,mC=`uniform sampler2D t2D; +uniform float backgroundIntensity; +varying vec2 vUv; +void main() { + vec4 texColor = texture2D( t2D, vUv ); + #ifdef DECODE_VIDEO_TEXTURE + texColor = vec4( mix( pow( texColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), texColor.rgb * 0.0773993808, vec3( lessThanEqual( texColor.rgb, vec3( 0.04045 ) ) ) ), texColor.w ); + #endif + texColor.rgb *= backgroundIntensity; + gl_FragColor = texColor; + #include <tonemapping_fragment> + #include <colorspace_fragment> +}`,gC=`varying vec3 vWorldDirection; +#include <common> +void main() { + vWorldDirection = transformDirection( position, modelMatrix ); + #include <begin_vertex> + #include <project_vertex> + gl_Position.z = gl_Position.w; +}`,xC=`#ifdef ENVMAP_TYPE_CUBE + uniform samplerCube envMap; +#elif defined( ENVMAP_TYPE_CUBE_UV ) + uniform sampler2D envMap; +#endif +uniform float backgroundBlurriness; +uniform float backgroundIntensity; +uniform mat3 backgroundRotation; +varying vec3 vWorldDirection; +#include <cube_uv_reflection_fragment> +void main() { + #ifdef ENVMAP_TYPE_CUBE + vec4 texColor = textureCube( envMap, backgroundRotation * vWorldDirection ); + #elif defined( ENVMAP_TYPE_CUBE_UV ) + vec4 texColor = textureCubeUV( envMap, backgroundRotation * vWorldDirection, backgroundBlurriness ); + #else + vec4 texColor = vec4( 0.0, 0.0, 0.0, 1.0 ); + #endif + texColor.rgb *= backgroundIntensity; + gl_FragColor = texColor; + #include <tonemapping_fragment> + #include <colorspace_fragment> +}`,vC=`varying vec3 vWorldDirection; +#include <common> +void main() { + vWorldDirection = transformDirection( position, modelMatrix ); + #include <begin_vertex> + #include <project_vertex> + gl_Position.z = gl_Position.w; +}`,_C=`uniform samplerCube tCube; +uniform float tFlip; +uniform float opacity; +varying vec3 vWorldDirection; +void main() { + vec4 texColor = textureCube( tCube, vec3( tFlip * vWorldDirection.x, vWorldDirection.yz ) ); + gl_FragColor = texColor; + gl_FragColor.a *= opacity; + #include <tonemapping_fragment> + #include <colorspace_fragment> +}`,bC=`#include <common> +#include <batching_pars_vertex> +#include <uv_pars_vertex> +#include <displacementmap_pars_vertex> +#include <morphtarget_pars_vertex> +#include <skinning_pars_vertex> +#include <logdepthbuf_pars_vertex> +#include <clipping_planes_pars_vertex> +varying vec2 vHighPrecisionZW; +void main() { + #include <uv_vertex> + #include <batching_vertex> + #include <skinbase_vertex> + #include <morphinstance_vertex> + #ifdef USE_DISPLACEMENTMAP + #include <beginnormal_vertex> + #include <morphnormal_vertex> + #include <skinnormal_vertex> + #endif + #include <begin_vertex> + #include <morphtarget_vertex> + #include <skinning_vertex> + #include <displacementmap_vertex> + #include <project_vertex> + #include <logdepthbuf_vertex> + #include <clipping_planes_vertex> + vHighPrecisionZW = gl_Position.zw; +}`,yC=`#if DEPTH_PACKING == 3200 + uniform float opacity; +#endif +#include <common> +#include <packing> +#include <uv_pars_fragment> +#include <map_pars_fragment> +#include <alphamap_pars_fragment> +#include <alphatest_pars_fragment> +#include <alphahash_pars_fragment> +#include <logdepthbuf_pars_fragment> +#include <clipping_planes_pars_fragment> +varying vec2 vHighPrecisionZW; +void main() { + vec4 diffuseColor = vec4( 1.0 ); + #include <clipping_planes_fragment> + #if DEPTH_PACKING == 3200 + diffuseColor.a = opacity; + #endif + #include <map_fragment> + #include <alphamap_fragment> + #include <alphatest_fragment> + #include <alphahash_fragment> + #include <logdepthbuf_fragment> + #ifdef USE_REVERSED_DEPTH_BUFFER + float fragCoordZ = vHighPrecisionZW[ 0 ] / vHighPrecisionZW[ 1 ]; + #else + float fragCoordZ = 0.5 * vHighPrecisionZW[ 0 ] / vHighPrecisionZW[ 1 ] + 0.5; + #endif + #if DEPTH_PACKING == 3200 + gl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity ); + #elif DEPTH_PACKING == 3201 + gl_FragColor = packDepthToRGBA( fragCoordZ ); + #elif DEPTH_PACKING == 3202 + gl_FragColor = vec4( packDepthToRGB( fragCoordZ ), 1.0 ); + #elif DEPTH_PACKING == 3203 + gl_FragColor = vec4( packDepthToRG( fragCoordZ ), 0.0, 1.0 ); + #endif +}`,SC=`#define DISTANCE +varying vec3 vWorldPosition; +#include <common> +#include <batching_pars_vertex> +#include <uv_pars_vertex> +#include <displacementmap_pars_vertex> +#include <morphtarget_pars_vertex> +#include <skinning_pars_vertex> +#include <clipping_planes_pars_vertex> +void main() { + #include <uv_vertex> + #include <batching_vertex> + #include <skinbase_vertex> + #include <morphinstance_vertex> + #ifdef USE_DISPLACEMENTMAP + #include <beginnormal_vertex> + #include <morphnormal_vertex> + #include <skinnormal_vertex> + #endif + #include <begin_vertex> + #include <morphtarget_vertex> + #include <skinning_vertex> + #include <displacementmap_vertex> + #include <project_vertex> + #include <worldpos_vertex> + #include <clipping_planes_vertex> + vWorldPosition = worldPosition.xyz; +}`,MC=`#define DISTANCE +uniform vec3 referencePosition; +uniform float nearDistance; +uniform float farDistance; +varying vec3 vWorldPosition; +#include <common> +#include <uv_pars_fragment> +#include <map_pars_fragment> +#include <alphamap_pars_fragment> +#include <alphatest_pars_fragment> +#include <alphahash_pars_fragment> +#include <clipping_planes_pars_fragment> +void main() { + vec4 diffuseColor = vec4( 1.0 ); + #include <clipping_planes_fragment> + #include <map_fragment> + #include <alphamap_fragment> + #include <alphatest_fragment> + #include <alphahash_fragment> + float dist = length( vWorldPosition - referencePosition ); + dist = ( dist - nearDistance ) / ( farDistance - nearDistance ); + dist = saturate( dist ); + gl_FragColor = vec4( dist, 0.0, 0.0, 1.0 ); +}`,EC=`varying vec3 vWorldDirection; +#include <common> +void main() { + vWorldDirection = transformDirection( position, modelMatrix ); + #include <begin_vertex> + #include <project_vertex> +}`,TC=`uniform sampler2D tEquirect; +varying vec3 vWorldDirection; +#include <common> +void main() { + vec3 direction = normalize( vWorldDirection ); + vec2 sampleUV = equirectUv( direction ); + gl_FragColor = texture2D( tEquirect, sampleUV ); + #include <tonemapping_fragment> + #include <colorspace_fragment> +}`,AC=`uniform float scale; +attribute float lineDistance; +varying float vLineDistance; +#include <common> +#include <uv_pars_vertex> +#include <color_pars_vertex> +#include <fog_pars_vertex> +#include <morphtarget_pars_vertex> +#include <logdepthbuf_pars_vertex> +#include <clipping_planes_pars_vertex> +void main() { + vLineDistance = scale * lineDistance; + #include <uv_vertex> + #include <color_vertex> + #include <morphinstance_vertex> + #include <morphcolor_vertex> + #include <begin_vertex> + #include <morphtarget_vertex> + #include <project_vertex> + #include <logdepthbuf_vertex> + #include <clipping_planes_vertex> + #include <fog_vertex> +}`,wC=`uniform vec3 diffuse; +uniform float opacity; +uniform float dashSize; +uniform float totalSize; +varying float vLineDistance; +#include <common> +#include <color_pars_fragment> +#include <uv_pars_fragment> +#include <map_pars_fragment> +#include <fog_pars_fragment> +#include <logdepthbuf_pars_fragment> +#include <clipping_planes_pars_fragment> +void main() { + vec4 diffuseColor = vec4( diffuse, opacity ); + #include <clipping_planes_fragment> + if ( mod( vLineDistance, totalSize ) > dashSize ) { + discard; + } + vec3 outgoingLight = vec3( 0.0 ); + #include <logdepthbuf_fragment> + #include <map_fragment> + #include <color_fragment> + outgoingLight = diffuseColor.rgb; + #include <opaque_fragment> + #include <tonemapping_fragment> + #include <colorspace_fragment> + #include <fog_fragment> + #include <premultiplied_alpha_fragment> +}`,NC=`#include <common> +#include <batching_pars_vertex> +#include <uv_pars_vertex> +#include <envmap_pars_vertex> +#include <color_pars_vertex> +#include <fog_pars_vertex> +#include <morphtarget_pars_vertex> +#include <skinning_pars_vertex> +#include <logdepthbuf_pars_vertex> +#include <clipping_planes_pars_vertex> +void main() { + #include <uv_vertex> + #include <color_vertex> + #include <morphinstance_vertex> + #include <morphcolor_vertex> + #include <batching_vertex> + #if defined ( USE_ENVMAP ) || defined ( USE_SKINNING ) + #include <beginnormal_vertex> + #include <morphnormal_vertex> + #include <skinbase_vertex> + #include <skinnormal_vertex> + #include <defaultnormal_vertex> + #endif + #include <begin_vertex> + #include <morphtarget_vertex> + #include <skinning_vertex> + #include <project_vertex> + #include <logdepthbuf_vertex> + #include <clipping_planes_vertex> + #include <worldpos_vertex> + #include <envmap_vertex> + #include <fog_vertex> +}`,CC=`uniform vec3 diffuse; +uniform float opacity; +#ifndef FLAT_SHADED + varying vec3 vNormal; +#endif +#include <common> +#include <dithering_pars_fragment> +#include <color_pars_fragment> +#include <uv_pars_fragment> +#include <map_pars_fragment> +#include <alphamap_pars_fragment> +#include <alphatest_pars_fragment> +#include <alphahash_pars_fragment> +#include <aomap_pars_fragment> +#include <lightmap_pars_fragment> +#include <envmap_common_pars_fragment> +#include <envmap_pars_fragment> +#include <fog_pars_fragment> +#include <specularmap_pars_fragment> +#include <logdepthbuf_pars_fragment> +#include <clipping_planes_pars_fragment> +void main() { + vec4 diffuseColor = vec4( diffuse, opacity ); + #include <clipping_planes_fragment> + #include <logdepthbuf_fragment> + #include <map_fragment> + #include <color_fragment> + #include <alphamap_fragment> + #include <alphatest_fragment> + #include <alphahash_fragment> + #include <specularmap_fragment> + ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); + #ifdef USE_LIGHTMAP + vec4 lightMapTexel = texture2D( lightMap, vLightMapUv ); + reflectedLight.indirectDiffuse += lightMapTexel.rgb * lightMapIntensity * RECIPROCAL_PI; + #else + reflectedLight.indirectDiffuse += vec3( 1.0 ); + #endif + #include <aomap_fragment> + reflectedLight.indirectDiffuse *= diffuseColor.rgb; + vec3 outgoingLight = reflectedLight.indirectDiffuse; + #include <envmap_fragment> + #include <opaque_fragment> + #include <tonemapping_fragment> + #include <colorspace_fragment> + #include <fog_fragment> + #include <premultiplied_alpha_fragment> + #include <dithering_fragment> +}`,RC=`#define LAMBERT +varying vec3 vViewPosition; +#include <common> +#include <batching_pars_vertex> +#include <uv_pars_vertex> +#include <displacementmap_pars_vertex> +#include <envmap_pars_vertex> +#include <color_pars_vertex> +#include <fog_pars_vertex> +#include <normal_pars_vertex> +#include <morphtarget_pars_vertex> +#include <skinning_pars_vertex> +#include <shadowmap_pars_vertex> +#include <logdepthbuf_pars_vertex> +#include <clipping_planes_pars_vertex> +void main() { + #include <uv_vertex> + #include <color_vertex> + #include <morphinstance_vertex> + #include <morphcolor_vertex> + #include <batching_vertex> + #include <beginnormal_vertex> + #include <morphnormal_vertex> + #include <skinbase_vertex> + #include <skinnormal_vertex> + #include <defaultnormal_vertex> + #include <normal_vertex> + #include <begin_vertex> + #include <morphtarget_vertex> + #include <skinning_vertex> + #include <displacementmap_vertex> + #include <project_vertex> + #include <logdepthbuf_vertex> + #include <clipping_planes_vertex> + vViewPosition = - mvPosition.xyz; + #include <worldpos_vertex> + #include <envmap_vertex> + #include <shadowmap_vertex> + #include <fog_vertex> +}`,DC=`#define LAMBERT +uniform vec3 diffuse; +uniform vec3 emissive; +uniform float opacity; +#include <common> +#include <dithering_pars_fragment> +#include <color_pars_fragment> +#include <uv_pars_fragment> +#include <map_pars_fragment> +#include <alphamap_pars_fragment> +#include <alphatest_pars_fragment> +#include <alphahash_pars_fragment> +#include <aomap_pars_fragment> +#include <lightmap_pars_fragment> +#include <emissivemap_pars_fragment> +#include <cube_uv_reflection_fragment> +#include <envmap_common_pars_fragment> +#include <envmap_pars_fragment> +#include <envmap_physical_pars_fragment> +#include <fog_pars_fragment> +#include <bsdfs> +#include <lights_pars_begin> +#include <normal_pars_fragment> +#include <lights_lambert_pars_fragment> +#include <shadowmap_pars_fragment> +#include <bumpmap_pars_fragment> +#include <normalmap_pars_fragment> +#include <specularmap_pars_fragment> +#include <logdepthbuf_pars_fragment> +#include <clipping_planes_pars_fragment> +void main() { + vec4 diffuseColor = vec4( diffuse, opacity ); + #include <clipping_planes_fragment> + ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); + vec3 totalEmissiveRadiance = emissive; + #include <logdepthbuf_fragment> + #include <map_fragment> + #include <color_fragment> + #include <alphamap_fragment> + #include <alphatest_fragment> + #include <alphahash_fragment> + #include <specularmap_fragment> + #include <normal_fragment_begin> + #include <normal_fragment_maps> + #include <emissivemap_fragment> + #include <lights_lambert_fragment> + #include <lights_fragment_begin> + #include <lights_fragment_maps> + #include <lights_fragment_end> + #include <aomap_fragment> + vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; + #include <envmap_fragment> + #include <opaque_fragment> + #include <tonemapping_fragment> + #include <colorspace_fragment> + #include <fog_fragment> + #include <premultiplied_alpha_fragment> + #include <dithering_fragment> +}`,LC=`#define MATCAP +varying vec3 vViewPosition; +#include <common> +#include <batching_pars_vertex> +#include <uv_pars_vertex> +#include <color_pars_vertex> +#include <displacementmap_pars_vertex> +#include <fog_pars_vertex> +#include <normal_pars_vertex> +#include <morphtarget_pars_vertex> +#include <skinning_pars_vertex> +#include <logdepthbuf_pars_vertex> +#include <clipping_planes_pars_vertex> +void main() { + #include <uv_vertex> + #include <color_vertex> + #include <morphinstance_vertex> + #include <morphcolor_vertex> + #include <batching_vertex> + #include <beginnormal_vertex> + #include <morphnormal_vertex> + #include <skinbase_vertex> + #include <skinnormal_vertex> + #include <defaultnormal_vertex> + #include <normal_vertex> + #include <begin_vertex> + #include <morphtarget_vertex> + #include <skinning_vertex> + #include <displacementmap_vertex> + #include <project_vertex> + #include <logdepthbuf_vertex> + #include <clipping_planes_vertex> + #include <fog_vertex> + vViewPosition = - mvPosition.xyz; +}`,UC=`#define MATCAP +uniform vec3 diffuse; +uniform float opacity; +uniform sampler2D matcap; +varying vec3 vViewPosition; +#include <common> +#include <dithering_pars_fragment> +#include <color_pars_fragment> +#include <uv_pars_fragment> +#include <map_pars_fragment> +#include <alphamap_pars_fragment> +#include <alphatest_pars_fragment> +#include <alphahash_pars_fragment> +#include <fog_pars_fragment> +#include <normal_pars_fragment> +#include <bumpmap_pars_fragment> +#include <normalmap_pars_fragment> +#include <logdepthbuf_pars_fragment> +#include <clipping_planes_pars_fragment> +void main() { + vec4 diffuseColor = vec4( diffuse, opacity ); + #include <clipping_planes_fragment> + #include <logdepthbuf_fragment> + #include <map_fragment> + #include <color_fragment> + #include <alphamap_fragment> + #include <alphatest_fragment> + #include <alphahash_fragment> + #include <normal_fragment_begin> + #include <normal_fragment_maps> + vec3 viewDir = normalize( vViewPosition ); + vec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) ); + vec3 y = cross( viewDir, x ); + vec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5; + #ifdef USE_MATCAP + vec4 matcapColor = texture2D( matcap, uv ); + #else + vec4 matcapColor = vec4( vec3( mix( 0.2, 0.8, uv.y ) ), 1.0 ); + #endif + vec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb; + #include <opaque_fragment> + #include <tonemapping_fragment> + #include <colorspace_fragment> + #include <fog_fragment> + #include <premultiplied_alpha_fragment> + #include <dithering_fragment> +}`,OC=`#define NORMAL +#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE ) + varying vec3 vViewPosition; +#endif +#include <common> +#include <batching_pars_vertex> +#include <uv_pars_vertex> +#include <displacementmap_pars_vertex> +#include <normal_pars_vertex> +#include <morphtarget_pars_vertex> +#include <skinning_pars_vertex> +#include <logdepthbuf_pars_vertex> +#include <clipping_planes_pars_vertex> +void main() { + #include <uv_vertex> + #include <batching_vertex> + #include <beginnormal_vertex> + #include <morphinstance_vertex> + #include <morphnormal_vertex> + #include <skinbase_vertex> + #include <skinnormal_vertex> + #include <defaultnormal_vertex> + #include <normal_vertex> + #include <begin_vertex> + #include <morphtarget_vertex> + #include <skinning_vertex> + #include <displacementmap_vertex> + #include <project_vertex> + #include <logdepthbuf_vertex> + #include <clipping_planes_vertex> +#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE ) + vViewPosition = - mvPosition.xyz; +#endif +}`,PC=`#define NORMAL +uniform float opacity; +#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE ) + varying vec3 vViewPosition; +#endif +#include <uv_pars_fragment> +#include <normal_pars_fragment> +#include <bumpmap_pars_fragment> +#include <normalmap_pars_fragment> +#include <logdepthbuf_pars_fragment> +#include <clipping_planes_pars_fragment> +void main() { + vec4 diffuseColor = vec4( 0.0, 0.0, 0.0, opacity ); + #include <clipping_planes_fragment> + #include <logdepthbuf_fragment> + #include <normal_fragment_begin> + #include <normal_fragment_maps> + gl_FragColor = vec4( normalize( normal ) * 0.5 + 0.5, diffuseColor.a ); + #ifdef OPAQUE + gl_FragColor.a = 1.0; + #endif +}`,zC=`#define PHONG +varying vec3 vViewPosition; +#include <common> +#include <batching_pars_vertex> +#include <uv_pars_vertex> +#include <displacementmap_pars_vertex> +#include <envmap_pars_vertex> +#include <color_pars_vertex> +#include <fog_pars_vertex> +#include <normal_pars_vertex> +#include <morphtarget_pars_vertex> +#include <skinning_pars_vertex> +#include <shadowmap_pars_vertex> +#include <logdepthbuf_pars_vertex> +#include <clipping_planes_pars_vertex> +void main() { + #include <uv_vertex> + #include <color_vertex> + #include <morphcolor_vertex> + #include <batching_vertex> + #include <beginnormal_vertex> + #include <morphinstance_vertex> + #include <morphnormal_vertex> + #include <skinbase_vertex> + #include <skinnormal_vertex> + #include <defaultnormal_vertex> + #include <normal_vertex> + #include <begin_vertex> + #include <morphtarget_vertex> + #include <skinning_vertex> + #include <displacementmap_vertex> + #include <project_vertex> + #include <logdepthbuf_vertex> + #include <clipping_planes_vertex> + vViewPosition = - mvPosition.xyz; + #include <worldpos_vertex> + #include <envmap_vertex> + #include <shadowmap_vertex> + #include <fog_vertex> +}`,jC=`#define PHONG +uniform vec3 diffuse; +uniform vec3 emissive; +uniform vec3 specular; +uniform float shininess; +uniform float opacity; +#include <common> +#include <dithering_pars_fragment> +#include <color_pars_fragment> +#include <uv_pars_fragment> +#include <map_pars_fragment> +#include <alphamap_pars_fragment> +#include <alphatest_pars_fragment> +#include <alphahash_pars_fragment> +#include <aomap_pars_fragment> +#include <lightmap_pars_fragment> +#include <emissivemap_pars_fragment> +#include <cube_uv_reflection_fragment> +#include <envmap_common_pars_fragment> +#include <envmap_pars_fragment> +#include <envmap_physical_pars_fragment> +#include <fog_pars_fragment> +#include <bsdfs> +#include <lights_pars_begin> +#include <normal_pars_fragment> +#include <lights_phong_pars_fragment> +#include <shadowmap_pars_fragment> +#include <bumpmap_pars_fragment> +#include <normalmap_pars_fragment> +#include <specularmap_pars_fragment> +#include <logdepthbuf_pars_fragment> +#include <clipping_planes_pars_fragment> +void main() { + vec4 diffuseColor = vec4( diffuse, opacity ); + #include <clipping_planes_fragment> + ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); + vec3 totalEmissiveRadiance = emissive; + #include <logdepthbuf_fragment> + #include <map_fragment> + #include <color_fragment> + #include <alphamap_fragment> + #include <alphatest_fragment> + #include <alphahash_fragment> + #include <specularmap_fragment> + #include <normal_fragment_begin> + #include <normal_fragment_maps> + #include <emissivemap_fragment> + #include <lights_phong_fragment> + #include <lights_fragment_begin> + #include <lights_fragment_maps> + #include <lights_fragment_end> + #include <aomap_fragment> + vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance; + #include <envmap_fragment> + #include <opaque_fragment> + #include <tonemapping_fragment> + #include <colorspace_fragment> + #include <fog_fragment> + #include <premultiplied_alpha_fragment> + #include <dithering_fragment> +}`,kC=`#define STANDARD +varying vec3 vViewPosition; +#ifdef USE_TRANSMISSION + varying vec3 vWorldPosition; +#endif +#include <common> +#include <batching_pars_vertex> +#include <uv_pars_vertex> +#include <displacementmap_pars_vertex> +#include <color_pars_vertex> +#include <fog_pars_vertex> +#include <normal_pars_vertex> +#include <morphtarget_pars_vertex> +#include <skinning_pars_vertex> +#include <shadowmap_pars_vertex> +#include <logdepthbuf_pars_vertex> +#include <clipping_planes_pars_vertex> +void main() { + #include <uv_vertex> + #include <color_vertex> + #include <morphinstance_vertex> + #include <morphcolor_vertex> + #include <batching_vertex> + #include <beginnormal_vertex> + #include <morphnormal_vertex> + #include <skinbase_vertex> + #include <skinnormal_vertex> + #include <defaultnormal_vertex> + #include <normal_vertex> + #include <begin_vertex> + #include <morphtarget_vertex> + #include <skinning_vertex> + #include <displacementmap_vertex> + #include <project_vertex> + #include <logdepthbuf_vertex> + #include <clipping_planes_vertex> + vViewPosition = - mvPosition.xyz; + #include <worldpos_vertex> + #include <shadowmap_vertex> + #include <fog_vertex> +#ifdef USE_TRANSMISSION + vWorldPosition = worldPosition.xyz; +#endif +}`,IC=`#define STANDARD +#ifdef PHYSICAL + #define IOR + #define USE_SPECULAR +#endif +uniform vec3 diffuse; +uniform vec3 emissive; +uniform float roughness; +uniform float metalness; +uniform float opacity; +#ifdef IOR + uniform float ior; +#endif +#ifdef USE_SPECULAR + uniform float specularIntensity; + uniform vec3 specularColor; + #ifdef USE_SPECULAR_COLORMAP + uniform sampler2D specularColorMap; + #endif + #ifdef USE_SPECULAR_INTENSITYMAP + uniform sampler2D specularIntensityMap; + #endif +#endif +#ifdef USE_CLEARCOAT + uniform float clearcoat; + uniform float clearcoatRoughness; +#endif +#ifdef USE_DISPERSION + uniform float dispersion; +#endif +#ifdef USE_IRIDESCENCE + uniform float iridescence; + uniform float iridescenceIOR; + uniform float iridescenceThicknessMinimum; + uniform float iridescenceThicknessMaximum; +#endif +#ifdef USE_SHEEN + uniform vec3 sheenColor; + uniform float sheenRoughness; + #ifdef USE_SHEEN_COLORMAP + uniform sampler2D sheenColorMap; + #endif + #ifdef USE_SHEEN_ROUGHNESSMAP + uniform sampler2D sheenRoughnessMap; + #endif +#endif +#ifdef USE_ANISOTROPY + uniform vec2 anisotropyVector; + #ifdef USE_ANISOTROPYMAP + uniform sampler2D anisotropyMap; + #endif +#endif +varying vec3 vViewPosition; +#include <common> +#include <dithering_pars_fragment> +#include <color_pars_fragment> +#include <uv_pars_fragment> +#include <map_pars_fragment> +#include <alphamap_pars_fragment> +#include <alphatest_pars_fragment> +#include <alphahash_pars_fragment> +#include <aomap_pars_fragment> +#include <lightmap_pars_fragment> +#include <emissivemap_pars_fragment> +#include <iridescence_fragment> +#include <cube_uv_reflection_fragment> +#include <envmap_common_pars_fragment> +#include <envmap_physical_pars_fragment> +#include <fog_pars_fragment> +#include <lights_pars_begin> +#include <normal_pars_fragment> +#include <lights_physical_pars_fragment> +#include <transmission_pars_fragment> +#include <shadowmap_pars_fragment> +#include <bumpmap_pars_fragment> +#include <normalmap_pars_fragment> +#include <clearcoat_pars_fragment> +#include <iridescence_pars_fragment> +#include <roughnessmap_pars_fragment> +#include <metalnessmap_pars_fragment> +#include <logdepthbuf_pars_fragment> +#include <clipping_planes_pars_fragment> +void main() { + vec4 diffuseColor = vec4( diffuse, opacity ); + #include <clipping_planes_fragment> + ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); + vec3 totalEmissiveRadiance = emissive; + #include <logdepthbuf_fragment> + #include <map_fragment> + #include <color_fragment> + #include <alphamap_fragment> + #include <alphatest_fragment> + #include <alphahash_fragment> + #include <roughnessmap_fragment> + #include <metalnessmap_fragment> + #include <normal_fragment_begin> + #include <normal_fragment_maps> + #include <clearcoat_normal_fragment_begin> + #include <clearcoat_normal_fragment_maps> + #include <emissivemap_fragment> + #include <lights_physical_fragment> + #include <lights_fragment_begin> + #include <lights_fragment_maps> + #include <lights_fragment_end> + #include <aomap_fragment> + vec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse; + vec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular; + #include <transmission_fragment> + vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance; + #ifdef USE_SHEEN + + outgoingLight = outgoingLight + sheenSpecularDirect + sheenSpecularIndirect; + + #endif + #ifdef USE_CLEARCOAT + float dotNVcc = saturate( dot( geometryClearcoatNormal, geometryViewDir ) ); + vec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc ); + outgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + ( clearcoatSpecularDirect + clearcoatSpecularIndirect ) * material.clearcoat; + #endif + #include <opaque_fragment> + #include <tonemapping_fragment> + #include <colorspace_fragment> + #include <fog_fragment> + #include <premultiplied_alpha_fragment> + #include <dithering_fragment> +}`,BC=`#define TOON +varying vec3 vViewPosition; +#include <common> +#include <batching_pars_vertex> +#include <uv_pars_vertex> +#include <displacementmap_pars_vertex> +#include <color_pars_vertex> +#include <fog_pars_vertex> +#include <normal_pars_vertex> +#include <morphtarget_pars_vertex> +#include <skinning_pars_vertex> +#include <shadowmap_pars_vertex> +#include <logdepthbuf_pars_vertex> +#include <clipping_planes_pars_vertex> +void main() { + #include <uv_vertex> + #include <color_vertex> + #include <morphinstance_vertex> + #include <morphcolor_vertex> + #include <batching_vertex> + #include <beginnormal_vertex> + #include <morphnormal_vertex> + #include <skinbase_vertex> + #include <skinnormal_vertex> + #include <defaultnormal_vertex> + #include <normal_vertex> + #include <begin_vertex> + #include <morphtarget_vertex> + #include <skinning_vertex> + #include <displacementmap_vertex> + #include <project_vertex> + #include <logdepthbuf_vertex> + #include <clipping_planes_vertex> + vViewPosition = - mvPosition.xyz; + #include <worldpos_vertex> + #include <shadowmap_vertex> + #include <fog_vertex> +}`,FC=`#define TOON +uniform vec3 diffuse; +uniform vec3 emissive; +uniform float opacity; +#include <common> +#include <dithering_pars_fragment> +#include <color_pars_fragment> +#include <uv_pars_fragment> +#include <map_pars_fragment> +#include <alphamap_pars_fragment> +#include <alphatest_pars_fragment> +#include <alphahash_pars_fragment> +#include <aomap_pars_fragment> +#include <lightmap_pars_fragment> +#include <emissivemap_pars_fragment> +#include <gradientmap_pars_fragment> +#include <fog_pars_fragment> +#include <bsdfs> +#include <lights_pars_begin> +#include <normal_pars_fragment> +#include <lights_toon_pars_fragment> +#include <shadowmap_pars_fragment> +#include <bumpmap_pars_fragment> +#include <normalmap_pars_fragment> +#include <logdepthbuf_pars_fragment> +#include <clipping_planes_pars_fragment> +void main() { + vec4 diffuseColor = vec4( diffuse, opacity ); + #include <clipping_planes_fragment> + ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); + vec3 totalEmissiveRadiance = emissive; + #include <logdepthbuf_fragment> + #include <map_fragment> + #include <color_fragment> + #include <alphamap_fragment> + #include <alphatest_fragment> + #include <alphahash_fragment> + #include <normal_fragment_begin> + #include <normal_fragment_maps> + #include <emissivemap_fragment> + #include <lights_toon_fragment> + #include <lights_fragment_begin> + #include <lights_fragment_maps> + #include <lights_fragment_end> + #include <aomap_fragment> + vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; + #include <opaque_fragment> + #include <tonemapping_fragment> + #include <colorspace_fragment> + #include <fog_fragment> + #include <premultiplied_alpha_fragment> + #include <dithering_fragment> +}`,HC=`uniform float size; +uniform float scale; +#include <common> +#include <color_pars_vertex> +#include <fog_pars_vertex> +#include <morphtarget_pars_vertex> +#include <logdepthbuf_pars_vertex> +#include <clipping_planes_pars_vertex> +#ifdef USE_POINTS_UV + varying vec2 vUv; + uniform mat3 uvTransform; +#endif +void main() { + #ifdef USE_POINTS_UV + vUv = ( uvTransform * vec3( uv, 1 ) ).xy; + #endif + #include <color_vertex> + #include <morphinstance_vertex> + #include <morphcolor_vertex> + #include <begin_vertex> + #include <morphtarget_vertex> + #include <project_vertex> + gl_PointSize = size; + #ifdef USE_SIZEATTENUATION + bool isPerspective = isPerspectiveMatrix( projectionMatrix ); + if ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z ); + #endif + #include <logdepthbuf_vertex> + #include <clipping_planes_vertex> + #include <worldpos_vertex> + #include <fog_vertex> +}`,GC=`uniform vec3 diffuse; +uniform float opacity; +#include <common> +#include <color_pars_fragment> +#include <map_particle_pars_fragment> +#include <alphatest_pars_fragment> +#include <alphahash_pars_fragment> +#include <fog_pars_fragment> +#include <logdepthbuf_pars_fragment> +#include <clipping_planes_pars_fragment> +void main() { + vec4 diffuseColor = vec4( diffuse, opacity ); + #include <clipping_planes_fragment> + vec3 outgoingLight = vec3( 0.0 ); + #include <logdepthbuf_fragment> + #include <map_particle_fragment> + #include <color_fragment> + #include <alphatest_fragment> + #include <alphahash_fragment> + outgoingLight = diffuseColor.rgb; + #include <opaque_fragment> + #include <tonemapping_fragment> + #include <colorspace_fragment> + #include <fog_fragment> + #include <premultiplied_alpha_fragment> +}`,VC=`#include <common> +#include <batching_pars_vertex> +#include <fog_pars_vertex> +#include <morphtarget_pars_vertex> +#include <skinning_pars_vertex> +#include <logdepthbuf_pars_vertex> +#include <shadowmap_pars_vertex> +void main() { + #include <batching_vertex> + #include <beginnormal_vertex> + #include <morphinstance_vertex> + #include <morphnormal_vertex> + #include <skinbase_vertex> + #include <skinnormal_vertex> + #include <defaultnormal_vertex> + #include <begin_vertex> + #include <morphtarget_vertex> + #include <skinning_vertex> + #include <project_vertex> + #include <logdepthbuf_vertex> + #include <worldpos_vertex> + #include <shadowmap_vertex> + #include <fog_vertex> +}`,WC=`uniform vec3 color; +uniform float opacity; +#include <common> +#include <fog_pars_fragment> +#include <bsdfs> +#include <lights_pars_begin> +#include <logdepthbuf_pars_fragment> +#include <shadowmap_pars_fragment> +#include <shadowmask_pars_fragment> +void main() { + #include <logdepthbuf_fragment> + gl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) ); + #include <tonemapping_fragment> + #include <colorspace_fragment> + #include <fog_fragment> + #include <premultiplied_alpha_fragment> +}`,XC=`uniform float rotation; +uniform vec2 center; +#include <common> +#include <uv_pars_vertex> +#include <fog_pars_vertex> +#include <logdepthbuf_pars_vertex> +#include <clipping_planes_pars_vertex> +void main() { + #include <uv_vertex> + vec4 mvPosition = modelViewMatrix[ 3 ]; + vec2 scale = vec2( length( modelMatrix[ 0 ].xyz ), length( modelMatrix[ 1 ].xyz ) ); + #ifndef USE_SIZEATTENUATION + bool isPerspective = isPerspectiveMatrix( projectionMatrix ); + if ( isPerspective ) scale *= - mvPosition.z; + #endif + vec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale; + vec2 rotatedPosition; + rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y; + rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y; + mvPosition.xy += rotatedPosition; + gl_Position = projectionMatrix * mvPosition; + #include <logdepthbuf_vertex> + #include <clipping_planes_vertex> + #include <fog_vertex> +}`,qC=`uniform vec3 diffuse; +uniform float opacity; +#include <common> +#include <uv_pars_fragment> +#include <map_pars_fragment> +#include <alphamap_pars_fragment> +#include <alphatest_pars_fragment> +#include <alphahash_pars_fragment> +#include <fog_pars_fragment> +#include <logdepthbuf_pars_fragment> +#include <clipping_planes_pars_fragment> +void main() { + vec4 diffuseColor = vec4( diffuse, opacity ); + #include <clipping_planes_fragment> + vec3 outgoingLight = vec3( 0.0 ); + #include <logdepthbuf_fragment> + #include <map_fragment> + #include <alphamap_fragment> + #include <alphatest_fragment> + #include <alphahash_fragment> + outgoingLight = diffuseColor.rgb; + #include <opaque_fragment> + #include <tonemapping_fragment> + #include <colorspace_fragment> + #include <fog_fragment> +}`,at={alphahash_fragment:pw,alphahash_pars_fragment:mw,alphamap_fragment:gw,alphamap_pars_fragment:xw,alphatest_fragment:vw,alphatest_pars_fragment:_w,aomap_fragment:bw,aomap_pars_fragment:yw,batching_pars_vertex:Sw,batching_vertex:Mw,begin_vertex:Ew,beginnormal_vertex:Tw,bsdfs:Aw,iridescence_fragment:ww,bumpmap_pars_fragment:Nw,clipping_planes_fragment:Cw,clipping_planes_pars_fragment:Rw,clipping_planes_pars_vertex:Dw,clipping_planes_vertex:Lw,color_fragment:Uw,color_pars_fragment:Ow,color_pars_vertex:Pw,color_vertex:zw,common:jw,cube_uv_reflection_fragment:kw,defaultnormal_vertex:Iw,displacementmap_pars_vertex:Bw,displacementmap_vertex:Fw,emissivemap_fragment:Hw,emissivemap_pars_fragment:Gw,colorspace_fragment:Vw,colorspace_pars_fragment:Ww,envmap_fragment:Xw,envmap_common_pars_fragment:qw,envmap_pars_fragment:Yw,envmap_pars_vertex:$w,envmap_physical_pars_fragment:rN,envmap_vertex:Kw,fog_vertex:Zw,fog_pars_vertex:Qw,fog_fragment:Jw,fog_pars_fragment:eN,gradientmap_pars_fragment:tN,lightmap_pars_fragment:nN,lights_lambert_fragment:iN,lights_lambert_pars_fragment:aN,lights_pars_begin:sN,lights_toon_fragment:lN,lights_toon_pars_fragment:oN,lights_phong_fragment:cN,lights_phong_pars_fragment:uN,lights_physical_fragment:dN,lights_physical_pars_fragment:hN,lights_fragment_begin:fN,lights_fragment_maps:pN,lights_fragment_end:mN,lightprobes_pars_fragment:gN,logdepthbuf_fragment:xN,logdepthbuf_pars_fragment:vN,logdepthbuf_pars_vertex:_N,logdepthbuf_vertex:bN,map_fragment:yN,map_pars_fragment:SN,map_particle_fragment:MN,map_particle_pars_fragment:EN,metalnessmap_fragment:TN,metalnessmap_pars_fragment:AN,morphinstance_vertex:wN,morphcolor_vertex:NN,morphnormal_vertex:CN,morphtarget_pars_vertex:RN,morphtarget_vertex:DN,normal_fragment_begin:LN,normal_fragment_maps:UN,normal_pars_fragment:ON,normal_pars_vertex:PN,normal_vertex:zN,normalmap_pars_fragment:jN,clearcoat_normal_fragment_begin:kN,clearcoat_normal_fragment_maps:IN,clearcoat_pars_fragment:BN,iridescence_pars_fragment:FN,opaque_fragment:HN,packing:GN,premultiplied_alpha_fragment:VN,project_vertex:WN,dithering_fragment:XN,dithering_pars_fragment:qN,roughnessmap_fragment:YN,roughnessmap_pars_fragment:$N,shadowmap_pars_fragment:KN,shadowmap_pars_vertex:ZN,shadowmap_vertex:QN,shadowmask_pars_fragment:JN,skinbase_vertex:eC,skinning_pars_vertex:tC,skinning_vertex:nC,skinnormal_vertex:iC,specularmap_fragment:aC,specularmap_pars_fragment:sC,tonemapping_fragment:rC,tonemapping_pars_fragment:lC,transmission_fragment:oC,transmission_pars_fragment:cC,uv_pars_fragment:uC,uv_pars_vertex:dC,uv_vertex:hC,worldpos_vertex:fC,background_vert:pC,background_frag:mC,backgroundCube_vert:gC,backgroundCube_frag:xC,cube_vert:vC,cube_frag:_C,depth_vert:bC,depth_frag:yC,distance_vert:SC,distance_frag:MC,equirect_vert:EC,equirect_frag:TC,linedashed_vert:AC,linedashed_frag:wC,meshbasic_vert:NC,meshbasic_frag:CC,meshlambert_vert:RC,meshlambert_frag:DC,meshmatcap_vert:LC,meshmatcap_frag:UC,meshnormal_vert:OC,meshnormal_frag:PC,meshphong_vert:zC,meshphong_frag:jC,meshphysical_vert:kC,meshphysical_frag:IC,meshtoon_vert:BC,meshtoon_frag:FC,points_vert:HC,points_frag:GC,shadow_vert:VC,shadow_frag:WC,sprite_vert:XC,sprite_frag:qC},ze={common:{diffuse:{value:new gt(16777215)},opacity:{value:1},map:{value:null},mapTransform:{value:new tt},alphaMap:{value:null},alphaMapTransform:{value:new tt},alphaTest:{value:0}},specularmap:{specularMap:{value:null},specularMapTransform:{value:new tt}},envmap:{envMap:{value:null},envMapRotation:{value:new tt},reflectivity:{value:1},ior:{value:1.5},refractionRatio:{value:.98},dfgLUT:{value:null}},aomap:{aoMap:{value:null},aoMapIntensity:{value:1},aoMapTransform:{value:new tt}},lightmap:{lightMap:{value:null},lightMapIntensity:{value:1},lightMapTransform:{value:new tt}},bumpmap:{bumpMap:{value:null},bumpMapTransform:{value:new tt},bumpScale:{value:1}},normalmap:{normalMap:{value:null},normalMapTransform:{value:new tt},normalScale:{value:new Mt(1,1)}},displacementmap:{displacementMap:{value:null},displacementMapTransform:{value:new tt},displacementScale:{value:1},displacementBias:{value:0}},emissivemap:{emissiveMap:{value:null},emissiveMapTransform:{value:new tt}},metalnessmap:{metalnessMap:{value:null},metalnessMapTransform:{value:new tt}},roughnessmap:{roughnessMap:{value:null},roughnessMapTransform:{value:new tt}},gradientmap:{gradientMap:{value:null}},fog:{fogDensity:{value:25e-5},fogNear:{value:1},fogFar:{value:2e3},fogColor:{value:new gt(16777215)}},lights:{ambientLightColor:{value:[]},lightProbe:{value:[]},directionalLights:{value:[],properties:{direction:{},color:{}}},directionalLightShadows:{value:[],properties:{shadowIntensity:1,shadowBias:{},shadowNormalBias:{},shadowRadius:{},shadowMapSize:{}}},directionalShadowMatrix:{value:[]},spotLights:{value:[],properties:{color:{},position:{},direction:{},distance:{},coneCos:{},penumbraCos:{},decay:{}}},spotLightShadows:{value:[],properties:{shadowIntensity:1,shadowBias:{},shadowNormalBias:{},shadowRadius:{},shadowMapSize:{}}},spotLightMap:{value:[]},spotLightMatrix:{value:[]},pointLights:{value:[],properties:{color:{},position:{},decay:{},distance:{}}},pointLightShadows:{value:[],properties:{shadowIntensity:1,shadowBias:{},shadowNormalBias:{},shadowRadius:{},shadowMapSize:{},shadowCameraNear:{},shadowCameraFar:{}}},pointShadowMatrix:{value:[]},hemisphereLights:{value:[],properties:{direction:{},skyColor:{},groundColor:{}}},rectAreaLights:{value:[],properties:{color:{},position:{},width:{},height:{}}},ltc_1:{value:null},ltc_2:{value:null},probesSH:{value:null},probesMin:{value:new Z},probesMax:{value:new Z},probesResolution:{value:new Z}},points:{diffuse:{value:new gt(16777215)},opacity:{value:1},size:{value:1},scale:{value:1},map:{value:null},alphaMap:{value:null},alphaMapTransform:{value:new tt},alphaTest:{value:0},uvTransform:{value:new tt}},sprite:{diffuse:{value:new gt(16777215)},opacity:{value:1},center:{value:new Mt(.5,.5)},rotation:{value:0},map:{value:null},mapTransform:{value:new tt},alphaMap:{value:null},alphaMapTransform:{value:new tt},alphaTest:{value:0}}},Gi={basic:{uniforms:Nn([ze.common,ze.specularmap,ze.envmap,ze.aomap,ze.lightmap,ze.fog]),vertexShader:at.meshbasic_vert,fragmentShader:at.meshbasic_frag},lambert:{uniforms:Nn([ze.common,ze.specularmap,ze.envmap,ze.aomap,ze.lightmap,ze.emissivemap,ze.bumpmap,ze.normalmap,ze.displacementmap,ze.fog,ze.lights,{emissive:{value:new gt(0)},envMapIntensity:{value:1}}]),vertexShader:at.meshlambert_vert,fragmentShader:at.meshlambert_frag},phong:{uniforms:Nn([ze.common,ze.specularmap,ze.envmap,ze.aomap,ze.lightmap,ze.emissivemap,ze.bumpmap,ze.normalmap,ze.displacementmap,ze.fog,ze.lights,{emissive:{value:new gt(0)},specular:{value:new gt(1118481)},shininess:{value:30},envMapIntensity:{value:1}}]),vertexShader:at.meshphong_vert,fragmentShader:at.meshphong_frag},standard:{uniforms:Nn([ze.common,ze.envmap,ze.aomap,ze.lightmap,ze.emissivemap,ze.bumpmap,ze.normalmap,ze.displacementmap,ze.roughnessmap,ze.metalnessmap,ze.fog,ze.lights,{emissive:{value:new gt(0)},roughness:{value:1},metalness:{value:0},envMapIntensity:{value:1}}]),vertexShader:at.meshphysical_vert,fragmentShader:at.meshphysical_frag},toon:{uniforms:Nn([ze.common,ze.aomap,ze.lightmap,ze.emissivemap,ze.bumpmap,ze.normalmap,ze.displacementmap,ze.gradientmap,ze.fog,ze.lights,{emissive:{value:new gt(0)}}]),vertexShader:at.meshtoon_vert,fragmentShader:at.meshtoon_frag},matcap:{uniforms:Nn([ze.common,ze.bumpmap,ze.normalmap,ze.displacementmap,ze.fog,{matcap:{value:null}}]),vertexShader:at.meshmatcap_vert,fragmentShader:at.meshmatcap_frag},points:{uniforms:Nn([ze.points,ze.fog]),vertexShader:at.points_vert,fragmentShader:at.points_frag},dashed:{uniforms:Nn([ze.common,ze.fog,{scale:{value:1},dashSize:{value:1},totalSize:{value:2}}]),vertexShader:at.linedashed_vert,fragmentShader:at.linedashed_frag},depth:{uniforms:Nn([ze.common,ze.displacementmap]),vertexShader:at.depth_vert,fragmentShader:at.depth_frag},normal:{uniforms:Nn([ze.common,ze.bumpmap,ze.normalmap,ze.displacementmap,{opacity:{value:1}}]),vertexShader:at.meshnormal_vert,fragmentShader:at.meshnormal_frag},sprite:{uniforms:Nn([ze.sprite,ze.fog]),vertexShader:at.sprite_vert,fragmentShader:at.sprite_frag},background:{uniforms:{uvTransform:{value:new tt},t2D:{value:null},backgroundIntensity:{value:1}},vertexShader:at.background_vert,fragmentShader:at.background_frag},backgroundCube:{uniforms:{envMap:{value:null},backgroundBlurriness:{value:0},backgroundIntensity:{value:1},backgroundRotation:{value:new tt}},vertexShader:at.backgroundCube_vert,fragmentShader:at.backgroundCube_frag},cube:{uniforms:{tCube:{value:null},tFlip:{value:-1},opacity:{value:1}},vertexShader:at.cube_vert,fragmentShader:at.cube_frag},equirect:{uniforms:{tEquirect:{value:null}},vertexShader:at.equirect_vert,fragmentShader:at.equirect_frag},distance:{uniforms:Nn([ze.common,ze.displacementmap,{referencePosition:{value:new Z},nearDistance:{value:1},farDistance:{value:1e3}}]),vertexShader:at.distance_vert,fragmentShader:at.distance_frag},shadow:{uniforms:Nn([ze.lights,ze.fog,{color:{value:new gt(0)},opacity:{value:1}}]),vertexShader:at.shadow_vert,fragmentShader:at.shadow_frag}};Gi.physical={uniforms:Nn([Gi.standard.uniforms,{clearcoat:{value:0},clearcoatMap:{value:null},clearcoatMapTransform:{value:new tt},clearcoatNormalMap:{value:null},clearcoatNormalMapTransform:{value:new tt},clearcoatNormalScale:{value:new Mt(1,1)},clearcoatRoughness:{value:0},clearcoatRoughnessMap:{value:null},clearcoatRoughnessMapTransform:{value:new tt},dispersion:{value:0},iridescence:{value:0},iridescenceMap:{value:null},iridescenceMapTransform:{value:new tt},iridescenceIOR:{value:1.3},iridescenceThicknessMinimum:{value:100},iridescenceThicknessMaximum:{value:400},iridescenceThicknessMap:{value:null},iridescenceThicknessMapTransform:{value:new tt},sheen:{value:0},sheenColor:{value:new gt(0)},sheenColorMap:{value:null},sheenColorMapTransform:{value:new tt},sheenRoughness:{value:1},sheenRoughnessMap:{value:null},sheenRoughnessMapTransform:{value:new tt},transmission:{value:0},transmissionMap:{value:null},transmissionMapTransform:{value:new tt},transmissionSamplerSize:{value:new Mt},transmissionSamplerMap:{value:null},thickness:{value:0},thicknessMap:{value:null},thicknessMapTransform:{value:new tt},attenuationDistance:{value:0},attenuationColor:{value:new gt(0)},specularColor:{value:new gt(1,1,1)},specularColorMap:{value:null},specularColorMapTransform:{value:new tt},specularIntensity:{value:1},specularIntensityMap:{value:null},specularIntensityMapTransform:{value:new tt},anisotropyVector:{value:new Mt},anisotropyMap:{value:null},anisotropyMapTransform:{value:new tt}}]),vertexShader:at.meshphysical_vert,fragmentShader:at.meshphysical_frag};const iu={r:0,b:0,g:0},YC=new zt,IS=new tt;IS.set(-1,0,0,0,1,0,0,0,1);function $C(t,e,n,i,a,s){const l=new gt(0);let o=a===!0?0:1,c,u,h=null,g=0,d=null;function p(v){let y=v.isScene===!0?v.background:null;if(y&&y.isTexture){const b=v.backgroundBlurriness>0;y=e.get(y,b)}return y}function x(v){let y=!1;const b=p(v);b===null?m(l,o):b&&b.isColor&&(m(b,1),y=!0);const T=t.xr.getEnvironmentBlendMode();T==="additive"?n.buffers.color.setClear(0,0,0,1,s):T==="alpha-blend"&&n.buffers.color.setClear(0,0,0,0,s),(t.autoClear||y)&&(n.buffers.depth.setTest(!0),n.buffers.depth.setMask(!0),n.buffers.color.setMask(!0),t.clear(t.autoClearColor,t.autoClearDepth,t.autoClearStencil))}function _(v,y){const b=p(y);b&&(b.isCubeTexture||b.mapping===Fd)?(u===void 0&&(u=new Ei(new Js(1,1,1),new Gn({name:"BackgroundCubeMaterial",uniforms:Sl(Gi.backgroundCube.uniforms),vertexShader:Gi.backgroundCube.vertexShader,fragmentShader:Gi.backgroundCube.fragmentShader,side:Wn,depthTest:!1,depthWrite:!1,fog:!1,allowOverride:!1})),u.geometry.deleteAttribute("normal"),u.geometry.deleteAttribute("uv"),u.onBeforeRender=function(T,M,A){this.matrixWorld.copyPosition(A.matrixWorld)},Object.defineProperty(u.material,"envMap",{get:function(){return this.uniforms.envMap.value}}),i.update(u)),u.material.uniforms.envMap.value=b,u.material.uniforms.backgroundBlurriness.value=y.backgroundBlurriness,u.material.uniforms.backgroundIntensity.value=y.backgroundIntensity,u.material.uniforms.backgroundRotation.value.setFromMatrix4(YC.makeRotationFromEuler(y.backgroundRotation)).transpose(),b.isCubeTexture&&b.isRenderTargetTexture===!1&&u.material.uniforms.backgroundRotation.value.premultiply(IS),u.material.toneMapped=ft.getTransfer(b.colorSpace)!==Et,(h!==b||g!==b.version||d!==t.toneMapping)&&(u.material.needsUpdate=!0,h=b,g=b.version,d=t.toneMapping),u.layers.enableAll(),v.unshift(u,u.geometry,u.material,0,0,null)):b&&b.isTexture&&(c===void 0&&(c=new Ei(new Hd(2,2),new Gn({name:"BackgroundMaterial",uniforms:Sl(Gi.background.uniforms),vertexShader:Gi.background.vertexShader,fragmentShader:Gi.background.fragmentShader,side:ys,depthTest:!1,depthWrite:!1,fog:!1,allowOverride:!1})),c.geometry.deleteAttribute("normal"),Object.defineProperty(c.material,"map",{get:function(){return this.uniforms.t2D.value}}),i.update(c)),c.material.uniforms.t2D.value=b,c.material.uniforms.backgroundIntensity.value=y.backgroundIntensity,c.material.toneMapped=ft.getTransfer(b.colorSpace)!==Et,b.matrixAutoUpdate===!0&&b.updateMatrix(),c.material.uniforms.uvTransform.value.copy(b.matrix),(h!==b||g!==b.version||d!==t.toneMapping)&&(c.material.needsUpdate=!0,h=b,g=b.version,d=t.toneMapping),c.layers.enableAll(),v.unshift(c,c.geometry,c.material,0,0,null))}function m(v,y){v.getRGB(iu,zS(t)),n.buffers.color.setClear(iu.r,iu.g,iu.b,y,s)}function f(){u!==void 0&&(u.geometry.dispose(),u.material.dispose(),u=void 0),c!==void 0&&(c.geometry.dispose(),c.material.dispose(),c=void 0)}return{getClearColor:function(){return l},setClearColor:function(v,y=1){l.set(v),o=y,m(l,o)},getClearAlpha:function(){return o},setClearAlpha:function(v){o=v,m(l,o)},render:x,addToRenderList:_,dispose:f}}function KC(t,e){const n=t.getParameter(t.MAX_VERTEX_ATTRIBS),i={},a=d(null);let s=a,l=!1;function o(U,I,Y,k,B){let z=!1;const j=g(U,k,Y,I);s!==j&&(s=j,u(s.object)),z=p(U,k,Y,B),z&&x(U,k,Y,B),B!==null&&e.update(B,t.ELEMENT_ARRAY_BUFFER),(z||l)&&(l=!1,b(U,I,Y,k),B!==null&&t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,e.get(B).buffer))}function c(){return t.createVertexArray()}function u(U){return t.bindVertexArray(U)}function h(U){return t.deleteVertexArray(U)}function g(U,I,Y,k){const B=k.wireframe===!0;let z=i[I.id];z===void 0&&(z={},i[I.id]=z);const j=U.isInstancedMesh===!0?U.id:0;let X=z[j];X===void 0&&(X={},z[j]=X);let q=X[Y.id];q===void 0&&(q={},X[Y.id]=q);let ve=q[B];return ve===void 0&&(ve=d(c()),q[B]=ve),ve}function d(U){const I=[],Y=[],k=[];for(let B=0;B<n;B++)I[B]=0,Y[B]=0,k[B]=0;return{geometry:null,program:null,wireframe:!1,newAttributes:I,enabledAttributes:Y,attributeDivisors:k,object:U,attributes:{},index:null}}function p(U,I,Y,k){const B=s.attributes,z=I.attributes;let j=0;const X=Y.getAttributes();for(const q in X)if(X[q].location>=0){const he=B[q];let Ae=z[q];if(Ae===void 0&&(q==="instanceMatrix"&&U.instanceMatrix&&(Ae=U.instanceMatrix),q==="instanceColor"&&U.instanceColor&&(Ae=U.instanceColor)),he===void 0||he.attribute!==Ae||Ae&&he.data!==Ae.data)return!0;j++}return s.attributesNum!==j||s.index!==k}function x(U,I,Y,k){const B={},z=I.attributes;let j=0;const X=Y.getAttributes();for(const q in X)if(X[q].location>=0){let he=z[q];he===void 0&&(q==="instanceMatrix"&&U.instanceMatrix&&(he=U.instanceMatrix),q==="instanceColor"&&U.instanceColor&&(he=U.instanceColor));const Ae={};Ae.attribute=he,he&&he.data&&(Ae.data=he.data),B[q]=Ae,j++}s.attributes=B,s.attributesNum=j,s.index=k}function _(){const U=s.newAttributes;for(let I=0,Y=U.length;I<Y;I++)U[I]=0}function m(U){f(U,0)}function f(U,I){const Y=s.newAttributes,k=s.enabledAttributes,B=s.attributeDivisors;Y[U]=1,k[U]===0&&(t.enableVertexAttribArray(U),k[U]=1),B[U]!==I&&(t.vertexAttribDivisor(U,I),B[U]=I)}function v(){const U=s.newAttributes,I=s.enabledAttributes;for(let Y=0,k=I.length;Y<k;Y++)I[Y]!==U[Y]&&(t.disableVertexAttribArray(Y),I[Y]=0)}function y(U,I,Y,k,B,z,j){j===!0?t.vertexAttribIPointer(U,I,Y,B,z):t.vertexAttribPointer(U,I,Y,k,B,z)}function b(U,I,Y,k){_();const B=k.attributes,z=Y.getAttributes(),j=I.defaultAttributeValues;for(const X in z){const q=z[X];if(q.location>=0){let ve=B[X];if(ve===void 0&&(X==="instanceMatrix"&&U.instanceMatrix&&(ve=U.instanceMatrix),X==="instanceColor"&&U.instanceColor&&(ve=U.instanceColor)),ve!==void 0){const he=ve.normalized,Ae=ve.itemSize,$=e.get(ve);if($===void 0)continue;const pe=$.buffer,we=$.type,ne=$.bytesPerElement,ge=we===t.INT||we===t.UNSIGNED_INT||ve.gpuType===gg;if(ve.isInterleavedBufferAttribute){const ue=ve.data,Le=ue.stride,Ie=ve.offset;if(ue.isInstancedInterleavedBuffer){for(let Xe=0;Xe<q.locationSize;Xe++)f(q.location+Xe,ue.meshPerAttribute);U.isInstancedMesh!==!0&&k._maxInstanceCount===void 0&&(k._maxInstanceCount=ue.meshPerAttribute*ue.count)}else for(let Xe=0;Xe<q.locationSize;Xe++)m(q.location+Xe);t.bindBuffer(t.ARRAY_BUFFER,pe);for(let Xe=0;Xe<q.locationSize;Xe++)y(q.location+Xe,Ae/q.locationSize,we,he,Le*ne,(Ie+Ae/q.locationSize*Xe)*ne,ge)}else{if(ve.isInstancedBufferAttribute){for(let ue=0;ue<q.locationSize;ue++)f(q.location+ue,ve.meshPerAttribute);U.isInstancedMesh!==!0&&k._maxInstanceCount===void 0&&(k._maxInstanceCount=ve.meshPerAttribute*ve.count)}else for(let ue=0;ue<q.locationSize;ue++)m(q.location+ue);t.bindBuffer(t.ARRAY_BUFFER,pe);for(let ue=0;ue<q.locationSize;ue++)y(q.location+ue,Ae/q.locationSize,we,he,Ae*ne,Ae/q.locationSize*ue*ne,ge)}}else if(j!==void 0){const he=j[X];if(he!==void 0)switch(he.length){case 2:t.vertexAttrib2fv(q.location,he);break;case 3:t.vertexAttrib3fv(q.location,he);break;case 4:t.vertexAttrib4fv(q.location,he);break;default:t.vertexAttrib1fv(q.location,he)}}}}v()}function T(){N();for(const U in i){const I=i[U];for(const Y in I){const k=I[Y];for(const B in k){const z=k[B];for(const j in z)h(z[j].object),delete z[j];delete k[B]}}delete i[U]}}function M(U){if(i[U.id]===void 0)return;const I=i[U.id];for(const Y in I){const k=I[Y];for(const B in k){const z=k[B];for(const j in z)h(z[j].object),delete z[j];delete k[B]}}delete i[U.id]}function A(U){for(const I in i){const Y=i[I];for(const k in Y){const B=Y[k];if(B[U.id]===void 0)continue;const z=B[U.id];for(const j in z)h(z[j].object),delete z[j];delete B[U.id]}}}function S(U){for(const I in i){const Y=i[I],k=U.isInstancedMesh===!0?U.id:0,B=Y[k];if(B!==void 0){for(const z in B){const j=B[z];for(const X in j)h(j[X].object),delete j[X];delete B[z]}delete Y[k],Object.keys(Y).length===0&&delete i[I]}}}function N(){D(),l=!0,s!==a&&(s=a,u(s.object))}function D(){a.geometry=null,a.program=null,a.wireframe=!1}return{setup:o,reset:N,resetDefaultState:D,dispose:T,releaseStatesOfGeometry:M,releaseStatesOfObject:S,releaseStatesOfProgram:A,initAttributes:_,enableAttribute:m,disableUnusedAttributes:v}}function ZC(t,e,n){let i;function a(c){i=c}function s(c,u){t.drawArrays(i,c,u),n.update(u,i,1)}function l(c,u,h){h!==0&&(t.drawArraysInstanced(i,c,u,h),n.update(u,i,h))}function o(c,u,h){if(h===0)return;e.get("WEBGL_multi_draw").multiDrawArraysWEBGL(i,c,0,u,0,h);let d=0;for(let p=0;p<h;p++)d+=u[p];n.update(d,i,1)}this.setMode=a,this.render=s,this.renderInstances=l,this.renderMultiDraw=o}function QC(t,e,n,i){let a;function s(){if(a!==void 0)return a;if(e.has("EXT_texture_filter_anisotropic")===!0){const A=e.get("EXT_texture_filter_anisotropic");a=t.getParameter(A.MAX_TEXTURE_MAX_ANISOTROPY_EXT)}else a=0;return a}function l(A){return!(A!==Ui&&i.convert(A)!==t.getParameter(t.IMPLEMENTATION_COLOR_READ_FORMAT))}function o(A){const S=A===Ra&&(e.has("EXT_color_buffer_half_float")||e.has("EXT_color_buffer_float"));return!(A!==vi&&i.convert(A)!==t.getParameter(t.IMPLEMENTATION_COLOR_READ_TYPE)&&A!==Li&&!S)}function c(A){if(A==="highp"){if(t.getShaderPrecisionFormat(t.VERTEX_SHADER,t.HIGH_FLOAT).precision>0&&t.getShaderPrecisionFormat(t.FRAGMENT_SHADER,t.HIGH_FLOAT).precision>0)return"highp";A="mediump"}return A==="mediump"&&t.getShaderPrecisionFormat(t.VERTEX_SHADER,t.MEDIUM_FLOAT).precision>0&&t.getShaderPrecisionFormat(t.FRAGMENT_SHADER,t.MEDIUM_FLOAT).precision>0?"mediump":"lowp"}let u=n.precision!==void 0?n.precision:"highp";const h=c(u);h!==u&&(Qe("WebGLRenderer:",u,"not supported, using",h,"instead."),u=h);const g=n.logarithmicDepthBuffer===!0,d=n.reversedDepthBuffer===!0&&e.has("EXT_clip_control");n.reversedDepthBuffer===!0&&d===!1&&Qe("WebGLRenderer: Unable to use reversed depth buffer due to missing EXT_clip_control extension. Fallback to default depth buffer.");const p=t.getParameter(t.MAX_TEXTURE_IMAGE_UNITS),x=t.getParameter(t.MAX_VERTEX_TEXTURE_IMAGE_UNITS),_=t.getParameter(t.MAX_TEXTURE_SIZE),m=t.getParameter(t.MAX_CUBE_MAP_TEXTURE_SIZE),f=t.getParameter(t.MAX_VERTEX_ATTRIBS),v=t.getParameter(t.MAX_VERTEX_UNIFORM_VECTORS),y=t.getParameter(t.MAX_VARYING_VECTORS),b=t.getParameter(t.MAX_FRAGMENT_UNIFORM_VECTORS),T=t.getParameter(t.MAX_SAMPLES),M=t.getParameter(t.SAMPLES);return{isWebGL2:!0,getMaxAnisotropy:s,getMaxPrecision:c,textureFormatReadable:l,textureTypeReadable:o,precision:u,logarithmicDepthBuffer:g,reversedDepthBuffer:d,maxTextures:p,maxVertexTextures:x,maxTextureSize:_,maxCubemapSize:m,maxAttributes:f,maxVertexUniforms:v,maxVaryings:y,maxFragmentUniforms:b,maxSamples:T,samples:M}}function JC(t){const e=this;let n=null,i=0,a=!1,s=!1;const l=new Ps,o=new tt,c={value:null,needsUpdate:!1};this.uniform=c,this.numPlanes=0,this.numIntersection=0,this.init=function(g,d){const p=g.length!==0||d||i!==0||a;return a=d,i=g.length,p},this.beginShadows=function(){s=!0,h(null)},this.endShadows=function(){s=!1},this.setGlobalState=function(g,d){n=h(g,d,0)},this.setState=function(g,d,p){const x=g.clippingPlanes,_=g.clipIntersection,m=g.clipShadows,f=t.get(g);if(!a||x===null||x.length===0||s&&!m)s?h(null):u();else{const v=s?0:i,y=v*4;let b=f.clippingState||null;c.value=b,b=h(x,d,y,p);for(let T=0;T!==y;++T)b[T]=n[T];f.clippingState=b,this.numIntersection=_?this.numPlanes:0,this.numPlanes+=v}};function u(){c.value!==n&&(c.value=n,c.needsUpdate=i>0),e.numPlanes=i,e.numIntersection=0}function h(g,d,p,x){const _=g!==null?g.length:0;let m=null;if(_!==0){if(m=c.value,x!==!0||m===null){const f=p+_*4,v=d.matrixWorldInverse;o.getNormalMatrix(v),(m===null||m.length<f)&&(m=new Float32Array(f));for(let y=0,b=p;y!==_;++y,b+=4)l.copy(g[y]).applyMatrix4(v,o),l.normal.toArray(m,b),m[b+3]=l.constant}c.value=m,c.needsUpdate=!0}return e.numPlanes=_,e.numIntersection=0,m}}const ss=4,n0=[.125,.215,.35,.446,.526,.582],js=20,eR=256,Ql=new wg,i0=new gt;let sf=null,rf=0,lf=0,of=!1;const tR=new Z;class a0{constructor(e){this._renderer=e,this._pingPongRenderTarget=null,this._lodMax=0,this._cubeSize=0,this._sizeLods=[],this._sigmas=[],this._lodMeshes=[],this._backgroundBox=null,this._cubemapMaterial=null,this._equirectMaterial=null,this._blurMaterial=null,this._ggxMaterial=null}fromScene(e,n=0,i=.1,a=100,s={}){const{size:l=256,position:o=tR}=s;sf=this._renderer.getRenderTarget(),rf=this._renderer.getActiveCubeFace(),lf=this._renderer.getActiveMipmapLevel(),of=this._renderer.xr.enabled,this._renderer.xr.enabled=!1,this._setSize(l);const c=this._allocateTargets();return c.depthBuffer=!0,this._sceneToCubeUV(e,i,a,c,o),n>0&&this._blur(c,0,0,n),this._applyPMREM(c),this._cleanup(c),c}fromEquirectangular(e,n=null){return this._fromTexture(e,n)}fromCubemap(e,n=null){return this._fromTexture(e,n)}compileCubemapShader(){this._cubemapMaterial===null&&(this._cubemapMaterial=l0(),this._compileMaterial(this._cubemapMaterial))}compileEquirectangularShader(){this._equirectMaterial===null&&(this._equirectMaterial=r0(),this._compileMaterial(this._equirectMaterial))}dispose(){this._dispose(),this._cubemapMaterial!==null&&this._cubemapMaterial.dispose(),this._equirectMaterial!==null&&this._equirectMaterial.dispose(),this._backgroundBox!==null&&(this._backgroundBox.geometry.dispose(),this._backgroundBox.material.dispose())}_setSize(e){this._lodMax=Math.floor(Math.log2(e)),this._cubeSize=Math.pow(2,this._lodMax)}_dispose(){this._blurMaterial!==null&&this._blurMaterial.dispose(),this._ggxMaterial!==null&&this._ggxMaterial.dispose(),this._pingPongRenderTarget!==null&&this._pingPongRenderTarget.dispose();for(let e=0;e<this._lodMeshes.length;e++)this._lodMeshes[e].geometry.dispose()}_cleanup(e){this._renderer.setRenderTarget(sf,rf,lf),this._renderer.xr.enabled=of,e.scissorTest=!1,Cr(e,0,0,e.width,e.height)}_fromTexture(e,n){e.mapping===Ks||e.mapping===bl?this._setSize(e.image.length===0?16:e.image[0].width||e.image[0].image.width):this._setSize(e.image.width/4),sf=this._renderer.getRenderTarget(),rf=this._renderer.getActiveCubeFace(),lf=this._renderer.getActiveMipmapLevel(),of=this._renderer.xr.enabled,this._renderer.xr.enabled=!1;const i=n||this._allocateTargets();return this._textureToCubeUV(e,i),this._applyPMREM(i),this._cleanup(i),i}_allocateTargets(){const e=3*Math.max(this._cubeSize,112),n=4*this._cubeSize,i={magFilter:wn,minFilter:wn,generateMipmaps:!1,type:Ra,format:Ui,colorSpace:dd,depthBuffer:!1},a=s0(e,n,i);if(this._pingPongRenderTarget===null||this._pingPongRenderTarget.width!==e||this._pingPongRenderTarget.height!==n){this._pingPongRenderTarget!==null&&this._dispose(),this._pingPongRenderTarget=s0(e,n,i);const{_lodMax:s}=this;({lodMeshes:this._lodMeshes,sizeLods:this._sizeLods,sigmas:this._sigmas}=nR(s)),this._blurMaterial=aR(s,e,n),this._ggxMaterial=iR(s,e,n)}return a}_compileMaterial(e){const n=new Ei(new qn,e);this._renderer.compile(n,Ql)}_sceneToCubeUV(e,n,i,a,s){const c=new Di(90,1,n,i),u=[1,-1,1,1,1,1],h=[1,1,1,-1,-1,-1],g=this._renderer,d=g.autoClear,p=g.toneMapping;g.getClearColor(i0),g.toneMapping=$i,g.autoClear=!1,g.state.buffers.depth.getReversed()&&(g.setRenderTarget(a),g.clearDepth(),g.setRenderTarget(null)),this._backgroundBox===null&&(this._backgroundBox=new Ei(new Js,new RS({name:"PMREM.Background",side:Wn,depthWrite:!1,depthTest:!1})));const _=this._backgroundBox,m=_.material;let f=!1;const v=e.background;v?v.isColor&&(m.color.copy(v),e.background=null,f=!0):(m.color.copy(i0),f=!0);for(let y=0;y<6;y++){const b=y%3;b===0?(c.up.set(0,u[y],0),c.position.set(s.x,s.y,s.z),c.lookAt(s.x+h[y],s.y,s.z)):b===1?(c.up.set(0,0,u[y]),c.position.set(s.x,s.y,s.z),c.lookAt(s.x,s.y+h[y],s.z)):(c.up.set(0,u[y],0),c.position.set(s.x,s.y,s.z),c.lookAt(s.x,s.y,s.z+h[y]));const T=this._cubeSize;Cr(a,b*T,y>2?T:0,T,T),g.setRenderTarget(a),f&&g.render(_,c),g.render(e,c)}g.toneMapping=p,g.autoClear=d,e.background=v}_textureToCubeUV(e,n){const i=this._renderer,a=e.mapping===Ks||e.mapping===bl;a?(this._cubemapMaterial===null&&(this._cubemapMaterial=l0()),this._cubemapMaterial.uniforms.flipEnvMap.value=e.isRenderTargetTexture===!1?-1:1):this._equirectMaterial===null&&(this._equirectMaterial=r0());const s=a?this._cubemapMaterial:this._equirectMaterial,l=this._lodMeshes[0];l.material=s;const o=s.uniforms;o.envMap.value=e;const c=this._cubeSize;Cr(n,0,0,3*c,2*c),i.setRenderTarget(n),i.render(l,Ql)}_applyPMREM(e){const n=this._renderer,i=n.autoClear;n.autoClear=!1;const a=this._lodMeshes.length;for(let s=1;s<a;s++)this._applyGGXFilter(e,s-1,s);n.autoClear=i}_applyGGXFilter(e,n,i){const a=this._renderer,s=this._pingPongRenderTarget,l=this._ggxMaterial,o=this._lodMeshes[i];o.material=l;const c=l.uniforms,u=i/(this._lodMeshes.length-1),h=n/(this._lodMeshes.length-1),g=Math.sqrt(u*u-h*h),d=0+u*1.25,p=g*d,{_lodMax:x}=this,_=this._sizeLods[i],m=3*_*(i>x-ss?i-x+ss:0),f=4*(this._cubeSize-_);c.envMap.value=e.texture,c.roughness.value=p,c.mipInt.value=x-n,Cr(s,m,f,3*_,2*_),a.setRenderTarget(s),a.render(o,Ql),c.envMap.value=s.texture,c.roughness.value=0,c.mipInt.value=x-i,Cr(e,m,f,3*_,2*_),a.setRenderTarget(e),a.render(o,Ql)}_blur(e,n,i,a,s){const l=this._pingPongRenderTarget;this._halfBlur(e,l,n,i,a,"latitudinal",s),this._halfBlur(l,e,i,i,a,"longitudinal",s)}_halfBlur(e,n,i,a,s,l,o){const c=this._renderer,u=this._blurMaterial;l!=="latitudinal"&&l!=="longitudinal"&&bt("blur direction must be either latitudinal or longitudinal!");const h=3,g=this._lodMeshes[a];g.material=u;const d=u.uniforms,p=this._sizeLods[i]-1,x=isFinite(s)?Math.PI/(2*p):2*Math.PI/(2*js-1),_=s/x,m=isFinite(s)?1+Math.floor(h*_):js;m>js&&Qe(`sigmaRadians, ${s}, is too large and will clip, as it requested ${m} samples when the maximum is set to ${js}`);const f=[];let v=0;for(let A=0;A<js;++A){const S=A/_,N=Math.exp(-S*S/2);f.push(N),A===0?v+=N:A<m&&(v+=2*N)}for(let A=0;A<f.length;A++)f[A]=f[A]/v;d.envMap.value=e.texture,d.samples.value=m,d.weights.value=f,d.latitudinal.value=l==="latitudinal",o&&(d.poleAxis.value=o);const{_lodMax:y}=this;d.dTheta.value=x,d.mipInt.value=y-i;const b=this._sizeLods[a],T=3*b*(a>y-ss?a-y+ss:0),M=4*(this._cubeSize-b);Cr(n,T,M,3*b,2*b),c.setRenderTarget(n),c.render(g,Ql)}}function nR(t){const e=[],n=[],i=[];let a=t;const s=t-ss+1+n0.length;for(let l=0;l<s;l++){const o=Math.pow(2,a);e.push(o);let c=1/o;l>t-ss?c=n0[l-t+ss-1]:l===0&&(c=0),n.push(c);const u=1/(o-2),h=-u,g=1+u,d=[h,h,g,h,g,g,h,h,g,g,h,g],p=6,x=6,_=3,m=2,f=1,v=new Float32Array(_*x*p),y=new Float32Array(m*x*p),b=new Float32Array(f*x*p);for(let M=0;M<p;M++){const A=M%3*2/3-1,S=M>2?0:-1,N=[A,S,0,A+2/3,S,0,A+2/3,S+1,0,A,S,0,A+2/3,S+1,0,A,S+1,0];v.set(N,_*x*M),y.set(d,m*x*M);const D=[M,M,M,M,M,M];b.set(D,f*x*M)}const T=new qn;T.setAttribute("position",new on(v,_)),T.setAttribute("uv",new on(y,m)),T.setAttribute("faceIndex",new on(b,f)),i.push(new Ei(T,null)),a>ss&&a--}return{lodMeshes:i,sizeLods:e,sigmas:n}}function s0(t,e,n){const i=new Ki(t,e,n);return i.texture.mapping=Fd,i.texture.name="PMREM.cubeUv",i.scissorTest=!0,i}function Cr(t,e,n,i,a){t.viewport.set(e,n,i,a),t.scissor.set(e,n,i,a)}function iR(t,e,n){return new Gn({name:"PMREMGGXConvolution",defines:{GGX_SAMPLES:eR,CUBEUV_TEXEL_WIDTH:1/e,CUBEUV_TEXEL_HEIGHT:1/n,CUBEUV_MAX_MIP:`${t}.0`},uniforms:{envMap:{value:null},roughness:{value:0},mipInt:{value:0}},vertexShader:Gd(),fragmentShader:` + + precision highp float; + precision highp int; + + varying vec3 vOutputDirection; + + uniform sampler2D envMap; + uniform float roughness; + uniform float mipInt; + + #define ENVMAP_TYPE_CUBE_UV + #include <cube_uv_reflection_fragment> + + #define PI 3.14159265359 + + // Van der Corput radical inverse + float radicalInverse_VdC(uint bits) { + bits = (bits << 16u) | (bits >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + return float(bits) * 2.3283064365386963e-10; // / 0x100000000 + } + + // Hammersley sequence + vec2 hammersley(uint i, uint N) { + return vec2(float(i) / float(N), radicalInverse_VdC(i)); + } + + // GGX VNDF importance sampling (Eric Heitz 2018) + // "Sampling the GGX Distribution of Visible Normals" + // https://jcgt.org/published/0007/04/01/ + vec3 importanceSampleGGX_VNDF(vec2 Xi, vec3 V, float roughness) { + float alpha = roughness * roughness; + + // Section 4.1: Orthonormal basis + vec3 T1 = vec3(1.0, 0.0, 0.0); + vec3 T2 = cross(V, T1); + + // Section 4.2: Parameterization of projected area + float r = sqrt(Xi.x); + float phi = 2.0 * PI * Xi.y; + float t1 = r * cos(phi); + float t2 = r * sin(phi); + float s = 0.5 * (1.0 + V.z); + t2 = (1.0 - s) * sqrt(1.0 - t1 * t1) + s * t2; + + // Section 4.3: Reprojection onto hemisphere + vec3 Nh = t1 * T1 + t2 * T2 + sqrt(max(0.0, 1.0 - t1 * t1 - t2 * t2)) * V; + + // Section 3.4: Transform back to ellipsoid configuration + return normalize(vec3(alpha * Nh.x, alpha * Nh.y, max(0.0, Nh.z))); + } + + void main() { + vec3 N = normalize(vOutputDirection); + vec3 V = N; // Assume view direction equals normal for pre-filtering + + vec3 prefilteredColor = vec3(0.0); + float totalWeight = 0.0; + + // For very low roughness, just sample the environment directly + if (roughness < 0.001) { + gl_FragColor = vec4(bilinearCubeUV(envMap, N, mipInt), 1.0); + return; + } + + // Tangent space basis for VNDF sampling + vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); + vec3 tangent = normalize(cross(up, N)); + vec3 bitangent = cross(N, tangent); + + for(uint i = 0u; i < uint(GGX_SAMPLES); i++) { + vec2 Xi = hammersley(i, uint(GGX_SAMPLES)); + + // For PMREM, V = N, so in tangent space V is always (0, 0, 1) + vec3 H_tangent = importanceSampleGGX_VNDF(Xi, vec3(0.0, 0.0, 1.0), roughness); + + // Transform H back to world space + vec3 H = normalize(tangent * H_tangent.x + bitangent * H_tangent.y + N * H_tangent.z); + vec3 L = normalize(2.0 * dot(V, H) * H - V); + + float NdotL = max(dot(N, L), 0.0); + + if(NdotL > 0.0) { + // Sample environment at fixed mip level + // VNDF importance sampling handles the distribution filtering + vec3 sampleColor = bilinearCubeUV(envMap, L, mipInt); + + // Weight by NdotL for the split-sum approximation + // VNDF PDF naturally accounts for the visible microfacet distribution + prefilteredColor += sampleColor * NdotL; + totalWeight += NdotL; + } + } + + if (totalWeight > 0.0) { + prefilteredColor = prefilteredColor / totalWeight; + } + + gl_FragColor = vec4(prefilteredColor, 1.0); + } + `,blending:Ma,depthTest:!1,depthWrite:!1})}function aR(t,e,n){const i=new Float32Array(js),a=new Z(0,1,0);return new Gn({name:"SphericalGaussianBlur",defines:{n:js,CUBEUV_TEXEL_WIDTH:1/e,CUBEUV_TEXEL_HEIGHT:1/n,CUBEUV_MAX_MIP:`${t}.0`},uniforms:{envMap:{value:null},samples:{value:1},weights:{value:i},latitudinal:{value:!1},dTheta:{value:0},mipInt:{value:0},poleAxis:{value:a}},vertexShader:Gd(),fragmentShader:` + + precision mediump float; + precision mediump int; + + varying vec3 vOutputDirection; + + uniform sampler2D envMap; + uniform int samples; + uniform float weights[ n ]; + uniform bool latitudinal; + uniform float dTheta; + uniform float mipInt; + uniform vec3 poleAxis; + + #define ENVMAP_TYPE_CUBE_UV + #include <cube_uv_reflection_fragment> + + vec3 getSample( float theta, vec3 axis ) { + + float cosTheta = cos( theta ); + // Rodrigues' axis-angle rotation + vec3 sampleDirection = vOutputDirection * cosTheta + + cross( axis, vOutputDirection ) * sin( theta ) + + axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta ); + + return bilinearCubeUV( envMap, sampleDirection, mipInt ); + + } + + void main() { + + vec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection ); + + if ( all( equal( axis, vec3( 0.0 ) ) ) ) { + + axis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x ); + + } + + axis = normalize( axis ); + + gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); + gl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis ); + + for ( int i = 1; i < n; i++ ) { + + if ( i >= samples ) { + + break; + + } + + float theta = dTheta * float( i ); + gl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis ); + gl_FragColor.rgb += weights[ i ] * getSample( theta, axis ); + + } + + } + `,blending:Ma,depthTest:!1,depthWrite:!1})}function r0(){return new Gn({name:"EquirectangularToCubeUV",uniforms:{envMap:{value:null}},vertexShader:Gd(),fragmentShader:` + + precision mediump float; + precision mediump int; + + varying vec3 vOutputDirection; + + uniform sampler2D envMap; + + #include <common> + + void main() { + + vec3 outputDirection = normalize( vOutputDirection ); + vec2 uv = equirectUv( outputDirection ); + + gl_FragColor = vec4( texture2D ( envMap, uv ).rgb, 1.0 ); + + } + `,blending:Ma,depthTest:!1,depthWrite:!1})}function l0(){return new Gn({name:"CubemapToCubeUV",uniforms:{envMap:{value:null},flipEnvMap:{value:-1}},vertexShader:Gd(),fragmentShader:` + + precision mediump float; + precision mediump int; + + uniform float flipEnvMap; + + varying vec3 vOutputDirection; + + uniform samplerCube envMap; + + void main() { + + gl_FragColor = textureCube( envMap, vec3( flipEnvMap * vOutputDirection.x, vOutputDirection.yz ) ); + + } + `,blending:Ma,depthTest:!1,depthWrite:!1})}function Gd(){return` + + precision mediump float; + precision mediump int; + + attribute float faceIndex; + + varying vec3 vOutputDirection; + + // RH coordinate system; PMREM face-indexing convention + vec3 getDirection( vec2 uv, float face ) { + + uv = 2.0 * uv - 1.0; + + vec3 direction = vec3( uv, 1.0 ); + + if ( face == 0.0 ) { + + direction = direction.zyx; // ( 1, v, u ) pos x + + } else if ( face == 1.0 ) { + + direction = direction.xzy; + direction.xz *= -1.0; // ( -u, 1, -v ) pos y + + } else if ( face == 2.0 ) { + + direction.x *= -1.0; // ( -u, v, 1 ) pos z + + } else if ( face == 3.0 ) { + + direction = direction.zyx; + direction.xz *= -1.0; // ( -1, v, -u ) neg x + + } else if ( face == 4.0 ) { + + direction = direction.xzy; + direction.xy *= -1.0; // ( -u, -1, v ) neg y + + } else if ( face == 5.0 ) { + + direction.z *= -1.0; // ( u, v, -1 ) neg z + + } + + return direction; + + } + + void main() { + + vOutputDirection = getDirection( uv, faceIndex ); + gl_Position = vec4( position, 1.0 ); + + } + `}class BS extends Ki{constructor(e=1,n={}){super(e,e,n),this.isWebGLCubeRenderTarget=!0;const i={width:e,height:e,depth:1},a=[i,i,i,i,i,i];this.texture=new OS(a),this._setTextureOptions(n),this.texture.isRenderTargetTexture=!0}fromEquirectangularTexture(e,n){this.texture.type=n.type,this.texture.colorSpace=n.colorSpace,this.texture.generateMipmaps=n.generateMipmaps,this.texture.minFilter=n.minFilter,this.texture.magFilter=n.magFilter;const i={uniforms:{tEquirect:{value:null}},vertexShader:` + + varying vec3 vWorldDirection; + + vec3 transformDirection( in vec3 dir, in mat4 matrix ) { + + return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); + + } + + void main() { + + vWorldDirection = transformDirection( position, modelMatrix ); + + #include <begin_vertex> + #include <project_vertex> + + } + `,fragmentShader:` + + uniform sampler2D tEquirect; + + varying vec3 vWorldDirection; + + #include <common> + + void main() { + + vec3 direction = normalize( vWorldDirection ); + + vec2 sampleUV = equirectUv( direction ); + + gl_FragColor = texture2D( tEquirect, sampleUV ); + + } + `},a=new Js(5,5,5),s=new Gn({name:"CubemapFromEquirect",uniforms:Sl(i.uniforms),vertexShader:i.vertexShader,fragmentShader:i.fragmentShader,side:Wn,blending:Ma});s.uniforms.tEquirect.value=n;const l=new Ei(a,s),o=n.minFilter;return n.minFilter===ks&&(n.minFilter=wn),new uw(1,10,this).update(e,l),n.minFilter=o,l.geometry.dispose(),l.material.dispose(),this}clear(e,n=!0,i=!0,a=!0){const s=e.getRenderTarget();for(let l=0;l<6;l++)e.setRenderTarget(this,l),e.clear(n,i,a);e.setRenderTarget(s)}}function sR(t){let e=new WeakMap,n=new WeakMap,i=null;function a(d,p=!1){return d==null?null:p?l(d):s(d)}function s(d){if(d&&d.isTexture){const p=d.mapping;if(p===Ch||p===Rh)if(e.has(d)){const x=e.get(d).texture;return o(x,d.mapping)}else{const x=d.image;if(x&&x.height>0){const _=new BS(x.height);return _.fromEquirectangularTexture(t,d),e.set(d,_),d.addEventListener("dispose",u),o(_.texture,d.mapping)}else return null}}return d}function l(d){if(d&&d.isTexture){const p=d.mapping,x=p===Ch||p===Rh,_=p===Ks||p===bl;if(x||_){let m=n.get(d);const f=m!==void 0?m.texture.pmremVersion:0;if(d.isRenderTargetTexture&&d.pmremVersion!==f)return i===null&&(i=new a0(t)),m=x?i.fromEquirectangular(d,m):i.fromCubemap(d,m),m.texture.pmremVersion=d.pmremVersion,n.set(d,m),m.texture;if(m!==void 0)return m.texture;{const v=d.image;return x&&v&&v.height>0||_&&v&&c(v)?(i===null&&(i=new a0(t)),m=x?i.fromEquirectangular(d):i.fromCubemap(d),m.texture.pmremVersion=d.pmremVersion,n.set(d,m),d.addEventListener("dispose",h),m.texture):null}}}return d}function o(d,p){return p===Ch?d.mapping=Ks:p===Rh&&(d.mapping=bl),d}function c(d){let p=0;const x=6;for(let _=0;_<x;_++)d[_]!==void 0&&p++;return p===x}function u(d){const p=d.target;p.removeEventListener("dispose",u);const x=e.get(p);x!==void 0&&(e.delete(p),x.dispose())}function h(d){const p=d.target;p.removeEventListener("dispose",h);const x=n.get(p);x!==void 0&&(n.delete(p),x.dispose())}function g(){e=new WeakMap,n=new WeakMap,i!==null&&(i.dispose(),i=null)}return{get:a,dispose:g}}function rR(t){const e={};function n(i){if(e[i]!==void 0)return e[i];const a=t.getExtension(i);return e[i]=a,a}return{has:function(i){return n(i)!==null},init:function(){n("EXT_color_buffer_float"),n("WEBGL_clip_cull_distance"),n("OES_texture_float_linear"),n("EXT_color_buffer_half_float"),n("WEBGL_multisampled_render_to_texture"),n("WEBGL_render_shared_exponent")},get:function(i){const a=n(i);return a===null&&al("WebGLRenderer: "+i+" extension not supported."),a}}}function lR(t,e,n,i){const a={},s=new WeakMap;function l(g){const d=g.target;d.index!==null&&e.remove(d.index);for(const x in d.attributes)e.remove(d.attributes[x]);d.removeEventListener("dispose",l),delete a[d.id];const p=s.get(d);p&&(e.remove(p),s.delete(d)),i.releaseStatesOfGeometry(d),d.isInstancedBufferGeometry===!0&&delete d._maxInstanceCount,n.memory.geometries--}function o(g,d){return a[d.id]===!0||(d.addEventListener("dispose",l),a[d.id]=!0,n.memory.geometries++),d}function c(g){const d=g.attributes;for(const p in d)e.update(d[p],t.ARRAY_BUFFER)}function u(g){const d=[],p=g.index,x=g.attributes.position;let _=0;if(x===void 0)return;if(p!==null){const v=p.array;_=p.version;for(let y=0,b=v.length;y<b;y+=3){const T=v[y+0],M=v[y+1],A=v[y+2];d.push(T,M,M,A,A,T)}}else{const v=x.array;_=x.version;for(let y=0,b=v.length/3-1;y<b;y+=3){const T=y+0,M=y+1,A=y+2;d.push(T,M,M,A,A,T)}}const m=new(x.count>=65535?CS:NS)(d,1);m.version=_;const f=s.get(g);f&&e.remove(f),s.set(g,m)}function h(g){const d=s.get(g);if(d){const p=g.index;p!==null&&d.version<p.version&&u(g)}else u(g);return s.get(g)}return{get:o,update:c,getWireframeAttribute:h}}function oR(t,e,n){let i;function a(g){i=g}let s,l;function o(g){s=g.type,l=g.bytesPerElement}function c(g,d){t.drawElements(i,d,s,g*l),n.update(d,i,1)}function u(g,d,p){p!==0&&(t.drawElementsInstanced(i,d,s,g*l,p),n.update(d,i,p))}function h(g,d,p){if(p===0)return;e.get("WEBGL_multi_draw").multiDrawElementsWEBGL(i,d,0,s,g,0,p);let _=0;for(let m=0;m<p;m++)_+=d[m];n.update(_,i,1)}this.setMode=a,this.setIndex=o,this.render=c,this.renderInstances=u,this.renderMultiDraw=h}function cR(t){const e={geometries:0,textures:0},n={frame:0,calls:0,triangles:0,points:0,lines:0};function i(s,l,o){switch(n.calls++,l){case t.TRIANGLES:n.triangles+=o*(s/3);break;case t.LINES:n.lines+=o*(s/2);break;case t.LINE_STRIP:n.lines+=o*(s-1);break;case t.LINE_LOOP:n.lines+=o*s;break;case t.POINTS:n.points+=o*s;break;default:bt("WebGLInfo: Unknown draw mode:",l);break}}function a(){n.calls=0,n.triangles=0,n.points=0,n.lines=0}return{memory:e,render:n,programs:null,autoReset:!0,reset:a,update:i}}function uR(t,e,n){const i=new WeakMap,a=new Yt;function s(l,o,c){const u=l.morphTargetInfluences,h=o.morphAttributes.position||o.morphAttributes.normal||o.morphAttributes.color,g=h!==void 0?h.length:0;let d=i.get(o);if(d===void 0||d.count!==g){let D=function(){S.dispose(),i.delete(o),o.removeEventListener("dispose",D)};var p=D;d!==void 0&&d.texture.dispose();const x=o.morphAttributes.position!==void 0,_=o.morphAttributes.normal!==void 0,m=o.morphAttributes.color!==void 0,f=o.morphAttributes.position||[],v=o.morphAttributes.normal||[],y=o.morphAttributes.color||[];let b=0;x===!0&&(b=1),_===!0&&(b=2),m===!0&&(b=3);let T=o.attributes.position.count*b,M=1;T>e.maxTextureSize&&(M=Math.ceil(T/e.maxTextureSize),T=e.maxTextureSize);const A=new Float32Array(T*M*4*g),S=new TS(A,T,M,g);S.type=Li,S.needsUpdate=!0;const N=b*4;for(let U=0;U<g;U++){const I=f[U],Y=v[U],k=y[U],B=T*M*4*U;for(let z=0;z<I.count;z++){const j=z*N;x===!0&&(a.fromBufferAttribute(I,z),A[B+j+0]=a.x,A[B+j+1]=a.y,A[B+j+2]=a.z,A[B+j+3]=0),_===!0&&(a.fromBufferAttribute(Y,z),A[B+j+4]=a.x,A[B+j+5]=a.y,A[B+j+6]=a.z,A[B+j+7]=0),m===!0&&(a.fromBufferAttribute(k,z),A[B+j+8]=a.x,A[B+j+9]=a.y,A[B+j+10]=a.z,A[B+j+11]=k.itemSize===4?a.w:1)}}d={count:g,texture:S,size:new Mt(T,M)},i.set(o,d),o.addEventListener("dispose",D)}if(l.isInstancedMesh===!0&&l.morphTexture!==null)c.getUniforms().setValue(t,"morphTexture",l.morphTexture,n);else{let x=0;for(let m=0;m<u.length;m++)x+=u[m];const _=o.morphTargetsRelative?1:1-x;c.getUniforms().setValue(t,"morphTargetBaseInfluence",_),c.getUniforms().setValue(t,"morphTargetInfluences",u)}c.getUniforms().setValue(t,"morphTargetsTexture",d.texture,n),c.getUniforms().setValue(t,"morphTargetsTextureSize",d.size)}return{update:s}}function dR(t,e,n,i,a){let s=new WeakMap;function l(u){const h=a.render.frame,g=u.geometry,d=e.get(u,g);if(s.get(d)!==h&&(e.update(d),s.set(d,h)),u.isInstancedMesh&&(u.hasEventListener("dispose",c)===!1&&u.addEventListener("dispose",c),s.get(u)!==h&&(n.update(u.instanceMatrix,t.ARRAY_BUFFER),u.instanceColor!==null&&n.update(u.instanceColor,t.ARRAY_BUFFER),s.set(u,h))),u.isSkinnedMesh){const p=u.skeleton;s.get(p)!==h&&(p.update(),s.set(p,h))}return d}function o(){s=new WeakMap}function c(u){const h=u.target;h.removeEventListener("dispose",c),i.releaseStatesOfObject(h),n.remove(h.instanceMatrix),h.instanceColor!==null&&n.remove(h.instanceColor)}return{update:l,dispose:o}}const hR={[uS]:"LINEAR_TONE_MAPPING",[dS]:"REINHARD_TONE_MAPPING",[hS]:"CINEON_TONE_MAPPING",[fS]:"ACES_FILMIC_TONE_MAPPING",[mS]:"AGX_TONE_MAPPING",[gS]:"NEUTRAL_TONE_MAPPING",[pS]:"CUSTOM_TONE_MAPPING"};function fR(t,e,n,i,a,s){const l=new Ki(e,n,{type:t,depthBuffer:a,stencilBuffer:s,samples:i?4:0,depthTexture:a?new yl(e,n):void 0}),o=new Ki(e,n,{type:Ra,depthBuffer:!1,stencilBuffer:!1}),c=new qn;c.setAttribute("position",new Si([-1,3,0,-1,-1,0,3,-1,0],3)),c.setAttribute("uv",new Si([0,2,0,0,2,0],2));const u=new lw({uniforms:{tDiffuse:{value:null}},vertexShader:` + precision highp float; + + uniform mat4 modelViewMatrix; + uniform mat4 projectionMatrix; + + attribute vec3 position; + attribute vec2 uv; + + varying vec2 vUv; + + void main() { + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + }`,fragmentShader:` + precision highp float; + + uniform sampler2D tDiffuse; + + varying vec2 vUv; + + #include <tonemapping_pars_fragment> + #include <colorspace_pars_fragment> + + void main() { + gl_FragColor = texture2D( tDiffuse, vUv ); + + #ifdef LINEAR_TONE_MAPPING + gl_FragColor.rgb = LinearToneMapping( gl_FragColor.rgb ); + #elif defined( REINHARD_TONE_MAPPING ) + gl_FragColor.rgb = ReinhardToneMapping( gl_FragColor.rgb ); + #elif defined( CINEON_TONE_MAPPING ) + gl_FragColor.rgb = CineonToneMapping( gl_FragColor.rgb ); + #elif defined( ACES_FILMIC_TONE_MAPPING ) + gl_FragColor.rgb = ACESFilmicToneMapping( gl_FragColor.rgb ); + #elif defined( AGX_TONE_MAPPING ) + gl_FragColor.rgb = AgXToneMapping( gl_FragColor.rgb ); + #elif defined( NEUTRAL_TONE_MAPPING ) + gl_FragColor.rgb = NeutralToneMapping( gl_FragColor.rgb ); + #elif defined( CUSTOM_TONE_MAPPING ) + gl_FragColor.rgb = CustomToneMapping( gl_FragColor.rgb ); + #endif + + #ifdef SRGB_TRANSFER + gl_FragColor = sRGBTransferOETF( gl_FragColor ); + #endif + }`,depthTest:!1,depthWrite:!1}),h=new Ei(c,u),g=new wg(-1,1,1,-1,0,1);let d=null,p=null,x=!1,_,m=null,f=[],v=!1;this.setSize=function(y,b){l.setSize(y,b),o.setSize(y,b);for(let T=0;T<f.length;T++){const M=f[T];M.setSize&&M.setSize(y,b)}},this.setEffects=function(y){f=y,v=f.length>0&&f[0].isRenderPass===!0;const b=l.width,T=l.height;for(let M=0;M<f.length;M++){const A=f[M];A.setSize&&A.setSize(b,T)}},this.begin=function(y,b){if(x||y.toneMapping===$i&&f.length===0)return!1;if(m=b,b!==null){const T=b.width,M=b.height;(l.width!==T||l.height!==M)&&this.setSize(T,M)}return v===!1&&y.setRenderTarget(l),_=y.toneMapping,y.toneMapping=$i,!0},this.hasRenderPass=function(){return v},this.end=function(y,b){y.toneMapping=_,x=!0;let T=l,M=o;for(let A=0;A<f.length;A++){const S=f[A];if(S.enabled!==!1&&(S.render(y,M,T,b),S.needsSwap!==!1)){const N=T;T=M,M=N}}if(d!==y.outputColorSpace||p!==y.toneMapping){d=y.outputColorSpace,p=y.toneMapping,u.defines={},ft.getTransfer(d)===Et&&(u.defines.SRGB_TRANSFER="");const A=hR[p];A&&(u.defines[A]=""),u.needsUpdate=!0}u.uniforms.tDiffuse.value=T.texture,y.setRenderTarget(m),y.render(h,g),m=null,x=!1},this.isCompositing=function(){return x},this.dispose=function(){l.depthTexture&&l.depthTexture.dispose(),l.dispose(),o.dispose(),c.dispose(),u.dispose()}}const FS=new Un,lm=new yl(1,1),HS=new TS,GS=new OA,VS=new OS,o0=[],c0=[],u0=new Float32Array(16),d0=new Float32Array(9),h0=new Float32Array(4);function Ol(t,e,n){const i=t[0];if(i<=0||i>0)return t;const a=e*n;let s=o0[a];if(s===void 0&&(s=new Float32Array(a),o0[a]=s),e!==0){i.toArray(s,0);for(let l=1,o=0;l!==e;++l)o+=n,t[l].toArray(s,o)}return s}function dn(t,e){if(t.length!==e.length)return!1;for(let n=0,i=t.length;n<i;n++)if(t[n]!==e[n])return!1;return!0}function hn(t,e){for(let n=0,i=e.length;n<i;n++)t[n]=e[n]}function Vd(t,e){let n=c0[e];n===void 0&&(n=new Int32Array(e),c0[e]=n);for(let i=0;i!==e;++i)n[i]=t.allocateTextureUnit();return n}function pR(t,e){const n=this.cache;n[0]!==e&&(t.uniform1f(this.addr,e),n[0]=e)}function mR(t,e){const n=this.cache;if(e.x!==void 0)(n[0]!==e.x||n[1]!==e.y)&&(t.uniform2f(this.addr,e.x,e.y),n[0]=e.x,n[1]=e.y);else{if(dn(n,e))return;t.uniform2fv(this.addr,e),hn(n,e)}}function gR(t,e){const n=this.cache;if(e.x!==void 0)(n[0]!==e.x||n[1]!==e.y||n[2]!==e.z)&&(t.uniform3f(this.addr,e.x,e.y,e.z),n[0]=e.x,n[1]=e.y,n[2]=e.z);else if(e.r!==void 0)(n[0]!==e.r||n[1]!==e.g||n[2]!==e.b)&&(t.uniform3f(this.addr,e.r,e.g,e.b),n[0]=e.r,n[1]=e.g,n[2]=e.b);else{if(dn(n,e))return;t.uniform3fv(this.addr,e),hn(n,e)}}function xR(t,e){const n=this.cache;if(e.x!==void 0)(n[0]!==e.x||n[1]!==e.y||n[2]!==e.z||n[3]!==e.w)&&(t.uniform4f(this.addr,e.x,e.y,e.z,e.w),n[0]=e.x,n[1]=e.y,n[2]=e.z,n[3]=e.w);else{if(dn(n,e))return;t.uniform4fv(this.addr,e),hn(n,e)}}function vR(t,e){const n=this.cache,i=e.elements;if(i===void 0){if(dn(n,e))return;t.uniformMatrix2fv(this.addr,!1,e),hn(n,e)}else{if(dn(n,i))return;h0.set(i),t.uniformMatrix2fv(this.addr,!1,h0),hn(n,i)}}function _R(t,e){const n=this.cache,i=e.elements;if(i===void 0){if(dn(n,e))return;t.uniformMatrix3fv(this.addr,!1,e),hn(n,e)}else{if(dn(n,i))return;d0.set(i),t.uniformMatrix3fv(this.addr,!1,d0),hn(n,i)}}function bR(t,e){const n=this.cache,i=e.elements;if(i===void 0){if(dn(n,e))return;t.uniformMatrix4fv(this.addr,!1,e),hn(n,e)}else{if(dn(n,i))return;u0.set(i),t.uniformMatrix4fv(this.addr,!1,u0),hn(n,i)}}function yR(t,e){const n=this.cache;n[0]!==e&&(t.uniform1i(this.addr,e),n[0]=e)}function SR(t,e){const n=this.cache;if(e.x!==void 0)(n[0]!==e.x||n[1]!==e.y)&&(t.uniform2i(this.addr,e.x,e.y),n[0]=e.x,n[1]=e.y);else{if(dn(n,e))return;t.uniform2iv(this.addr,e),hn(n,e)}}function MR(t,e){const n=this.cache;if(e.x!==void 0)(n[0]!==e.x||n[1]!==e.y||n[2]!==e.z)&&(t.uniform3i(this.addr,e.x,e.y,e.z),n[0]=e.x,n[1]=e.y,n[2]=e.z);else{if(dn(n,e))return;t.uniform3iv(this.addr,e),hn(n,e)}}function ER(t,e){const n=this.cache;if(e.x!==void 0)(n[0]!==e.x||n[1]!==e.y||n[2]!==e.z||n[3]!==e.w)&&(t.uniform4i(this.addr,e.x,e.y,e.z,e.w),n[0]=e.x,n[1]=e.y,n[2]=e.z,n[3]=e.w);else{if(dn(n,e))return;t.uniform4iv(this.addr,e),hn(n,e)}}function TR(t,e){const n=this.cache;n[0]!==e&&(t.uniform1ui(this.addr,e),n[0]=e)}function AR(t,e){const n=this.cache;if(e.x!==void 0)(n[0]!==e.x||n[1]!==e.y)&&(t.uniform2ui(this.addr,e.x,e.y),n[0]=e.x,n[1]=e.y);else{if(dn(n,e))return;t.uniform2uiv(this.addr,e),hn(n,e)}}function wR(t,e){const n=this.cache;if(e.x!==void 0)(n[0]!==e.x||n[1]!==e.y||n[2]!==e.z)&&(t.uniform3ui(this.addr,e.x,e.y,e.z),n[0]=e.x,n[1]=e.y,n[2]=e.z);else{if(dn(n,e))return;t.uniform3uiv(this.addr,e),hn(n,e)}}function NR(t,e){const n=this.cache;if(e.x!==void 0)(n[0]!==e.x||n[1]!==e.y||n[2]!==e.z||n[3]!==e.w)&&(t.uniform4ui(this.addr,e.x,e.y,e.z,e.w),n[0]=e.x,n[1]=e.y,n[2]=e.z,n[3]=e.w);else{if(dn(n,e))return;t.uniform4uiv(this.addr,e),hn(n,e)}}function CR(t,e,n){const i=this.cache,a=n.allocateTextureUnit();i[0]!==a&&(t.uniform1i(this.addr,a),i[0]=a);let s;this.type===t.SAMPLER_2D_SHADOW?(lm.compareFunction=n.isReversedDepthBuffer()?Eg:Mg,s=lm):s=FS,n.setTexture2D(e||s,a)}function RR(t,e,n){const i=this.cache,a=n.allocateTextureUnit();i[0]!==a&&(t.uniform1i(this.addr,a),i[0]=a),n.setTexture3D(e||GS,a)}function DR(t,e,n){const i=this.cache,a=n.allocateTextureUnit();i[0]!==a&&(t.uniform1i(this.addr,a),i[0]=a),n.setTextureCube(e||VS,a)}function LR(t,e,n){const i=this.cache,a=n.allocateTextureUnit();i[0]!==a&&(t.uniform1i(this.addr,a),i[0]=a),n.setTexture2DArray(e||HS,a)}function UR(t){switch(t){case 5126:return pR;case 35664:return mR;case 35665:return gR;case 35666:return xR;case 35674:return vR;case 35675:return _R;case 35676:return bR;case 5124:case 35670:return yR;case 35667:case 35671:return SR;case 35668:case 35672:return MR;case 35669:case 35673:return ER;case 5125:return TR;case 36294:return AR;case 36295:return wR;case 36296:return NR;case 35678:case 36198:case 36298:case 36306:case 35682:return CR;case 35679:case 36299:case 36307:return RR;case 35680:case 36300:case 36308:case 36293:return DR;case 36289:case 36303:case 36311:case 36292:return LR}}function OR(t,e){t.uniform1fv(this.addr,e)}function PR(t,e){const n=Ol(e,this.size,2);t.uniform2fv(this.addr,n)}function zR(t,e){const n=Ol(e,this.size,3);t.uniform3fv(this.addr,n)}function jR(t,e){const n=Ol(e,this.size,4);t.uniform4fv(this.addr,n)}function kR(t,e){const n=Ol(e,this.size,4);t.uniformMatrix2fv(this.addr,!1,n)}function IR(t,e){const n=Ol(e,this.size,9);t.uniformMatrix3fv(this.addr,!1,n)}function BR(t,e){const n=Ol(e,this.size,16);t.uniformMatrix4fv(this.addr,!1,n)}function FR(t,e){t.uniform1iv(this.addr,e)}function HR(t,e){t.uniform2iv(this.addr,e)}function GR(t,e){t.uniform3iv(this.addr,e)}function VR(t,e){t.uniform4iv(this.addr,e)}function WR(t,e){t.uniform1uiv(this.addr,e)}function XR(t,e){t.uniform2uiv(this.addr,e)}function qR(t,e){t.uniform3uiv(this.addr,e)}function YR(t,e){t.uniform4uiv(this.addr,e)}function $R(t,e,n){const i=this.cache,a=e.length,s=Vd(n,a);dn(i,s)||(t.uniform1iv(this.addr,s),hn(i,s));let l;this.type===t.SAMPLER_2D_SHADOW?l=lm:l=FS;for(let o=0;o!==a;++o)n.setTexture2D(e[o]||l,s[o])}function KR(t,e,n){const i=this.cache,a=e.length,s=Vd(n,a);dn(i,s)||(t.uniform1iv(this.addr,s),hn(i,s));for(let l=0;l!==a;++l)n.setTexture3D(e[l]||GS,s[l])}function ZR(t,e,n){const i=this.cache,a=e.length,s=Vd(n,a);dn(i,s)||(t.uniform1iv(this.addr,s),hn(i,s));for(let l=0;l!==a;++l)n.setTextureCube(e[l]||VS,s[l])}function QR(t,e,n){const i=this.cache,a=e.length,s=Vd(n,a);dn(i,s)||(t.uniform1iv(this.addr,s),hn(i,s));for(let l=0;l!==a;++l)n.setTexture2DArray(e[l]||HS,s[l])}function JR(t){switch(t){case 5126:return OR;case 35664:return PR;case 35665:return zR;case 35666:return jR;case 35674:return kR;case 35675:return IR;case 35676:return BR;case 5124:case 35670:return FR;case 35667:case 35671:return HR;case 35668:case 35672:return GR;case 35669:case 35673:return VR;case 5125:return WR;case 36294:return XR;case 36295:return qR;case 36296:return YR;case 35678:case 36198:case 36298:case 36306:case 35682:return $R;case 35679:case 36299:case 36307:return KR;case 35680:case 36300:case 36308:case 36293:return ZR;case 36289:case 36303:case 36311:case 36292:return QR}}class e3{constructor(e,n,i){this.id=e,this.addr=i,this.cache=[],this.type=n.type,this.setValue=UR(n.type)}}class t3{constructor(e,n,i){this.id=e,this.addr=i,this.cache=[],this.type=n.type,this.size=n.size,this.setValue=JR(n.type)}}class n3{constructor(e){this.id=e,this.seq=[],this.map={}}setValue(e,n,i){const a=this.seq;for(let s=0,l=a.length;s!==l;++s){const o=a[s];o.setValue(e,n[o.id],i)}}}const cf=/(\w+)(\])?(\[|\.)?/g;function f0(t,e){t.seq.push(e),t.map[e.id]=e}function i3(t,e,n){const i=t.name,a=i.length;for(cf.lastIndex=0;;){const s=cf.exec(i),l=cf.lastIndex;let o=s[1];const c=s[2]==="]",u=s[3];if(c&&(o=o|0),u===void 0||u==="["&&l+2===a){f0(n,u===void 0?new e3(o,t,e):new t3(o,t,e));break}else{let g=n.map[o];g===void 0&&(g=new n3(o),f0(n,g)),n=g}}}class Lu{constructor(e,n){this.seq=[],this.map={};const i=e.getProgramParameter(n,e.ACTIVE_UNIFORMS);for(let l=0;l<i;++l){const o=e.getActiveUniform(n,l),c=e.getUniformLocation(n,o.name);i3(o,c,this)}const a=[],s=[];for(const l of this.seq)l.type===e.SAMPLER_2D_SHADOW||l.type===e.SAMPLER_CUBE_SHADOW||l.type===e.SAMPLER_2D_ARRAY_SHADOW?a.push(l):s.push(l);a.length>0&&(this.seq=a.concat(s))}setValue(e,n,i,a){const s=this.map[n];s!==void 0&&s.setValue(e,i,a)}setOptional(e,n,i){const a=n[i];a!==void 0&&this.setValue(e,i,a)}static upload(e,n,i,a){for(let s=0,l=n.length;s!==l;++s){const o=n[s],c=i[o.id];c.needsUpdate!==!1&&o.setValue(e,c.value,a)}}static seqWithValue(e,n){const i=[];for(let a=0,s=e.length;a!==s;++a){const l=e[a];l.id in n&&i.push(l)}return i}}function p0(t,e,n){const i=t.createShader(e);return t.shaderSource(i,n),t.compileShader(i),i}const a3=37297;let s3=0;function r3(t,e){const n=t.split(` +`),i=[],a=Math.max(e-6,0),s=Math.min(e+6,n.length);for(let l=a;l<s;l++){const o=l+1;i.push(`${o===e?">":" "} ${o}: ${n[l]}`)}return i.join(` +`)}const m0=new tt;function l3(t){ft._getMatrix(m0,ft.workingColorSpace,t);const e=`mat3( ${m0.elements.map(n=>n.toFixed(4))} )`;switch(ft.getTransfer(t)){case hd:return[e,"LinearTransferOETF"];case Et:return[e,"sRGBTransferOETF"];default:return Qe("WebGLProgram: Unsupported color space: ",t),[e,"LinearTransferOETF"]}}function g0(t,e,n){const i=t.getShaderParameter(e,t.COMPILE_STATUS),s=(t.getShaderInfoLog(e)||"").trim();if(i&&s==="")return"";const l=/ERROR: 0:(\d+)/.exec(s);if(l){const o=parseInt(l[1]);return n.toUpperCase()+` + +`+s+` + +`+r3(t.getShaderSource(e),o)}else return s}function o3(t,e){const n=l3(e);return[`vec4 ${t}( vec4 value ) {`,` return ${n[1]}( vec4( value.rgb * ${n[0]}, value.a ) );`,"}"].join(` +`)}const c3={[uS]:"Linear",[dS]:"Reinhard",[hS]:"Cineon",[fS]:"ACESFilmic",[mS]:"AgX",[gS]:"Neutral",[pS]:"Custom"};function u3(t,e){const n=c3[e];return n===void 0?(Qe("WebGLProgram: Unsupported toneMapping:",e),"vec3 "+t+"( vec3 color ) { return LinearToneMapping( color ); }"):"vec3 "+t+"( vec3 color ) { return "+n+"ToneMapping( color ); }"}const au=new Z;function d3(){ft.getLuminanceCoefficients(au);const t=au.x.toFixed(4),e=au.y.toFixed(4),n=au.z.toFixed(4);return["float luminance( const in vec3 rgb ) {",` const vec3 weights = vec3( ${t}, ${e}, ${n} );`," return dot( weights, rgb );","}"].join(` +`)}function h3(t){return[t.extensionClipCullDistance?"#extension GL_ANGLE_clip_cull_distance : require":"",t.extensionMultiDraw?"#extension GL_ANGLE_multi_draw : require":""].filter(oo).join(` +`)}function f3(t){const e=[];for(const n in t){const i=t[n];i!==!1&&e.push("#define "+n+" "+i)}return e.join(` +`)}function p3(t,e){const n={},i=t.getProgramParameter(e,t.ACTIVE_ATTRIBUTES);for(let a=0;a<i;a++){const s=t.getActiveAttrib(e,a),l=s.name;let o=1;s.type===t.FLOAT_MAT2&&(o=2),s.type===t.FLOAT_MAT3&&(o=3),s.type===t.FLOAT_MAT4&&(o=4),n[l]={type:s.type,location:t.getAttribLocation(e,l),locationSize:o}}return n}function oo(t){return t!==""}function x0(t,e){const n=e.numSpotLightShadows+e.numSpotLightMaps-e.numSpotLightShadowsWithMaps;return t.replace(/NUM_DIR_LIGHTS/g,e.numDirLights).replace(/NUM_SPOT_LIGHTS/g,e.numSpotLights).replace(/NUM_SPOT_LIGHT_MAPS/g,e.numSpotLightMaps).replace(/NUM_SPOT_LIGHT_COORDS/g,n).replace(/NUM_RECT_AREA_LIGHTS/g,e.numRectAreaLights).replace(/NUM_POINT_LIGHTS/g,e.numPointLights).replace(/NUM_HEMI_LIGHTS/g,e.numHemiLights).replace(/NUM_DIR_LIGHT_SHADOWS/g,e.numDirLightShadows).replace(/NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS/g,e.numSpotLightShadowsWithMaps).replace(/NUM_SPOT_LIGHT_SHADOWS/g,e.numSpotLightShadows).replace(/NUM_POINT_LIGHT_SHADOWS/g,e.numPointLightShadows)}function v0(t,e){return t.replace(/NUM_CLIPPING_PLANES/g,e.numClippingPlanes).replace(/UNION_CLIPPING_PLANES/g,e.numClippingPlanes-e.numClipIntersection)}const m3=/^[ \t]*#include +<([\w\d./]+)>/gm;function om(t){return t.replace(m3,x3)}const g3=new Map;function x3(t,e){let n=at[e];if(n===void 0){const i=g3.get(e);if(i!==void 0)n=at[i],Qe('WebGLRenderer: Shader chunk "%s" has been deprecated. Use "%s" instead.',e,i);else throw new Error("THREE.WebGLProgram: Can not resolve #include <"+e+">")}return om(n)}const v3=/#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*i\s*\+\+\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end/g;function _0(t){return t.replace(v3,_3)}function _3(t,e,n,i){let a="";for(let s=parseInt(e);s<parseInt(n);s++)a+=i.replace(/\[\s*i\s*\]/g,"[ "+s+" ]").replace(/UNROLLED_LOOP_INDEX/g,s);return a}function b0(t){let e=`precision ${t.precision} float; + precision ${t.precision} int; + precision ${t.precision} sampler2D; + precision ${t.precision} samplerCube; + precision ${t.precision} sampler3D; + precision ${t.precision} sampler2DArray; + precision ${t.precision} sampler2DShadow; + precision ${t.precision} samplerCubeShadow; + precision ${t.precision} sampler2DArrayShadow; + precision ${t.precision} isampler2D; + precision ${t.precision} isampler3D; + precision ${t.precision} isamplerCube; + precision ${t.precision} isampler2DArray; + precision ${t.precision} usampler2D; + precision ${t.precision} usampler3D; + precision ${t.precision} usamplerCube; + precision ${t.precision} usampler2DArray; + `;return t.precision==="highp"?e+=` +#define HIGH_PRECISION`:t.precision==="mediump"?e+=` +#define MEDIUM_PRECISION`:t.precision==="lowp"&&(e+=` +#define LOW_PRECISION`),e}const b3={[Au]:"SHADOWMAP_TYPE_PCF",[lo]:"SHADOWMAP_TYPE_VSM"};function y3(t){return b3[t.shadowMapType]||"SHADOWMAP_TYPE_BASIC"}const S3={[Ks]:"ENVMAP_TYPE_CUBE",[bl]:"ENVMAP_TYPE_CUBE",[Fd]:"ENVMAP_TYPE_CUBE_UV"};function M3(t){return t.envMap===!1?"ENVMAP_TYPE_CUBE":S3[t.envMapMode]||"ENVMAP_TYPE_CUBE"}const E3={[bl]:"ENVMAP_MODE_REFRACTION"};function T3(t){return t.envMap===!1?"ENVMAP_MODE_REFLECTION":E3[t.envMapMode]||"ENVMAP_MODE_REFLECTION"}const A3={[cS]:"ENVMAP_BLENDING_MULTIPLY",[fA]:"ENVMAP_BLENDING_MIX",[pA]:"ENVMAP_BLENDING_ADD"};function w3(t){return t.envMap===!1?"ENVMAP_BLENDING_NONE":A3[t.combine]||"ENVMAP_BLENDING_NONE"}function N3(t){const e=t.envMapCubeUVHeight;if(e===null)return null;const n=Math.log2(e)-2,i=1/e;return{texelWidth:1/(3*Math.max(Math.pow(2,n),7*16)),texelHeight:i,maxMip:n}}function C3(t,e,n,i){const a=t.getContext(),s=n.defines;let l=n.vertexShader,o=n.fragmentShader;const c=y3(n),u=M3(n),h=T3(n),g=w3(n),d=N3(n),p=h3(n),x=f3(s),_=a.createProgram();let m,f,v=n.glslVersion?"#version "+n.glslVersion+` +`:"";n.isRawShaderMaterial?(m=["#define SHADER_TYPE "+n.shaderType,"#define SHADER_NAME "+n.shaderName,x].filter(oo).join(` +`),m.length>0&&(m+=` +`),f=["#define SHADER_TYPE "+n.shaderType,"#define SHADER_NAME "+n.shaderName,x].filter(oo).join(` +`),f.length>0&&(f+=` +`)):(m=[b0(n),"#define SHADER_TYPE "+n.shaderType,"#define SHADER_NAME "+n.shaderName,x,n.extensionClipCullDistance?"#define USE_CLIP_DISTANCE":"",n.batching?"#define USE_BATCHING":"",n.batchingColor?"#define USE_BATCHING_COLOR":"",n.instancing?"#define USE_INSTANCING":"",n.instancingColor?"#define USE_INSTANCING_COLOR":"",n.instancingMorph?"#define USE_INSTANCING_MORPH":"",n.useFog&&n.fog?"#define USE_FOG":"",n.useFog&&n.fogExp2?"#define FOG_EXP2":"",n.map?"#define USE_MAP":"",n.envMap?"#define USE_ENVMAP":"",n.envMap?"#define "+h:"",n.lightMap?"#define USE_LIGHTMAP":"",n.aoMap?"#define USE_AOMAP":"",n.bumpMap?"#define USE_BUMPMAP":"",n.normalMap?"#define USE_NORMALMAP":"",n.normalMapObjectSpace?"#define USE_NORMALMAP_OBJECTSPACE":"",n.normalMapTangentSpace?"#define USE_NORMALMAP_TANGENTSPACE":"",n.displacementMap?"#define USE_DISPLACEMENTMAP":"",n.emissiveMap?"#define USE_EMISSIVEMAP":"",n.anisotropy?"#define USE_ANISOTROPY":"",n.anisotropyMap?"#define USE_ANISOTROPYMAP":"",n.clearcoatMap?"#define USE_CLEARCOATMAP":"",n.clearcoatRoughnessMap?"#define USE_CLEARCOAT_ROUGHNESSMAP":"",n.clearcoatNormalMap?"#define USE_CLEARCOAT_NORMALMAP":"",n.iridescenceMap?"#define USE_IRIDESCENCEMAP":"",n.iridescenceThicknessMap?"#define USE_IRIDESCENCE_THICKNESSMAP":"",n.specularMap?"#define USE_SPECULARMAP":"",n.specularColorMap?"#define USE_SPECULAR_COLORMAP":"",n.specularIntensityMap?"#define USE_SPECULAR_INTENSITYMAP":"",n.roughnessMap?"#define USE_ROUGHNESSMAP":"",n.metalnessMap?"#define USE_METALNESSMAP":"",n.alphaMap?"#define USE_ALPHAMAP":"",n.alphaHash?"#define USE_ALPHAHASH":"",n.transmission?"#define USE_TRANSMISSION":"",n.transmissionMap?"#define USE_TRANSMISSIONMAP":"",n.thicknessMap?"#define USE_THICKNESSMAP":"",n.sheenColorMap?"#define USE_SHEEN_COLORMAP":"",n.sheenRoughnessMap?"#define USE_SHEEN_ROUGHNESSMAP":"",n.mapUv?"#define MAP_UV "+n.mapUv:"",n.alphaMapUv?"#define ALPHAMAP_UV "+n.alphaMapUv:"",n.lightMapUv?"#define LIGHTMAP_UV "+n.lightMapUv:"",n.aoMapUv?"#define AOMAP_UV "+n.aoMapUv:"",n.emissiveMapUv?"#define EMISSIVEMAP_UV "+n.emissiveMapUv:"",n.bumpMapUv?"#define BUMPMAP_UV "+n.bumpMapUv:"",n.normalMapUv?"#define NORMALMAP_UV "+n.normalMapUv:"",n.displacementMapUv?"#define DISPLACEMENTMAP_UV "+n.displacementMapUv:"",n.metalnessMapUv?"#define METALNESSMAP_UV "+n.metalnessMapUv:"",n.roughnessMapUv?"#define ROUGHNESSMAP_UV "+n.roughnessMapUv:"",n.anisotropyMapUv?"#define ANISOTROPYMAP_UV "+n.anisotropyMapUv:"",n.clearcoatMapUv?"#define CLEARCOATMAP_UV "+n.clearcoatMapUv:"",n.clearcoatNormalMapUv?"#define CLEARCOAT_NORMALMAP_UV "+n.clearcoatNormalMapUv:"",n.clearcoatRoughnessMapUv?"#define CLEARCOAT_ROUGHNESSMAP_UV "+n.clearcoatRoughnessMapUv:"",n.iridescenceMapUv?"#define IRIDESCENCEMAP_UV "+n.iridescenceMapUv:"",n.iridescenceThicknessMapUv?"#define IRIDESCENCE_THICKNESSMAP_UV "+n.iridescenceThicknessMapUv:"",n.sheenColorMapUv?"#define SHEEN_COLORMAP_UV "+n.sheenColorMapUv:"",n.sheenRoughnessMapUv?"#define SHEEN_ROUGHNESSMAP_UV "+n.sheenRoughnessMapUv:"",n.specularMapUv?"#define SPECULARMAP_UV "+n.specularMapUv:"",n.specularColorMapUv?"#define SPECULAR_COLORMAP_UV "+n.specularColorMapUv:"",n.specularIntensityMapUv?"#define SPECULAR_INTENSITYMAP_UV "+n.specularIntensityMapUv:"",n.transmissionMapUv?"#define TRANSMISSIONMAP_UV "+n.transmissionMapUv:"",n.thicknessMapUv?"#define THICKNESSMAP_UV "+n.thicknessMapUv:"",n.vertexTangents&&n.flatShading===!1?"#define USE_TANGENT":"",n.vertexNormals?"#define HAS_NORMAL":"",n.vertexColors?"#define USE_COLOR":"",n.vertexAlphas?"#define USE_COLOR_ALPHA":"",n.vertexUv1s?"#define USE_UV1":"",n.vertexUv2s?"#define USE_UV2":"",n.vertexUv3s?"#define USE_UV3":"",n.pointsUvs?"#define USE_POINTS_UV":"",n.flatShading?"#define FLAT_SHADED":"",n.skinning?"#define USE_SKINNING":"",n.morphTargets?"#define USE_MORPHTARGETS":"",n.morphNormals&&n.flatShading===!1?"#define USE_MORPHNORMALS":"",n.morphColors?"#define USE_MORPHCOLORS":"",n.morphTargetsCount>0?"#define MORPHTARGETS_TEXTURE_STRIDE "+n.morphTextureStride:"",n.morphTargetsCount>0?"#define MORPHTARGETS_COUNT "+n.morphTargetsCount:"",n.doubleSided?"#define DOUBLE_SIDED":"",n.flipSided?"#define FLIP_SIDED":"",n.shadowMapEnabled?"#define USE_SHADOWMAP":"",n.shadowMapEnabled?"#define "+c:"",n.sizeAttenuation?"#define USE_SIZEATTENUATION":"",n.numLightProbes>0?"#define USE_LIGHT_PROBES":"",n.logarithmicDepthBuffer?"#define USE_LOGARITHMIC_DEPTH_BUFFER":"",n.reversedDepthBuffer?"#define USE_REVERSED_DEPTH_BUFFER":"","uniform mat4 modelMatrix;","uniform mat4 modelViewMatrix;","uniform mat4 projectionMatrix;","uniform mat4 viewMatrix;","uniform mat3 normalMatrix;","uniform vec3 cameraPosition;","uniform bool isOrthographic;","#ifdef USE_INSTANCING"," attribute mat4 instanceMatrix;","#endif","#ifdef USE_INSTANCING_COLOR"," attribute vec3 instanceColor;","#endif","#ifdef USE_INSTANCING_MORPH"," uniform sampler2D morphTexture;","#endif","attribute vec3 position;","attribute vec3 normal;","attribute vec2 uv;","#ifdef USE_UV1"," attribute vec2 uv1;","#endif","#ifdef USE_UV2"," attribute vec2 uv2;","#endif","#ifdef USE_UV3"," attribute vec2 uv3;","#endif","#ifdef USE_TANGENT"," attribute vec4 tangent;","#endif","#if defined( USE_COLOR_ALPHA )"," attribute vec4 color;","#elif defined( USE_COLOR )"," attribute vec3 color;","#endif","#ifdef USE_SKINNING"," attribute vec4 skinIndex;"," attribute vec4 skinWeight;","#endif",` +`].filter(oo).join(` +`),f=[b0(n),"#define SHADER_TYPE "+n.shaderType,"#define SHADER_NAME "+n.shaderName,x,n.useFog&&n.fog?"#define USE_FOG":"",n.useFog&&n.fogExp2?"#define FOG_EXP2":"",n.alphaToCoverage?"#define ALPHA_TO_COVERAGE":"",n.map?"#define USE_MAP":"",n.matcap?"#define USE_MATCAP":"",n.envMap?"#define USE_ENVMAP":"",n.envMap?"#define "+u:"",n.envMap?"#define "+h:"",n.envMap?"#define "+g:"",d?"#define CUBEUV_TEXEL_WIDTH "+d.texelWidth:"",d?"#define CUBEUV_TEXEL_HEIGHT "+d.texelHeight:"",d?"#define CUBEUV_MAX_MIP "+d.maxMip+".0":"",n.lightMap?"#define USE_LIGHTMAP":"",n.aoMap?"#define USE_AOMAP":"",n.bumpMap?"#define USE_BUMPMAP":"",n.normalMap?"#define USE_NORMALMAP":"",n.normalMapObjectSpace?"#define USE_NORMALMAP_OBJECTSPACE":"",n.normalMapTangentSpace?"#define USE_NORMALMAP_TANGENTSPACE":"",n.packedNormalMap?"#define USE_PACKED_NORMALMAP":"",n.emissiveMap?"#define USE_EMISSIVEMAP":"",n.anisotropy?"#define USE_ANISOTROPY":"",n.anisotropyMap?"#define USE_ANISOTROPYMAP":"",n.clearcoat?"#define USE_CLEARCOAT":"",n.clearcoatMap?"#define USE_CLEARCOATMAP":"",n.clearcoatRoughnessMap?"#define USE_CLEARCOAT_ROUGHNESSMAP":"",n.clearcoatNormalMap?"#define USE_CLEARCOAT_NORMALMAP":"",n.dispersion?"#define USE_DISPERSION":"",n.iridescence?"#define USE_IRIDESCENCE":"",n.iridescenceMap?"#define USE_IRIDESCENCEMAP":"",n.iridescenceThicknessMap?"#define USE_IRIDESCENCE_THICKNESSMAP":"",n.specularMap?"#define USE_SPECULARMAP":"",n.specularColorMap?"#define USE_SPECULAR_COLORMAP":"",n.specularIntensityMap?"#define USE_SPECULAR_INTENSITYMAP":"",n.roughnessMap?"#define USE_ROUGHNESSMAP":"",n.metalnessMap?"#define USE_METALNESSMAP":"",n.alphaMap?"#define USE_ALPHAMAP":"",n.alphaTest?"#define USE_ALPHATEST":"",n.alphaHash?"#define USE_ALPHAHASH":"",n.sheen?"#define USE_SHEEN":"",n.sheenColorMap?"#define USE_SHEEN_COLORMAP":"",n.sheenRoughnessMap?"#define USE_SHEEN_ROUGHNESSMAP":"",n.transmission?"#define USE_TRANSMISSION":"",n.transmissionMap?"#define USE_TRANSMISSIONMAP":"",n.thicknessMap?"#define USE_THICKNESSMAP":"",n.vertexTangents&&n.flatShading===!1?"#define USE_TANGENT":"",n.vertexColors||n.instancingColor?"#define USE_COLOR":"",n.vertexAlphas||n.batchingColor?"#define USE_COLOR_ALPHA":"",n.vertexUv1s?"#define USE_UV1":"",n.vertexUv2s?"#define USE_UV2":"",n.vertexUv3s?"#define USE_UV3":"",n.pointsUvs?"#define USE_POINTS_UV":"",n.gradientMap?"#define USE_GRADIENTMAP":"",n.flatShading?"#define FLAT_SHADED":"",n.doubleSided?"#define DOUBLE_SIDED":"",n.flipSided?"#define FLIP_SIDED":"",n.shadowMapEnabled?"#define USE_SHADOWMAP":"",n.shadowMapEnabled?"#define "+c:"",n.premultipliedAlpha?"#define PREMULTIPLIED_ALPHA":"",n.numLightProbes>0?"#define USE_LIGHT_PROBES":"",n.numLightProbeGrids>0?"#define USE_LIGHT_PROBES_GRID":"",n.decodeVideoTexture?"#define DECODE_VIDEO_TEXTURE":"",n.decodeVideoTextureEmissive?"#define DECODE_VIDEO_TEXTURE_EMISSIVE":"",n.logarithmicDepthBuffer?"#define USE_LOGARITHMIC_DEPTH_BUFFER":"",n.reversedDepthBuffer?"#define USE_REVERSED_DEPTH_BUFFER":"","uniform mat4 viewMatrix;","uniform vec3 cameraPosition;","uniform bool isOrthographic;",n.toneMapping!==$i?"#define TONE_MAPPING":"",n.toneMapping!==$i?at.tonemapping_pars_fragment:"",n.toneMapping!==$i?u3("toneMapping",n.toneMapping):"",n.dithering?"#define DITHERING":"",n.opaque?"#define OPAQUE":"",at.colorspace_pars_fragment,o3("linearToOutputTexel",n.outputColorSpace),d3(),n.useDepthPacking?"#define DEPTH_PACKING "+n.depthPacking:"",` +`].filter(oo).join(` +`)),l=om(l),l=x0(l,n),l=v0(l,n),o=om(o),o=x0(o,n),o=v0(o,n),l=_0(l),o=_0(o),n.isRawShaderMaterial!==!0&&(v=`#version 300 es +`,m=[p,"#define attribute in","#define varying out","#define texture2D texture"].join(` +`)+` +`+m,f=["#define varying in",n.glslVersion===Tv?"":"layout(location = 0) out highp vec4 pc_fragColor;",n.glslVersion===Tv?"":"#define gl_FragColor pc_fragColor","#define gl_FragDepthEXT gl_FragDepth","#define texture2D texture","#define textureCube texture","#define texture2DProj textureProj","#define texture2DLodEXT textureLod","#define texture2DProjLodEXT textureProjLod","#define textureCubeLodEXT textureLod","#define texture2DGradEXT textureGrad","#define texture2DProjGradEXT textureProjGrad","#define textureCubeGradEXT textureGrad"].join(` +`)+` +`+f);const y=v+m+l,b=v+f+o,T=p0(a,a.VERTEX_SHADER,y),M=p0(a,a.FRAGMENT_SHADER,b);a.attachShader(_,T),a.attachShader(_,M),n.index0AttributeName!==void 0?a.bindAttribLocation(_,0,n.index0AttributeName):n.hasPositionAttribute===!0&&a.bindAttribLocation(_,0,"position"),a.linkProgram(_);function A(U){if(t.debug.checkShaderErrors){const I=a.getProgramInfoLog(_)||"",Y=a.getShaderInfoLog(T)||"",k=a.getShaderInfoLog(M)||"",B=I.trim(),z=Y.trim(),j=k.trim();let X=!0,q=!0;if(a.getProgramParameter(_,a.LINK_STATUS)===!1)if(X=!1,typeof t.debug.onShaderError=="function")t.debug.onShaderError(a,_,T,M);else{const ve=g0(a,T,"vertex"),he=g0(a,M,"fragment");bt("WebGLProgram: Shader Error "+a.getError()+" - VALIDATE_STATUS "+a.getProgramParameter(_,a.VALIDATE_STATUS)+` + +Material Name: `+U.name+` +Material Type: `+U.type+` + +Program Info Log: `+B+` +`+ve+` +`+he)}else B!==""?Qe("WebGLProgram: Program Info Log:",B):(z===""||j==="")&&(q=!1);q&&(U.diagnostics={runnable:X,programLog:B,vertexShader:{log:z,prefix:m},fragmentShader:{log:j,prefix:f}})}a.deleteShader(T),a.deleteShader(M),S=new Lu(a,_),N=p3(a,_)}let S;this.getUniforms=function(){return S===void 0&&A(this),S};let N;this.getAttributes=function(){return N===void 0&&A(this),N};let D=n.rendererExtensionParallelShaderCompile===!1;return this.isReady=function(){return D===!1&&(D=a.getProgramParameter(_,a3)),D},this.destroy=function(){i.releaseStatesOfProgram(this),a.deleteProgram(_),this.program=void 0},this.type=n.shaderType,this.name=n.shaderName,this.id=s3++,this.cacheKey=e,this.usedTimes=1,this.program=_,this.vertexShader=T,this.fragmentShader=M,this}let R3=0;class D3{constructor(){this.shaderCache=new Map,this.materialCache=new Map}update(e,n,i){const a=this._getShaderCacheForMaterial(e);return a.has(n)===!1&&(a.add(n),n.usedTimes++),a.has(i)===!1&&(a.add(i),i.usedTimes++),this}remove(e){const n=this.materialCache.get(e);for(const i of n)i.usedTimes--,i.usedTimes===0&&this.shaderCache.delete(i.code);return this.materialCache.delete(e),this}getVertexShaderStage(e){return this._getShaderStage(e.vertexShader)}getFragmentShaderStage(e){return this._getShaderStage(e.fragmentShader)}dispose(){this.shaderCache.clear(),this.materialCache.clear()}_getShaderCacheForMaterial(e){const n=this.materialCache;let i=n.get(e);return i===void 0&&(i=new Set,n.set(e,i)),i}_getShaderStage(e){const n=this.shaderCache;let i=n.get(e);return i===void 0&&(i=new L3(e),n.set(e,i)),i}}class L3{constructor(e){this.id=R3++,this.code=e,this.usedTimes=0}}function U3(t){return t===Zs||t===cd||t===ud}function O3(t,e,n,i,a,s){const l=new AS,o=new D3,c=new Set,u=[],h=new Map,g=i.logarithmicDepthBuffer;let d=i.precision;const p={MeshDepthMaterial:"depth",MeshDistanceMaterial:"distance",MeshNormalMaterial:"normal",MeshBasicMaterial:"basic",MeshLambertMaterial:"lambert",MeshPhongMaterial:"phong",MeshToonMaterial:"toon",MeshStandardMaterial:"physical",MeshPhysicalMaterial:"physical",MeshMatcapMaterial:"matcap",LineBasicMaterial:"basic",LineDashedMaterial:"dashed",PointsMaterial:"points",ShadowMaterial:"shadow",SpriteMaterial:"sprite"};function x(S){return c.add(S),S===0?"uv":`uv${S}`}function _(S,N,D,U,I,Y){const k=U.fog,B=I.geometry,z=S.isMeshStandardMaterial||S.isMeshLambertMaterial||S.isMeshPhongMaterial?U.environment:null,j=S.isMeshStandardMaterial||S.isMeshLambertMaterial&&!S.envMap||S.isMeshPhongMaterial&&!S.envMap,X=e.get(S.envMap||z,j),q=X&&X.mapping===Fd?X.image.height:null,ve=p[S.type];S.precision!==null&&(d=i.getMaxPrecision(S.precision),d!==S.precision&&Qe("WebGLProgram.getParameters:",S.precision,"not supported, using",d,"instead."));const he=B.morphAttributes.position||B.morphAttributes.normal||B.morphAttributes.color,Ae=he!==void 0?he.length:0;let $=0;B.morphAttributes.position!==void 0&&($=1),B.morphAttributes.normal!==void 0&&($=2),B.morphAttributes.color!==void 0&&($=3);let pe,we,ne,ge;if(ve){const je=Gi[ve];pe=je.vertexShader,we=je.fragmentShader}else{pe=S.vertexShader,we=S.fragmentShader;const je=o.getVertexShaderStage(S),xt=o.getFragmentShaderStage(S);o.update(S,je,xt),ne=je.id,ge=xt.id}const ue=t.getRenderTarget(),Le=t.state.buffers.depth.getReversed(),Ie=I.isInstancedMesh===!0,Xe=I.isBatchedMesh===!0,dt=!!S.map,re=!!S.matcap,H=!!X,le=!!S.aoMap,se=!!S.lightMap,Me=!!S.bumpMap&&S.wireframe===!1,Oe=!!S.normalMap,De=!!S.displacementMap,Re=!!S.emissiveMap,Fe=!!S.metalnessMap,ye=!!S.roughnessMap,O=S.anisotropy>0,Ye=S.clearcoat>0,$e=S.dispersion>0,R=S.iridescence>0,E=S.sheen>0,V=S.transmission>0,K=O&&!!S.anisotropyMap,J=Ye&&!!S.clearcoatMap,Se=Ye&&!!S.clearcoatNormalMap,_e=Ye&&!!S.clearcoatRoughnessMap,te=R&&!!S.iridescenceMap,Q=R&&!!S.iridescenceThicknessMap,oe=E&&!!S.sheenColorMap,me=E&&!!S.sheenRoughnessMap,xe=!!S.specularMap,Te=!!S.specularColorMap,Be=!!S.specularIntensityMap,Ve=V&&!!S.transmissionMap,Ke=V&&!!S.thicknessMap,G=!!S.gradientMap,Ne=!!S.alphaMap,ce=S.alphaTest>0,Ce=!!S.alphaHash,Ue=!!S.extensions;let be=$i;S.toneMapped&&(ue===null||ue.isXRRenderTarget===!0)&&(be=t.toneMapping);const qe={shaderID:ve,shaderType:S.type,shaderName:S.name,vertexShader:pe,fragmentShader:we,defines:S.defines,customVertexShaderID:ne,customFragmentShaderID:ge,isRawShaderMaterial:S.isRawShaderMaterial===!0,glslVersion:S.glslVersion,precision:d,batching:Xe,batchingColor:Xe&&I._colorsTexture!==null,instancing:Ie,instancingColor:Ie&&I.instanceColor!==null,instancingMorph:Ie&&I.morphTexture!==null,outputColorSpace:ue===null?t.outputColorSpace:ue.isXRRenderTarget===!0?ue.texture.colorSpace:ft.workingColorSpace,alphaToCoverage:!!S.alphaToCoverage,map:dt,matcap:re,envMap:H,envMapMode:H&&X.mapping,envMapCubeUVHeight:q,aoMap:le,lightMap:se,bumpMap:Me,normalMap:Oe,displacementMap:De,emissiveMap:Re,normalMapObjectSpace:Oe&&S.normalMapType===xA,normalMapTangentSpace:Oe&&S.normalMapType===Sv,packedNormalMap:Oe&&S.normalMapType===Sv&&U3(S.normalMap.format),metalnessMap:Fe,roughnessMap:ye,anisotropy:O,anisotropyMap:K,clearcoat:Ye,clearcoatMap:J,clearcoatNormalMap:Se,clearcoatRoughnessMap:_e,dispersion:$e,iridescence:R,iridescenceMap:te,iridescenceThicknessMap:Q,sheen:E,sheenColorMap:oe,sheenRoughnessMap:me,specularMap:xe,specularColorMap:Te,specularIntensityMap:Be,transmission:V,transmissionMap:Ve,thicknessMap:Ke,gradientMap:G,opaque:S.transparent===!1&&S.blending===il&&S.alphaToCoverage===!1,alphaMap:Ne,alphaTest:ce,alphaHash:Ce,combine:S.combine,mapUv:dt&&x(S.map.channel),aoMapUv:le&&x(S.aoMap.channel),lightMapUv:se&&x(S.lightMap.channel),bumpMapUv:Me&&x(S.bumpMap.channel),normalMapUv:Oe&&x(S.normalMap.channel),displacementMapUv:De&&x(S.displacementMap.channel),emissiveMapUv:Re&&x(S.emissiveMap.channel),metalnessMapUv:Fe&&x(S.metalnessMap.channel),roughnessMapUv:ye&&x(S.roughnessMap.channel),anisotropyMapUv:K&&x(S.anisotropyMap.channel),clearcoatMapUv:J&&x(S.clearcoatMap.channel),clearcoatNormalMapUv:Se&&x(S.clearcoatNormalMap.channel),clearcoatRoughnessMapUv:_e&&x(S.clearcoatRoughnessMap.channel),iridescenceMapUv:te&&x(S.iridescenceMap.channel),iridescenceThicknessMapUv:Q&&x(S.iridescenceThicknessMap.channel),sheenColorMapUv:oe&&x(S.sheenColorMap.channel),sheenRoughnessMapUv:me&&x(S.sheenRoughnessMap.channel),specularMapUv:xe&&x(S.specularMap.channel),specularColorMapUv:Te&&x(S.specularColorMap.channel),specularIntensityMapUv:Be&&x(S.specularIntensityMap.channel),transmissionMapUv:Ve&&x(S.transmissionMap.channel),thicknessMapUv:Ke&&x(S.thicknessMap.channel),alphaMapUv:Ne&&x(S.alphaMap.channel),vertexTangents:!!B.attributes.tangent&&(Oe||O),vertexNormals:!!B.attributes.normal,vertexColors:S.vertexColors,vertexAlphas:S.vertexColors===!0&&!!B.attributes.color&&B.attributes.color.itemSize===4,pointsUvs:I.isPoints===!0&&!!B.attributes.uv&&(dt||Ne),fog:!!k,useFog:S.fog===!0,fogExp2:!!k&&k.isFogExp2,flatShading:S.wireframe===!1&&(S.flatShading===!0||B.attributes.normal===void 0&&Oe===!1&&(S.isMeshLambertMaterial||S.isMeshPhongMaterial||S.isMeshStandardMaterial||S.isMeshPhysicalMaterial)),sizeAttenuation:S.sizeAttenuation===!0,logarithmicDepthBuffer:g,reversedDepthBuffer:Le,skinning:I.isSkinnedMesh===!0,hasPositionAttribute:B.attributes.position!==void 0,morphTargets:B.morphAttributes.position!==void 0,morphNormals:B.morphAttributes.normal!==void 0,morphColors:B.morphAttributes.color!==void 0,morphTargetsCount:Ae,morphTextureStride:$,numDirLights:N.directional.length,numPointLights:N.point.length,numSpotLights:N.spot.length,numSpotLightMaps:N.spotLightMap.length,numRectAreaLights:N.rectArea.length,numHemiLights:N.hemi.length,numDirLightShadows:N.directionalShadowMap.length,numPointLightShadows:N.pointShadowMap.length,numSpotLightShadows:N.spotShadowMap.length,numSpotLightShadowsWithMaps:N.numSpotLightShadowsWithMaps,numLightProbes:N.numLightProbes,numLightProbeGrids:Y.length,numClippingPlanes:s.numPlanes,numClipIntersection:s.numIntersection,dithering:S.dithering,shadowMapEnabled:t.shadowMap.enabled&&D.length>0,shadowMapType:t.shadowMap.type,toneMapping:be,decodeVideoTexture:dt&&S.map.isVideoTexture===!0&&ft.getTransfer(S.map.colorSpace)===Et,decodeVideoTextureEmissive:Re&&S.emissiveMap.isVideoTexture===!0&&ft.getTransfer(S.emissiveMap.colorSpace)===Et,premultipliedAlpha:S.premultipliedAlpha,doubleSided:S.side===ma,flipSided:S.side===Wn,useDepthPacking:S.depthPacking>=0,depthPacking:S.depthPacking||0,index0AttributeName:S.index0AttributeName,extensionClipCullDistance:Ue&&S.extensions.clipCullDistance===!0&&n.has("WEBGL_clip_cull_distance"),extensionMultiDraw:(Ue&&S.extensions.multiDraw===!0||Xe)&&n.has("WEBGL_multi_draw"),rendererExtensionParallelShaderCompile:n.has("KHR_parallel_shader_compile"),customProgramCacheKey:S.customProgramCacheKey()};return qe.vertexUv1s=c.has(1),qe.vertexUv2s=c.has(2),qe.vertexUv3s=c.has(3),c.clear(),qe}function m(S){const N=[];if(S.shaderID?N.push(S.shaderID):(N.push(S.customVertexShaderID),N.push(S.customFragmentShaderID)),S.defines!==void 0)for(const D in S.defines)N.push(D),N.push(S.defines[D]);return S.isRawShaderMaterial===!1&&(f(N,S),v(N,S),N.push(t.outputColorSpace)),N.push(S.customProgramCacheKey),N.join()}function f(S,N){S.push(N.precision),S.push(N.outputColorSpace),S.push(N.envMapMode),S.push(N.envMapCubeUVHeight),S.push(N.mapUv),S.push(N.alphaMapUv),S.push(N.lightMapUv),S.push(N.aoMapUv),S.push(N.bumpMapUv),S.push(N.normalMapUv),S.push(N.displacementMapUv),S.push(N.emissiveMapUv),S.push(N.metalnessMapUv),S.push(N.roughnessMapUv),S.push(N.anisotropyMapUv),S.push(N.clearcoatMapUv),S.push(N.clearcoatNormalMapUv),S.push(N.clearcoatRoughnessMapUv),S.push(N.iridescenceMapUv),S.push(N.iridescenceThicknessMapUv),S.push(N.sheenColorMapUv),S.push(N.sheenRoughnessMapUv),S.push(N.specularMapUv),S.push(N.specularColorMapUv),S.push(N.specularIntensityMapUv),S.push(N.transmissionMapUv),S.push(N.thicknessMapUv),S.push(N.combine),S.push(N.fogExp2),S.push(N.sizeAttenuation),S.push(N.morphTargetsCount),S.push(N.morphAttributeCount),S.push(N.numDirLights),S.push(N.numPointLights),S.push(N.numSpotLights),S.push(N.numSpotLightMaps),S.push(N.numHemiLights),S.push(N.numRectAreaLights),S.push(N.numDirLightShadows),S.push(N.numPointLightShadows),S.push(N.numSpotLightShadows),S.push(N.numSpotLightShadowsWithMaps),S.push(N.numLightProbes),S.push(N.shadowMapType),S.push(N.toneMapping),S.push(N.numClippingPlanes),S.push(N.numClipIntersection),S.push(N.depthPacking)}function v(S,N){l.disableAll(),N.instancing&&l.enable(0),N.instancingColor&&l.enable(1),N.instancingMorph&&l.enable(2),N.matcap&&l.enable(3),N.envMap&&l.enable(4),N.normalMapObjectSpace&&l.enable(5),N.normalMapTangentSpace&&l.enable(6),N.clearcoat&&l.enable(7),N.iridescence&&l.enable(8),N.alphaTest&&l.enable(9),N.vertexColors&&l.enable(10),N.vertexAlphas&&l.enable(11),N.vertexUv1s&&l.enable(12),N.vertexUv2s&&l.enable(13),N.vertexUv3s&&l.enable(14),N.vertexTangents&&l.enable(15),N.anisotropy&&l.enable(16),N.alphaHash&&l.enable(17),N.batching&&l.enable(18),N.dispersion&&l.enable(19),N.batchingColor&&l.enable(20),N.gradientMap&&l.enable(21),N.packedNormalMap&&l.enable(22),N.vertexNormals&&l.enable(23),S.push(l.mask),l.disableAll(),N.fog&&l.enable(0),N.useFog&&l.enable(1),N.flatShading&&l.enable(2),N.logarithmicDepthBuffer&&l.enable(3),N.reversedDepthBuffer&&l.enable(4),N.skinning&&l.enable(5),N.morphTargets&&l.enable(6),N.morphNormals&&l.enable(7),N.morphColors&&l.enable(8),N.premultipliedAlpha&&l.enable(9),N.shadowMapEnabled&&l.enable(10),N.doubleSided&&l.enable(11),N.flipSided&&l.enable(12),N.useDepthPacking&&l.enable(13),N.dithering&&l.enable(14),N.transmission&&l.enable(15),N.sheen&&l.enable(16),N.opaque&&l.enable(17),N.pointsUvs&&l.enable(18),N.decodeVideoTexture&&l.enable(19),N.decodeVideoTextureEmissive&&l.enable(20),N.alphaToCoverage&&l.enable(21),N.numLightProbeGrids>0&&l.enable(22),N.hasPositionAttribute&&l.enable(23),S.push(l.mask)}function y(S){const N=p[S.type];let D;if(N){const U=Gi[N];D=aw.clone(U.uniforms)}else D=S.uniforms;return D}function b(S,N){let D=h.get(N);return D!==void 0?++D.usedTimes:(D=new C3(t,N,S,a),u.push(D),h.set(N,D)),D}function T(S){if(--S.usedTimes===0){const N=u.indexOf(S);u[N]=u[u.length-1],u.pop(),h.delete(S.cacheKey),S.destroy()}}function M(S){o.remove(S)}function A(){o.dispose()}return{getParameters:_,getProgramCacheKey:m,getUniforms:y,acquireProgram:b,releaseProgram:T,releaseShaderCache:M,programs:u,dispose:A}}function P3(){let t=new WeakMap;function e(l){return t.has(l)}function n(l){let o=t.get(l);return o===void 0&&(o={},t.set(l,o)),o}function i(l){t.delete(l)}function a(l,o,c){t.get(l)[o]=c}function s(){t=new WeakMap}return{has:e,get:n,remove:i,update:a,dispose:s}}function z3(t,e){return t.groupOrder!==e.groupOrder?t.groupOrder-e.groupOrder:t.renderOrder!==e.renderOrder?t.renderOrder-e.renderOrder:t.material.id!==e.material.id?t.material.id-e.material.id:t.materialVariant!==e.materialVariant?t.materialVariant-e.materialVariant:t.z!==e.z?t.z-e.z:t.id-e.id}function y0(t,e){return t.groupOrder!==e.groupOrder?t.groupOrder-e.groupOrder:t.renderOrder!==e.renderOrder?t.renderOrder-e.renderOrder:t.z!==e.z?e.z-t.z:t.id-e.id}function S0(){const t=[];let e=0;const n=[],i=[],a=[];function s(){e=0,n.length=0,i.length=0,a.length=0}function l(d){let p=0;return d.isInstancedMesh&&(p+=2),d.isSkinnedMesh&&(p+=1),p}function o(d,p,x,_,m,f){let v=t[e];return v===void 0?(v={id:d.id,object:d,geometry:p,material:x,materialVariant:l(d),groupOrder:_,renderOrder:d.renderOrder,z:m,group:f},t[e]=v):(v.id=d.id,v.object=d,v.geometry=p,v.material=x,v.materialVariant=l(d),v.groupOrder=_,v.renderOrder=d.renderOrder,v.z=m,v.group=f),e++,v}function c(d,p,x,_,m,f){const v=o(d,p,x,_,m,f);x.transmission>0?i.push(v):x.transparent===!0?a.push(v):n.push(v)}function u(d,p,x,_,m,f){const v=o(d,p,x,_,m,f);x.transmission>0?i.unshift(v):x.transparent===!0?a.unshift(v):n.unshift(v)}function h(d,p,x){n.length>1&&n.sort(d||z3),i.length>1&&i.sort(p||y0),a.length>1&&a.sort(p||y0),x&&(n.reverse(),i.reverse(),a.reverse())}function g(){for(let d=e,p=t.length;d<p;d++){const x=t[d];if(x.id===null)break;x.id=null,x.object=null,x.geometry=null,x.material=null,x.group=null}}return{opaque:n,transmissive:i,transparent:a,init:s,push:c,unshift:u,finish:g,sort:h}}function j3(){let t=new WeakMap;function e(i,a){const s=t.get(i);let l;return s===void 0?(l=new S0,t.set(i,[l])):a>=s.length?(l=new S0,s.push(l)):l=s[a],l}function n(){t=new WeakMap}return{get:e,dispose:n}}function k3(){const t={};return{get:function(e){if(t[e.id]!==void 0)return t[e.id];let n;switch(e.type){case"DirectionalLight":n={direction:new Z,color:new gt};break;case"SpotLight":n={position:new Z,direction:new Z,color:new gt,distance:0,coneCos:0,penumbraCos:0,decay:0};break;case"PointLight":n={position:new Z,color:new gt,distance:0,decay:0};break;case"HemisphereLight":n={direction:new Z,skyColor:new gt,groundColor:new gt};break;case"RectAreaLight":n={color:new gt,position:new Z,halfWidth:new Z,halfHeight:new Z};break}return t[e.id]=n,n}}}function I3(){const t={};return{get:function(e){if(t[e.id]!==void 0)return t[e.id];let n;switch(e.type){case"DirectionalLight":n={shadowIntensity:1,shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new Mt};break;case"SpotLight":n={shadowIntensity:1,shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new Mt};break;case"PointLight":n={shadowIntensity:1,shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new Mt,shadowCameraNear:1,shadowCameraFar:1e3};break}return t[e.id]=n,n}}}let B3=0;function F3(t,e){return(e.castShadow?2:0)-(t.castShadow?2:0)+(e.map?1:0)-(t.map?1:0)}function H3(t){const e=new k3,n=I3(),i={version:0,hash:{directionalLength:-1,pointLength:-1,spotLength:-1,rectAreaLength:-1,hemiLength:-1,numDirectionalShadows:-1,numPointShadows:-1,numSpotShadows:-1,numSpotMaps:-1,numLightProbes:-1},ambient:[0,0,0],probe:[],directional:[],directionalShadow:[],directionalShadowMap:[],directionalShadowMatrix:[],spot:[],spotLightMap:[],spotShadow:[],spotShadowMap:[],spotLightMatrix:[],rectArea:[],rectAreaLTC1:null,rectAreaLTC2:null,point:[],pointShadow:[],pointShadowMap:[],pointShadowMatrix:[],hemi:[],numSpotLightShadowsWithMaps:0,numLightProbes:0};for(let u=0;u<9;u++)i.probe.push(new Z);const a=new Z,s=new zt,l=new zt;function o(u){let h=0,g=0,d=0;for(let N=0;N<9;N++)i.probe[N].set(0,0,0);let p=0,x=0,_=0,m=0,f=0,v=0,y=0,b=0,T=0,M=0,A=0;u.sort(F3);for(let N=0,D=u.length;N<D;N++){const U=u[N],I=U.color,Y=U.intensity,k=U.distance;let B=null;if(U.shadow&&U.shadow.map&&(U.shadow.map.texture.format===Zs?B=U.shadow.map.texture:B=U.shadow.map.depthTexture||U.shadow.map.texture),U.isAmbientLight)h+=I.r*Y,g+=I.g*Y,d+=I.b*Y;else if(U.isLightProbe){for(let z=0;z<9;z++)i.probe[z].addScaledVector(U.sh.coefficients[z],Y);A++}else if(U.isDirectionalLight){const z=e.get(U);if(z.color.copy(U.color).multiplyScalar(U.intensity),U.castShadow){const j=U.shadow,X=n.get(U);X.shadowIntensity=j.intensity,X.shadowBias=j.bias,X.shadowNormalBias=j.normalBias,X.shadowRadius=j.radius,X.shadowMapSize=j.mapSize,i.directionalShadow[p]=X,i.directionalShadowMap[p]=B,i.directionalShadowMatrix[p]=U.shadow.matrix,v++}i.directional[p]=z,p++}else if(U.isSpotLight){const z=e.get(U);z.position.setFromMatrixPosition(U.matrixWorld),z.color.copy(I).multiplyScalar(Y),z.distance=k,z.coneCos=Math.cos(U.angle),z.penumbraCos=Math.cos(U.angle*(1-U.penumbra)),z.decay=U.decay,i.spot[_]=z;const j=U.shadow;if(U.map&&(i.spotLightMap[T]=U.map,T++,j.updateMatrices(U),U.castShadow&&M++),i.spotLightMatrix[_]=j.matrix,U.castShadow){const X=n.get(U);X.shadowIntensity=j.intensity,X.shadowBias=j.bias,X.shadowNormalBias=j.normalBias,X.shadowRadius=j.radius,X.shadowMapSize=j.mapSize,i.spotShadow[_]=X,i.spotShadowMap[_]=B,b++}_++}else if(U.isRectAreaLight){const z=e.get(U);z.color.copy(I).multiplyScalar(Y),z.halfWidth.set(U.width*.5,0,0),z.halfHeight.set(0,U.height*.5,0),i.rectArea[m]=z,m++}else if(U.isPointLight){const z=e.get(U);if(z.color.copy(U.color).multiplyScalar(U.intensity),z.distance=U.distance,z.decay=U.decay,U.castShadow){const j=U.shadow,X=n.get(U);X.shadowIntensity=j.intensity,X.shadowBias=j.bias,X.shadowNormalBias=j.normalBias,X.shadowRadius=j.radius,X.shadowMapSize=j.mapSize,X.shadowCameraNear=j.camera.near,X.shadowCameraFar=j.camera.far,i.pointShadow[x]=X,i.pointShadowMap[x]=B,i.pointShadowMatrix[x]=U.shadow.matrix,y++}i.point[x]=z,x++}else if(U.isHemisphereLight){const z=e.get(U);z.skyColor.copy(U.color).multiplyScalar(Y),z.groundColor.copy(U.groundColor).multiplyScalar(Y),i.hemi[f]=z,f++}}m>0&&(t.has("OES_texture_float_linear")===!0?(i.rectAreaLTC1=ze.LTC_FLOAT_1,i.rectAreaLTC2=ze.LTC_FLOAT_2):(i.rectAreaLTC1=ze.LTC_HALF_1,i.rectAreaLTC2=ze.LTC_HALF_2)),i.ambient[0]=h,i.ambient[1]=g,i.ambient[2]=d;const S=i.hash;(S.directionalLength!==p||S.pointLength!==x||S.spotLength!==_||S.rectAreaLength!==m||S.hemiLength!==f||S.numDirectionalShadows!==v||S.numPointShadows!==y||S.numSpotShadows!==b||S.numSpotMaps!==T||S.numLightProbes!==A)&&(i.directional.length=p,i.spot.length=_,i.rectArea.length=m,i.point.length=x,i.hemi.length=f,i.directionalShadow.length=v,i.directionalShadowMap.length=v,i.pointShadow.length=y,i.pointShadowMap.length=y,i.spotShadow.length=b,i.spotShadowMap.length=b,i.directionalShadowMatrix.length=v,i.pointShadowMatrix.length=y,i.spotLightMatrix.length=b+T-M,i.spotLightMap.length=T,i.numSpotLightShadowsWithMaps=M,i.numLightProbes=A,S.directionalLength=p,S.pointLength=x,S.spotLength=_,S.rectAreaLength=m,S.hemiLength=f,S.numDirectionalShadows=v,S.numPointShadows=y,S.numSpotShadows=b,S.numSpotMaps=T,S.numLightProbes=A,i.version=B3++)}function c(u,h){let g=0,d=0,p=0,x=0,_=0;const m=h.matrixWorldInverse;for(let f=0,v=u.length;f<v;f++){const y=u[f];if(y.isDirectionalLight){const b=i.directional[g];b.direction.setFromMatrixPosition(y.matrixWorld),a.setFromMatrixPosition(y.target.matrixWorld),b.direction.sub(a),b.direction.transformDirection(m),g++}else if(y.isSpotLight){const b=i.spot[p];b.position.setFromMatrixPosition(y.matrixWorld),b.position.applyMatrix4(m),b.direction.setFromMatrixPosition(y.matrixWorld),a.setFromMatrixPosition(y.target.matrixWorld),b.direction.sub(a),b.direction.transformDirection(m),p++}else if(y.isRectAreaLight){const b=i.rectArea[x];b.position.setFromMatrixPosition(y.matrixWorld),b.position.applyMatrix4(m),l.identity(),s.copy(y.matrixWorld),s.premultiply(m),l.extractRotation(s),b.halfWidth.set(y.width*.5,0,0),b.halfHeight.set(0,y.height*.5,0),b.halfWidth.applyMatrix4(l),b.halfHeight.applyMatrix4(l),x++}else if(y.isPointLight){const b=i.point[d];b.position.setFromMatrixPosition(y.matrixWorld),b.position.applyMatrix4(m),d++}else if(y.isHemisphereLight){const b=i.hemi[_];b.direction.setFromMatrixPosition(y.matrixWorld),b.direction.transformDirection(m),_++}}}return{setup:o,setupView:c,state:i}}function M0(t){const e=new H3(t),n=[],i=[],a=[];function s(d){g.camera=d,n.length=0,i.length=0,a.length=0}function l(d){n.push(d)}function o(d){i.push(d)}function c(d){a.push(d)}function u(){e.setup(n)}function h(d){e.setupView(n,d)}const g={lightsArray:n,shadowsArray:i,lightProbeGridArray:a,camera:null,lights:e,transmissionRenderTarget:{},textureUnits:0};return{init:s,state:g,setupLights:u,setupLightsView:h,pushLight:l,pushShadow:o,pushLightProbeGrid:c}}function G3(t){let e=new WeakMap;function n(a,s=0){const l=e.get(a);let o;return l===void 0?(o=new M0(t),e.set(a,[o])):s>=l.length?(o=new M0(t),l.push(o)):o=l[s],o}function i(){e=new WeakMap}return{get:n,dispose:i}}const V3=`void main() { + gl_Position = vec4( position, 1.0 ); +}`,W3=`uniform sampler2D shadow_pass; +uniform vec2 resolution; +uniform float radius; +void main() { + const float samples = float( VSM_SAMPLES ); + float mean = 0.0; + float squared_mean = 0.0; + float uvStride = samples <= 1.0 ? 0.0 : 2.0 / ( samples - 1.0 ); + float uvStart = samples <= 1.0 ? 0.0 : - 1.0; + for ( float i = 0.0; i < samples; i ++ ) { + float uvOffset = uvStart + i * uvStride; + #ifdef HORIZONTAL_PASS + vec2 distribution = texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ).rg; + mean += distribution.x; + squared_mean += distribution.y * distribution.y + distribution.x * distribution.x; + #else + float depth = texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ).r; + mean += depth; + squared_mean += depth * depth; + #endif + } + mean = mean / samples; + squared_mean = squared_mean / samples; + float std_dev = sqrt( max( 0.0, squared_mean - mean * mean ) ); + gl_FragColor = vec4( mean, std_dev, 0.0, 1.0 ); +}`,X3=[new Z(1,0,0),new Z(-1,0,0),new Z(0,1,0),new Z(0,-1,0),new Z(0,0,1),new Z(0,0,-1)],q3=[new Z(0,-1,0),new Z(0,-1,0),new Z(0,0,1),new Z(0,0,-1),new Z(0,-1,0),new Z(0,-1,0)],E0=new zt,Jl=new Z,uf=new Z;function Y3(t,e,n){let i=new LS;const a=new Mt,s=new Mt,l=new Yt,o=new ow,c=new cw,u={},h=n.maxTextureSize,g={[ys]:Wn,[Wn]:ys,[ma]:ma},d=new Gn({defines:{VSM_SAMPLES:8},uniforms:{shadow_pass:{value:null},resolution:{value:new Mt},radius:{value:4}},vertexShader:V3,fragmentShader:W3}),p=d.clone();p.defines.HORIZONTAL_PASS=1;const x=new qn;x.setAttribute("position",new on(new Float32Array([-1,-1,.5,3,-1,.5,-1,3,.5]),3));const _=new Ei(x,d),m=this;this.enabled=!1,this.autoUpdate=!0,this.needsUpdate=!1,this.type=Au;let f=this.type;this.render=function(M,A,S){if(m.enabled===!1||m.autoUpdate===!1&&m.needsUpdate===!1||M.length===0)return;this.type===Y1&&(Qe("WebGLShadowMap: PCFSoftShadowMap has been deprecated. Using PCFShadowMap instead."),this.type=Au);const N=t.getRenderTarget(),D=t.getActiveCubeFace(),U=t.getActiveMipmapLevel(),I=t.state;I.setBlending(Ma),I.buffers.depth.getReversed()===!0?I.buffers.color.setClear(0,0,0,0):I.buffers.color.setClear(1,1,1,1),I.buffers.depth.setTest(!0),I.setScissorTest(!1);const Y=f!==this.type;Y&&A.traverse(function(k){k.material&&(Array.isArray(k.material)?k.material.forEach(B=>B.needsUpdate=!0):k.material.needsUpdate=!0)});for(let k=0,B=M.length;k<B;k++){const z=M[k],j=z.shadow;if(j===void 0){Qe("WebGLShadowMap:",z,"has no shadow.");continue}if(j.autoUpdate===!1&&j.needsUpdate===!1)continue;a.copy(j.mapSize);const X=j.getFrameExtents();a.multiply(X),s.copy(j.mapSize),(a.x>h||a.y>h)&&(a.x>h&&(s.x=Math.floor(h/X.x),a.x=s.x*X.x,j.mapSize.x=s.x),a.y>h&&(s.y=Math.floor(h/X.y),a.y=s.y*X.y,j.mapSize.y=s.y));const q=t.state.buffers.depth.getReversed();if(j.camera._reversedDepth=q,j.map===null||Y===!0){if(j.map!==null&&(j.map.depthTexture!==null&&(j.map.depthTexture.dispose(),j.map.depthTexture=null),j.map.dispose()),this.type===lo){if(z.isPointLight){Qe("WebGLShadowMap: VSM shadow maps are not supported for PointLights. Use PCF or BasicShadowMap instead.");continue}j.map=new Ki(a.x,a.y,{format:Zs,type:Ra,minFilter:wn,magFilter:wn,generateMipmaps:!1}),j.map.texture.name=z.name+".shadowMap",j.map.depthTexture=new yl(a.x,a.y,Li),j.map.depthTexture.name=z.name+".shadowMapDepth",j.map.depthTexture.format=Da,j.map.depthTexture.compareFunction=null,j.map.depthTexture.minFilter=Sn,j.map.depthTexture.magFilter=Sn}else z.isPointLight?(j.map=new BS(a.x),j.map.depthTexture=new tw(a.x,Zi)):(j.map=new Ki(a.x,a.y),j.map.depthTexture=new yl(a.x,a.y,Zi)),j.map.depthTexture.name=z.name+".shadowMap",j.map.depthTexture.format=Da,this.type===Au?(j.map.depthTexture.compareFunction=q?Eg:Mg,j.map.depthTexture.minFilter=wn,j.map.depthTexture.magFilter=wn):(j.map.depthTexture.compareFunction=null,j.map.depthTexture.minFilter=Sn,j.map.depthTexture.magFilter=Sn);j.camera.updateProjectionMatrix()}const ve=j.map.isWebGLCubeRenderTarget?6:1;for(let he=0;he<ve;he++){if(j.map.isWebGLCubeRenderTarget)t.setRenderTarget(j.map,he),t.clear();else{he===0&&(t.setRenderTarget(j.map),t.clear());const Ae=j.getViewport(he);l.set(s.x*Ae.x,s.y*Ae.y,s.x*Ae.z,s.y*Ae.w),I.viewport(l)}if(z.isPointLight){const Ae=j.camera,$=j.matrix,pe=z.distance||Ae.far;pe!==Ae.far&&(Ae.far=pe,Ae.updateProjectionMatrix()),Jl.setFromMatrixPosition(z.matrixWorld),Ae.position.copy(Jl),uf.copy(Ae.position),uf.add(X3[he]),Ae.up.copy(q3[he]),Ae.lookAt(uf),Ae.updateMatrixWorld(),$.makeTranslation(-Jl.x,-Jl.y,-Jl.z),E0.multiplyMatrices(Ae.projectionMatrix,Ae.matrixWorldInverse),j._frustum.setFromProjectionMatrix(E0,Ae.coordinateSystem,Ae.reversedDepth)}else j.updateMatrices(z);i=j.getFrustum(),b(A,S,j.camera,z,this.type)}j.isPointLightShadow!==!0&&this.type===lo&&v(j,S),j.needsUpdate=!1}f=this.type,m.needsUpdate=!1,t.setRenderTarget(N,D,U)};function v(M,A){const S=e.update(_);d.defines.VSM_SAMPLES!==M.blurSamples&&(d.defines.VSM_SAMPLES=M.blurSamples,p.defines.VSM_SAMPLES=M.blurSamples,d.needsUpdate=!0,p.needsUpdate=!0),M.mapPass===null&&(M.mapPass=new Ki(a.x,a.y,{format:Zs,type:Ra})),d.uniforms.shadow_pass.value=M.map.depthTexture,d.uniforms.resolution.value=M.mapSize,d.uniforms.radius.value=M.radius,t.setRenderTarget(M.mapPass),t.clear(),t.renderBufferDirect(A,null,S,d,_,null),p.uniforms.shadow_pass.value=M.mapPass.texture,p.uniforms.resolution.value=M.mapSize,p.uniforms.radius.value=M.radius,t.setRenderTarget(M.map),t.clear(),t.renderBufferDirect(A,null,S,p,_,null)}function y(M,A,S,N){let D=null;const U=S.isPointLight===!0?M.customDistanceMaterial:M.customDepthMaterial;if(U!==void 0)D=U;else if(D=S.isPointLight===!0?c:o,t.localClippingEnabled&&A.clipShadows===!0&&Array.isArray(A.clippingPlanes)&&A.clippingPlanes.length!==0||A.displacementMap&&A.displacementScale!==0||A.alphaMap&&A.alphaTest>0||A.map&&A.alphaTest>0||A.alphaToCoverage===!0){const I=D.uuid,Y=A.uuid;let k=u[I];k===void 0&&(k={},u[I]=k);let B=k[Y];B===void 0&&(B=D.clone(),k[Y]=B,A.addEventListener("dispose",T)),D=B}if(D.visible=A.visible,D.wireframe=A.wireframe,N===lo?D.side=A.shadowSide!==null?A.shadowSide:A.side:D.side=A.shadowSide!==null?A.shadowSide:g[A.side],D.alphaMap=A.alphaMap,D.alphaTest=A.alphaToCoverage===!0?.5:A.alphaTest,D.map=A.map,D.clipShadows=A.clipShadows,D.clippingPlanes=A.clippingPlanes,D.clipIntersection=A.clipIntersection,D.displacementMap=A.displacementMap,D.displacementScale=A.displacementScale,D.displacementBias=A.displacementBias,D.wireframeLinewidth=A.wireframeLinewidth,D.linewidth=A.linewidth,S.isPointLight===!0&&D.isMeshDistanceMaterial===!0){const I=t.properties.get(D);I.light=S}return D}function b(M,A,S,N,D){if(M.visible===!1)return;if(M.layers.test(A.layers)&&(M.isMesh||M.isLine||M.isPoints)&&(M.castShadow||M.receiveShadow&&D===lo)&&(!M.frustumCulled||i.intersectsObject(M))){M.modelViewMatrix.multiplyMatrices(S.matrixWorldInverse,M.matrixWorld);const Y=e.update(M),k=M.material;if(Array.isArray(k)){const B=Y.groups;for(let z=0,j=B.length;z<j;z++){const X=B[z],q=k[X.materialIndex];if(q&&q.visible){const ve=y(M,q,N,D);M.onBeforeShadow(t,M,A,S,Y,ve,X),t.renderBufferDirect(S,null,Y,ve,M,X),M.onAfterShadow(t,M,A,S,Y,ve,X)}}}else if(k.visible){const B=y(M,k,N,D);M.onBeforeShadow(t,M,A,S,Y,B,null),t.renderBufferDirect(S,null,Y,B,M,null),M.onAfterShadow(t,M,A,S,Y,B,null)}}const I=M.children;for(let Y=0,k=I.length;Y<k;Y++)b(I[Y],A,S,N,D)}function T(M){M.target.removeEventListener("dispose",T);for(const S in u){const N=u[S],D=M.target.uuid;D in N&&(N[D].dispose(),delete N[D])}}}function $3(t,e){function n(){let G=!1;const Ne=new Yt;let ce=null;const Ce=new Yt(0,0,0,0);return{setMask:function(Ue){ce!==Ue&&!G&&(t.colorMask(Ue,Ue,Ue,Ue),ce=Ue)},setLocked:function(Ue){G=Ue},setClear:function(Ue,be,qe,je,xt){xt===!0&&(Ue*=je,be*=je,qe*=je),Ne.set(Ue,be,qe,je),Ce.equals(Ne)===!1&&(t.clearColor(Ue,be,qe,je),Ce.copy(Ne))},reset:function(){G=!1,ce=null,Ce.set(-1,0,0,0)}}}function i(){let G=!1,Ne=!1,ce=null,Ce=null,Ue=null;return{setReversed:function(be){if(Ne!==be){const qe=e.get("EXT_clip_control");be?qe.clipControlEXT(qe.LOWER_LEFT_EXT,qe.ZERO_TO_ONE_EXT):qe.clipControlEXT(qe.LOWER_LEFT_EXT,qe.NEGATIVE_ONE_TO_ONE_EXT),Ne=be;const je=Ue;Ue=null,this.setClear(je)}},getReversed:function(){return Ne},setTest:function(be){be?ue(t.DEPTH_TEST):Le(t.DEPTH_TEST)},setMask:function(be){ce!==be&&!G&&(t.depthMask(be),ce=be)},setFunc:function(be){if(Ne&&(be=wA[be]),Ce!==be){switch(be){case bp:t.depthFunc(t.NEVER);break;case yp:t.depthFunc(t.ALWAYS);break;case Sp:t.depthFunc(t.LESS);break;case _l:t.depthFunc(t.LEQUAL);break;case Mp:t.depthFunc(t.EQUAL);break;case Ep:t.depthFunc(t.GEQUAL);break;case Tp:t.depthFunc(t.GREATER);break;case Ap:t.depthFunc(t.NOTEQUAL);break;default:t.depthFunc(t.LEQUAL)}Ce=be}},setLocked:function(be){G=be},setClear:function(be){Ue!==be&&(Ue=be,Ne&&(be=1-be),t.clearDepth(be))},reset:function(){G=!1,ce=null,Ce=null,Ue=null,Ne=!1}}}function a(){let G=!1,Ne=null,ce=null,Ce=null,Ue=null,be=null,qe=null,je=null,xt=null;return{setTest:function(vt){G||(vt?ue(t.STENCIL_TEST):Le(t.STENCIL_TEST))},setMask:function(vt){Ne!==vt&&!G&&(t.stencilMask(vt),Ne=vt)},setFunc:function(vt,Jt,rn){(ce!==vt||Ce!==Jt||Ue!==rn)&&(t.stencilFunc(vt,Jt,rn),ce=vt,Ce=Jt,Ue=rn)},setOp:function(vt,Jt,rn){(be!==vt||qe!==Jt||je!==rn)&&(t.stencilOp(vt,Jt,rn),be=vt,qe=Jt,je=rn)},setLocked:function(vt){G=vt},setClear:function(vt){xt!==vt&&(t.clearStencil(vt),xt=vt)},reset:function(){G=!1,Ne=null,ce=null,Ce=null,Ue=null,be=null,qe=null,je=null,xt=null}}}const s=new n,l=new i,o=new a,c=new WeakMap,u=new WeakMap;let h={},g={},d={},p=new WeakMap,x=[],_=null,m=!1,f=null,v=null,y=null,b=null,T=null,M=null,A=null,S=new gt(0,0,0),N=0,D=!1,U=null,I=null,Y=null,k=null,B=null;const z=t.getParameter(t.MAX_COMBINED_TEXTURE_IMAGE_UNITS);let j=!1,X=0;const q=t.getParameter(t.VERSION);q.indexOf("WebGL")!==-1?(X=parseFloat(/^WebGL (\d)/.exec(q)[1]),j=X>=1):q.indexOf("OpenGL ES")!==-1&&(X=parseFloat(/^OpenGL ES (\d)/.exec(q)[1]),j=X>=2);let ve=null,he={};const Ae=t.getParameter(t.SCISSOR_BOX),$=t.getParameter(t.VIEWPORT),pe=new Yt().fromArray(Ae),we=new Yt().fromArray($);function ne(G,Ne,ce,Ce){const Ue=new Uint8Array(4),be=t.createTexture();t.bindTexture(G,be),t.texParameteri(G,t.TEXTURE_MIN_FILTER,t.NEAREST),t.texParameteri(G,t.TEXTURE_MAG_FILTER,t.NEAREST);for(let qe=0;qe<ce;qe++)G===t.TEXTURE_3D||G===t.TEXTURE_2D_ARRAY?t.texImage3D(Ne,0,t.RGBA,1,1,Ce,0,t.RGBA,t.UNSIGNED_BYTE,Ue):t.texImage2D(Ne+qe,0,t.RGBA,1,1,0,t.RGBA,t.UNSIGNED_BYTE,Ue);return be}const ge={};ge[t.TEXTURE_2D]=ne(t.TEXTURE_2D,t.TEXTURE_2D,1),ge[t.TEXTURE_CUBE_MAP]=ne(t.TEXTURE_CUBE_MAP,t.TEXTURE_CUBE_MAP_POSITIVE_X,6),ge[t.TEXTURE_2D_ARRAY]=ne(t.TEXTURE_2D_ARRAY,t.TEXTURE_2D_ARRAY,1,1),ge[t.TEXTURE_3D]=ne(t.TEXTURE_3D,t.TEXTURE_3D,1,1),s.setClear(0,0,0,1),l.setClear(1),o.setClear(0),ue(t.DEPTH_TEST),l.setFunc(_l),Me(!1),Oe(vv),ue(t.CULL_FACE),le(Ma);function ue(G){h[G]!==!0&&(t.enable(G),h[G]=!0)}function Le(G){h[G]!==!1&&(t.disable(G),h[G]=!1)}function Ie(G,Ne){return d[G]!==Ne?(t.bindFramebuffer(G,Ne),d[G]=Ne,G===t.DRAW_FRAMEBUFFER&&(d[t.FRAMEBUFFER]=Ne),G===t.FRAMEBUFFER&&(d[t.DRAW_FRAMEBUFFER]=Ne),!0):!1}function Xe(G,Ne){let ce=x,Ce=!1;if(G){ce=p.get(Ne),ce===void 0&&(ce=[],p.set(Ne,ce));const Ue=G.textures;if(ce.length!==Ue.length||ce[0]!==t.COLOR_ATTACHMENT0){for(let be=0,qe=Ue.length;be<qe;be++)ce[be]=t.COLOR_ATTACHMENT0+be;ce.length=Ue.length,Ce=!0}}else ce[0]!==t.BACK&&(ce[0]=t.BACK,Ce=!0);Ce&&t.drawBuffers(ce)}function dt(G){return _!==G?(t.useProgram(G),_=G,!0):!1}const re={[zs]:t.FUNC_ADD,[K1]:t.FUNC_SUBTRACT,[Z1]:t.FUNC_REVERSE_SUBTRACT};re[Q1]=t.MIN,re[J1]=t.MAX;const H={[eA]:t.ZERO,[tA]:t.ONE,[nA]:t.SRC_COLOR,[vp]:t.SRC_ALPHA,[oA]:t.SRC_ALPHA_SATURATE,[rA]:t.DST_COLOR,[aA]:t.DST_ALPHA,[iA]:t.ONE_MINUS_SRC_COLOR,[_p]:t.ONE_MINUS_SRC_ALPHA,[lA]:t.ONE_MINUS_DST_COLOR,[sA]:t.ONE_MINUS_DST_ALPHA,[cA]:t.CONSTANT_COLOR,[uA]:t.ONE_MINUS_CONSTANT_COLOR,[dA]:t.CONSTANT_ALPHA,[hA]:t.ONE_MINUS_CONSTANT_ALPHA};function le(G,Ne,ce,Ce,Ue,be,qe,je,xt,vt){if(G===Ma){m===!0&&(Le(t.BLEND),m=!1);return}if(m===!1&&(ue(t.BLEND),m=!0),G!==$1){if(G!==f||vt!==D){if((v!==zs||T!==zs)&&(t.blendEquation(t.FUNC_ADD),v=zs,T=zs),vt)switch(G){case il:t.blendFuncSeparate(t.ONE,t.ONE_MINUS_SRC_ALPHA,t.ONE,t.ONE_MINUS_SRC_ALPHA);break;case _v:t.blendFunc(t.ONE,t.ONE);break;case bv:t.blendFuncSeparate(t.ZERO,t.ONE_MINUS_SRC_COLOR,t.ZERO,t.ONE);break;case yv:t.blendFuncSeparate(t.DST_COLOR,t.ONE_MINUS_SRC_ALPHA,t.ZERO,t.ONE);break;default:bt("WebGLState: Invalid blending: ",G);break}else switch(G){case il:t.blendFuncSeparate(t.SRC_ALPHA,t.ONE_MINUS_SRC_ALPHA,t.ONE,t.ONE_MINUS_SRC_ALPHA);break;case _v:t.blendFuncSeparate(t.SRC_ALPHA,t.ONE,t.ONE,t.ONE);break;case bv:bt("WebGLState: SubtractiveBlending requires material.premultipliedAlpha = true");break;case yv:bt("WebGLState: MultiplyBlending requires material.premultipliedAlpha = true");break;default:bt("WebGLState: Invalid blending: ",G);break}y=null,b=null,M=null,A=null,S.set(0,0,0),N=0,f=G,D=vt}return}Ue=Ue||Ne,be=be||ce,qe=qe||Ce,(Ne!==v||Ue!==T)&&(t.blendEquationSeparate(re[Ne],re[Ue]),v=Ne,T=Ue),(ce!==y||Ce!==b||be!==M||qe!==A)&&(t.blendFuncSeparate(H[ce],H[Ce],H[be],H[qe]),y=ce,b=Ce,M=be,A=qe),(je.equals(S)===!1||xt!==N)&&(t.blendColor(je.r,je.g,je.b,xt),S.copy(je),N=xt),f=G,D=!1}function se(G,Ne){G.side===ma?Le(t.CULL_FACE):ue(t.CULL_FACE);let ce=G.side===Wn;Ne&&(ce=!ce),Me(ce),G.blending===il&&G.transparent===!1?le(Ma):le(G.blending,G.blendEquation,G.blendSrc,G.blendDst,G.blendEquationAlpha,G.blendSrcAlpha,G.blendDstAlpha,G.blendColor,G.blendAlpha,G.premultipliedAlpha),l.setFunc(G.depthFunc),l.setTest(G.depthTest),l.setMask(G.depthWrite),s.setMask(G.colorWrite);const Ce=G.stencilWrite;o.setTest(Ce),Ce&&(o.setMask(G.stencilWriteMask),o.setFunc(G.stencilFunc,G.stencilRef,G.stencilFuncMask),o.setOp(G.stencilFail,G.stencilZFail,G.stencilZPass)),Re(G.polygonOffset,G.polygonOffsetFactor,G.polygonOffsetUnits),G.alphaToCoverage===!0?ue(t.SAMPLE_ALPHA_TO_COVERAGE):Le(t.SAMPLE_ALPHA_TO_COVERAGE)}function Me(G){U!==G&&(G?t.frontFace(t.CW):t.frontFace(t.CCW),U=G)}function Oe(G){G!==X1?(ue(t.CULL_FACE),G!==I&&(G===vv?t.cullFace(t.BACK):G===q1?t.cullFace(t.FRONT):t.cullFace(t.FRONT_AND_BACK))):Le(t.CULL_FACE),I=G}function De(G){G!==Y&&(j&&t.lineWidth(G),Y=G)}function Re(G,Ne,ce){G?(ue(t.POLYGON_OFFSET_FILL),(k!==Ne||B!==ce)&&(k=Ne,B=ce,l.getReversed()&&(Ne=-Ne),t.polygonOffset(Ne,ce))):Le(t.POLYGON_OFFSET_FILL)}function Fe(G){G?ue(t.SCISSOR_TEST):Le(t.SCISSOR_TEST)}function ye(G){G===void 0&&(G=t.TEXTURE0+z-1),ve!==G&&(t.activeTexture(G),ve=G)}function O(G,Ne,ce){ce===void 0&&(ve===null?ce=t.TEXTURE0+z-1:ce=ve);let Ce=he[ce];Ce===void 0&&(Ce={type:void 0,texture:void 0},he[ce]=Ce),(Ce.type!==G||Ce.texture!==Ne)&&(ve!==ce&&(t.activeTexture(ce),ve=ce),t.bindTexture(G,Ne||ge[G]),Ce.type=G,Ce.texture=Ne)}function Ye(){const G=he[ve];G!==void 0&&G.type!==void 0&&(t.bindTexture(G.type,null),G.type=void 0,G.texture=void 0)}function $e(){try{t.compressedTexImage2D(...arguments)}catch(G){bt("WebGLState:",G)}}function R(){try{t.compressedTexImage3D(...arguments)}catch(G){bt("WebGLState:",G)}}function E(){try{t.texSubImage2D(...arguments)}catch(G){bt("WebGLState:",G)}}function V(){try{t.texSubImage3D(...arguments)}catch(G){bt("WebGLState:",G)}}function K(){try{t.compressedTexSubImage2D(...arguments)}catch(G){bt("WebGLState:",G)}}function J(){try{t.compressedTexSubImage3D(...arguments)}catch(G){bt("WebGLState:",G)}}function Se(){try{t.texStorage2D(...arguments)}catch(G){bt("WebGLState:",G)}}function _e(){try{t.texStorage3D(...arguments)}catch(G){bt("WebGLState:",G)}}function te(){try{t.texImage2D(...arguments)}catch(G){bt("WebGLState:",G)}}function Q(){try{t.texImage3D(...arguments)}catch(G){bt("WebGLState:",G)}}function oe(G){return g[G]!==void 0?g[G]:t.getParameter(G)}function me(G,Ne){g[G]!==Ne&&(t.pixelStorei(G,Ne),g[G]=Ne)}function xe(G){pe.equals(G)===!1&&(t.scissor(G.x,G.y,G.z,G.w),pe.copy(G))}function Te(G){we.equals(G)===!1&&(t.viewport(G.x,G.y,G.z,G.w),we.copy(G))}function Be(G,Ne){let ce=u.get(Ne);ce===void 0&&(ce=new WeakMap,u.set(Ne,ce));let Ce=ce.get(G);Ce===void 0&&(Ce=t.getUniformBlockIndex(Ne,G.name),ce.set(G,Ce))}function Ve(G,Ne){const Ce=u.get(Ne).get(G);c.get(Ne)!==Ce&&(t.uniformBlockBinding(Ne,Ce,G.__bindingPointIndex),c.set(Ne,Ce))}function Ke(){t.disable(t.BLEND),t.disable(t.CULL_FACE),t.disable(t.DEPTH_TEST),t.disable(t.POLYGON_OFFSET_FILL),t.disable(t.SCISSOR_TEST),t.disable(t.STENCIL_TEST),t.disable(t.SAMPLE_ALPHA_TO_COVERAGE),t.blendEquation(t.FUNC_ADD),t.blendFunc(t.ONE,t.ZERO),t.blendFuncSeparate(t.ONE,t.ZERO,t.ONE,t.ZERO),t.blendColor(0,0,0,0),t.colorMask(!0,!0,!0,!0),t.clearColor(0,0,0,0),t.depthMask(!0),t.depthFunc(t.LESS),l.setReversed(!1),t.clearDepth(1),t.stencilMask(4294967295),t.stencilFunc(t.ALWAYS,0,4294967295),t.stencilOp(t.KEEP,t.KEEP,t.KEEP),t.clearStencil(0),t.cullFace(t.BACK),t.frontFace(t.CCW),t.polygonOffset(0,0),t.activeTexture(t.TEXTURE0),t.bindFramebuffer(t.FRAMEBUFFER,null),t.bindFramebuffer(t.DRAW_FRAMEBUFFER,null),t.bindFramebuffer(t.READ_FRAMEBUFFER,null),t.useProgram(null),t.lineWidth(1),t.scissor(0,0,t.canvas.width,t.canvas.height),t.viewport(0,0,t.canvas.width,t.canvas.height),t.pixelStorei(t.PACK_ALIGNMENT,4),t.pixelStorei(t.UNPACK_ALIGNMENT,4),t.pixelStorei(t.UNPACK_FLIP_Y_WEBGL,!1),t.pixelStorei(t.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1),t.pixelStorei(t.UNPACK_COLORSPACE_CONVERSION_WEBGL,t.BROWSER_DEFAULT_WEBGL),t.pixelStorei(t.PACK_ROW_LENGTH,0),t.pixelStorei(t.PACK_SKIP_PIXELS,0),t.pixelStorei(t.PACK_SKIP_ROWS,0),t.pixelStorei(t.UNPACK_ROW_LENGTH,0),t.pixelStorei(t.UNPACK_IMAGE_HEIGHT,0),t.pixelStorei(t.UNPACK_SKIP_PIXELS,0),t.pixelStorei(t.UNPACK_SKIP_ROWS,0),t.pixelStorei(t.UNPACK_SKIP_IMAGES,0),h={},g={},ve=null,he={},d={},p=new WeakMap,x=[],_=null,m=!1,f=null,v=null,y=null,b=null,T=null,M=null,A=null,S=new gt(0,0,0),N=0,D=!1,U=null,I=null,Y=null,k=null,B=null,pe.set(0,0,t.canvas.width,t.canvas.height),we.set(0,0,t.canvas.width,t.canvas.height),s.reset(),l.reset(),o.reset()}return{buffers:{color:s,depth:l,stencil:o},enable:ue,disable:Le,bindFramebuffer:Ie,drawBuffers:Xe,useProgram:dt,setBlending:le,setMaterial:se,setFlipSided:Me,setCullFace:Oe,setLineWidth:De,setPolygonOffset:Re,setScissorTest:Fe,activeTexture:ye,bindTexture:O,unbindTexture:Ye,compressedTexImage2D:$e,compressedTexImage3D:R,texImage2D:te,texImage3D:Q,pixelStorei:me,getParameter:oe,updateUBOMapping:Be,uniformBlockBinding:Ve,texStorage2D:Se,texStorage3D:_e,texSubImage2D:E,texSubImage3D:V,compressedTexSubImage2D:K,compressedTexSubImage3D:J,scissor:xe,viewport:Te,reset:Ke}}function K3(t,e,n,i,a,s,l){const o=e.has("WEBGL_multisampled_render_to_texture")?e.get("WEBGL_multisampled_render_to_texture"):null,c=typeof navigator>"u"?!1:/OculusBrowser/g.test(navigator.userAgent),u=new Mt,h=new WeakMap,g=new Set;let d;const p=new WeakMap;let x=!1;try{x=typeof OffscreenCanvas<"u"&&new OffscreenCanvas(1,1).getContext("2d")!==null}catch{}function _(R,E){return x?new OffscreenCanvas(R,E):pd("canvas")}function m(R,E,V){let K=1;const J=$e(R);if((J.width>V||J.height>V)&&(K=V/Math.max(J.width,J.height)),K<1)if(typeof HTMLImageElement<"u"&&R instanceof HTMLImageElement||typeof HTMLCanvasElement<"u"&&R instanceof HTMLCanvasElement||typeof ImageBitmap<"u"&&R instanceof ImageBitmap||typeof VideoFrame<"u"&&R instanceof VideoFrame){const Se=Math.floor(K*J.width),_e=Math.floor(K*J.height);d===void 0&&(d=_(Se,_e));const te=E?_(Se,_e):d;return te.width=Se,te.height=_e,te.getContext("2d").drawImage(R,0,0,Se,_e),Qe("WebGLRenderer: Texture has been resized from ("+J.width+"x"+J.height+") to ("+Se+"x"+_e+")."),te}else return"data"in R&&Qe("WebGLRenderer: Image in DataTexture is too big ("+J.width+"x"+J.height+")."),R;return R}function f(R){return R.generateMipmaps}function v(R){t.generateMipmap(R)}function y(R){return R.isWebGLCubeRenderTarget?t.TEXTURE_CUBE_MAP:R.isWebGL3DRenderTarget?t.TEXTURE_3D:R.isWebGLArrayRenderTarget||R.isCompressedArrayTexture?t.TEXTURE_2D_ARRAY:t.TEXTURE_2D}function b(R,E,V,K,J,Se=!1){if(R!==null){if(t[R]!==void 0)return t[R];Qe("WebGLRenderer: Attempt to use non-existing WebGL internal format '"+R+"'")}let _e;K&&(_e=e.get("EXT_texture_norm16"),_e||Qe("WebGLRenderer: Unable to use normalized textures without EXT_texture_norm16 extension"));let te=E;if(E===t.RED&&(V===t.FLOAT&&(te=t.R32F),V===t.HALF_FLOAT&&(te=t.R16F),V===t.UNSIGNED_BYTE&&(te=t.R8),V===t.UNSIGNED_SHORT&&_e&&(te=_e.R16_EXT),V===t.SHORT&&_e&&(te=_e.R16_SNORM_EXT)),E===t.RED_INTEGER&&(V===t.UNSIGNED_BYTE&&(te=t.R8UI),V===t.UNSIGNED_SHORT&&(te=t.R16UI),V===t.UNSIGNED_INT&&(te=t.R32UI),V===t.BYTE&&(te=t.R8I),V===t.SHORT&&(te=t.R16I),V===t.INT&&(te=t.R32I)),E===t.RG&&(V===t.FLOAT&&(te=t.RG32F),V===t.HALF_FLOAT&&(te=t.RG16F),V===t.UNSIGNED_BYTE&&(te=t.RG8),V===t.UNSIGNED_SHORT&&_e&&(te=_e.RG16_EXT),V===t.SHORT&&_e&&(te=_e.RG16_SNORM_EXT)),E===t.RG_INTEGER&&(V===t.UNSIGNED_BYTE&&(te=t.RG8UI),V===t.UNSIGNED_SHORT&&(te=t.RG16UI),V===t.UNSIGNED_INT&&(te=t.RG32UI),V===t.BYTE&&(te=t.RG8I),V===t.SHORT&&(te=t.RG16I),V===t.INT&&(te=t.RG32I)),E===t.RGB_INTEGER&&(V===t.UNSIGNED_BYTE&&(te=t.RGB8UI),V===t.UNSIGNED_SHORT&&(te=t.RGB16UI),V===t.UNSIGNED_INT&&(te=t.RGB32UI),V===t.BYTE&&(te=t.RGB8I),V===t.SHORT&&(te=t.RGB16I),V===t.INT&&(te=t.RGB32I)),E===t.RGBA_INTEGER&&(V===t.UNSIGNED_BYTE&&(te=t.RGBA8UI),V===t.UNSIGNED_SHORT&&(te=t.RGBA16UI),V===t.UNSIGNED_INT&&(te=t.RGBA32UI),V===t.BYTE&&(te=t.RGBA8I),V===t.SHORT&&(te=t.RGBA16I),V===t.INT&&(te=t.RGBA32I)),E===t.RGB&&(V===t.UNSIGNED_SHORT&&_e&&(te=_e.RGB16_EXT),V===t.SHORT&&_e&&(te=_e.RGB16_SNORM_EXT),V===t.UNSIGNED_INT_5_9_9_9_REV&&(te=t.RGB9_E5),V===t.UNSIGNED_INT_10F_11F_11F_REV&&(te=t.R11F_G11F_B10F)),E===t.RGBA){const Q=Se?hd:ft.getTransfer(J);V===t.FLOAT&&(te=t.RGBA32F),V===t.HALF_FLOAT&&(te=t.RGBA16F),V===t.UNSIGNED_BYTE&&(te=Q===Et?t.SRGB8_ALPHA8:t.RGBA8),V===t.UNSIGNED_SHORT&&_e&&(te=_e.RGBA16_EXT),V===t.SHORT&&_e&&(te=_e.RGBA16_SNORM_EXT),V===t.UNSIGNED_SHORT_4_4_4_4&&(te=t.RGBA4),V===t.UNSIGNED_SHORT_5_5_5_1&&(te=t.RGB5_A1)}return(te===t.R16F||te===t.R32F||te===t.RG16F||te===t.RG32F||te===t.RGBA16F||te===t.RGBA32F)&&e.get("EXT_color_buffer_float"),te}function T(R,E){let V;return R?E===null||E===Zi||E===Xo?V=t.DEPTH24_STENCIL8:E===Li?V=t.DEPTH32F_STENCIL8:E===Wo&&(V=t.DEPTH24_STENCIL8,Qe("DepthTexture: 16 bit depth attachment is not supported with stencil. Using 24-bit attachment.")):E===null||E===Zi||E===Xo?V=t.DEPTH_COMPONENT24:E===Li?V=t.DEPTH_COMPONENT32F:E===Wo&&(V=t.DEPTH_COMPONENT16),V}function M(R,E){return f(R)===!0||R.isFramebufferTexture&&R.minFilter!==Sn&&R.minFilter!==wn?Math.log2(Math.max(E.width,E.height))+1:R.mipmaps!==void 0&&R.mipmaps.length>0?R.mipmaps.length:R.isCompressedTexture&&Array.isArray(R.image)?E.mipmaps.length:1}function A(R){const E=R.target;E.removeEventListener("dispose",A),N(E),E.isVideoTexture&&h.delete(E),E.isHTMLTexture&&g.delete(E)}function S(R){const E=R.target;E.removeEventListener("dispose",S),U(E)}function N(R){const E=i.get(R);if(E.__webglInit===void 0)return;const V=R.source,K=p.get(V);if(K){const J=K[E.__cacheKey];J.usedTimes--,J.usedTimes===0&&D(R),Object.keys(K).length===0&&p.delete(V)}i.remove(R)}function D(R){const E=i.get(R);t.deleteTexture(E.__webglTexture);const V=R.source,K=p.get(V);delete K[E.__cacheKey],l.memory.textures--}function U(R){const E=i.get(R);if(R.depthTexture&&(R.depthTexture.dispose(),i.remove(R.depthTexture)),R.isWebGLCubeRenderTarget)for(let K=0;K<6;K++){if(Array.isArray(E.__webglFramebuffer[K]))for(let J=0;J<E.__webglFramebuffer[K].length;J++)t.deleteFramebuffer(E.__webglFramebuffer[K][J]);else t.deleteFramebuffer(E.__webglFramebuffer[K]);E.__webglDepthbuffer&&t.deleteRenderbuffer(E.__webglDepthbuffer[K])}else{if(Array.isArray(E.__webglFramebuffer))for(let K=0;K<E.__webglFramebuffer.length;K++)t.deleteFramebuffer(E.__webglFramebuffer[K]);else t.deleteFramebuffer(E.__webglFramebuffer);if(E.__webglDepthbuffer&&t.deleteRenderbuffer(E.__webglDepthbuffer),E.__webglMultisampledFramebuffer&&t.deleteFramebuffer(E.__webglMultisampledFramebuffer),E.__webglColorRenderbuffer)for(let K=0;K<E.__webglColorRenderbuffer.length;K++)E.__webglColorRenderbuffer[K]&&t.deleteRenderbuffer(E.__webglColorRenderbuffer[K]);E.__webglDepthRenderbuffer&&t.deleteRenderbuffer(E.__webglDepthRenderbuffer)}const V=R.textures;for(let K=0,J=V.length;K<J;K++){const Se=i.get(V[K]);Se.__webglTexture&&(t.deleteTexture(Se.__webglTexture),l.memory.textures--),i.remove(V[K])}i.remove(R)}let I=0;function Y(){I=0}function k(){return I}function B(R){I=R}function z(){const R=I;return R>=a.maxTextures&&Qe("WebGLTextures: Trying to use "+R+" texture units while this GPU supports only "+a.maxTextures),I+=1,R}function j(R){const E=[];return E.push(R.wrapS),E.push(R.wrapT),E.push(R.wrapR||0),E.push(R.magFilter),E.push(R.minFilter),E.push(R.anisotropy),E.push(R.internalFormat),E.push(R.format),E.push(R.type),E.push(R.generateMipmaps),E.push(R.premultiplyAlpha),E.push(R.flipY),E.push(R.unpackAlignment),E.push(R.colorSpace),E.join()}function X(R,E){const V=i.get(R);if(R.isVideoTexture&&O(R),R.isRenderTargetTexture===!1&&R.isExternalTexture!==!0&&R.version>0&&V.__version!==R.version){const K=R.image;if(K===null)Qe("WebGLRenderer: Texture marked for update but no image data found.");else if(K.complete===!1)Qe("WebGLRenderer: Texture marked for update but image is incomplete");else{Le(V,R,E);return}}else R.isExternalTexture&&(V.__webglTexture=R.sourceTexture?R.sourceTexture:null);n.bindTexture(t.TEXTURE_2D,V.__webglTexture,t.TEXTURE0+E)}function q(R,E){const V=i.get(R);if(R.isRenderTargetTexture===!1&&R.version>0&&V.__version!==R.version){Le(V,R,E);return}else R.isExternalTexture&&(V.__webglTexture=R.sourceTexture?R.sourceTexture:null);n.bindTexture(t.TEXTURE_2D_ARRAY,V.__webglTexture,t.TEXTURE0+E)}function ve(R,E){const V=i.get(R);if(R.isRenderTargetTexture===!1&&R.version>0&&V.__version!==R.version){Le(V,R,E);return}n.bindTexture(t.TEXTURE_3D,V.__webglTexture,t.TEXTURE0+E)}function he(R,E){const V=i.get(R);if(R.isCubeDepthTexture!==!0&&R.version>0&&V.__version!==R.version){Ie(V,R,E);return}n.bindTexture(t.TEXTURE_CUBE_MAP,V.__webglTexture,t.TEXTURE0+E)}const Ae={[wp]:t.REPEAT,[_a]:t.CLAMP_TO_EDGE,[Np]:t.MIRRORED_REPEAT},$={[Sn]:t.NEAREST,[mA]:t.NEAREST_MIPMAP_NEAREST,[Nc]:t.NEAREST_MIPMAP_LINEAR,[wn]:t.LINEAR,[Dh]:t.LINEAR_MIPMAP_NEAREST,[ks]:t.LINEAR_MIPMAP_LINEAR},pe={[vA]:t.NEVER,[MA]:t.ALWAYS,[_A]:t.LESS,[Mg]:t.LEQUAL,[bA]:t.EQUAL,[Eg]:t.GEQUAL,[yA]:t.GREATER,[SA]:t.NOTEQUAL};function we(R,E){if(E.type===Li&&e.has("OES_texture_float_linear")===!1&&(E.magFilter===wn||E.magFilter===Dh||E.magFilter===Nc||E.magFilter===ks||E.minFilter===wn||E.minFilter===Dh||E.minFilter===Nc||E.minFilter===ks)&&Qe("WebGLRenderer: Unable to use linear filtering with floating point textures. OES_texture_float_linear not supported on this device."),t.texParameteri(R,t.TEXTURE_WRAP_S,Ae[E.wrapS]),t.texParameteri(R,t.TEXTURE_WRAP_T,Ae[E.wrapT]),(R===t.TEXTURE_3D||R===t.TEXTURE_2D_ARRAY)&&t.texParameteri(R,t.TEXTURE_WRAP_R,Ae[E.wrapR]),t.texParameteri(R,t.TEXTURE_MAG_FILTER,$[E.magFilter]),t.texParameteri(R,t.TEXTURE_MIN_FILTER,$[E.minFilter]),E.compareFunction&&(t.texParameteri(R,t.TEXTURE_COMPARE_MODE,t.COMPARE_REF_TO_TEXTURE),t.texParameteri(R,t.TEXTURE_COMPARE_FUNC,pe[E.compareFunction])),e.has("EXT_texture_filter_anisotropic")===!0){if(E.magFilter===Sn||E.minFilter!==Nc&&E.minFilter!==ks||E.type===Li&&e.has("OES_texture_float_linear")===!1)return;if(E.anisotropy>1||i.get(E).__currentAnisotropy){const V=e.get("EXT_texture_filter_anisotropic");t.texParameterf(R,V.TEXTURE_MAX_ANISOTROPY_EXT,Math.min(E.anisotropy,a.getMaxAnisotropy())),i.get(E).__currentAnisotropy=E.anisotropy}}}function ne(R,E){let V=!1;R.__webglInit===void 0&&(R.__webglInit=!0,E.addEventListener("dispose",A));const K=E.source;let J=p.get(K);J===void 0&&(J={},p.set(K,J));const Se=j(E);if(Se!==R.__cacheKey){J[Se]===void 0&&(J[Se]={texture:t.createTexture(),usedTimes:0},l.memory.textures++,V=!0),J[Se].usedTimes++;const _e=J[R.__cacheKey];_e!==void 0&&(J[R.__cacheKey].usedTimes--,_e.usedTimes===0&&D(E)),R.__cacheKey=Se,R.__webglTexture=J[Se].texture}return V}function ge(R,E,V){return Math.floor(Math.floor(R/V)/E)}function ue(R,E,V,K){const Se=R.updateRanges;if(Se.length===0)n.texSubImage2D(t.TEXTURE_2D,0,0,0,E.width,E.height,V,K,E.data);else{Se.sort((me,xe)=>me.start-xe.start);let _e=0;for(let me=1;me<Se.length;me++){const xe=Se[_e],Te=Se[me],Be=xe.start+xe.count,Ve=ge(Te.start,E.width,4),Ke=ge(xe.start,E.width,4);Te.start<=Be+1&&Ve===Ke&&ge(Te.start+Te.count-1,E.width,4)===Ve?xe.count=Math.max(xe.count,Te.start+Te.count-xe.start):(++_e,Se[_e]=Te)}Se.length=_e+1;const te=n.getParameter(t.UNPACK_ROW_LENGTH),Q=n.getParameter(t.UNPACK_SKIP_PIXELS),oe=n.getParameter(t.UNPACK_SKIP_ROWS);n.pixelStorei(t.UNPACK_ROW_LENGTH,E.width);for(let me=0,xe=Se.length;me<xe;me++){const Te=Se[me],Be=Math.floor(Te.start/4),Ve=Math.ceil(Te.count/4),Ke=Be%E.width,G=Math.floor(Be/E.width),Ne=Ve,ce=1;n.pixelStorei(t.UNPACK_SKIP_PIXELS,Ke),n.pixelStorei(t.UNPACK_SKIP_ROWS,G),n.texSubImage2D(t.TEXTURE_2D,0,Ke,G,Ne,ce,V,K,E.data)}R.clearUpdateRanges(),n.pixelStorei(t.UNPACK_ROW_LENGTH,te),n.pixelStorei(t.UNPACK_SKIP_PIXELS,Q),n.pixelStorei(t.UNPACK_SKIP_ROWS,oe)}}function Le(R,E,V){let K=t.TEXTURE_2D;(E.isDataArrayTexture||E.isCompressedArrayTexture)&&(K=t.TEXTURE_2D_ARRAY),E.isData3DTexture&&(K=t.TEXTURE_3D);const J=ne(R,E),Se=E.source;n.bindTexture(K,R.__webglTexture,t.TEXTURE0+V);const _e=i.get(Se);if(Se.version!==_e.__version||J===!0){if(n.activeTexture(t.TEXTURE0+V),(typeof ImageBitmap<"u"&&E.image instanceof ImageBitmap)===!1){const ce=ft.getPrimaries(ft.workingColorSpace),Ce=E.colorSpace===Qa?null:ft.getPrimaries(E.colorSpace),Ue=E.colorSpace===Qa||ce===Ce?t.NONE:t.BROWSER_DEFAULT_WEBGL;n.pixelStorei(t.UNPACK_FLIP_Y_WEBGL,E.flipY),n.pixelStorei(t.UNPACK_PREMULTIPLY_ALPHA_WEBGL,E.premultiplyAlpha),n.pixelStorei(t.UNPACK_COLORSPACE_CONVERSION_WEBGL,Ue)}n.pixelStorei(t.UNPACK_ALIGNMENT,E.unpackAlignment);let Q=m(E.image,!1,a.maxTextureSize);Q=Ye(E,Q);const oe=s.convert(E.format,E.colorSpace),me=s.convert(E.type);let xe=b(E.internalFormat,oe,me,E.normalized,E.colorSpace,E.isVideoTexture);we(K,E);let Te;const Be=E.mipmaps,Ve=E.isVideoTexture!==!0,Ke=_e.__version===void 0||J===!0,G=Se.dataReady,Ne=M(E,Q);if(E.isDepthTexture)xe=T(E.format===Is,E.type),Ke&&(Ve?n.texStorage2D(t.TEXTURE_2D,1,xe,Q.width,Q.height):n.texImage2D(t.TEXTURE_2D,0,xe,Q.width,Q.height,0,oe,me,null));else if(E.isDataTexture)if(Be.length>0){Ve&&Ke&&n.texStorage2D(t.TEXTURE_2D,Ne,xe,Be[0].width,Be[0].height);for(let ce=0,Ce=Be.length;ce<Ce;ce++)Te=Be[ce],Ve?G&&n.texSubImage2D(t.TEXTURE_2D,ce,0,0,Te.width,Te.height,oe,me,Te.data):n.texImage2D(t.TEXTURE_2D,ce,xe,Te.width,Te.height,0,oe,me,Te.data);E.generateMipmaps=!1}else Ve?(Ke&&n.texStorage2D(t.TEXTURE_2D,Ne,xe,Q.width,Q.height),G&&ue(E,Q,oe,me)):n.texImage2D(t.TEXTURE_2D,0,xe,Q.width,Q.height,0,oe,me,Q.data);else if(E.isCompressedTexture)if(E.isCompressedArrayTexture){Ve&&Ke&&n.texStorage3D(t.TEXTURE_2D_ARRAY,Ne,xe,Be[0].width,Be[0].height,Q.depth);for(let ce=0,Ce=Be.length;ce<Ce;ce++)if(Te=Be[ce],E.format!==Ui)if(oe!==null)if(Ve){if(G)if(E.layerUpdates.size>0){const Ue=t0(Te.width,Te.height,E.format,E.type);for(const be of E.layerUpdates){const qe=Te.data.subarray(be*Ue/Te.data.BYTES_PER_ELEMENT,(be+1)*Ue/Te.data.BYTES_PER_ELEMENT);n.compressedTexSubImage3D(t.TEXTURE_2D_ARRAY,ce,0,0,be,Te.width,Te.height,1,oe,qe)}E.clearLayerUpdates()}else n.compressedTexSubImage3D(t.TEXTURE_2D_ARRAY,ce,0,0,0,Te.width,Te.height,Q.depth,oe,Te.data)}else n.compressedTexImage3D(t.TEXTURE_2D_ARRAY,ce,xe,Te.width,Te.height,Q.depth,0,Te.data,0,0);else Qe("WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()");else Ve?G&&n.texSubImage3D(t.TEXTURE_2D_ARRAY,ce,0,0,0,Te.width,Te.height,Q.depth,oe,me,Te.data):n.texImage3D(t.TEXTURE_2D_ARRAY,ce,xe,Te.width,Te.height,Q.depth,0,oe,me,Te.data)}else{Ve&&Ke&&n.texStorage2D(t.TEXTURE_2D,Ne,xe,Be[0].width,Be[0].height);for(let ce=0,Ce=Be.length;ce<Ce;ce++)Te=Be[ce],E.format!==Ui?oe!==null?Ve?G&&n.compressedTexSubImage2D(t.TEXTURE_2D,ce,0,0,Te.width,Te.height,oe,Te.data):n.compressedTexImage2D(t.TEXTURE_2D,ce,xe,Te.width,Te.height,0,Te.data):Qe("WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()"):Ve?G&&n.texSubImage2D(t.TEXTURE_2D,ce,0,0,Te.width,Te.height,oe,me,Te.data):n.texImage2D(t.TEXTURE_2D,ce,xe,Te.width,Te.height,0,oe,me,Te.data)}else if(E.isDataArrayTexture)if(Ve){if(Ke&&n.texStorage3D(t.TEXTURE_2D_ARRAY,Ne,xe,Q.width,Q.height,Q.depth),G)if(E.layerUpdates.size>0){const ce=t0(Q.width,Q.height,E.format,E.type);for(const Ce of E.layerUpdates){const Ue=Q.data.subarray(Ce*ce/Q.data.BYTES_PER_ELEMENT,(Ce+1)*ce/Q.data.BYTES_PER_ELEMENT);n.texSubImage3D(t.TEXTURE_2D_ARRAY,0,0,0,Ce,Q.width,Q.height,1,oe,me,Ue)}E.clearLayerUpdates()}else n.texSubImage3D(t.TEXTURE_2D_ARRAY,0,0,0,0,Q.width,Q.height,Q.depth,oe,me,Q.data)}else n.texImage3D(t.TEXTURE_2D_ARRAY,0,xe,Q.width,Q.height,Q.depth,0,oe,me,Q.data);else if(E.isData3DTexture)Ve?(Ke&&n.texStorage3D(t.TEXTURE_3D,Ne,xe,Q.width,Q.height,Q.depth),G&&n.texSubImage3D(t.TEXTURE_3D,0,0,0,0,Q.width,Q.height,Q.depth,oe,me,Q.data)):n.texImage3D(t.TEXTURE_3D,0,xe,Q.width,Q.height,Q.depth,0,oe,me,Q.data);else if(E.isFramebufferTexture){if(Ke)if(Ve)n.texStorage2D(t.TEXTURE_2D,Ne,xe,Q.width,Q.height);else{let ce=Q.width,Ce=Q.height;for(let Ue=0;Ue<Ne;Ue++)n.texImage2D(t.TEXTURE_2D,Ue,xe,ce,Ce,0,oe,me,null),ce>>=1,Ce>>=1}}else if(E.isHTMLTexture){if("texElementImage2D"in t){const ce=t.canvas;if(ce.hasAttribute("layoutsubtree")||ce.setAttribute("layoutsubtree","true"),Q.parentNode!==ce){ce.appendChild(Q),g.add(E),ce.onpaint=Ce=>{const Ue=Ce.changedElements;for(const be of g)Ue.includes(be.image)&&(be.needsUpdate=!0)},ce.requestPaint();return}if(t.texElementImage2D.length===3)t.texElementImage2D(t.TEXTURE_2D,t.RGBA8,Q);else{const Ue=t.RGBA,be=t.RGBA,qe=t.UNSIGNED_BYTE;t.texElementImage2D(t.TEXTURE_2D,0,Ue,be,qe,Q)}t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,t.LINEAR),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_S,t.CLAMP_TO_EDGE),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_T,t.CLAMP_TO_EDGE)}}else if(Be.length>0){if(Ve&&Ke){const ce=$e(Be[0]);n.texStorage2D(t.TEXTURE_2D,Ne,xe,ce.width,ce.height)}for(let ce=0,Ce=Be.length;ce<Ce;ce++)Te=Be[ce],Ve?G&&n.texSubImage2D(t.TEXTURE_2D,ce,0,0,oe,me,Te):n.texImage2D(t.TEXTURE_2D,ce,xe,oe,me,Te);E.generateMipmaps=!1}else if(Ve){if(Ke){const ce=$e(Q);n.texStorage2D(t.TEXTURE_2D,Ne,xe,ce.width,ce.height)}G&&n.texSubImage2D(t.TEXTURE_2D,0,0,0,oe,me,Q)}else n.texImage2D(t.TEXTURE_2D,0,xe,oe,me,Q);f(E)&&v(K),_e.__version=Se.version,E.onUpdate&&E.onUpdate(E)}R.__version=E.version}function Ie(R,E,V){if(E.image.length!==6)return;const K=ne(R,E),J=E.source;n.bindTexture(t.TEXTURE_CUBE_MAP,R.__webglTexture,t.TEXTURE0+V);const Se=i.get(J);if(J.version!==Se.__version||K===!0){n.activeTexture(t.TEXTURE0+V);const _e=ft.getPrimaries(ft.workingColorSpace),te=E.colorSpace===Qa?null:ft.getPrimaries(E.colorSpace),Q=E.colorSpace===Qa||_e===te?t.NONE:t.BROWSER_DEFAULT_WEBGL;n.pixelStorei(t.UNPACK_FLIP_Y_WEBGL,E.flipY),n.pixelStorei(t.UNPACK_PREMULTIPLY_ALPHA_WEBGL,E.premultiplyAlpha),n.pixelStorei(t.UNPACK_ALIGNMENT,E.unpackAlignment),n.pixelStorei(t.UNPACK_COLORSPACE_CONVERSION_WEBGL,Q);const oe=E.isCompressedTexture||E.image[0].isCompressedTexture,me=E.image[0]&&E.image[0].isDataTexture,xe=[];for(let be=0;be<6;be++)!oe&&!me?xe[be]=m(E.image[be],!0,a.maxCubemapSize):xe[be]=me?E.image[be].image:E.image[be],xe[be]=Ye(E,xe[be]);const Te=xe[0],Be=s.convert(E.format,E.colorSpace),Ve=s.convert(E.type),Ke=b(E.internalFormat,Be,Ve,E.normalized,E.colorSpace),G=E.isVideoTexture!==!0,Ne=Se.__version===void 0||K===!0,ce=J.dataReady;let Ce=M(E,Te);we(t.TEXTURE_CUBE_MAP,E);let Ue;if(oe){G&&Ne&&n.texStorage2D(t.TEXTURE_CUBE_MAP,Ce,Ke,Te.width,Te.height);for(let be=0;be<6;be++){Ue=xe[be].mipmaps;for(let qe=0;qe<Ue.length;qe++){const je=Ue[qe];E.format!==Ui?Be!==null?G?ce&&n.compressedTexSubImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+be,qe,0,0,je.width,je.height,Be,je.data):n.compressedTexImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+be,qe,Ke,je.width,je.height,0,je.data):Qe("WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()"):G?ce&&n.texSubImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+be,qe,0,0,je.width,je.height,Be,Ve,je.data):n.texImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+be,qe,Ke,je.width,je.height,0,Be,Ve,je.data)}}}else{if(Ue=E.mipmaps,G&&Ne){Ue.length>0&&Ce++;const be=$e(xe[0]);n.texStorage2D(t.TEXTURE_CUBE_MAP,Ce,Ke,be.width,be.height)}for(let be=0;be<6;be++)if(me){G?ce&&n.texSubImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+be,0,0,0,xe[be].width,xe[be].height,Be,Ve,xe[be].data):n.texImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+be,0,Ke,xe[be].width,xe[be].height,0,Be,Ve,xe[be].data);for(let qe=0;qe<Ue.length;qe++){const xt=Ue[qe].image[be].image;G?ce&&n.texSubImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+be,qe+1,0,0,xt.width,xt.height,Be,Ve,xt.data):n.texImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+be,qe+1,Ke,xt.width,xt.height,0,Be,Ve,xt.data)}}else{G?ce&&n.texSubImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+be,0,0,0,Be,Ve,xe[be]):n.texImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+be,0,Ke,Be,Ve,xe[be]);for(let qe=0;qe<Ue.length;qe++){const je=Ue[qe];G?ce&&n.texSubImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+be,qe+1,0,0,Be,Ve,je.image[be]):n.texImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+be,qe+1,Ke,Be,Ve,je.image[be])}}}f(E)&&v(t.TEXTURE_CUBE_MAP),Se.__version=J.version,E.onUpdate&&E.onUpdate(E)}R.__version=E.version}function Xe(R,E,V,K,J,Se){const _e=s.convert(V.format,V.colorSpace),te=s.convert(V.type),Q=b(V.internalFormat,_e,te,V.normalized,V.colorSpace),oe=i.get(E),me=i.get(V);if(me.__renderTarget=E,!oe.__hasExternalTextures){const xe=Math.max(1,E.width>>Se),Te=Math.max(1,E.height>>Se);J===t.TEXTURE_3D||J===t.TEXTURE_2D_ARRAY?n.texImage3D(J,Se,Q,xe,Te,E.depth,0,_e,te,null):n.texImage2D(J,Se,Q,xe,Te,0,_e,te,null)}n.bindFramebuffer(t.FRAMEBUFFER,R),ye(E)?o.framebufferTexture2DMultisampleEXT(t.FRAMEBUFFER,K,J,me.__webglTexture,0,Fe(E)):(J===t.TEXTURE_2D||J>=t.TEXTURE_CUBE_MAP_POSITIVE_X&&J<=t.TEXTURE_CUBE_MAP_NEGATIVE_Z)&&t.framebufferTexture2D(t.FRAMEBUFFER,K,J,me.__webglTexture,Se),n.bindFramebuffer(t.FRAMEBUFFER,null)}function dt(R,E,V){if(t.bindRenderbuffer(t.RENDERBUFFER,R),E.depthBuffer){const K=E.depthTexture,J=K&&K.isDepthTexture?K.type:null,Se=T(E.stencilBuffer,J),_e=E.stencilBuffer?t.DEPTH_STENCIL_ATTACHMENT:t.DEPTH_ATTACHMENT;ye(E)?o.renderbufferStorageMultisampleEXT(t.RENDERBUFFER,Fe(E),Se,E.width,E.height):V?t.renderbufferStorageMultisample(t.RENDERBUFFER,Fe(E),Se,E.width,E.height):t.renderbufferStorage(t.RENDERBUFFER,Se,E.width,E.height),t.framebufferRenderbuffer(t.FRAMEBUFFER,_e,t.RENDERBUFFER,R)}else{const K=E.textures;for(let J=0;J<K.length;J++){const Se=K[J],_e=s.convert(Se.format,Se.colorSpace),te=s.convert(Se.type),Q=b(Se.internalFormat,_e,te,Se.normalized,Se.colorSpace);ye(E)?o.renderbufferStorageMultisampleEXT(t.RENDERBUFFER,Fe(E),Q,E.width,E.height):V?t.renderbufferStorageMultisample(t.RENDERBUFFER,Fe(E),Q,E.width,E.height):t.renderbufferStorage(t.RENDERBUFFER,Q,E.width,E.height)}}t.bindRenderbuffer(t.RENDERBUFFER,null)}function re(R,E,V){const K=E.isWebGLCubeRenderTarget===!0;if(n.bindFramebuffer(t.FRAMEBUFFER,R),!(E.depthTexture&&E.depthTexture.isDepthTexture))throw new Error("THREE.WebGLTextures: renderTarget.depthTexture must be an instance of THREE.DepthTexture.");const J=i.get(E.depthTexture);if(J.__renderTarget=E,(!J.__webglTexture||E.depthTexture.image.width!==E.width||E.depthTexture.image.height!==E.height)&&(E.depthTexture.image.width=E.width,E.depthTexture.image.height=E.height,E.depthTexture.needsUpdate=!0),K){if(J.__webglInit===void 0&&(J.__webglInit=!0,E.depthTexture.addEventListener("dispose",A)),J.__webglTexture===void 0){J.__webglTexture=t.createTexture(),n.bindTexture(t.TEXTURE_CUBE_MAP,J.__webglTexture),we(t.TEXTURE_CUBE_MAP,E.depthTexture);const oe=s.convert(E.depthTexture.format),me=s.convert(E.depthTexture.type);let xe;E.depthTexture.format===Da?xe=t.DEPTH_COMPONENT24:E.depthTexture.format===Is&&(xe=t.DEPTH24_STENCIL8);for(let Te=0;Te<6;Te++)t.texImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+Te,0,xe,E.width,E.height,0,oe,me,null)}}else X(E.depthTexture,0);const Se=J.__webglTexture,_e=Fe(E),te=K?t.TEXTURE_CUBE_MAP_POSITIVE_X+V:t.TEXTURE_2D,Q=E.depthTexture.format===Is?t.DEPTH_STENCIL_ATTACHMENT:t.DEPTH_ATTACHMENT;if(E.depthTexture.format===Da)ye(E)?o.framebufferTexture2DMultisampleEXT(t.FRAMEBUFFER,Q,te,Se,0,_e):t.framebufferTexture2D(t.FRAMEBUFFER,Q,te,Se,0);else if(E.depthTexture.format===Is)ye(E)?o.framebufferTexture2DMultisampleEXT(t.FRAMEBUFFER,Q,te,Se,0,_e):t.framebufferTexture2D(t.FRAMEBUFFER,Q,te,Se,0);else throw new Error("THREE.WebGLTextures: Unknown depthTexture format.")}function H(R){const E=i.get(R),V=R.isWebGLCubeRenderTarget===!0;if(E.__boundDepthTexture!==R.depthTexture){const K=R.depthTexture;if(E.__depthDisposeCallback&&E.__depthDisposeCallback(),K){const J=()=>{delete E.__boundDepthTexture,delete E.__depthDisposeCallback,K.removeEventListener("dispose",J)};K.addEventListener("dispose",J),E.__depthDisposeCallback=J}E.__boundDepthTexture=K}if(R.depthTexture&&!E.__autoAllocateDepthBuffer)if(V)for(let K=0;K<6;K++)re(E.__webglFramebuffer[K],R,K);else{const K=R.texture.mipmaps;K&&K.length>0?re(E.__webglFramebuffer[0],R,0):re(E.__webglFramebuffer,R,0)}else if(V){E.__webglDepthbuffer=[];for(let K=0;K<6;K++)if(n.bindFramebuffer(t.FRAMEBUFFER,E.__webglFramebuffer[K]),E.__webglDepthbuffer[K]===void 0)E.__webglDepthbuffer[K]=t.createRenderbuffer(),dt(E.__webglDepthbuffer[K],R,!1);else{const J=R.stencilBuffer?t.DEPTH_STENCIL_ATTACHMENT:t.DEPTH_ATTACHMENT,Se=E.__webglDepthbuffer[K];t.bindRenderbuffer(t.RENDERBUFFER,Se),t.framebufferRenderbuffer(t.FRAMEBUFFER,J,t.RENDERBUFFER,Se)}}else{const K=R.texture.mipmaps;if(K&&K.length>0?n.bindFramebuffer(t.FRAMEBUFFER,E.__webglFramebuffer[0]):n.bindFramebuffer(t.FRAMEBUFFER,E.__webglFramebuffer),E.__webglDepthbuffer===void 0)E.__webglDepthbuffer=t.createRenderbuffer(),dt(E.__webglDepthbuffer,R,!1);else{const J=R.stencilBuffer?t.DEPTH_STENCIL_ATTACHMENT:t.DEPTH_ATTACHMENT,Se=E.__webglDepthbuffer;t.bindRenderbuffer(t.RENDERBUFFER,Se),t.framebufferRenderbuffer(t.FRAMEBUFFER,J,t.RENDERBUFFER,Se)}}n.bindFramebuffer(t.FRAMEBUFFER,null)}function le(R,E,V){const K=i.get(R);E!==void 0&&Xe(K.__webglFramebuffer,R,R.texture,t.COLOR_ATTACHMENT0,t.TEXTURE_2D,0),V!==void 0&&H(R)}function se(R){const E=R.texture,V=i.get(R),K=i.get(E);R.addEventListener("dispose",S);const J=R.textures,Se=R.isWebGLCubeRenderTarget===!0,_e=J.length>1;if(_e||(K.__webglTexture===void 0&&(K.__webglTexture=t.createTexture()),K.__version=E.version,l.memory.textures++),Se){V.__webglFramebuffer=[];for(let te=0;te<6;te++)if(E.mipmaps&&E.mipmaps.length>0){V.__webglFramebuffer[te]=[];for(let Q=0;Q<E.mipmaps.length;Q++)V.__webglFramebuffer[te][Q]=t.createFramebuffer()}else V.__webglFramebuffer[te]=t.createFramebuffer()}else{if(E.mipmaps&&E.mipmaps.length>0){V.__webglFramebuffer=[];for(let te=0;te<E.mipmaps.length;te++)V.__webglFramebuffer[te]=t.createFramebuffer()}else V.__webglFramebuffer=t.createFramebuffer();if(_e)for(let te=0,Q=J.length;te<Q;te++){const oe=i.get(J[te]);oe.__webglTexture===void 0&&(oe.__webglTexture=t.createTexture(),l.memory.textures++)}if(R.samples>0&&ye(R)===!1){V.__webglMultisampledFramebuffer=t.createFramebuffer(),V.__webglColorRenderbuffer=[],n.bindFramebuffer(t.FRAMEBUFFER,V.__webglMultisampledFramebuffer);for(let te=0;te<J.length;te++){const Q=J[te];V.__webglColorRenderbuffer[te]=t.createRenderbuffer(),t.bindRenderbuffer(t.RENDERBUFFER,V.__webglColorRenderbuffer[te]);const oe=s.convert(Q.format,Q.colorSpace),me=s.convert(Q.type),xe=b(Q.internalFormat,oe,me,Q.normalized,Q.colorSpace,R.isXRRenderTarget===!0),Te=Fe(R);t.renderbufferStorageMultisample(t.RENDERBUFFER,Te,xe,R.width,R.height),t.framebufferRenderbuffer(t.FRAMEBUFFER,t.COLOR_ATTACHMENT0+te,t.RENDERBUFFER,V.__webglColorRenderbuffer[te])}t.bindRenderbuffer(t.RENDERBUFFER,null),R.depthBuffer&&(V.__webglDepthRenderbuffer=t.createRenderbuffer(),dt(V.__webglDepthRenderbuffer,R,!0)),n.bindFramebuffer(t.FRAMEBUFFER,null)}}if(Se){n.bindTexture(t.TEXTURE_CUBE_MAP,K.__webglTexture),we(t.TEXTURE_CUBE_MAP,E);for(let te=0;te<6;te++)if(E.mipmaps&&E.mipmaps.length>0)for(let Q=0;Q<E.mipmaps.length;Q++)Xe(V.__webglFramebuffer[te][Q],R,E,t.COLOR_ATTACHMENT0,t.TEXTURE_CUBE_MAP_POSITIVE_X+te,Q);else Xe(V.__webglFramebuffer[te],R,E,t.COLOR_ATTACHMENT0,t.TEXTURE_CUBE_MAP_POSITIVE_X+te,0);f(E)&&v(t.TEXTURE_CUBE_MAP),n.unbindTexture()}else if(_e){for(let te=0,Q=J.length;te<Q;te++){const oe=J[te],me=i.get(oe);let xe=t.TEXTURE_2D;(R.isWebGL3DRenderTarget||R.isWebGLArrayRenderTarget)&&(xe=R.isWebGL3DRenderTarget?t.TEXTURE_3D:t.TEXTURE_2D_ARRAY),n.bindTexture(xe,me.__webglTexture),we(xe,oe),Xe(V.__webglFramebuffer,R,oe,t.COLOR_ATTACHMENT0+te,xe,0),f(oe)&&v(xe)}n.unbindTexture()}else{let te=t.TEXTURE_2D;if((R.isWebGL3DRenderTarget||R.isWebGLArrayRenderTarget)&&(te=R.isWebGL3DRenderTarget?t.TEXTURE_3D:t.TEXTURE_2D_ARRAY),n.bindTexture(te,K.__webglTexture),we(te,E),E.mipmaps&&E.mipmaps.length>0)for(let Q=0;Q<E.mipmaps.length;Q++)Xe(V.__webglFramebuffer[Q],R,E,t.COLOR_ATTACHMENT0,te,Q);else Xe(V.__webglFramebuffer,R,E,t.COLOR_ATTACHMENT0,te,0);f(E)&&v(te),n.unbindTexture()}R.depthBuffer&&H(R)}function Me(R){const E=R.textures;for(let V=0,K=E.length;V<K;V++){const J=E[V];if(f(J)){const Se=y(R),_e=i.get(J).__webglTexture;n.bindTexture(Se,_e),v(Se),n.unbindTexture()}}}const Oe=[],De=[];function Re(R){if(R.samples>0){if(ye(R)===!1){const E=R.textures,V=R.width,K=R.height;let J=t.COLOR_BUFFER_BIT;const Se=R.stencilBuffer?t.DEPTH_STENCIL_ATTACHMENT:t.DEPTH_ATTACHMENT,_e=i.get(R),te=E.length>1;if(te)for(let oe=0;oe<E.length;oe++)n.bindFramebuffer(t.FRAMEBUFFER,_e.__webglMultisampledFramebuffer),t.framebufferRenderbuffer(t.FRAMEBUFFER,t.COLOR_ATTACHMENT0+oe,t.RENDERBUFFER,null),n.bindFramebuffer(t.FRAMEBUFFER,_e.__webglFramebuffer),t.framebufferTexture2D(t.DRAW_FRAMEBUFFER,t.COLOR_ATTACHMENT0+oe,t.TEXTURE_2D,null,0);n.bindFramebuffer(t.READ_FRAMEBUFFER,_e.__webglMultisampledFramebuffer);const Q=R.texture.mipmaps;Q&&Q.length>0?n.bindFramebuffer(t.DRAW_FRAMEBUFFER,_e.__webglFramebuffer[0]):n.bindFramebuffer(t.DRAW_FRAMEBUFFER,_e.__webglFramebuffer);for(let oe=0;oe<E.length;oe++){if(R.resolveDepthBuffer&&(R.depthBuffer&&(J|=t.DEPTH_BUFFER_BIT),R.stencilBuffer&&R.resolveStencilBuffer&&(J|=t.STENCIL_BUFFER_BIT)),te){t.framebufferRenderbuffer(t.READ_FRAMEBUFFER,t.COLOR_ATTACHMENT0,t.RENDERBUFFER,_e.__webglColorRenderbuffer[oe]);const me=i.get(E[oe]).__webglTexture;t.framebufferTexture2D(t.DRAW_FRAMEBUFFER,t.COLOR_ATTACHMENT0,t.TEXTURE_2D,me,0)}t.blitFramebuffer(0,0,V,K,0,0,V,K,J,t.NEAREST),c===!0&&(Oe.length=0,De.length=0,Oe.push(t.COLOR_ATTACHMENT0+oe),R.depthBuffer&&R.resolveDepthBuffer===!1&&(Oe.push(Se),De.push(Se),t.invalidateFramebuffer(t.DRAW_FRAMEBUFFER,De)),t.invalidateFramebuffer(t.READ_FRAMEBUFFER,Oe))}if(n.bindFramebuffer(t.READ_FRAMEBUFFER,null),n.bindFramebuffer(t.DRAW_FRAMEBUFFER,null),te)for(let oe=0;oe<E.length;oe++){n.bindFramebuffer(t.FRAMEBUFFER,_e.__webglMultisampledFramebuffer),t.framebufferRenderbuffer(t.FRAMEBUFFER,t.COLOR_ATTACHMENT0+oe,t.RENDERBUFFER,_e.__webglColorRenderbuffer[oe]);const me=i.get(E[oe]).__webglTexture;n.bindFramebuffer(t.FRAMEBUFFER,_e.__webglFramebuffer),t.framebufferTexture2D(t.DRAW_FRAMEBUFFER,t.COLOR_ATTACHMENT0+oe,t.TEXTURE_2D,me,0)}n.bindFramebuffer(t.DRAW_FRAMEBUFFER,_e.__webglMultisampledFramebuffer)}else if(R.depthBuffer&&R.resolveDepthBuffer===!1&&c){const E=R.stencilBuffer?t.DEPTH_STENCIL_ATTACHMENT:t.DEPTH_ATTACHMENT;t.invalidateFramebuffer(t.DRAW_FRAMEBUFFER,[E])}}}function Fe(R){return Math.min(a.maxSamples,R.samples)}function ye(R){const E=i.get(R);return R.samples>0&&e.has("WEBGL_multisampled_render_to_texture")===!0&&E.__useRenderToTexture!==!1}function O(R){const E=l.render.frame;h.get(R)!==E&&(h.set(R,E),R.update())}function Ye(R,E){const V=R.colorSpace,K=R.format,J=R.type;return R.isCompressedTexture===!0||R.isVideoTexture===!0||V!==dd&&V!==Qa&&(ft.getTransfer(V)===Et?(K!==Ui||J!==vi)&&Qe("WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType."):bt("WebGLTextures: Unsupported texture color space:",V)),E}function $e(R){return typeof HTMLImageElement<"u"&&R instanceof HTMLImageElement?(u.width=R.naturalWidth||R.width,u.height=R.naturalHeight||R.height):typeof VideoFrame<"u"&&R instanceof VideoFrame?(u.width=R.displayWidth,u.height=R.displayHeight):(u.width=R.width,u.height=R.height),u}this.allocateTextureUnit=z,this.resetTextureUnits=Y,this.getTextureUnits=k,this.setTextureUnits=B,this.setTexture2D=X,this.setTexture2DArray=q,this.setTexture3D=ve,this.setTextureCube=he,this.rebindTextures=le,this.setupRenderTarget=se,this.updateRenderTargetMipmap=Me,this.updateMultisampleRenderTarget=Re,this.setupDepthRenderbuffer=H,this.setupFrameBufferTexture=Xe,this.useMultisampledRTT=ye,this.isReversedDepthBuffer=function(){return n.buffers.depth.getReversed()}}function Z3(t,e){function n(i,a=Qa){let s;const l=ft.getTransfer(a);if(i===vi)return t.UNSIGNED_BYTE;if(i===xg)return t.UNSIGNED_SHORT_4_4_4_4;if(i===vg)return t.UNSIGNED_SHORT_5_5_5_1;if(i===bS)return t.UNSIGNED_INT_5_9_9_9_REV;if(i===yS)return t.UNSIGNED_INT_10F_11F_11F_REV;if(i===vS)return t.BYTE;if(i===_S)return t.SHORT;if(i===Wo)return t.UNSIGNED_SHORT;if(i===gg)return t.INT;if(i===Zi)return t.UNSIGNED_INT;if(i===Li)return t.FLOAT;if(i===Ra)return t.HALF_FLOAT;if(i===SS)return t.ALPHA;if(i===MS)return t.RGB;if(i===Ui)return t.RGBA;if(i===Da)return t.DEPTH_COMPONENT;if(i===Is)return t.DEPTH_STENCIL;if(i===_g)return t.RED;if(i===bg)return t.RED_INTEGER;if(i===Zs)return t.RG;if(i===yg)return t.RG_INTEGER;if(i===Sg)return t.RGBA_INTEGER;if(i===wu||i===Nu||i===Cu||i===Ru)if(l===Et)if(s=e.get("WEBGL_compressed_texture_s3tc_srgb"),s!==null){if(i===wu)return s.COMPRESSED_SRGB_S3TC_DXT1_EXT;if(i===Nu)return s.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;if(i===Cu)return s.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;if(i===Ru)return s.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}else return null;else if(s=e.get("WEBGL_compressed_texture_s3tc"),s!==null){if(i===wu)return s.COMPRESSED_RGB_S3TC_DXT1_EXT;if(i===Nu)return s.COMPRESSED_RGBA_S3TC_DXT1_EXT;if(i===Cu)return s.COMPRESSED_RGBA_S3TC_DXT3_EXT;if(i===Ru)return s.COMPRESSED_RGBA_S3TC_DXT5_EXT}else return null;if(i===Cp||i===Rp||i===Dp||i===Lp)if(s=e.get("WEBGL_compressed_texture_pvrtc"),s!==null){if(i===Cp)return s.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;if(i===Rp)return s.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;if(i===Dp)return s.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;if(i===Lp)return s.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG}else return null;if(i===Up||i===Op||i===Pp||i===zp||i===jp||i===cd||i===kp)if(s=e.get("WEBGL_compressed_texture_etc"),s!==null){if(i===Up||i===Op)return l===Et?s.COMPRESSED_SRGB8_ETC2:s.COMPRESSED_RGB8_ETC2;if(i===Pp)return l===Et?s.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:s.COMPRESSED_RGBA8_ETC2_EAC;if(i===zp)return s.COMPRESSED_R11_EAC;if(i===jp)return s.COMPRESSED_SIGNED_R11_EAC;if(i===cd)return s.COMPRESSED_RG11_EAC;if(i===kp)return s.COMPRESSED_SIGNED_RG11_EAC}else return null;if(i===Ip||i===Bp||i===Fp||i===Hp||i===Gp||i===Vp||i===Wp||i===Xp||i===qp||i===Yp||i===$p||i===Kp||i===Zp||i===Qp)if(s=e.get("WEBGL_compressed_texture_astc"),s!==null){if(i===Ip)return l===Et?s.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:s.COMPRESSED_RGBA_ASTC_4x4_KHR;if(i===Bp)return l===Et?s.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:s.COMPRESSED_RGBA_ASTC_5x4_KHR;if(i===Fp)return l===Et?s.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:s.COMPRESSED_RGBA_ASTC_5x5_KHR;if(i===Hp)return l===Et?s.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:s.COMPRESSED_RGBA_ASTC_6x5_KHR;if(i===Gp)return l===Et?s.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:s.COMPRESSED_RGBA_ASTC_6x6_KHR;if(i===Vp)return l===Et?s.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:s.COMPRESSED_RGBA_ASTC_8x5_KHR;if(i===Wp)return l===Et?s.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:s.COMPRESSED_RGBA_ASTC_8x6_KHR;if(i===Xp)return l===Et?s.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:s.COMPRESSED_RGBA_ASTC_8x8_KHR;if(i===qp)return l===Et?s.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:s.COMPRESSED_RGBA_ASTC_10x5_KHR;if(i===Yp)return l===Et?s.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:s.COMPRESSED_RGBA_ASTC_10x6_KHR;if(i===$p)return l===Et?s.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:s.COMPRESSED_RGBA_ASTC_10x8_KHR;if(i===Kp)return l===Et?s.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:s.COMPRESSED_RGBA_ASTC_10x10_KHR;if(i===Zp)return l===Et?s.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:s.COMPRESSED_RGBA_ASTC_12x10_KHR;if(i===Qp)return l===Et?s.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:s.COMPRESSED_RGBA_ASTC_12x12_KHR}else return null;if(i===Jp||i===em||i===tm)if(s=e.get("EXT_texture_compression_bptc"),s!==null){if(i===Jp)return l===Et?s.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT:s.COMPRESSED_RGBA_BPTC_UNORM_EXT;if(i===em)return s.COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT;if(i===tm)return s.COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT}else return null;if(i===nm||i===im||i===ud||i===am)if(s=e.get("EXT_texture_compression_rgtc"),s!==null){if(i===nm)return s.COMPRESSED_RED_RGTC1_EXT;if(i===im)return s.COMPRESSED_SIGNED_RED_RGTC1_EXT;if(i===ud)return s.COMPRESSED_RED_GREEN_RGTC2_EXT;if(i===am)return s.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT}else return null;return i===Xo?t.UNSIGNED_INT_24_8:t[i]!==void 0?t[i]:null}return{convert:n}}const Q3=` +void main() { + + gl_Position = vec4( position, 1.0 ); + +}`,J3=` +uniform sampler2DArray depthColor; +uniform float depthWidth; +uniform float depthHeight; + +void main() { + + vec2 coord = vec2( gl_FragCoord.x / depthWidth, gl_FragCoord.y / depthHeight ); + + if ( coord.x >= 1.0 ) { + + gl_FragDepth = texture( depthColor, vec3( coord.x - 1.0, coord.y, 1 ) ).r; + + } else { + + gl_FragDepth = texture( depthColor, vec3( coord.x, coord.y, 0 ) ).r; + + } + +}`;class e2{constructor(){this.texture=null,this.mesh=null,this.depthNear=0,this.depthFar=0}init(e,n){if(this.texture===null){const i=new PS(e.texture);(e.depthNear!==n.depthNear||e.depthFar!==n.depthFar)&&(this.depthNear=e.depthNear,this.depthFar=e.depthFar),this.texture=i}}getMesh(e){if(this.texture!==null&&this.mesh===null){const n=e.cameras[0].viewport,i=new Gn({vertexShader:Q3,fragmentShader:J3,uniforms:{depthColor:{value:this.texture},depthWidth:{value:n.z},depthHeight:{value:n.w}}});this.mesh=new Ei(new Hd(20,20),i)}return this.mesh}reset(){this.texture=null,this.mesh=null}getDepthTexture(){return this.texture}}class t2 extends rr{constructor(e,n){super();const i=this;let a=null,s=1,l=null,o="local-floor",c=1,u=null,h=null,g=null,d=null,p=null,x=null;const _=typeof XRWebGLBinding<"u",m=new e2,f={},v=n.getContextAttributes();let y=null,b=null;const T=[],M=[],A=new Mt;let S=null;const N=new Di;N.viewport=new Yt;const D=new Di;D.viewport=new Yt;const U=[N,D],I=new dw;let Y=null,k=null;this.cameraAutoUpdate=!0,this.enabled=!1,this.isPresenting=!1,this.getController=function(ne){let ge=T[ne];return ge===void 0&&(ge=new kh,T[ne]=ge),ge.getTargetRaySpace()},this.getControllerGrip=function(ne){let ge=T[ne];return ge===void 0&&(ge=new kh,T[ne]=ge),ge.getGripSpace()},this.getHand=function(ne){let ge=T[ne];return ge===void 0&&(ge=new kh,T[ne]=ge),ge.getHandSpace()};function B(ne){const ge=M.indexOf(ne.inputSource);if(ge===-1)return;const ue=T[ge];ue!==void 0&&(ue.update(ne.inputSource,ne.frame,u||l),ue.dispatchEvent({type:ne.type,data:ne.inputSource}))}function z(){a.removeEventListener("select",B),a.removeEventListener("selectstart",B),a.removeEventListener("selectend",B),a.removeEventListener("squeeze",B),a.removeEventListener("squeezestart",B),a.removeEventListener("squeezeend",B),a.removeEventListener("end",z),a.removeEventListener("inputsourceschange",j);for(let ne=0;ne<T.length;ne++){const ge=M[ne];ge!==null&&(M[ne]=null,T[ne].disconnect(ge))}Y=null,k=null,m.reset();for(const ne in f)delete f[ne];e.setRenderTarget(y),p=null,d=null,g=null,a=null,b=null,we.stop(),i.isPresenting=!1,e.setPixelRatio(S),e.setSize(A.width,A.height,!1),i.dispatchEvent({type:"sessionend"})}this.setFramebufferScaleFactor=function(ne){s=ne,i.isPresenting===!0&&Qe("WebXRManager: Cannot change framebuffer scale while presenting.")},this.setReferenceSpaceType=function(ne){o=ne,i.isPresenting===!0&&Qe("WebXRManager: Cannot change reference space type while presenting.")},this.getReferenceSpace=function(){return u||l},this.setReferenceSpace=function(ne){u=ne},this.getBaseLayer=function(){return d!==null?d:p},this.getBinding=function(){return g===null&&_&&(g=new XRWebGLBinding(a,n)),g},this.getFrame=function(){return x},this.getSession=function(){return a},this.setSession=async function(ne){if(a=ne,a!==null){if(y=e.getRenderTarget(),a.addEventListener("select",B),a.addEventListener("selectstart",B),a.addEventListener("selectend",B),a.addEventListener("squeeze",B),a.addEventListener("squeezestart",B),a.addEventListener("squeezeend",B),a.addEventListener("end",z),a.addEventListener("inputsourceschange",j),v.xrCompatible!==!0&&await n.makeXRCompatible(),S=e.getPixelRatio(),e.getSize(A),_&&"createProjectionLayer"in XRWebGLBinding.prototype){let ue=null,Le=null,Ie=null;v.depth&&(Ie=v.stencil?n.DEPTH24_STENCIL8:n.DEPTH_COMPONENT24,ue=v.stencil?Is:Da,Le=v.stencil?Xo:Zi);const Xe={colorFormat:n.RGBA8,depthFormat:Ie,scaleFactor:s};g=this.getBinding(),d=g.createProjectionLayer(Xe),a.updateRenderState({layers:[d]}),e.setPixelRatio(1),e.setSize(d.textureWidth,d.textureHeight,!1),b=new Ki(d.textureWidth,d.textureHeight,{format:Ui,type:vi,depthTexture:new yl(d.textureWidth,d.textureHeight,Le,void 0,void 0,void 0,void 0,void 0,void 0,ue),stencilBuffer:v.stencil,colorSpace:e.outputColorSpace,samples:v.antialias?4:0,resolveDepthBuffer:d.ignoreDepthValues===!1,resolveStencilBuffer:d.ignoreDepthValues===!1})}else{const ue={antialias:v.antialias,alpha:!0,depth:v.depth,stencil:v.stencil,framebufferScaleFactor:s};p=new XRWebGLLayer(a,n,ue),a.updateRenderState({baseLayer:p}),e.setPixelRatio(1),e.setSize(p.framebufferWidth,p.framebufferHeight,!1),b=new Ki(p.framebufferWidth,p.framebufferHeight,{format:Ui,type:vi,colorSpace:e.outputColorSpace,stencilBuffer:v.stencil,resolveDepthBuffer:p.ignoreDepthValues===!1,resolveStencilBuffer:p.ignoreDepthValues===!1})}b.isXRRenderTarget=!0,this.setFoveation(c),u=null,l=await a.requestReferenceSpace(o),we.setContext(a),we.start(),i.isPresenting=!0,i.dispatchEvent({type:"sessionstart"})}},this.getEnvironmentBlendMode=function(){if(a!==null)return a.environmentBlendMode},this.getDepthTexture=function(){return m.getDepthTexture()};function j(ne){for(let ge=0;ge<ne.removed.length;ge++){const ue=ne.removed[ge],Le=M.indexOf(ue);Le>=0&&(M[Le]=null,T[Le].disconnect(ue))}for(let ge=0;ge<ne.added.length;ge++){const ue=ne.added[ge];let Le=M.indexOf(ue);if(Le===-1){for(let Xe=0;Xe<T.length;Xe++)if(Xe>=M.length){M.push(ue),Le=Xe;break}else if(M[Xe]===null){M[Xe]=ue,Le=Xe;break}if(Le===-1)break}const Ie=T[Le];Ie&&Ie.connect(ue)}}const X=new Z,q=new Z;function ve(ne,ge,ue){X.setFromMatrixPosition(ge.matrixWorld),q.setFromMatrixPosition(ue.matrixWorld);const Le=X.distanceTo(q),Ie=ge.projectionMatrix.elements,Xe=ue.projectionMatrix.elements,dt=Ie[14]/(Ie[10]-1),re=Ie[14]/(Ie[10]+1),H=(Ie[9]+1)/Ie[5],le=(Ie[9]-1)/Ie[5],se=(Ie[8]-1)/Ie[0],Me=(Xe[8]+1)/Xe[0],Oe=dt*se,De=dt*Me,Re=Le/(-se+Me),Fe=Re*-se;if(ge.matrixWorld.decompose(ne.position,ne.quaternion,ne.scale),ne.translateX(Fe),ne.translateZ(Re),ne.matrixWorld.compose(ne.position,ne.quaternion,ne.scale),ne.matrixWorldInverse.copy(ne.matrixWorld).invert(),Ie[10]===-1)ne.projectionMatrix.copy(ge.projectionMatrix),ne.projectionMatrixInverse.copy(ge.projectionMatrixInverse);else{const ye=dt+Re,O=re+Re,Ye=Oe-Fe,$e=De+(Le-Fe),R=H*re/O*ye,E=le*re/O*ye;ne.projectionMatrix.makePerspective(Ye,$e,R,E,ye,O),ne.projectionMatrixInverse.copy(ne.projectionMatrix).invert()}}function he(ne,ge){ge===null?ne.matrixWorld.copy(ne.matrix):ne.matrixWorld.multiplyMatrices(ge.matrixWorld,ne.matrix),ne.matrixWorldInverse.copy(ne.matrixWorld).invert()}this.updateCamera=function(ne){if(a===null)return;let ge=ne.near,ue=ne.far;m.texture!==null&&(m.depthNear>0&&(ge=m.depthNear),m.depthFar>0&&(ue=m.depthFar)),I.near=D.near=N.near=ge,I.far=D.far=N.far=ue,(Y!==I.near||k!==I.far)&&(a.updateRenderState({depthNear:I.near,depthFar:I.far}),Y=I.near,k=I.far),I.layers.mask=ne.layers.mask|6,N.layers.mask=I.layers.mask&-5,D.layers.mask=I.layers.mask&-3;const Le=ne.parent,Ie=I.cameras;he(I,Le);for(let Xe=0;Xe<Ie.length;Xe++)he(Ie[Xe],Le);Ie.length===2?ve(I,N,D):I.projectionMatrix.copy(N.projectionMatrix),Ae(ne,I,Le)};function Ae(ne,ge,ue){ue===null?ne.matrix.copy(ge.matrixWorld):(ne.matrix.copy(ue.matrixWorld),ne.matrix.invert(),ne.matrix.multiply(ge.matrixWorld)),ne.matrix.decompose(ne.position,ne.quaternion,ne.scale),ne.updateMatrixWorld(!0),ne.projectionMatrix.copy(ge.projectionMatrix),ne.projectionMatrixInverse.copy(ge.projectionMatrixInverse),ne.isPerspectiveCamera&&(ne.fov=sm*2*Math.atan(1/ne.projectionMatrix.elements[5]),ne.zoom=1)}this.getCamera=function(){return I},this.getFoveation=function(){if(!(d===null&&p===null))return c},this.setFoveation=function(ne){c=ne,d!==null&&(d.fixedFoveation=ne),p!==null&&p.fixedFoveation!==void 0&&(p.fixedFoveation=ne)},this.hasDepthSensing=function(){return m.texture!==null},this.getDepthSensingMesh=function(){return m.getMesh(I)},this.getCameraTexture=function(ne){return f[ne]};let $=null;function pe(ne,ge){if(h=ge.getViewerPose(u||l),x=ge,h!==null){const ue=h.views;p!==null&&(e.setRenderTargetFramebuffer(b,p.framebuffer),e.setRenderTarget(b));let Le=!1;ue.length!==I.cameras.length&&(I.cameras.length=0,Le=!0);for(let re=0;re<ue.length;re++){const H=ue[re];let le=null;if(p!==null)le=p.getViewport(H);else{const Me=g.getViewSubImage(d,H);le=Me.viewport,re===0&&(e.setRenderTargetTextures(b,Me.colorTexture,Me.depthStencilTexture),e.setRenderTarget(b))}let se=U[re];se===void 0&&(se=new Di,se.layers.enable(re),se.viewport=new Yt,U[re]=se),se.matrix.fromArray(H.transform.matrix),se.matrix.decompose(se.position,se.quaternion,se.scale),se.projectionMatrix.fromArray(H.projectionMatrix),se.projectionMatrixInverse.copy(se.projectionMatrix).invert(),se.viewport.set(le.x,le.y,le.width,le.height),re===0&&(I.matrix.copy(se.matrix),I.matrix.decompose(I.position,I.quaternion,I.scale)),Le===!0&&I.cameras.push(se)}const Ie=a.enabledFeatures;if(Ie&&Ie.includes("depth-sensing")&&a.depthUsage=="gpu-optimized"&&_){g=i.getBinding();const re=g.getDepthInformation(ue[0]);re&&re.isValid&&re.texture&&m.init(re,a.renderState)}if(Ie&&Ie.includes("camera-access")&&_){e.state.unbindTexture(),g=i.getBinding();for(let re=0;re<ue.length;re++){const H=ue[re].camera;if(H){let le=f[H];le||(le=new PS,f[H]=le);const se=g.getCameraImage(H);le.sourceTexture=se}}}}for(let ue=0;ue<T.length;ue++){const Le=M[ue],Ie=T[ue];Le!==null&&Ie!==void 0&&Ie.update(Le,ge,u||l)}$&&$(ne,ge),ge.detectedPlanes&&i.dispatchEvent({type:"planesdetected",data:ge}),x=null}const we=new kS;we.setAnimationLoop(pe),this.setAnimationLoop=function(ne){$=ne},this.dispose=function(){}}}const n2=new zt,WS=new tt;WS.set(-1,0,0,0,1,0,0,0,1);function i2(t,e){function n(m,f){m.matrixAutoUpdate===!0&&m.updateMatrix(),f.value.copy(m.matrix)}function i(m,f){f.color.getRGB(m.fogColor.value,zS(t)),f.isFog?(m.fogNear.value=f.near,m.fogFar.value=f.far):f.isFogExp2&&(m.fogDensity.value=f.density)}function a(m,f,v,y,b){f.isNodeMaterial?f.uniformsNeedUpdate=!1:f.isMeshBasicMaterial?s(m,f):f.isMeshLambertMaterial?(s(m,f),f.envMap&&(m.envMapIntensity.value=f.envMapIntensity)):f.isMeshToonMaterial?(s(m,f),g(m,f)):f.isMeshPhongMaterial?(s(m,f),h(m,f),f.envMap&&(m.envMapIntensity.value=f.envMapIntensity)):f.isMeshStandardMaterial?(s(m,f),d(m,f),f.isMeshPhysicalMaterial&&p(m,f,b)):f.isMeshMatcapMaterial?(s(m,f),x(m,f)):f.isMeshDepthMaterial?s(m,f):f.isMeshDistanceMaterial?(s(m,f),_(m,f)):f.isMeshNormalMaterial?s(m,f):f.isLineBasicMaterial?(l(m,f),f.isLineDashedMaterial&&o(m,f)):f.isPointsMaterial?c(m,f,v,y):f.isSpriteMaterial?u(m,f):f.isShadowMaterial?(m.color.value.copy(f.color),m.opacity.value=f.opacity):f.isShaderMaterial&&(f.uniformsNeedUpdate=!1)}function s(m,f){m.opacity.value=f.opacity,f.color&&m.diffuse.value.copy(f.color),f.emissive&&m.emissive.value.copy(f.emissive).multiplyScalar(f.emissiveIntensity),f.map&&(m.map.value=f.map,n(f.map,m.mapTransform)),f.alphaMap&&(m.alphaMap.value=f.alphaMap,n(f.alphaMap,m.alphaMapTransform)),f.bumpMap&&(m.bumpMap.value=f.bumpMap,n(f.bumpMap,m.bumpMapTransform),m.bumpScale.value=f.bumpScale,f.side===Wn&&(m.bumpScale.value*=-1)),f.normalMap&&(m.normalMap.value=f.normalMap,n(f.normalMap,m.normalMapTransform),m.normalScale.value.copy(f.normalScale),f.side===Wn&&m.normalScale.value.negate()),f.displacementMap&&(m.displacementMap.value=f.displacementMap,n(f.displacementMap,m.displacementMapTransform),m.displacementScale.value=f.displacementScale,m.displacementBias.value=f.displacementBias),f.emissiveMap&&(m.emissiveMap.value=f.emissiveMap,n(f.emissiveMap,m.emissiveMapTransform)),f.specularMap&&(m.specularMap.value=f.specularMap,n(f.specularMap,m.specularMapTransform)),f.alphaTest>0&&(m.alphaTest.value=f.alphaTest);const v=e.get(f),y=v.envMap,b=v.envMapRotation;y&&(m.envMap.value=y,m.envMapRotation.value.setFromMatrix4(n2.makeRotationFromEuler(b)).transpose(),y.isCubeTexture&&y.isRenderTargetTexture===!1&&m.envMapRotation.value.premultiply(WS),m.reflectivity.value=f.reflectivity,m.ior.value=f.ior,m.refractionRatio.value=f.refractionRatio),f.lightMap&&(m.lightMap.value=f.lightMap,m.lightMapIntensity.value=f.lightMapIntensity,n(f.lightMap,m.lightMapTransform)),f.aoMap&&(m.aoMap.value=f.aoMap,m.aoMapIntensity.value=f.aoMapIntensity,n(f.aoMap,m.aoMapTransform))}function l(m,f){m.diffuse.value.copy(f.color),m.opacity.value=f.opacity,f.map&&(m.map.value=f.map,n(f.map,m.mapTransform))}function o(m,f){m.dashSize.value=f.dashSize,m.totalSize.value=f.dashSize+f.gapSize,m.scale.value=f.scale}function c(m,f,v,y){m.diffuse.value.copy(f.color),m.opacity.value=f.opacity,m.size.value=f.size*v,m.scale.value=y*.5,f.map&&(m.map.value=f.map,n(f.map,m.uvTransform)),f.alphaMap&&(m.alphaMap.value=f.alphaMap,n(f.alphaMap,m.alphaMapTransform)),f.alphaTest>0&&(m.alphaTest.value=f.alphaTest)}function u(m,f){m.diffuse.value.copy(f.color),m.opacity.value=f.opacity,m.rotation.value=f.rotation,f.map&&(m.map.value=f.map,n(f.map,m.mapTransform)),f.alphaMap&&(m.alphaMap.value=f.alphaMap,n(f.alphaMap,m.alphaMapTransform)),f.alphaTest>0&&(m.alphaTest.value=f.alphaTest)}function h(m,f){m.specular.value.copy(f.specular),m.shininess.value=Math.max(f.shininess,1e-4)}function g(m,f){f.gradientMap&&(m.gradientMap.value=f.gradientMap)}function d(m,f){m.metalness.value=f.metalness,f.metalnessMap&&(m.metalnessMap.value=f.metalnessMap,n(f.metalnessMap,m.metalnessMapTransform)),m.roughness.value=f.roughness,f.roughnessMap&&(m.roughnessMap.value=f.roughnessMap,n(f.roughnessMap,m.roughnessMapTransform)),f.envMap&&(m.envMapIntensity.value=f.envMapIntensity)}function p(m,f,v){m.ior.value=f.ior,f.sheen>0&&(m.sheenColor.value.copy(f.sheenColor).multiplyScalar(f.sheen),m.sheenRoughness.value=f.sheenRoughness,f.sheenColorMap&&(m.sheenColorMap.value=f.sheenColorMap,n(f.sheenColorMap,m.sheenColorMapTransform)),f.sheenRoughnessMap&&(m.sheenRoughnessMap.value=f.sheenRoughnessMap,n(f.sheenRoughnessMap,m.sheenRoughnessMapTransform))),f.clearcoat>0&&(m.clearcoat.value=f.clearcoat,m.clearcoatRoughness.value=f.clearcoatRoughness,f.clearcoatMap&&(m.clearcoatMap.value=f.clearcoatMap,n(f.clearcoatMap,m.clearcoatMapTransform)),f.clearcoatRoughnessMap&&(m.clearcoatRoughnessMap.value=f.clearcoatRoughnessMap,n(f.clearcoatRoughnessMap,m.clearcoatRoughnessMapTransform)),f.clearcoatNormalMap&&(m.clearcoatNormalMap.value=f.clearcoatNormalMap,n(f.clearcoatNormalMap,m.clearcoatNormalMapTransform),m.clearcoatNormalScale.value.copy(f.clearcoatNormalScale),f.side===Wn&&m.clearcoatNormalScale.value.negate())),f.dispersion>0&&(m.dispersion.value=f.dispersion),f.iridescence>0&&(m.iridescence.value=f.iridescence,m.iridescenceIOR.value=f.iridescenceIOR,m.iridescenceThicknessMinimum.value=f.iridescenceThicknessRange[0],m.iridescenceThicknessMaximum.value=f.iridescenceThicknessRange[1],f.iridescenceMap&&(m.iridescenceMap.value=f.iridescenceMap,n(f.iridescenceMap,m.iridescenceMapTransform)),f.iridescenceThicknessMap&&(m.iridescenceThicknessMap.value=f.iridescenceThicknessMap,n(f.iridescenceThicknessMap,m.iridescenceThicknessMapTransform))),f.transmission>0&&(m.transmission.value=f.transmission,m.transmissionSamplerMap.value=v.texture,m.transmissionSamplerSize.value.set(v.width,v.height),f.transmissionMap&&(m.transmissionMap.value=f.transmissionMap,n(f.transmissionMap,m.transmissionMapTransform)),m.thickness.value=f.thickness,f.thicknessMap&&(m.thicknessMap.value=f.thicknessMap,n(f.thicknessMap,m.thicknessMapTransform)),m.attenuationDistance.value=f.attenuationDistance,m.attenuationColor.value.copy(f.attenuationColor)),f.anisotropy>0&&(m.anisotropyVector.value.set(f.anisotropy*Math.cos(f.anisotropyRotation),f.anisotropy*Math.sin(f.anisotropyRotation)),f.anisotropyMap&&(m.anisotropyMap.value=f.anisotropyMap,n(f.anisotropyMap,m.anisotropyMapTransform))),m.specularIntensity.value=f.specularIntensity,m.specularColor.value.copy(f.specularColor),f.specularColorMap&&(m.specularColorMap.value=f.specularColorMap,n(f.specularColorMap,m.specularColorMapTransform)),f.specularIntensityMap&&(m.specularIntensityMap.value=f.specularIntensityMap,n(f.specularIntensityMap,m.specularIntensityMapTransform))}function x(m,f){f.matcap&&(m.matcap.value=f.matcap)}function _(m,f){const v=e.get(f).light;m.referencePosition.value.setFromMatrixPosition(v.matrixWorld),m.nearDistance.value=v.shadow.camera.near,m.farDistance.value=v.shadow.camera.far}return{refreshFogUniforms:i,refreshMaterialUniforms:a}}function a2(t,e,n,i){let a={},s={},l=[];const o=t.getParameter(t.MAX_UNIFORM_BUFFER_BINDINGS);function c(b,T){const M=T.program;i.uniformBlockBinding(b,M)}function u(b,T){let M=a[b.id];M===void 0&&(m(b),M=h(b),a[b.id]=M,b.addEventListener("dispose",v));const A=T.program;i.updateUBOMapping(b,A);const S=e.render.frame;s[b.id]!==S&&(d(b),s[b.id]=S)}function h(b){const T=g();b.__bindingPointIndex=T;const M=t.createBuffer(),A=b.__size,S=b.usage;return t.bindBuffer(t.UNIFORM_BUFFER,M),t.bufferData(t.UNIFORM_BUFFER,A,S),t.bindBuffer(t.UNIFORM_BUFFER,null),t.bindBufferBase(t.UNIFORM_BUFFER,T,M),M}function g(){for(let b=0;b<o;b++)if(l.indexOf(b)===-1)return l.push(b),b;return bt("WebGLRenderer: Maximum number of simultaneously usable uniforms groups reached."),0}function d(b){const T=a[b.id],M=b.uniforms,A=b.__cache;t.bindBuffer(t.UNIFORM_BUFFER,T);for(let S=0,N=M.length;S<N;S++){const D=M[S];if(Array.isArray(D))for(let U=0,I=D.length;U<I;U++)p(D[U],S,U,A);else p(D,S,0,A)}t.bindBuffer(t.UNIFORM_BUFFER,null)}function p(b,T,M,A){if(_(b,T,M,A)===!0){const S=b.__offset,N=b.value;if(Array.isArray(N)){let D=0;for(let U=0;U<N.length;U++){const I=N[U],Y=f(I);x(I,b.__data,D),typeof I!="number"&&typeof I!="boolean"&&!I.isMatrix3&&!ArrayBuffer.isView(I)&&(D+=Y.storage/Float32Array.BYTES_PER_ELEMENT)}}else x(N,b.__data,0);t.bufferSubData(t.UNIFORM_BUFFER,S,b.__data)}}function x(b,T,M){typeof b=="number"||typeof b=="boolean"?T[0]=b:b.isMatrix3?(T[0]=b.elements[0],T[1]=b.elements[1],T[2]=b.elements[2],T[3]=0,T[4]=b.elements[3],T[5]=b.elements[4],T[6]=b.elements[5],T[7]=0,T[8]=b.elements[6],T[9]=b.elements[7],T[10]=b.elements[8],T[11]=0):ArrayBuffer.isView(b)?T.set(new b.constructor(b.buffer,b.byteOffset,T.length)):b.toArray(T,M)}function _(b,T,M,A){const S=b.value,N=T+"_"+M;if(A[N]===void 0)return typeof S=="number"||typeof S=="boolean"?A[N]=S:ArrayBuffer.isView(S)?A[N]=S.slice():A[N]=S.clone(),!0;{const D=A[N];if(typeof S=="number"||typeof S=="boolean"){if(D!==S)return A[N]=S,!0}else{if(ArrayBuffer.isView(S))return!0;if(D.equals(S)===!1)return D.copy(S),!0}}return!1}function m(b){const T=b.uniforms;let M=0;const A=16;for(let N=0,D=T.length;N<D;N++){const U=Array.isArray(T[N])?T[N]:[T[N]];for(let I=0,Y=U.length;I<Y;I++){const k=U[I],B=Array.isArray(k.value)?k.value:[k.value];for(let z=0,j=B.length;z<j;z++){const X=B[z],q=f(X),ve=M%A,he=ve%q.boundary,Ae=ve+he;M+=he,Ae!==0&&A-Ae<q.storage&&(M+=A-Ae),k.__data=new Float32Array(q.storage/Float32Array.BYTES_PER_ELEMENT),k.__offset=M,M+=q.storage}}}const S=M%A;return S>0&&(M+=A-S),b.__size=M,b.__cache={},this}function f(b){const T={boundary:0,storage:0};return typeof b=="number"||typeof b=="boolean"?(T.boundary=4,T.storage=4):b.isVector2?(T.boundary=8,T.storage=8):b.isVector3||b.isColor?(T.boundary=16,T.storage=12):b.isVector4?(T.boundary=16,T.storage=16):b.isMatrix3?(T.boundary=48,T.storage=48):b.isMatrix4?(T.boundary=64,T.storage=64):b.isTexture?Qe("WebGLRenderer: Texture samplers can not be part of an uniforms group."):ArrayBuffer.isView(b)?(T.boundary=16,T.storage=b.byteLength):Qe("WebGLRenderer: Unsupported uniform value type.",b),T}function v(b){const T=b.target;T.removeEventListener("dispose",v);const M=l.indexOf(T.__bindingPointIndex);l.splice(M,1),t.deleteBuffer(a[T.id]),delete a[T.id],delete s[T.id]}function y(){for(const b in a)t.deleteBuffer(a[b]);l=[],a={},s={}}return{bind:c,update:u,dispose:y}}const s2=new Uint16Array([12469,15057,12620,14925,13266,14620,13807,14376,14323,13990,14545,13625,14713,13328,14840,12882,14931,12528,14996,12233,15039,11829,15066,11525,15080,11295,15085,10976,15082,10705,15073,10495,13880,14564,13898,14542,13977,14430,14158,14124,14393,13732,14556,13410,14702,12996,14814,12596,14891,12291,14937,11834,14957,11489,14958,11194,14943,10803,14921,10506,14893,10278,14858,9960,14484,14039,14487,14025,14499,13941,14524,13740,14574,13468,14654,13106,14743,12678,14818,12344,14867,11893,14889,11509,14893,11180,14881,10751,14852,10428,14812,10128,14765,9754,14712,9466,14764,13480,14764,13475,14766,13440,14766,13347,14769,13070,14786,12713,14816,12387,14844,11957,14860,11549,14868,11215,14855,10751,14825,10403,14782,10044,14729,9651,14666,9352,14599,9029,14967,12835,14966,12831,14963,12804,14954,12723,14936,12564,14917,12347,14900,11958,14886,11569,14878,11247,14859,10765,14828,10401,14784,10011,14727,9600,14660,9289,14586,8893,14508,8533,15111,12234,15110,12234,15104,12216,15092,12156,15067,12010,15028,11776,14981,11500,14942,11205,14902,10752,14861,10393,14812,9991,14752,9570,14682,9252,14603,8808,14519,8445,14431,8145,15209,11449,15208,11451,15202,11451,15190,11438,15163,11384,15117,11274,15055,10979,14994,10648,14932,10343,14871,9936,14803,9532,14729,9218,14645,8742,14556,8381,14461,8020,14365,7603,15273,10603,15272,10607,15267,10619,15256,10631,15231,10614,15182,10535,15118,10389,15042,10167,14963,9787,14883,9447,14800,9115,14710,8665,14615,8318,14514,7911,14411,7507,14279,7198,15314,9675,15313,9683,15309,9712,15298,9759,15277,9797,15229,9773,15166,9668,15084,9487,14995,9274,14898,8910,14800,8539,14697,8234,14590,7790,14479,7409,14367,7067,14178,6621,15337,8619,15337,8631,15333,8677,15325,8769,15305,8871,15264,8940,15202,8909,15119,8775,15022,8565,14916,8328,14804,8009,14688,7614,14569,7287,14448,6888,14321,6483,14088,6171,15350,7402,15350,7419,15347,7480,15340,7613,15322,7804,15287,7973,15229,8057,15148,8012,15046,7846,14933,7611,14810,7357,14682,7069,14552,6656,14421,6316,14251,5948,14007,5528,15356,5942,15356,5977,15353,6119,15348,6294,15332,6551,15302,6824,15249,7044,15171,7122,15070,7050,14949,6861,14818,6611,14679,6349,14538,6067,14398,5651,14189,5311,13935,4958,15359,4123,15359,4153,15356,4296,15353,4646,15338,5160,15311,5508,15263,5829,15188,6042,15088,6094,14966,6001,14826,5796,14678,5543,14527,5287,14377,4985,14133,4586,13869,4257,15360,1563,15360,1642,15358,2076,15354,2636,15341,3350,15317,4019,15273,4429,15203,4732,15105,4911,14981,4932,14836,4818,14679,4621,14517,4386,14359,4156,14083,3795,13808,3437,15360,122,15360,137,15358,285,15355,636,15344,1274,15322,2177,15281,2765,15215,3223,15120,3451,14995,3569,14846,3567,14681,3466,14511,3305,14344,3121,14037,2800,13753,2467,15360,0,15360,1,15359,21,15355,89,15346,253,15325,479,15287,796,15225,1148,15133,1492,15008,1749,14856,1882,14685,1886,14506,1783,14324,1608,13996,1398,13702,1183]);let ki=null;function r2(){return ki===null&&(ki=new DS(s2,16,16,Zs,Ra),ki.name="DFG_LUT",ki.minFilter=wn,ki.magFilter=wn,ki.wrapS=_a,ki.wrapT=_a,ki.generateMipmaps=!1,ki.needsUpdate=!0),ki}class l2{constructor(e={}){const{canvas:n=TA(),context:i=null,depth:a=!0,stencil:s=!1,alpha:l=!1,antialias:o=!1,premultipliedAlpha:c=!0,preserveDrawingBuffer:u=!1,powerPreference:h="default",failIfMajorPerformanceCaveat:g=!1,reversedDepthBuffer:d=!1,outputBufferType:p=vi}=e;this.isWebGLRenderer=!0;let x;if(i!==null){if(typeof WebGLRenderingContext<"u"&&i instanceof WebGLRenderingContext)throw new Error("THREE.WebGLRenderer: WebGL 1 is not supported since r163.");x=i.getContextAttributes().alpha}else x=l;const _=p,m=new Set([Sg,yg,bg]),f=new Set([vi,Zi,Wo,Xo,xg,vg]),v=new Uint32Array(4),y=new Int32Array(4),b=new Z;let T=null,M=null;const A=[],S=[];let N=null;this.domElement=n,this.debug={checkShaderErrors:!0,onShaderError:null},this.autoClear=!0,this.autoClearColor=!0,this.autoClearDepth=!0,this.autoClearStencil=!0,this.sortObjects=!0,this.clippingPlanes=[],this.localClippingEnabled=!1,this.toneMapping=$i,this.toneMappingExposure=1,this.transmissionResolutionScale=1;const D=this;let U=!1,I=null,Y=null,k=null,B=null;this._outputColorSpace=di;let z=0,j=0,X=null,q=-1,ve=null;const he=new Yt,Ae=new Yt;let $=null;const pe=new gt(0);let we=0,ne=n.width,ge=n.height,ue=1,Le=null,Ie=null;const Xe=new Yt(0,0,ne,ge),dt=new Yt(0,0,ne,ge);let re=!1;const H=new LS;let le=!1,se=!1;const Me=new zt,Oe=new Z,De=new Yt,Re={background:null,fog:null,environment:null,overrideMaterial:null,isScene:!0};let Fe=!1;function ye(){return X===null?ue:1}let O=i;function Ye(C,W){return n.getContext(C,W)}try{const C={alpha:!0,depth:a,stencil:s,antialias:o,premultipliedAlpha:c,preserveDrawingBuffer:u,powerPreference:h,failIfMajorPerformanceCaveat:g};if("setAttribute"in n&&n.setAttribute("data-engine",`three.js r${mg}`),n.addEventListener("webglcontextlost",xt,!1),n.addEventListener("webglcontextrestored",vt,!1),n.addEventListener("webglcontextcreationerror",Jt,!1),O===null){const W="webgl2";if(O=Ye(W,C),O===null)throw Ye(W)?new Error("THREE.WebGLRenderer: Error creating WebGL context with your selected attributes."):new Error("THREE.WebGLRenderer: Error creating WebGL context.")}}catch(C){throw bt("WebGLRenderer: "+C.message),C}let $e,R,E,V,K,J,Se,_e,te,Q,oe,me,xe,Te,Be,Ve,Ke,G,Ne,ce,Ce,Ue,be;function qe(){$e=new rR(O),$e.init(),Ce=new Z3(O,$e),R=new QC(O,$e,e,Ce),E=new $3(O,$e),R.reversedDepthBuffer&&d&&E.buffers.depth.setReversed(!0),Y=O.createFramebuffer(),k=O.createFramebuffer(),B=O.createFramebuffer(),V=new cR(O),K=new P3,J=new K3(O,$e,E,K,R,Ce,V),Se=new sR(D),_e=new fw(O),Ue=new KC(O,_e),te=new lR(O,_e,V,Ue),Q=new dR(O,te,_e,Ue,V),G=new uR(O,R,J),Be=new JC(K),oe=new O3(D,Se,$e,R,Ue,Be),me=new i2(D,K),xe=new j3,Te=new G3($e),Ke=new $C(D,Se,E,Q,x,c),Ve=new Y3(D,Q,R),be=new a2(O,V,R,E),Ne=new ZC(O,$e,V),ce=new oR(O,$e,V),V.programs=oe.programs,D.capabilities=R,D.extensions=$e,D.properties=K,D.renderLists=xe,D.shadowMap=Ve,D.state=E,D.info=V}qe(),_!==vi&&(N=new fR(_,n.width,n.height,o,a,s));const je=new t2(D,O);this.xr=je,this.getContext=function(){return O},this.getContextAttributes=function(){return O.getContextAttributes()},this.forceContextLoss=function(){const C=$e.get("WEBGL_lose_context");C&&C.loseContext()},this.forceContextRestore=function(){const C=$e.get("WEBGL_lose_context");C&&C.restoreContext()},this.getPixelRatio=function(){return ue},this.setPixelRatio=function(C){C!==void 0&&(ue=C,this.setSize(ne,ge,!1))},this.getSize=function(C){return C.set(ne,ge)},this.setSize=function(C,W,ee=!0){if(je.isPresenting){Qe("WebGLRenderer: Can't change size while VR device is presenting.");return}ne=C,ge=W,n.width=Math.floor(C*ue),n.height=Math.floor(W*ue),ee===!0&&(n.style.width=C+"px",n.style.height=W+"px"),N!==null&&N.setSize(n.width,n.height),this.setViewport(0,0,C,W)},this.getDrawingBufferSize=function(C){return C.set(ne*ue,ge*ue).floor()},this.setDrawingBufferSize=function(C,W,ee){ne=C,ge=W,ue=ee,n.width=Math.floor(C*ee),n.height=Math.floor(W*ee),this.setViewport(0,0,C,W)},this.setEffects=function(C){if(_===vi){bt("WebGLRenderer: setEffects() requires outputBufferType set to HalfFloatType or FloatType.");return}if(C){for(let W=0;W<C.length;W++)if(C[W].isOutputPass===!0){Qe("WebGLRenderer: OutputPass is not needed in setEffects(). Tone mapping and color space conversion are applied automatically.");break}}N.setEffects(C||[])},this.getCurrentViewport=function(C){return C.copy(he)},this.getViewport=function(C){return C.copy(Xe)},this.setViewport=function(C,W,ee,w){C.isVector4?Xe.set(C.x,C.y,C.z,C.w):Xe.set(C,W,ee,w),E.viewport(he.copy(Xe).multiplyScalar(ue).round())},this.getScissor=function(C){return C.copy(dt)},this.setScissor=function(C,W,ee,w){C.isVector4?dt.set(C.x,C.y,C.z,C.w):dt.set(C,W,ee,w),E.scissor(Ae.copy(dt).multiplyScalar(ue).round())},this.getScissorTest=function(){return re},this.setScissorTest=function(C){E.setScissorTest(re=C)},this.setOpaqueSort=function(C){Le=C},this.setTransparentSort=function(C){Ie=C},this.getClearColor=function(C){return C.copy(Ke.getClearColor())},this.setClearColor=function(){Ke.setClearColor(...arguments)},this.getClearAlpha=function(){return Ke.getClearAlpha()},this.setClearAlpha=function(){Ke.setClearAlpha(...arguments)},this.clear=function(C=!0,W=!0,ee=!0){let w=0;if(C){let L=!1;if(X!==null){const ie=X.texture.format;L=m.has(ie)}if(L){const ie=X.texture.type,ae=f.has(ie),Ee=Ke.getClearColor(),ke=Ke.getClearAlpha(),We=Ee.r,Je=Ee.g,Ze=Ee.b;ae?(v[0]=We,v[1]=Je,v[2]=Ze,v[3]=ke,O.clearBufferuiv(O.COLOR,0,v)):(y[0]=We,y[1]=Je,y[2]=Ze,y[3]=ke,O.clearBufferiv(O.COLOR,0,y))}else w|=O.COLOR_BUFFER_BIT}W&&(w|=O.DEPTH_BUFFER_BIT,this.state.buffers.depth.setMask(!0)),ee&&(w|=O.STENCIL_BUFFER_BIT,this.state.buffers.stencil.setMask(4294967295)),w!==0&&O.clear(w)},this.clearColor=function(){this.clear(!0,!1,!1)},this.clearDepth=function(){this.clear(!1,!0,!1)},this.clearStencil=function(){this.clear(!1,!1,!0)},this.setNodesHandler=function(C){C.setRenderer(this),I=C},this.dispose=function(){n.removeEventListener("webglcontextlost",xt,!1),n.removeEventListener("webglcontextrestored",vt,!1),n.removeEventListener("webglcontextcreationerror",Jt,!1),Ke.dispose(),xe.dispose(),Te.dispose(),K.dispose(),Se.dispose(),Q.dispose(),Ue.dispose(),be.dispose(),oe.dispose(),je.dispose(),je.removeEventListener("sessionstart",F),je.removeEventListener("sessionend",de),Pe.stop()};function xt(C){C.preventDefault(),wv("WebGLRenderer: Context Lost."),U=!0}function vt(){wv("WebGLRenderer: Context Restored."),U=!1;const C=V.autoReset,W=Ve.enabled,ee=Ve.autoUpdate,w=Ve.needsUpdate,L=Ve.type;qe(),V.autoReset=C,Ve.enabled=W,Ve.autoUpdate=ee,Ve.needsUpdate=w,Ve.type=L}function Jt(C){bt("WebGLRenderer: A WebGL context could not be created. Reason: ",C.statusMessage)}function rn(C){const W=C.target;W.removeEventListener("dispose",rn),Ua(W)}function Ua(C){Ct(C),K.remove(C)}function Ct(C){const W=K.get(C).programs;W!==void 0&&(W.forEach(function(ee){oe.releaseProgram(ee)}),C.isShaderMaterial&&oe.releaseShaderCache(C))}this.renderBufferDirect=function(C,W,ee,w,L,ie){W===null&&(W=Re);const ae=L.isMesh&&L.matrixWorld.determinantAffine()<0,Ee=oc(C,W,ee,w,L);E.setMaterial(w,ae);let ke=ee.index,We=1;if(w.wireframe===!0){if(ke=te.getWireframeAttribute(ee),ke===void 0)return;We=2}const Je=ee.drawRange,Ze=ee.attributes.position;let He=Je.start*We,rt=(Je.start+Je.count)*We;ie!==null&&(He=Math.max(He,ie.start*We),rt=Math.min(rt,(ie.start+ie.count)*We)),ke!==null?(He=Math.max(He,0),rt=Math.min(rt,ke.count)):Ze!=null&&(He=Math.max(He,0),rt=Math.min(rt,Ze.count));const Ht=rt-He;if(Ht<0||Ht===1/0)return;Ue.setup(L,w,Ee,ee,ke);let Wt,Rt=Ne;if(ke!==null&&(Wt=_e.get(ke),Rt=ce,Rt.setIndex(Wt)),L.isMesh)w.wireframe===!0?(E.setLineWidth(w.wireframeLinewidth*ye()),Rt.setMode(O.LINES)):Rt.setMode(O.TRIANGLES);else if(L.isLine){let En=w.linewidth;En===void 0&&(En=1),E.setLineWidth(En*ye()),L.isLineSegments?Rt.setMode(O.LINES):L.isLineLoop?Rt.setMode(O.LINE_LOOP):Rt.setMode(O.LINE_STRIP)}else L.isPoints?Rt.setMode(O.POINTS):L.isSprite&&Rt.setMode(O.TRIANGLES);if(L.isBatchedMesh)if($e.get("WEBGL_multi_draw"))Rt.renderMultiDraw(L._multiDrawStarts,L._multiDrawCounts,L._multiDrawCount);else{const En=L._multiDrawStarts,Ge=L._multiDrawCounts,$n=L._multiDrawCount,_t=ke?_e.get(ke).bytesPerElement:1,oi=K.get(w).currentProgram.getUniforms();for(let zi=0;zi<$n;zi++)oi.setValue(O,"_gl_DrawID",zi),Rt.render(En[zi]/_t,Ge[zi])}else if(L.isInstancedMesh)Rt.renderInstances(He,Ht,L.count);else if(ee.isInstancedBufferGeometry){const En=ee._maxInstanceCount!==void 0?ee._maxInstanceCount:1/0,Ge=Math.min(ee.instanceCount,En);Rt.renderInstances(He,Ht,Ge)}else Rt.render(He,Ht)};function ea(C,W,ee){C.transparent===!0&&C.side===ma&&C.forceSinglePass===!1?(C.side=Wn,C.needsUpdate=!0,Oa(C,W,ee),C.side=ys,C.needsUpdate=!0,Oa(C,W,ee),C.side=ma):Oa(C,W,ee)}this.compile=function(C,W,ee=null){ee===null&&(ee=C),M=Te.get(ee),M.init(W),S.push(M),ee.traverseVisible(function(L){L.isLight&&L.layers.test(W.layers)&&(M.pushLight(L),L.castShadow&&M.pushShadow(L))}),C!==ee&&C.traverseVisible(function(L){L.isLight&&L.layers.test(W.layers)&&(M.pushLight(L),L.castShadow&&M.pushShadow(L))}),M.setupLights();const w=new Set;return C.traverse(function(L){if(!(L.isMesh||L.isPoints||L.isLine||L.isSprite))return;const ie=L.material;if(ie)if(Array.isArray(ie))for(let ae=0;ae<ie.length;ae++){const Ee=ie[ae];ea(Ee,ee,L),w.add(Ee)}else ea(ie,ee,L),w.add(ie)}),M=S.pop(),w},this.compileAsync=function(C,W,ee=null){const w=this.compile(C,W,ee);return new Promise(L=>{function ie(){if(w.forEach(function(ae){K.get(ae).currentProgram.isReady()&&w.delete(ae)}),w.size===0){L(C);return}setTimeout(ie,10)}$e.get("KHR_parallel_shader_compile")!==null?ie():setTimeout(ie,10)})};let ta=null;function Pi(C){ta&&ta(C)}function F(){Pe.stop()}function de(){Pe.start()}const Pe=new kS;Pe.setAnimationLoop(Pi),typeof self<"u"&&Pe.setContext(self),this.setAnimationLoop=function(C){ta=C,je.setAnimationLoop(C),C===null?Pe.stop():Pe.start()},je.addEventListener("sessionstart",F),je.addEventListener("sessionend",de),this.render=function(C,W){if(W!==void 0&&W.isCamera!==!0){bt("WebGLRenderer.render: camera is not an instance of THREE.Camera.");return}if(U===!0)return;I!==null&&I.renderStart(C,W);const ee=je.enabled===!0&&je.isPresenting===!0,w=N!==null&&(X===null||ee)&&N.begin(D,X);if(C.matrixWorldAutoUpdate===!0&&C.updateMatrixWorld(),W.parent===null&&W.matrixWorldAutoUpdate===!0&&W.updateMatrixWorld(),je.enabled===!0&&je.isPresenting===!0&&(N===null||N.isCompositing()===!1)&&(je.cameraAutoUpdate===!0&&je.updateCamera(W),W=je.getCamera()),C.isScene===!0&&C.onBeforeRender(D,C,W,X),M=Te.get(C,S.length),M.init(W),M.state.textureUnits=J.getTextureUnits(),S.push(M),Me.multiplyMatrices(W.projectionMatrix,W.matrixWorldInverse),H.setFromProjectionMatrix(Me,qi,W.reversedDepth),se=this.localClippingEnabled,le=Be.init(this.clippingPlanes,se),T=xe.get(C,A.length),T.init(),A.push(T),je.enabled===!0&&je.isPresenting===!0){const ae=D.xr.getDepthSensingMesh();ae!==null&&ht(ae,W,-1/0,D.sortObjects)}ht(C,W,0,D.sortObjects),T.finish(),D.sortObjects===!0&&T.sort(Le,Ie,W.reversedDepth),Fe=je.enabled===!1||je.isPresenting===!1||je.hasDepthSensing()===!1,Fe&&Ke.addToRenderList(T,C),this.info.render.frame++,this.info.autoReset===!0&&this.info.reset(),le===!0&&Be.beginShadows();const L=M.state.shadowsArray;if(Ve.render(L,C,W),le===!0&&Be.endShadows(),(w&&N.hasRenderPass())===!1){const ae=T.opaque,Ee=T.transmissive;if(M.setupLights(),W.isArrayCamera){const ke=W.cameras;if(Ee.length>0)for(let We=0,Je=ke.length;We<Je;We++){const Ze=ke[We];ln(ae,Ee,C,Ze)}Fe&&Ke.render(C);for(let We=0,Je=ke.length;We<Je;We++){const Ze=ke[We];$t(T,C,Ze,Ze.viewport)}}else Ee.length>0&&ln(ae,Ee,C,W),Fe&&Ke.render(C),$t(T,C,W)}X!==null&&j===0&&(J.updateMultisampleRenderTarget(X),J.updateRenderTargetMipmap(X)),w&&N.end(D),C.isScene===!0&&C.onAfterRender(D,C,W),Ue.resetDefaultState(),q=-1,ve=null,S.pop(),S.length>0?(M=S[S.length-1],J.setTextureUnits(M.state.textureUnits),le===!0&&Be.setGlobalState(D.clippingPlanes,M.state.camera)):M=null,A.pop(),A.length>0?T=A[A.length-1]:T=null,I!==null&&I.renderEnd()};function ht(C,W,ee,w){if(C.visible===!1)return;if(C.layers.test(W.layers)){if(C.isGroup)ee=C.renderOrder;else if(C.isLOD)C.autoUpdate===!0&&C.update(W);else if(C.isLightProbeGrid)M.pushLightProbeGrid(C);else if(C.isLight)M.pushLight(C),C.castShadow&&M.pushShadow(C);else if(C.isSprite){if(!C.frustumCulled||H.intersectsSprite(C)){w&&De.setFromMatrixPosition(C.matrixWorld).applyMatrix4(Me);const ae=Q.update(C),Ee=C.material;Ee.visible&&T.push(C,ae,Ee,ee,De.z,null)}}else if((C.isMesh||C.isLine||C.isPoints)&&(!C.frustumCulled||H.intersectsObject(C))){const ae=Q.update(C),Ee=C.material;if(w&&(C.boundingSphere!==void 0?(C.boundingSphere===null&&C.computeBoundingSphere(),De.copy(C.boundingSphere.center)):(ae.boundingSphere===null&&ae.computeBoundingSphere(),De.copy(ae.boundingSphere.center)),De.applyMatrix4(C.matrixWorld).applyMatrix4(Me)),Array.isArray(Ee)){const ke=ae.groups;for(let We=0,Je=ke.length;We<Je;We++){const Ze=ke[We],He=Ee[Ze.materialIndex];He&&He.visible&&T.push(C,ae,He,ee,De.z,Ze)}}else Ee.visible&&T.push(C,ae,Ee,ee,De.z,null)}}const ie=C.children;for(let ae=0,Ee=ie.length;ae<Ee;ae++)ht(ie[ae],W,ee,w)}function $t(C,W,ee,w){const{opaque:L,transmissive:ie,transparent:ae}=C;M.setupLightsView(ee),le===!0&&Be.setGlobalState(D.clippingPlanes,ee),w&&E.viewport(he.copy(w)),L.length>0&&Ft(L,W,ee),ie.length>0&&Ft(ie,W,ee),ae.length>0&&Ft(ae,W,ee),E.buffers.depth.setTest(!0),E.buffers.depth.setMask(!0),E.buffers.color.setMask(!0),E.setPolygonOffset(!1)}function ln(C,W,ee,w){if((ee.isScene===!0?ee.overrideMaterial:null)!==null)return;if(M.state.transmissionRenderTarget[w.id]===void 0){const He=$e.has("EXT_color_buffer_half_float")||$e.has("EXT_color_buffer_float");M.state.transmissionRenderTarget[w.id]=new Ki(1,1,{generateMipmaps:!0,type:He?Ra:vi,minFilter:ks,samples:Math.max(4,R.samples),stencilBuffer:s,resolveDepthBuffer:!1,resolveStencilBuffer:!1,colorSpace:ft.workingColorSpace})}const ie=M.state.transmissionRenderTarget[w.id],ae=w.viewport||he;ie.setSize(ae.z*D.transmissionResolutionScale,ae.w*D.transmissionResolutionScale);const Ee=D.getRenderTarget(),ke=D.getActiveCubeFace(),We=D.getActiveMipmapLevel();D.setRenderTarget(ie),D.getClearColor(pe),we=D.getClearAlpha(),we<1&&D.setClearColor(16777215,.5),D.clear(),Fe&&Ke.render(ee);const Je=D.toneMapping;D.toneMapping=$i;const Ze=w.viewport;if(w.viewport!==void 0&&(w.viewport=void 0),M.setupLightsView(w),le===!0&&Be.setGlobalState(D.clippingPlanes,w),Ft(C,ee,w),J.updateMultisampleRenderTarget(ie),J.updateRenderTargetMipmap(ie),$e.has("WEBGL_multisampled_render_to_texture")===!1){let He=!1;for(let rt=0,Ht=W.length;rt<Ht;rt++){const Wt=W[rt],{object:Rt,geometry:En,material:Ge,group:$n}=Wt;if(Ge.side===ma&&Rt.layers.test(w.layers)){const _t=Ge.side;Ge.side=Wn,Ge.needsUpdate=!0,cr(Rt,ee,w,En,Ge,$n),Ge.side=_t,Ge.needsUpdate=!0,He=!0}}He===!0&&(J.updateMultisampleRenderTarget(ie),J.updateRenderTargetMipmap(ie))}D.setRenderTarget(Ee,ke,We),D.setClearColor(pe,we),Ze!==void 0&&(w.viewport=Ze),D.toneMapping=Je}function Ft(C,W,ee){const w=W.isScene===!0?W.overrideMaterial:null;for(let L=0,ie=C.length;L<ie;L++){const ae=C[L],{object:Ee,geometry:ke,group:We}=ae;let Je=ae.material;Je.allowOverride===!0&&w!==null&&(Je=w),Ee.layers.test(ee.layers)&&cr(Ee,W,ee,ke,Je,We)}}function cr(C,W,ee,w,L,ie){C.onBeforeRender(D,W,ee,w,L,ie),C.modelViewMatrix.multiplyMatrices(ee.matrixWorldInverse,C.matrixWorld),C.normalMatrix.getNormalMatrix(C.modelViewMatrix),L.onBeforeRender(D,W,ee,w,C,ie),L.transparent===!0&&L.side===ma&&L.forceSinglePass===!1?(L.side=Wn,L.needsUpdate=!0,D.renderBufferDirect(ee,W,w,L,C,ie),L.side=ys,L.needsUpdate=!0,D.renderBufferDirect(ee,W,w,L,C,ie),L.side=ma):D.renderBufferDirect(ee,W,w,L,C,ie),C.onAfterRender(D,W,ee,w,L,ie)}function Oa(C,W,ee){W.isScene!==!0&&(W=Re);const w=K.get(C),L=M.state.lights,ie=M.state.shadowsArray,ae=L.state.version,Ee=oe.getParameters(C,L.state,ie,W,ee,M.state.lightProbeGridArray),ke=oe.getProgramCacheKey(Ee);let We=w.programs;w.environment=C.isMeshStandardMaterial||C.isMeshLambertMaterial||C.isMeshPhongMaterial?W.environment:null,w.fog=W.fog;const Je=C.isMeshStandardMaterial||C.isMeshLambertMaterial&&!C.envMap||C.isMeshPhongMaterial&&!C.envMap;w.envMap=Se.get(C.envMap||w.environment,Je),w.envMapRotation=w.environment!==null&&C.envMap===null?W.environmentRotation:C.envMapRotation,We===void 0&&(C.addEventListener("dispose",rn),We=new Map,w.programs=We);let Ze=We.get(ke);if(Ze!==void 0){if(w.currentProgram===Ze&&w.lightsStateVersion===ae)return ur(C,Ee),Ze}else Ee.uniforms=oe.getUniforms(C),I!==null&&C.isNodeMaterial&&I.build(C,ee,Ee),C.onBeforeCompile(Ee,D),Ze=oe.acquireProgram(Ee,ke),We.set(ke,Ze),w.uniforms=Ee.uniforms;const He=w.uniforms;return(!C.isShaderMaterial&&!C.isRawShaderMaterial||C.clipping===!0)&&(He.clippingPlanes=Be.uniform),ur(C,Ee),w.needsLights=uc(C),w.lightsStateVersion=ae,w.needsLights&&(He.ambientLightColor.value=L.state.ambient,He.lightProbe.value=L.state.probe,He.directionalLights.value=L.state.directional,He.directionalLightShadows.value=L.state.directionalShadow,He.spotLights.value=L.state.spot,He.spotLightShadows.value=L.state.spotShadow,He.rectAreaLights.value=L.state.rectArea,He.ltc_1.value=L.state.rectAreaLTC1,He.ltc_2.value=L.state.rectAreaLTC2,He.pointLights.value=L.state.point,He.pointLightShadows.value=L.state.pointShadow,He.hemisphereLights.value=L.state.hemi,He.directionalShadowMatrix.value=L.state.directionalShadowMatrix,He.spotLightMatrix.value=L.state.spotLightMatrix,He.spotLightMap.value=L.state.spotLightMap,He.pointShadowMatrix.value=L.state.pointShadowMatrix),w.lightProbeGrid=M.state.lightProbeGridArray.length>0,w.currentProgram=Ze,w.uniformsList=null,Ze}function Es(C){if(C.uniformsList===null){const W=C.currentProgram.getUniforms();C.uniformsList=Lu.seqWithValue(W.seq,C.uniforms)}return C.uniformsList}function ur(C,W){const ee=K.get(C);ee.outputColorSpace=W.outputColorSpace,ee.batching=W.batching,ee.batchingColor=W.batchingColor,ee.instancing=W.instancing,ee.instancingColor=W.instancingColor,ee.instancingMorph=W.instancingMorph,ee.skinning=W.skinning,ee.morphTargets=W.morphTargets,ee.morphNormals=W.morphNormals,ee.morphColors=W.morphColors,ee.morphTargetsCount=W.morphTargetsCount,ee.numClippingPlanes=W.numClippingPlanes,ee.numIntersection=W.numClipIntersection,ee.vertexAlphas=W.vertexAlphas,ee.vertexTangents=W.vertexTangents,ee.toneMapping=W.toneMapping}function Pl(C,W){if(C.length===0)return null;if(C.length===1)return C[0].texture!==null?C[0]:null;b.setFromMatrixPosition(W.matrixWorld);for(let ee=0,w=C.length;ee<w;ee++){const L=C[ee];if(L.texture!==null&&L.boundingBox.containsPoint(b))return L}return null}function oc(C,W,ee,w,L){W.isScene!==!0&&(W=Re),J.resetTextureUnits();const ie=W.fog,ae=w.isMeshStandardMaterial||w.isMeshLambertMaterial||w.isMeshPhongMaterial?W.environment:null,Ee=X===null?D.outputColorSpace:X.isXRRenderTarget===!0?X.texture.colorSpace:ft.workingColorSpace,ke=w.isMeshStandardMaterial||w.isMeshLambertMaterial&&!w.envMap||w.isMeshPhongMaterial&&!w.envMap,We=Se.get(w.envMap||ae,ke),Je=w.vertexColors===!0&&!!ee.attributes.color&&ee.attributes.color.itemSize===4,Ze=!!ee.attributes.tangent&&(!!w.normalMap||w.anisotropy>0),He=!!ee.morphAttributes.position,rt=!!ee.morphAttributes.normal,Ht=!!ee.morphAttributes.color;let Wt=$i;w.toneMapped&&(X===null||X.isXRRenderTarget===!0)&&(Wt=D.toneMapping);const Rt=ee.morphAttributes.position||ee.morphAttributes.normal||ee.morphAttributes.color,En=Rt!==void 0?Rt.length:0,Ge=K.get(w),$n=M.state.lights;if(le===!0&&(se===!0||C!==ve)){const Ot=C===ve&&w.id===q;Be.setState(w,C,Ot)}let _t=!1;w.version===Ge.__version?(Ge.needsLights&&Ge.lightsStateVersion!==$n.state.version||Ge.outputColorSpace!==Ee||L.isBatchedMesh&&Ge.batching===!1||!L.isBatchedMesh&&Ge.batching===!0||L.isBatchedMesh&&Ge.batchingColor===!0&&L.colorTexture===null||L.isBatchedMesh&&Ge.batchingColor===!1&&L.colorTexture!==null||L.isInstancedMesh&&Ge.instancing===!1||!L.isInstancedMesh&&Ge.instancing===!0||L.isSkinnedMesh&&Ge.skinning===!1||!L.isSkinnedMesh&&Ge.skinning===!0||L.isInstancedMesh&&Ge.instancingColor===!0&&L.instanceColor===null||L.isInstancedMesh&&Ge.instancingColor===!1&&L.instanceColor!==null||L.isInstancedMesh&&Ge.instancingMorph===!0&&L.morphTexture===null||L.isInstancedMesh&&Ge.instancingMorph===!1&&L.morphTexture!==null||Ge.envMap!==We||w.fog===!0&&Ge.fog!==ie||Ge.numClippingPlanes!==void 0&&(Ge.numClippingPlanes!==Be.numPlanes||Ge.numIntersection!==Be.numIntersection)||Ge.vertexAlphas!==Je||Ge.vertexTangents!==Ze||Ge.morphTargets!==He||Ge.morphNormals!==rt||Ge.morphColors!==Ht||Ge.toneMapping!==Wt||Ge.morphTargetsCount!==En||!!Ge.lightProbeGrid!=M.state.lightProbeGridArray.length>0)&&(_t=!0):(_t=!0,Ge.__version=w.version);let oi=Ge.currentProgram;_t===!0&&(oi=Oa(w,W,L),I&&w.isNodeMaterial&&I.onUpdateProgram(w,oi,Ge));let zi=!1,Pa=!1,dr=!1;const Dt=oi.getUniforms(),Kt=Ge.uniforms;if(E.useProgram(oi.program)&&(zi=!0,Pa=!0,dr=!0),w.id!==q&&(q=w.id,Pa=!0),Ge.needsLights){const Ot=Pl(M.state.lightProbeGridArray,L);Ge.lightProbeGrid!==Ot&&(Ge.lightProbeGrid=Ot,Pa=!0)}if(zi||ve!==C){E.buffers.depth.getReversed()&&C.reversedDepth!==!0&&(C._reversedDepth=!0,C.updateProjectionMatrix()),Dt.setValue(O,"projectionMatrix",C.projectionMatrix),Dt.setValue(O,"viewMatrix",C.matrixWorldInverse);const ja=Dt.map.cameraPosition;ja!==void 0&&ja.setValue(O,Oe.setFromMatrixPosition(C.matrixWorld)),R.logarithmicDepthBuffer&&Dt.setValue(O,"logDepthBufFC",2/(Math.log(C.far+1)/Math.LN2)),(w.isMeshPhongMaterial||w.isMeshToonMaterial||w.isMeshLambertMaterial||w.isMeshBasicMaterial||w.isMeshStandardMaterial||w.isShaderMaterial)&&Dt.setValue(O,"isOrthographic",C.isOrthographicCamera===!0),ve!==C&&(ve=C,Pa=!0,dr=!0)}if(Ge.needsLights&&($n.state.directionalShadowMap.length>0&&Dt.setValue(O,"directionalShadowMap",$n.state.directionalShadowMap,J),$n.state.spotShadowMap.length>0&&Dt.setValue(O,"spotShadowMap",$n.state.spotShadowMap,J),$n.state.pointShadowMap.length>0&&Dt.setValue(O,"pointShadowMap",$n.state.pointShadowMap,J)),L.isSkinnedMesh){Dt.setOptional(O,L,"bindMatrix"),Dt.setOptional(O,L,"bindMatrixInverse");const Ot=L.skeleton;Ot&&(Ot.boneTexture===null&&Ot.computeBoneTexture(),Dt.setValue(O,"boneTexture",Ot.boneTexture,J))}L.isBatchedMesh&&(Dt.setOptional(O,L,"batchingTexture"),Dt.setValue(O,"batchingTexture",L._matricesTexture,J),Dt.setOptional(O,L,"batchingIdTexture"),Dt.setValue(O,"batchingIdTexture",L._indirectTexture,J),Dt.setOptional(O,L,"batchingColorTexture"),L._colorsTexture!==null&&Dt.setValue(O,"batchingColorTexture",L._colorsTexture,J));const za=ee.morphAttributes;if((za.position!==void 0||za.normal!==void 0||za.color!==void 0)&&G.update(L,ee,oi),(Pa||Ge.receiveShadow!==L.receiveShadow)&&(Ge.receiveShadow=L.receiveShadow,Dt.setValue(O,"receiveShadow",L.receiveShadow)),(w.isMeshStandardMaterial||w.isMeshLambertMaterial||w.isMeshPhongMaterial)&&w.envMap===null&&W.environment!==null&&(Kt.envMapIntensity.value=W.environmentIntensity),Kt.dfgLUT!==void 0&&(Kt.dfgLUT.value=r2()),Pa){if(Dt.setValue(O,"toneMappingExposure",D.toneMappingExposure),Ge.needsLights&&cc(Kt,dr),ie&&w.fog===!0&&me.refreshFogUniforms(Kt,ie),me.refreshMaterialUniforms(Kt,w,ue,ge,M.state.transmissionRenderTarget[C.id]),Ge.needsLights&&Ge.lightProbeGrid){const Ot=Ge.lightProbeGrid;Kt.probesSH.value=Ot.texture,Kt.probesMin.value.copy(Ot.boundingBox.min),Kt.probesMax.value.copy(Ot.boundingBox.max),Kt.probesResolution.value.copy(Ot.resolution)}Lu.upload(O,Es(Ge),Kt,J)}if(w.isShaderMaterial&&w.uniformsNeedUpdate===!0&&(Lu.upload(O,Es(Ge),Kt,J),w.uniformsNeedUpdate=!1),w.isSpriteMaterial&&Dt.setValue(O,"center",L.center),Dt.setValue(O,"modelViewMatrix",L.modelViewMatrix),Dt.setValue(O,"normalMatrix",L.normalMatrix),Dt.setValue(O,"modelMatrix",L.matrixWorld),w.uniformsGroups!==void 0){const Ot=w.uniformsGroups;for(let ja=0,hr=Ot.length;ja<hr;ja++){const zg=Ot[ja];be.update(zg,oi),be.bind(zg,oi)}}return oi}function cc(C,W){C.ambientLightColor.needsUpdate=W,C.lightProbe.needsUpdate=W,C.directionalLights.needsUpdate=W,C.directionalLightShadows.needsUpdate=W,C.pointLights.needsUpdate=W,C.pointLightShadows.needsUpdate=W,C.spotLights.needsUpdate=W,C.spotLightShadows.needsUpdate=W,C.rectAreaLights.needsUpdate=W,C.hemisphereLights.needsUpdate=W}function uc(C){return C.isMeshLambertMaterial||C.isMeshToonMaterial||C.isMeshPhongMaterial||C.isMeshStandardMaterial||C.isShadowMaterial||C.isShaderMaterial&&C.lights===!0}this.getActiveCubeFace=function(){return z},this.getActiveMipmapLevel=function(){return j},this.getRenderTarget=function(){return X},this.setRenderTargetTextures=function(C,W,ee){const w=K.get(C);w.__autoAllocateDepthBuffer=C.resolveDepthBuffer===!1,w.__autoAllocateDepthBuffer===!1&&(w.__useRenderToTexture=!1),K.get(C.texture).__webglTexture=W,K.get(C.depthTexture).__webglTexture=w.__autoAllocateDepthBuffer?void 0:ee,w.__hasExternalTextures=!0},this.setRenderTargetFramebuffer=function(C,W){const ee=K.get(C);ee.__webglFramebuffer=W,ee.__useDefaultFramebuffer=W===void 0},this.setRenderTarget=function(C,W=0,ee=0){X=C,z=W,j=ee;let w=null,L=!1,ie=!1;if(C){const Ee=K.get(C);if(Ee.__useDefaultFramebuffer!==void 0){E.bindFramebuffer(O.FRAMEBUFFER,Ee.__webglFramebuffer),he.copy(C.viewport),Ae.copy(C.scissor),$=C.scissorTest,E.viewport(he),E.scissor(Ae),E.setScissorTest($),q=-1;return}else if(Ee.__webglFramebuffer===void 0)J.setupRenderTarget(C);else if(Ee.__hasExternalTextures)J.rebindTextures(C,K.get(C.texture).__webglTexture,K.get(C.depthTexture).__webglTexture);else if(C.depthBuffer){const Je=C.depthTexture;if(Ee.__boundDepthTexture!==Je){if(Je!==null&&K.has(Je)&&(C.width!==Je.image.width||C.height!==Je.image.height))throw new Error("THREE.WebGLRenderer: Attached DepthTexture is initialized to the incorrect size.");J.setupDepthRenderbuffer(C)}}const ke=C.texture;(ke.isData3DTexture||ke.isDataArrayTexture||ke.isCompressedArrayTexture)&&(ie=!0);const We=K.get(C).__webglFramebuffer;C.isWebGLCubeRenderTarget?(Array.isArray(We[W])?w=We[W][ee]:w=We[W],L=!0):C.samples>0&&J.useMultisampledRTT(C)===!1?w=K.get(C).__webglMultisampledFramebuffer:Array.isArray(We)?w=We[ee]:w=We,he.copy(C.viewport),Ae.copy(C.scissor),$=C.scissorTest}else he.copy(Xe).multiplyScalar(ue).floor(),Ae.copy(dt).multiplyScalar(ue).floor(),$=re;if(ee!==0&&(w=Y),E.bindFramebuffer(O.FRAMEBUFFER,w)&&E.drawBuffers(C,w),E.viewport(he),E.scissor(Ae),E.setScissorTest($),L){const Ee=K.get(C.texture);O.framebufferTexture2D(O.FRAMEBUFFER,O.COLOR_ATTACHMENT0,O.TEXTURE_CUBE_MAP_POSITIVE_X+W,Ee.__webglTexture,ee)}else if(ie){const Ee=W;for(let ke=0;ke<C.textures.length;ke++){const We=K.get(C.textures[ke]);O.framebufferTextureLayer(O.FRAMEBUFFER,O.COLOR_ATTACHMENT0+ke,We.__webglTexture,ee,Ee)}}else if(C!==null&&ee!==0){const Ee=K.get(C.texture);O.framebufferTexture2D(O.FRAMEBUFFER,O.COLOR_ATTACHMENT0,O.TEXTURE_2D,Ee.__webglTexture,ee)}q=-1},this.readRenderTargetPixels=function(C,W,ee,w,L,ie,ae,Ee=0){if(!(C&&C.isWebGLRenderTarget)){bt("WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.");return}let ke=K.get(C).__webglFramebuffer;if(C.isWebGLCubeRenderTarget&&ae!==void 0&&(ke=ke[ae]),ke){E.bindFramebuffer(O.FRAMEBUFFER,ke);try{const We=C.textures[Ee],Je=We.format,Ze=We.type;if(C.textures.length>1&&O.readBuffer(O.COLOR_ATTACHMENT0+Ee),!R.textureFormatReadable(Je)){bt("WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.");return}if(!R.textureTypeReadable(Ze)){bt("WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.");return}W>=0&&W<=C.width-w&&ee>=0&&ee<=C.height-L&&O.readPixels(W,ee,w,L,Ce.convert(Je),Ce.convert(Ze),ie)}finally{const We=X!==null?K.get(X).__webglFramebuffer:null;E.bindFramebuffer(O.FRAMEBUFFER,We)}}},this.readRenderTargetPixelsAsync=async function(C,W,ee,w,L,ie,ae,Ee=0){if(!(C&&C.isWebGLRenderTarget))throw new Error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.");let ke=K.get(C).__webglFramebuffer;if(C.isWebGLCubeRenderTarget&&ae!==void 0&&(ke=ke[ae]),ke)if(W>=0&&W<=C.width-w&&ee>=0&&ee<=C.height-L){E.bindFramebuffer(O.FRAMEBUFFER,ke);const We=C.textures[Ee],Je=We.format,Ze=We.type;if(C.textures.length>1&&O.readBuffer(O.COLOR_ATTACHMENT0+Ee),!R.textureFormatReadable(Je))throw new Error("THREE.WebGLRenderer.readRenderTargetPixelsAsync: renderTarget is not in RGBA or implementation defined format.");if(!R.textureTypeReadable(Ze))throw new Error("THREE.WebGLRenderer.readRenderTargetPixelsAsync: renderTarget is not in UnsignedByteType or implementation defined type.");const He=O.createBuffer();O.bindBuffer(O.PIXEL_PACK_BUFFER,He),O.bufferData(O.PIXEL_PACK_BUFFER,ie.byteLength,O.STREAM_READ),O.readPixels(W,ee,w,L,Ce.convert(Je),Ce.convert(Ze),0);const rt=X!==null?K.get(X).__webglFramebuffer:null;E.bindFramebuffer(O.FRAMEBUFFER,rt);const Ht=O.fenceSync(O.SYNC_GPU_COMMANDS_COMPLETE,0);return O.flush(),await AA(O,Ht,4),O.bindBuffer(O.PIXEL_PACK_BUFFER,He),O.getBufferSubData(O.PIXEL_PACK_BUFFER,0,ie),O.deleteBuffer(He),O.deleteSync(Ht),ie}else throw new Error("THREE.WebGLRenderer.readRenderTargetPixelsAsync: requested read bounds are out of range.")},this.copyFramebufferToTexture=function(C,W=null,ee=0){const w=Math.pow(2,-ee),L=Math.floor(C.image.width*w),ie=Math.floor(C.image.height*w),ae=W!==null?W.x:0,Ee=W!==null?W.y:0;J.setTexture2D(C,0),O.copyTexSubImage2D(O.TEXTURE_2D,ee,0,0,ae,Ee,L,ie),E.unbindTexture()},this.copyTextureToTexture=function(C,W,ee=null,w=null,L=0,ie=0){let ae,Ee,ke,We,Je,Ze,He,rt,Ht;const Wt=C.isCompressedTexture?C.mipmaps[ie]:C.image;if(ee!==null)ae=ee.max.x-ee.min.x,Ee=ee.max.y-ee.min.y,ke=ee.isBox3?ee.max.z-ee.min.z:1,We=ee.min.x,Je=ee.min.y,Ze=ee.isBox3?ee.min.z:0;else{const Kt=Math.pow(2,-L);ae=Math.floor(Wt.width*Kt),Ee=Math.floor(Wt.height*Kt),C.isDataArrayTexture?ke=Wt.depth:C.isData3DTexture?ke=Math.floor(Wt.depth*Kt):ke=1,We=0,Je=0,Ze=0}w!==null?(He=w.x,rt=w.y,Ht=w.z):(He=0,rt=0,Ht=0);const Rt=Ce.convert(W.format),En=Ce.convert(W.type);let Ge;W.isData3DTexture?(J.setTexture3D(W,0),Ge=O.TEXTURE_3D):W.isDataArrayTexture||W.isCompressedArrayTexture?(J.setTexture2DArray(W,0),Ge=O.TEXTURE_2D_ARRAY):(J.setTexture2D(W,0),Ge=O.TEXTURE_2D),E.activeTexture(O.TEXTURE0),E.pixelStorei(O.UNPACK_FLIP_Y_WEBGL,W.flipY),E.pixelStorei(O.UNPACK_PREMULTIPLY_ALPHA_WEBGL,W.premultiplyAlpha),E.pixelStorei(O.UNPACK_ALIGNMENT,W.unpackAlignment);const $n=E.getParameter(O.UNPACK_ROW_LENGTH),_t=E.getParameter(O.UNPACK_IMAGE_HEIGHT),oi=E.getParameter(O.UNPACK_SKIP_PIXELS),zi=E.getParameter(O.UNPACK_SKIP_ROWS),Pa=E.getParameter(O.UNPACK_SKIP_IMAGES);E.pixelStorei(O.UNPACK_ROW_LENGTH,Wt.width),E.pixelStorei(O.UNPACK_IMAGE_HEIGHT,Wt.height),E.pixelStorei(O.UNPACK_SKIP_PIXELS,We),E.pixelStorei(O.UNPACK_SKIP_ROWS,Je),E.pixelStorei(O.UNPACK_SKIP_IMAGES,Ze);const dr=C.isDataArrayTexture||C.isData3DTexture,Dt=W.isDataArrayTexture||W.isData3DTexture;if(C.isDepthTexture){const Kt=K.get(C),za=K.get(W),Ot=K.get(Kt.__renderTarget),ja=K.get(za.__renderTarget);E.bindFramebuffer(O.READ_FRAMEBUFFER,Ot.__webglFramebuffer),E.bindFramebuffer(O.DRAW_FRAMEBUFFER,ja.__webglFramebuffer);for(let hr=0;hr<ke;hr++)dr&&(O.framebufferTextureLayer(O.READ_FRAMEBUFFER,O.COLOR_ATTACHMENT0,K.get(C).__webglTexture,L,Ze+hr),O.framebufferTextureLayer(O.DRAW_FRAMEBUFFER,O.COLOR_ATTACHMENT0,K.get(W).__webglTexture,ie,Ht+hr)),O.blitFramebuffer(We,Je,ae,Ee,He,rt,ae,Ee,O.DEPTH_BUFFER_BIT,O.NEAREST);E.bindFramebuffer(O.READ_FRAMEBUFFER,null),E.bindFramebuffer(O.DRAW_FRAMEBUFFER,null)}else if(L!==0||C.isRenderTargetTexture||K.has(C)){const Kt=K.get(C),za=K.get(W);E.bindFramebuffer(O.READ_FRAMEBUFFER,k),E.bindFramebuffer(O.DRAW_FRAMEBUFFER,B);for(let Ot=0;Ot<ke;Ot++)dr?O.framebufferTextureLayer(O.READ_FRAMEBUFFER,O.COLOR_ATTACHMENT0,Kt.__webglTexture,L,Ze+Ot):O.framebufferTexture2D(O.READ_FRAMEBUFFER,O.COLOR_ATTACHMENT0,O.TEXTURE_2D,Kt.__webglTexture,L),Dt?O.framebufferTextureLayer(O.DRAW_FRAMEBUFFER,O.COLOR_ATTACHMENT0,za.__webglTexture,ie,Ht+Ot):O.framebufferTexture2D(O.DRAW_FRAMEBUFFER,O.COLOR_ATTACHMENT0,O.TEXTURE_2D,za.__webglTexture,ie),L!==0?O.blitFramebuffer(We,Je,ae,Ee,He,rt,ae,Ee,O.COLOR_BUFFER_BIT,O.NEAREST):Dt?O.copyTexSubImage3D(Ge,ie,He,rt,Ht+Ot,We,Je,ae,Ee):O.copyTexSubImage2D(Ge,ie,He,rt,We,Je,ae,Ee);E.bindFramebuffer(O.READ_FRAMEBUFFER,null),E.bindFramebuffer(O.DRAW_FRAMEBUFFER,null)}else Dt?C.isDataTexture||C.isData3DTexture?O.texSubImage3D(Ge,ie,He,rt,Ht,ae,Ee,ke,Rt,En,Wt.data):W.isCompressedArrayTexture?O.compressedTexSubImage3D(Ge,ie,He,rt,Ht,ae,Ee,ke,Rt,Wt.data):O.texSubImage3D(Ge,ie,He,rt,Ht,ae,Ee,ke,Rt,En,Wt):C.isDataTexture?O.texSubImage2D(O.TEXTURE_2D,ie,He,rt,ae,Ee,Rt,En,Wt.data):C.isCompressedTexture?O.compressedTexSubImage2D(O.TEXTURE_2D,ie,He,rt,Wt.width,Wt.height,Rt,Wt.data):O.texSubImage2D(O.TEXTURE_2D,ie,He,rt,ae,Ee,Rt,En,Wt);E.pixelStorei(O.UNPACK_ROW_LENGTH,$n),E.pixelStorei(O.UNPACK_IMAGE_HEIGHT,_t),E.pixelStorei(O.UNPACK_SKIP_PIXELS,oi),E.pixelStorei(O.UNPACK_SKIP_ROWS,zi),E.pixelStorei(O.UNPACK_SKIP_IMAGES,Pa),ie===0&&W.generateMipmaps&&O.generateMipmap(Ge),E.unbindTexture()},this.initRenderTarget=function(C){K.get(C).__webglFramebuffer===void 0&&J.setupRenderTarget(C)},this.initTexture=function(C){C.isCubeTexture?J.setTextureCube(C,0):C.isData3DTexture?J.setTexture3D(C,0):C.isDataArrayTexture||C.isCompressedArrayTexture?J.setTexture2DArray(C,0):J.setTexture2D(C,0),E.unbindTexture()},this.resetState=function(){z=0,j=0,X=null,E.reset(),Ue.reset()},typeof __THREE_DEVTOOLS__<"u"&&__THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe",{detail:this}))}get coordinateSystem(){return qi}get outputColorSpace(){return this._outputColorSpace}set outputColorSpace(e){this._outputColorSpace=e;const n=this.getContext();n.drawingBufferColorSpace=ft._getDrawingBufferColorSpace(e),n.unpackColorSpace=ft._getUnpackColorSpace()}}const o2=1024,c2=2048,T0=96,A0=new Map;function u2(t){if(!t.agentColor)return null;const e=t.agentColor+(t.access||"");let n=A0.get(e);if(!n){n=new gt;const i=/hsl\(\s*([\d.]+)[\s,]+([\d.]+)%[\s,]+([\d.]+)%/.exec(t.agentColor);if(i){const a=t.access==="read"?Math.max(.18,+i[3]/100*.55):+i[3]/100;n.setHSL(+i[1]/360,+i[2]/100,a)}else try{n.set(t.agentColor)}catch{n.setHSL(0,0,.5)}A0.set(e,n)}return n}function d2(t,e,n,{onSelect:i,onZoom:a}){let s;try{s=new l2({antialias:!0,alpha:!0,canvas:t})}catch{}if(!s)return null;s.setPixelRatio(Math.min(devicePixelRatio,2)),s.setClearColor(0,0);const l=new HA,o=new wg(-1,1,1,-1,-400,600),c=` + attribute vec3 aColor; attribute vec2 aHi; // x = highlight, y = fade + varying vec3 vN, vW, vColor; varying vec2 vHi; + void main(){ + vColor = aColor; vHi = aHi; + vec3 tp = position; + #ifdef USE_INSTANCING + tp = (instanceMatrix * vec4(position, 1.0)).xyz; + vN = normalize(mat3(instanceMatrix) * normal); + #else + vN = normal; + #endif + vW = tp; + gl_Position = projectionMatrix * modelViewMatrix * vec4(tp, 1.0); + }`,u=` + precision highp float; + varying vec3 vN, vW, vColor; varying vec2 vHi; + uniform float uHatch, uTime; + void main(){ + vec3 n = normalize(vN); + // One fixed brightness per axis: an isometric drawing doesn't need real + // lighting, it needs the three faces instantly distinguishable. + float f = n.y > 0.5 ? 1.0 : (abs(n.x) > 0.5 ? 0.74 : 0.56); + vec3 paper = vec3(0.867, 0.827, 0.706); + vec3 c = mix(paper, vColor, 0.62) * f; + + // Hatching: drawn only on upward faces, in world space, so the lines + // continue across neighbouring buildings. + if (n.y > 0.5 && uHatch > 0.5) { + float s = sin((vW.x + vW.z) * 3.2 + vW.y * 0.4); + c -= vec3(0.055) * smoothstep(0.72, 1.0, abs(s)); + } + c = mix(c, vec3(0.98, 0.94, 0.86), vHi.x * 0.42); + c = mix(paper * 0.94, c, mix(0.30, 1.0, 1.0 - vHi.y)); + gl_FragColor = vec4(c, 1.0); + }`,h=new Js(1,1,1),g=new Gn({uniforms:{uHatch:{value:1},uTime:{value:0}},vertexShader:c,fragmentShader:u});let d=o2,p=new Vv(h,g,d);p.frustumCulled=!1;let x=new $r(new Float32Array(d*3),3),_=new $r(new Float32Array(d*2),2);h.setAttribute("aColor",x),h.setAttribute("aHi",_),l.add(p);const m=new nw(h),f=new US({color:3814695,transparent:!0,opacity:.3});let v=[];for(let ye=0;ye<d;ye++){const O=new nf(m,f);O.visible=!1,v.push(O),l.add(O)}const y=(ye,O)=>{let Ye=Math.max(1,ye);for(;Ye<O;)Ye*=2;return Ye};function b(ye){if(ye<=d)return;const O=y(d,ye),Ye=p,$e=v,R=new Vv(h,g,O);R.frustumCulled=!1,R.count=0;const E=new $r(new Float32Array(O*3),3),V=new $r(new Float32Array(O*2),2);h.setAttribute("aColor",E),h.setAttribute("aHi",V);const K=[];for(let J=0;J<O;J++){const Se=new nf(m,f);Se.visible=!1,K.push(Se),l.add(Se)}l.remove(Ye);for(const J of $e)l.remove(J);p=R,x=E,_=V,v=K,d=O}const T=()=>new Gn({uniforms:{},vertexShader:`varying vec3 vN; varying vec3 vW; + void main(){ vN = normal; vW = position; + gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); }`,fragmentShader:`precision mediump float; varying vec3 vN; varying vec3 vW; + void main(){ + float f = vN.y > 0.5 ? 1.0 : 0.78; + vec3 c = vec3(0.816, 0.776, 0.651) * f; + if (vN.y > 0.5) { + // A fine grid on the plot's top face, so it reads as surveyed land. + vec2 g = abs(fract(vW.xz * 0.5) - 0.5); + c -= vec3(0.03) * smoothstep(0.46, 0.5, max(g.x, g.y)); + } + gl_FragColor = vec4(c, 1.0); + }`}),M=[],A=new Js(1,1,1);for(let ye=0;ye<T0;ye++){const O=new Ei(A,T());O.visible=!1,M.push(O),l.add(O)}const S=3;let N=c2,D=new Float32Array(N*S*3),U=new Float32Array(N*S);const I=new qn;I.setAttribute("position",new on(D,3)),I.setAttribute("aA",new on(U,1));const Y=new ew(I,new Gn({uniforms:{uPx:{value:4}},vertexShader:`attribute float aA; varying float vA; uniform float uPx; + void main(){ vA = aA; + gl_PointSize = uPx; + gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); }`,fragmentShader:`precision mediump float; varying float vA; + void main(){ + float d = length(gl_PointCoord - 0.5) * 2.0; + if (d > 1.0 || vA <= 0.0) discard; + gl_FragColor = vec4(0.35, 0.22, 0.12, (1.0 - d) * vA); + }`,transparent:!0,depthWrite:!1}));Y.frustumCulled=!1,l.add(Y);let k=new Float32Array(N*6),B=new Float32Array(N*2);const z=new qn;z.setAttribute("position",new on(k,3)),z.setAttribute("aA",new on(B,1));const j=new nf(z,new Gn({vertexShader:`attribute float aA; varying float vA; + void main(){ vA = aA; gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); }`,fragmentShader:`precision mediump float; varying float vA; + void main(){ gl_FragColor = vec4(0.30, 0.19, 0.10, vA); }`,transparent:!0,depthWrite:!1}));j.frustumCulled=!1,l.add(j);function X(ye){ye<=N||(N=y(N,ye),D=new Float32Array(N*S*3),U=new Float32Array(N*S),k=new Float32Array(N*6),B=new Float32Array(N*2),I.setAttribute("position",new on(D,3)),I.setAttribute("aA",new on(U,1)),z.setAttribute("position",new on(k,3)),z.setAttribute("aA",new on(B,1)))}const q={yaw:Math.PI*.25,tYaw:Math.PI*.25,zoom:16,tZoom:16},ve=Math.atan(1/Math.SQRT2);let he=!1,Ae=0,$=0,pe=!0;const we={x:-1,y:-1,live:!1};t.addEventListener("pointerdown",ye=>{he=!0,$=0,Ae=ye.clientX,t.setPointerCapture(ye.pointerId),t.classList.add("drag")}),t.addEventListener("pointerup",ye=>{he=!1,t.classList.remove("drag"),t.releasePointerCapture(ye.pointerId)}),t.addEventListener("pointermove",ye=>{const O=t.getBoundingClientRect();we.x=ye.clientX-O.left,we.y=ye.clientY-O.top,we.live=!0,he&&($+=Math.abs(ye.clientX-Ae),q.tYaw-=(ye.clientX-Ae)*.006,Ae=ye.clientX,pe=!1,H(!1))}),t.addEventListener("pointerleave",()=>{we.live=!1});const ne=q.tZoom,ge=()=>a(Math.round(ne/q.tZoom*100)),ue=ye=>{q.tZoom=Math.max(4,Math.min(60,q.tZoom*ye)),ge()};t.addEventListener("wheel",ye=>{ye.preventDefault(),ue(1+Math.sign(ye.deltaY)*.11)},{passive:!1}),ge();let Le=null,Ie=null,Xe=null,dt="",re=null,H=()=>{};t.addEventListener("click",()=>{$>5||i(Le&&Ie!==Le?Le:null)});const le=Vo.map(ye=>new gt(pg(ye)||"#8a4b2a")),se=new Z,Me=new zt,Oe=new gt;let De=!0,Re=performance.now();function Fe(ye){requestAnimationFrame(Fe);const O=Math.min(.05,(ye-Re)/1e3);Re=ye;const Ye=e.clientWidth,$e=e.clientHeight;if(!Ye||!$e)return;t.width!==Math.round(Ye*s.getPixelRatio())&&s.setSize(Ye,$e,!1);const R=1-Math.pow(.002,O);W1(),b(Vt.length),X(as.length),pe&&(q.tYaw+=O*.12),q.yaw+=(q.tYaw-q.yaw)*R,q.zoom+=(q.tZoom-q.zoom)*R;const E=q.zoom*4,V=E*(Ye/$e);o.left=-V,o.right=V,o.top=E,o.bottom=-E,o.updateProjectionMatrix();const K=180;o.position.set(Math.cos(q.yaw)*Math.cos(ve)*K,Math.sin(ve)*K,Math.sin(q.yaw)*Math.cos(ve)*K),o.lookAt(0,6,0);for(let Q=0;Q<T0;Q++){const oe=M[Q],me=ha[Q];if(!me||Q>=ha.length){oe.visible=!1;continue}me.x=me.x===void 0?me.tx+me.tw/2:me.x,me.z=me.z===void 0?me.tz+me.th/2:me.z;const xe=me.tx+me.tw/2,Te=me.tz+me.th/2;me.x+=(xe-me.x)*R,me.z+=(Te-me.z)*R,me.w+=(me.tw-me.w)*R,me.h+=(me.th-me.h)*R,oe.visible=!0,oe.position.set(me.x,-.25,me.z),oe.scale.set(Math.max(.01,me.w-Yr*.45),.5,Math.max(.01,me.h-Yr*.45))}const J=Ie?new Set([Ie.path,...Ie.deps,...Ie.usedBy]):null,Se=Ie||Le||Xe,_e=Vt.length;p.count=_e;for(let Q=0;Q<_e;Q++){const oe=Vt[Q];oe.x!==oe.tx&&(oe.x+=(oe.tx-oe.x)*R*.7),oe.z!==oe.tz&&(oe.z+=(oe.tz-oe.z)*R*.7);const me=F1(oe);oe.h=oe.h===void 0?me:oe.h+(me-oe.h)*(oe.dying?R*1.4:R*.6),oe.pulse=Math.max(0,(oe.pulse||0)-O*1.6),Me.makeScale(Hi*.68,Math.max(.01,oe.h),Hi*.68),Me.setPosition(oe.x,oe.h/2,oe.z),p.setMatrixAt(Q,Me);const xe=v[Q];xe.visible=!0,xe.scale.set(Hi*.68,Math.max(.01,oe.h),Hi*.68),xe.position.set(oe.x,oe.h/2,oe.z);const Te=u2(oe);Te?Oe.copy(Te):Oe.copy(le[(oe.ki??0)%le.length]).offsetHSL(0,0,(lS.get(oe.dir)-.5)*.17),x.array[Q*3]=Oe.r,x.array[Q*3+1]=Oe.g,x.array[Q*3+2]=Oe.b;const Be=oe===Se?1:Math.min(.85,oe.pulse||0);let Ve=J?J.has(oe.path)?0:1:dt&&!oe.path.toLowerCase().includes(dt)?1:0;!J&&!dt&&re&&(Ve=oe.top===re?0:1),_.array[Q*2]+=(Be-_.array[Q*2])*R,_.array[Q*2+1]+=(Ve-_.array[Q*2+1])*R}for(let Q=_e;Q<d;Q++)v[Q].visible=!1;p.instanceMatrix.needsUpdate=!0,x.needsUpdate=_.needsUpdate=!0;const te=as.length;z.setDrawRange(0,te*2),I.setDrawRange(0,te*S);for(let Q=0;Q<te;Q++){const oe=as[Q],me=oe.from,xe=oe.to,Te=Q*6;k[Te]=me.x,k[Te+1]=me.h,k[Te+2]=me.z,k[Te+3]=xe.x,k[Te+4]=xe.h,k[Te+5]=xe.z;const Be=!J||J.has(me.path)&&J.has(xe.path),Ve=Ie&&(me===Ie||xe===Ie),Ke=Ve?.55:Be?.1:.02;B[Q*2]+=(Ke-B[Q*2])*R,B[Q*2+1]=B[Q*2];for(let G=0;G<S;G++){const Ne=Q*S+G,ce=(ye/2600+(Q*.37+G/S))%1,Ce=Math.sin(ce*Math.PI)*Math.hypot(xe.x-me.x,xe.z-me.z)*.22;D[Ne*3]=me.x+(xe.x-me.x)*ce,D[Ne*3+1]=me.h+(xe.h-me.h)*ce+Ce+1.2,D[Ne*3+2]=me.z+(xe.z-me.z)*ce,U[Ne]=(De?1:0)*(Ve?1:Be?.45:.06)*Math.sin(ce*Math.PI)}}if(z.getAttribute("position").needsUpdate=!0,z.getAttribute("aA").needsUpdate=!0,I.getAttribute("position").needsUpdate=!0,I.getAttribute("aA").needsUpdate=!0,Y.material.uniforms.uPx.value=3.4*s.getPixelRatio(),we.live&&!he){let Q=null,oe=26*26;for(let me=0;me<_e;me++){const xe=Vt[me];if(xe.dying||xe.h<1)continue;se.set(xe.x,xe.h*.6,xe.z).project(o);const Te=(se.x*.5+.5)*Ye,Be=(-se.y*.5+.5)*$e,Ve=(Te-we.x)**2+(Be-we.y)**2;Ve<oe&&(oe=Ve,Q=xe,xe.sx=Te,xe.sy=Be)}Le=Q,t.style.cursor=he?"grabbing":Q?"pointer":"grab"}else we.live||(Le=null);Le?(n.style.display="block",n.style.left=Le.sx+"px",n.style.top=Le.sy+"px",n.innerHTML=`<b>${Le.name}</b> · ${Le.loc} lines<br>${Le.dir} · referenced by ${Le.usedBy.length}`):n.style.display="none",s.render(l,o)}return requestAnimationFrame(Fe),{setFlow:ye=>{De=ye},setHatch:ye=>{g.uniforms.uHatch.value=ye?1:0},setSpin:ye=>{pe=ye},spinning:()=>pe,onSpinChange:ye=>{H=ye},dolly:ue,reset:()=>{q.tYaw=Math.PI*.25,q.tZoom=ne,ge()},setSel:ye=>{Ie=ye},setRailHover:ye=>{Xe=ye},setQuery:ye=>{dt=ye},setFocusTop:ye=>{re=ye}}}const Rr=new Map;function w0(t){var n,i;const e=((n=t==null?void 0:t.properties)==null?void 0:n.path)||((i=t==null?void 0:t.properties)==null?void 0:i.filePath)||(t==null?void 0:t.uri);return typeof e=="string"&&e.length>0?e:null}function h2(t){var i,a,s;const e=((i=t==null?void 0:t.properties)==null?void 0:i.loc)??((a=t==null?void 0:t.properties)==null?void 0:a.lines)??((s=t==null?void 0:t.properties)==null?void 0:s.size),n=Number(e);return Number.isFinite(n)&&n>0?Math.min(4e3,Math.round(n)):40}function f2(t,e){const i=String(t).replace(/\\/g,"/").split("/");return i[0]==="Code"&&i[1]?i[1].split("--")[0]:["Master","Roadmap","Auftrag","Planung","Codebasis","PLUG-Ordner","Aufräumen"].includes(i[0])?i[0]:e}function p2(t){var h;const e=t==null?void 0:t.workspace,n=(h=t==null?void 0:t.graph)==null?void 0:h.nodes;if(!(e!=null&&e.id)||!Array.isArray(n))return{workspaces:Rr.size,buildings:0,added:0};const i=Qy(e.name||e.canonicalPath||e.id)||e.id;for(const g of gn.slice())g.sim&&ro.unregister(g.id);const a=Array.isArray(t.graph.edges)?t.graph.edges:[],s=new Map(n.filter(g=>g&&typeof g.id=="string").map(g=>[g.id,g])),l=new Map;for(const g of a){const d=s.get(g==null?void 0:g.sourceId),p=s.get(g==null?void 0:g.targetId);if(!d||!p)continue;const x=w0(p);x&&(l.has(d.id)||l.set(d.id,[]),l.get(d.id).push(x))}const o=[];for(const g of n){const d=w0(g);d&&o.push({node:g,path:d})}o.sort((g,d)=>g.path.localeCompare(d.path));let c=0,u=0;for(const{node:g,path:d}of o){const p=f2(d,i);Rr.has(p)||(ro.register({id:p,name:p}),Rr.set(p,new Set));const x=Rr.get(p);if(x.has(d))continue;x.add(d),u+=x.size;const _=g.properties||{};ro.grow(p,{path:d,loc:h2(g),deps:l.get(g.id)||[],note:g.type||"",agentColor:_.agentColor||_.readerColor||null,agentName:_.agentName||_.readerName||null,access:_.agentColor?"write":_.readerColor?"read":null}),c+=1}return c>0&&ro.event(String(e.id),`${c} indexed object${c===1?"":"s"} added across ${Rr.size} districts`),{workspaces:Rr.size,buildings:u,added:c,total:o.length,truncated:!1}}function m2({snapshot:t,onSelectFile:e}){P.useEffect(()=>{t&&p2(t)},[t]);const[n,i]=P.useState(!0),[a,s]=P.useState("loc"),[l,o]=P.useState(!0),[c,u]=P.useState(!0),[h,g]=P.useState(!0),[d,p]=P.useState(100),[x,_]=P.useState(null),[m,f]=P.useState(""),[v,y]=P.useState(null),[b,T]=P.useState(!0),[,M]=P.useReducer(k=>k+1,0),A=P.useRef(null),S=P.useRef(null),N=P.useRef(null),D=P.useRef(null);P.useEffect(()=>{const k=d2(A.current,S.current,N.current,{onSelect:B=>_(B),onZoom:B=>p(B)});if(!k){i(!1);return}D.current=k,k.onSpinChange(B=>g(B))},[]),P.useEffect(()=>{const k=H1(()=>M());return()=>{k()}},[]),P.useEffect(()=>{!x&&Vt.length>0&&_(Vt[0])},[Vt.length,x]),P.useEffect(()=>{var k;(k=D.current)==null||k.setSel(x)},[x]),P.useEffect(()=>{var k;(k=D.current)==null||k.setQuery(m)},[m]),P.useEffect(()=>{var k;(k=D.current)==null||k.setFocusTop(v)},[v]),P.useEffect(()=>{const k=B=>{var j,X;const z=B.target;if(/^(INPUT|TEXTAREA)$/.test(z.tagName)){B.key==="Escape"&&z.blur();return}B.key==="Escape"?(_(null),y(null)):B.key==="="||B.key==="+"?(j=D.current)==null||j.dolly(.8474576271186441):B.key==="-"||B.key==="_"?(X=D.current)==null||X.dolly(1.18):(B.key==="e"||B.key==="E")&&T(q=>!q)};return addEventListener("keydown",k),()=>removeEventListener("keydown",k)},[]);const U=Vt.reduce((k,B)=>k+B.loc,0),I=gn.reduce((k,B)=>k+B.events,0),Y=(k,B)=>B.length?r.jsxs(r.Fragment,{children:[r.jsxs("h3",{children:[k+" ",r.jsx("span",{style:{color:"var(--faint)"},children:B.length})]}),B.map(z=>{const j=Ao[z];return j&&r.jsxs("div",{className:"dep","data-p":z,onClick:()=>_(j),children:[r.jsx("span",{className:"sw",style:{background:I1(j)}}),r.jsx("span",{children:z})]},z)})]}):null;return r.jsxs("div",{id:"app",className:x?void 0:"closed",children:[r.jsxs("aside",{children:[r.jsxs("div",{className:"hd",children:[r.jsx("h1",{children:"Code-Stadt"}),r.jsx("div",{className:"repo",id:"repo",children:"3D-Topologie · Repositories als Distrikte, Module als Gebäude"}),r.jsxs("div",{className:"kpis",children:[r.jsxs("div",{children:[r.jsx("b",{id:"k-ws",children:gn.length}),r.jsx("i",{children:"Workspaces"})]}),r.jsxs("div",{children:[r.jsx("b",{id:"k-bld",children:Vt.length}),r.jsx("i",{children:"Dateien"})]}),r.jsxs("div",{children:[r.jsx("b",{id:"k-ev",children:I}),r.jsx("i",{children:"Ereignisse"})]})]})]}),r.jsx("div",{className:"q",children:r.jsx("input",{id:"q",type:"search",placeholder:"Modul oder Datei suchen …",spellCheck:!1,onChange:k=>f(k.target.value.trim().toLowerCase())})}),r.jsx("div",{className:"tree",id:"tree",children:gn.length?gn.map(k=>{const B=Vt.filter(j=>j.dir===k.id),z=B.reduce((j,X)=>j+X.loc,0);return r.jsxs("div",{className:"ws"+(v===k.id?" on":"")+(k.dying?" dying":""),onClick:()=>y(j=>j===k.id?null:k.id),children:[r.jsxs("div",{className:"wsrow",children:[r.jsx("span",{className:"sw",style:{background:xv(k)}}),r.jsx("span",{className:"nm",children:k.name}),k.sim?r.jsx("span",{className:"tag",children:"sim"}):null,r.jsxs("span",{className:"lc",children:[B.length," Dat. · ",z," Z."]})]}),r.jsx("div",{className:"loadbar",children:r.jsx("i",{style:{width:Math.round(k.load*100)+"%",background:xv(k)}})})]},k.id)}):r.jsxs("div",{className:"empty",children:["Keine Workspaces für die 3D-Stadtansicht indiziert.",r.jsx("br",{}),r.jsx("br",{}),"Sobald Repositories indiziert sind, wachsen sie hier als Stadt."]})})]}),r.jsxs("div",{id:"stage",ref:S,children:[r.jsx("canvas",{id:"cv",ref:A}),r.jsx("div",{id:"tip",ref:N}),r.jsxs("div",{id:"crumb",children:["PLUGBRAIN / ",r.jsx("b",{id:"crumb-t",children:x?x.path.toUpperCase():v?v.toUpperCase():"STADT-ÜBERSICHT"})]}),b&&r.jsx("div",{id:"feed",children:Tu.slice(0,9).map(k=>r.jsxs("div",{className:"fe",children:[r.jsx("span",{className:"ft",children:k.t.toLocaleTimeString("de-DE",{hour12:!1})}),r.jsx("span",{className:"fw",style:{color:"var(--accent)"},children:k.ws}),r.jsx("span",{className:"fm",children:k.msg})]},k.id))}),r.jsx("div",{id:"legend",children:r.jsx("div",{style:{color:"var(--faint)"},children:`Distrikt = Workspace · Gebäude = Datei · Höhe = ${a==="loc"?"Zeilenanzahl":"Abhängigkeiten"} · Licht = Trace-Aktivität`})}),r.jsxs("div",{id:"bar",children:[[["loc","Höhe = Zeilen"],["dep","Höhe = Abhängigkeiten"]].map(([k,B])=>r.jsx("button",{className:"tb"+(a===k?" on":""),"data-h":k,type:"button",onClick:()=>{B1(k),s(k)},children:B},k)),r.jsx("div",{className:"vsep"}),r.jsx("button",{className:"tb"+(l?" on":""),id:"t-flow",type:"button",onClick:()=>{o(k=>{var B;return(B=D.current)==null||B.setFlow(!k),!k})},children:"Fluss"}),r.jsx("button",{className:"tb"+(c?" on":""),id:"t-hatch",type:"button",onClick:()=>{u(k=>{var B;return(B=D.current)==null||B.setHatch(!k),!k})},children:"Schraffur"}),r.jsx("button",{className:"tb"+(h?" on":""),id:"t-spin",type:"button",onClick:()=>{g(k=>{var B;return(B=D.current)==null||B.setSpin(!k),!k})},children:"Orbit"}),r.jsx("button",{className:"tb"+(b?" on":""),id:"t-feed",type:"button",title:"Feed ein-/ausblenden (E)",onClick:()=>T(k=>!k),children:"Feed"}),r.jsx("div",{className:"vsep"}),r.jsx("button",{className:"tb",id:"zout",type:"button",title:"Herauszoomen",onClick:()=>{var k;return(k=D.current)==null?void 0:k.dolly(1.18)},children:"−"}),r.jsx("button",{className:"tb",id:"zlvl",type:"button",title:"Zoom zurücksetzen",onClick:()=>{var k;return(k=D.current)==null?void 0:k.reset()},children:d+"%"}),r.jsx("button",{className:"tb",id:"zin",type:"button",title:"Hineinzoomen",onClick:()=>{var k;return(k=D.current)==null?void 0:k.dolly(1/1.18)},children:"+"}),r.jsx("div",{className:"vsep"}),r.jsx("button",{className:"tb",id:"t-reset",type:"button",onClick:()=>{var k;(k=D.current)==null||k.reset(),_(null),y(null)},children:"Zurücksetzen"})]}),r.jsxs("div",{id:"gate",style:n?void 0:{display:"grid"},children:["WebGL ist auf diesem Gerät nicht verfügbar.",r.jsx("br",{}),"Die Workspace-Verwaltung bleibt vollständig nutzbar."]})]}),r.jsx("div",{id:"side",children:r.jsx("div",{id:"dt",children:x&&r.jsxs("div",{className:"dt",children:[r.jsx("div",{className:"kind",children:x.dir+"/"}),r.jsx("h2",{children:x.name}),x.note?r.jsx("div",{className:"note",children:x.note}):null,e&&r.jsx("button",{type:"button",className:"btn primary",style:{marginTop:"10px",marginBottom:"14px",width:"100%",padding:"8px 12px"},onClick:()=>e(x.path),children:"Datei in Quellansicht öffnen"}),r.jsxs("dl",{children:[r.jsx("dt",{children:"Size"}),r.jsx("dd",{children:x.loc}),r.jsx("dt",{children:"References"}),r.jsx("dd",{children:x.deps.length}),r.jsx("dt",{children:"Referenced by"}),r.jsx("dd",{children:x.usedBy.length}),r.jsx("dt",{children:"Share of total"}),r.jsx("dd",{children:U?(x.loc/U*100).toFixed(1)+"%":"—"})]}),Y("References",x.deps),Y("Referenced by",x.usedBy)]})})})]})}const N0={agent:"Agent",task:"Aufgabe",worker:"Worker",worktree:"Worktree",file:"Datei",artifact:"Artefakt",route:"Route"},C0={live:"Live-Ereignis",recovered:"wiederhergestelltes Ereignis","historical-import":"historischer Import"};function df(t){const e=new Date(t);return Number.isNaN(e.getTime())?t:e.toLocaleString()}function hf(t){if(t.kind!=="worker")return null;switch(t.proof){case"process-started":return"Start im Trace beobachtet — keine Aussage über den aktuellen Prozesszustand.";case"finished":return"Abschluss im Trace beobachtet.";case"proof-unavailable":return"Kein beobachteter Prozessstart; dieser Worker wird nicht als laufend dargestellt.";default:return"Kein Prozessbeweis vorhanden."}}function g2(t){const e=t.id.slice(t.id.indexOf(":")+1);return t.kind==="agent"?{agentId:e}:t.kind==="task"?{taskId:e}:t.kind==="worker"?{workerId:e}:null}function R0(t,e){var n;return((n=t.find(i=>i.id===e))==null?void 0:n.label)??e}function x2({mesh:t,workspaceId:e,onSelectFile:n,focusAgentId:i}){const[a,s]=P.useState(null),[l,o]=P.useState([]),[c,u]=P.useState(!1),[h,g]=P.useState(""),d=P.useMemo(()=>(t==null?void 0:t.nodes.find(x=>x.id===a))??null,[t,a]);return P.useEffect(()=>{if(!i)return;const x=`agent:${i}`;t!=null&&t.nodes.some(_=>_.id===x)&&s(x)},[i,t]),P.useEffect(()=>{a!==null&&d===null&&s(null)},[d,a]),P.useEffect(()=>{const x=d?g2(d):null;if(!e||x===null){o([]),g(""),u(!1);return}let _=!0;return u(!0),g(""),_1(e,{...x,limit:12}).then(m=>{_&&o(m)}).catch(()=>{_&&(o([]),g("Die Trace-Zeitleiste ist derzeit nicht verfügbar."))}).finally(()=>{_&&u(!1)}),()=>{_=!1}},[d,e]),e?t===null||t.workspaceId!==e?r.jsxs("section",{className:"mesh-trace mesh-trace--empty","aria-live":"polite",children:[r.jsx("h2",{children:"Agent Mesh"}),r.jsx("p",{children:"Die Core-Trace-Projektion ist für diesen Workspace noch nicht verfügbar."}),r.jsx("p",{className:"mesh-trace__muted",children:"Es werden weder Registry-Einträge noch historische Aktivitätszähler als Ersatz angezeigt."})]}):t.nodes.length===0&&t.edges.length===0?r.jsxs("section",{className:"mesh-trace mesh-trace--empty","aria-live":"polite",children:[r.jsx("h2",{children:"Agent Mesh"}),r.jsx("p",{children:"Für diesen Workspace wurde noch keine trace-gestützte Arbeit beobachtet."}),r.jsx("p",{className:"mesh-trace__muted",children:"Keine simulierten Agenten, keine Roster-Fallbacks und kein daraus abgeleiteter Prozessstatus."})]}):r.jsxs("section",{className:"mesh-trace","aria-label":"Trace-backed Agent Mesh",children:[r.jsxs("header",{className:"mesh-trace__header",children:[r.jsxs("div",{children:[r.jsxs("h2",{children:["Agent Mesh ",r.jsx("span",{children:"Trace-backed"})]}),r.jsx("p",{children:"Jeder Knoten und jede Kante stammt aus einem autoritätsbestätigten Trace-Ereignis."})]}),r.jsxs("dl",{className:"mesh-trace__totals",children:[r.jsxs("div",{children:[r.jsx("dt",{children:"Knoten"}),r.jsx("dd",{children:t.totals.nodes??t.nodes.length})]}),r.jsxs("div",{children:[r.jsx("dt",{children:"Kanten"}),r.jsx("dd",{children:t.totals.edges??t.edges.length})]}),r.jsxs("div",{children:[r.jsx("dt",{children:"ohne Startbeweis"}),r.jsx("dd",{children:t.unprovenWorkers.length})]})]})]}),t.unprovenWorkers.length>0&&r.jsxs("aside",{className:"mesh-trace__notice","aria-label":"Unproven workers",children:[r.jsx("strong",{children:"Unbelegte Worker werden nicht als laufend angezeigt."}),r.jsx("ul",{children:t.unprovenWorkers.map(x=>r.jsxs("li",{children:[r.jsx("code",{children:x.workerId}),x.taskId?r.jsxs(r.Fragment,{children:[" · Aufgabe ",r.jsx("code",{children:x.taskId})]}):""," — ",x.reason]},x.workerId))})]}),r.jsxs("div",{className:"mesh-trace__grid",children:[r.jsxs("section",{className:"mesh-trace__panel","aria-label":"Trace nodes",children:[r.jsxs("h3",{children:["Knoten ",r.jsx("span",{children:t.nodes.length})]}),r.jsx("ol",{className:"mesh-trace__nodes",children:t.nodes.map(x=>{const _=hf(x),m=x.id===a;return r.jsx("li",{children:r.jsxs("button",{type:"button",className:m?"mesh-trace__node is-selected":"mesh-trace__node",onClick:()=>s(x.id),"aria-pressed":m,children:[r.jsx("span",{className:"mesh-trace__kind",children:N0[x.kind]}),r.jsx("span",{className:"mesh-trace__label",title:x.label,children:x.label}),r.jsxs("span",{className:"mesh-trace__events",children:[x.eventCount," Ereignis",x.eventCount===1?"":"se"]}),r.jsx("span",{className:"mesh-trace__provenance",title:"Ereignis-Provenienz, nicht aktueller Prozessstatus",children:C0[x.provenance]}),_&&r.jsx("span",{className:"mesh-trace__proof",children:_})]})},x.id)})})]}),r.jsxs("section",{className:"mesh-trace__panel","aria-label":"Trace edges",children:[r.jsxs("h3",{children:["Kanten ",r.jsx("span",{children:t.edges.length})]}),r.jsx("ol",{className:"mesh-trace__edges",children:t.edges.map(x=>r.jsxs("li",{children:[r.jsx("span",{className:"mesh-trace__edge-kind",children:x.kind}),r.jsx("span",{title:x.from,children:R0(t.nodes,x.from)}),r.jsx("span",{"aria-hidden":"true",children:"→"}),r.jsx("span",{title:x.to,children:R0(t.nodes,x.to)}),r.jsxs("small",{children:[x.count," Ereignis",x.count===1?"":"se"," · Belege: ",x.evidence.join(", ")]})]},x.id))})]})]}),d&&r.jsxs("aside",{className:"mesh-trace__detail","aria-label":"Details for "+d.label,children:[r.jsxs("div",{className:"mesh-trace__detail-head",children:[r.jsxs("div",{children:[r.jsx("span",{className:"mesh-trace__kind",children:N0[d.kind]}),r.jsx("h3",{children:d.label})]}),r.jsx("button",{type:"button",onClick:()=>s(null),"aria-label":"Detailansicht schließen",children:"×"})]}),r.jsxs("dl",{children:[r.jsxs("div",{children:[r.jsx("dt",{children:"Erstmals"}),r.jsx("dd",{children:df(d.firstSeen)})]}),r.jsxs("div",{children:[r.jsx("dt",{children:"Zuletzt"}),r.jsx("dd",{children:df(d.lastSeen)})]}),r.jsxs("div",{children:[r.jsx("dt",{children:"Provenienz"}),r.jsx("dd",{children:C0[d.provenance]})]}),hf(d)&&r.jsxs("div",{children:[r.jsx("dt",{children:"Worker-Beweis"}),r.jsx("dd",{children:hf(d)})]}),Object.entries(d.detail).map(([x,_])=>r.jsxs("div",{children:[r.jsx("dt",{children:x}),r.jsx("dd",{children:_??"—"})]},x))]}),d.kind==="file"&&n&&r.jsx("button",{type:"button",className:"mesh-trace__source",onClick:()=>n(d.label),children:"Datei im Source-View öffnen"}),r.jsxs("section",{className:"mesh-trace__timeline","aria-label":"Trace timeline",children:[r.jsx("h4",{children:"Beobachtete Ereignisse"}),c&&r.jsx("p",{children:"Lade Trace-Ereignisse …"}),h&&r.jsx("p",{role:"status",children:h}),!c&&!h&&l.length===0&&r.jsx("p",{children:"Für diesen Knotentyp gibt es keine gefilterte Zeitleiste."}),r.jsx("ol",{children:l.map(x=>r.jsxs("li",{children:[r.jsx("code",{children:x.type})," ",r.jsx("time",{dateTime:x.occurredAt,children:df(x.occurredAt)}),r.jsx("span",{children:x.summary})]},x.eventId))})]})]})]}):r.jsxs("section",{className:"mesh-trace mesh-trace--empty","aria-live":"polite",children:[r.jsx("h2",{children:"Agent Mesh"}),r.jsx("p",{children:"Wähle einen registrierten Workspace. Ohne Workspace kann keine Trace-Projektion behauptet werden."})]})}function v2({tasks:t,depth:e}){return r.jsxs("div",{className:"queue",children:[r.jsx("div",{className:"queue__intro",style:{padding:"12px 16px",background:"var(--panel)",borderBottom:"1px solid var(--line)",fontSize:"13px",color:"var(--muted)"},children:"Warteschlange für Workspace-Aufgaben — freie Agenten beanspruchen Aufgaben selbstständig (First-In-First-Out)."}),t.length===0?r.jsxs("div",{className:"brain-empty",style:{padding:"48px 24px",textAlign:"center",color:"var(--muted)"},children:[r.jsx("p",{style:{margin:0,fontSize:"14px"},children:"Keine Aufgaben in der Warteschlange."}),r.jsx("small",{style:{color:"var(--faint)",display:"block",marginTop:"6px"},children:"Nichts wartet und nichts wird erfunden. Neue Aufgaben erscheinen hier automatisch."})]}):r.jsxs(r.Fragment,{children:[r.jsxs("div",{className:"queue__figures",children:[r.jsx(su,{value:e,label:"WARTEND",tone:e>8?"hot":void 0}),r.jsx(su,{value:t.filter(n=>n.state==="claimed").length,label:"IN ARBEIT"}),r.jsx(su,{value:t.filter(n=>n.state==="delivered").length,label:"GELIEFERT"}),r.jsx(su,{value:t.filter(n=>n.state==="claimed"&&n.stale).length,label:"STILL",tone:t.some(n=>n.stale)?"hot":void 0})]}),r.jsx("ol",{className:"queue__list",children:t.map(n=>r.jsxs("li",{className:`queue__row queue__row--${n.state}`,children:[r.jsx("span",{className:"queue__state",children:_2[n.state]??n.state}),r.jsx("span",{className:"queue__title",title:n.title,children:n.title}),r.jsx("span",{className:"queue__holder",children:n.claimed_by?`Wird bearbeitet von Agent: ${n.claimed_by}`:n.addressed_to?`Zugewiesen an: Agent ${n.addressed_to}`:"Frei für den nächsten verfügbaren Agenten"}),n.stale&&r.jsx("span",{className:"queue__stale",title:"Keine Regung seit dem Claim. PlugBrain meldet das nur — es beendet keinen Claim.",children:"still"}),n.delivered_path&&r.jsxs("span",{className:"queue__path",title:`Geliefert nach: ${n.delivered_path}`,children:["Ergebnis: ",n.delivered_path]})]},n.id))})]})]})}const _2={pending:"WARTET",claimed:"IN ARBEIT",delivered:"GELIEFERT",cancelled:"ABGEBROCHEN"};function su({value:t,label:e,tone:n}){return r.jsxs("div",{className:`queue__figure${n==="hot"?" queue__figure--hot":""}`,children:[r.jsx("strong",{children:t}),r.jsx("span",{children:e})]})}const b2=8e3;function y2(t,e=b2){return new Promise((n,i)=>{const a=globalThis.setTimeout(()=>{i(new Error(`Brain-Dateiabruf hat nach ${e/1e3} Sekunden nicht geantwortet. Bitte nach dem Indexlauf erneut versuchen.`))},e);t.then(s=>{globalThis.clearTimeout(a),n(s)},s=>{globalThis.clearTimeout(a),i(s)})})}function D0({workspaceId:t,path:e,highlightLine:n,onClose:i,onNavigateFile:a}){const[s,l]=P.useState(!0),[o,c]=P.useState(null),[u,h]=P.useState(null),[g,d]=P.useState(null),[p,x]=P.useState([]),_=P.useRef(null);if(P.useEffect(()=>{let y=!0;return l(!0),c(null),h(null),d(null),x([]),y2(A1(t,e)).then(b=>{y&&(c(b),l(!1))}).catch(b=>{y&&(c({ok:!1,path:e,content:"",bytes:0,lang:null,error:String((b==null?void 0:b.message)??b)}),l(!1))}),rS(t).then(b=>{y&&h(b)}).catch(()=>{}),S1(t,e).then(b=>{y&&d(b)}).catch(()=>{}),C1(t,e).then(b=>{y&&x(b)}).catch(()=>{}),()=>{y=!1}},[t,e]),P.useEffect(()=>{!s&&_.current&&_.current.scrollIntoView({behavior:"smooth",block:"center"})},[s,n]),s)return r.jsxs("div",{className:"source-container source-container--loading",children:[r.jsx("div",{className:"source-spinner"}),r.jsxs("p",{children:["Lade Dateiinhalt aus dem Brain (",e,") …"]})]});if(!o||!o.ok)return r.jsxs("div",{className:"source-container source-container--error",role:"alert",children:[r.jsxs("div",{className:"source-header",children:[r.jsx("span",{className:"source-header__path mono",children:e}),i&&r.jsx("button",{type:"button",className:"source-close-btn",onClick:i,title:"Schließen",children:"✕"})]}),r.jsxs("div",{className:"source-error-box",children:[r.jsx("div",{className:"source-error-icon","aria-hidden":"true",children:"!"}),r.jsx("h3",{children:"Fehler beim Laden der Datei"}),r.jsx("p",{className:"source-error-msg",children:(o==null?void 0:o.error)||"Die Datei existiert nicht im Workspace oder der Pfad ist ungültig."}),r.jsxs("div",{className:"source-error-details mono",children:["Workspace: ",t,r.jsx("br",{}),"Pfad: ",e]})]})]});const m=o.content.split(/\r?\n/),f=m.length,v=u!=null&&u.head?u.head.slice(0,8):null;return r.jsxs("div",{className:"source-container",children:[r.jsxs("div",{className:"source-header",children:[r.jsxs("div",{className:"source-header__meta",children:[r.jsx("span",{className:"source-header__path mono",title:o.path,children:o.path}),o.lang&&r.jsx("span",{className:"source-badge source-badge--lang",children:o.lang}),r.jsxs("span",{className:"source-badge source-badge--info",children:[f," Zeilen · ",o.bytes," B"]}),v&&r.jsxs("span",{className:"source-badge source-badge--git",title:`Git Revision: ${u==null?void 0:u.head}`,children:["git: ",v," (",(u==null?void 0:u.branch)??"detached",")"]}),(g==null?void 0:g.owner)&&r.jsxs("span",{className:"source-badge source-badge--agent",style:{borderColor:g.owner.color},title:`Zuletzt geändert durch ${g.owner.name} (${g.owner.at})`,children:[r.jsx("i",{style:{background:g.owner.color}}),g.owner.name]})]}),r.jsxs("div",{className:"source-header__actions",children:[n&&r.jsxs("span",{className:"source-badge source-badge--highlight",children:["Fokus: Zeile ",n]}),i&&r.jsx("button",{type:"button",className:"source-close-btn",onClick:i,title:"Quellansicht schließen",children:"✕"})]})]}),r.jsxs("div",{className:"source-body",children:[r.jsx("div",{className:"source-code-view",children:r.jsx("table",{className:"source-table",children:r.jsx("tbody",{children:m.map((y,b)=>{const T=b+1,M=n===T;return r.jsxs("tr",{ref:M?_:void 0,className:`source-line-row ${M?"source-line-row--highlight":""}`,children:[r.jsx("td",{className:"source-line-num mono","data-line":T,children:T}),r.jsx("td",{className:"source-line-code mono",children:r.jsx("pre",{children:y||" "})})]},T)})})})}),p.length>0&&r.jsxs("div",{className:"source-backlinks",style:{padding:"12px 16px",borderTop:"1px solid var(--line)",background:"rgba(255,255,255,0.02)"},children:[r.jsxs("div",{style:{fontSize:"12px",fontWeight:600,color:"var(--accent)",marginBottom:"6px"},children:["← Rückverweise / Backlinks (",p.length,")"]}),r.jsx("div",{style:{display:"flex",flexWrap:"wrap",gap:"8px"},children:p.map((y,b)=>r.jsxs("div",{className:"search-hit-card",style:{padding:"6px 10px",fontSize:"11px",cursor:"pointer"},onClick:()=>a==null?void 0:a(y.path,y.line),title:`Zeile ${y.line} in ${y.path}`,children:[r.jsx("span",{className:"mono",style:{color:"var(--accent)"},children:y.path}),r.jsxs("span",{style:{color:"var(--faint)",marginLeft:"6px"},children:[":",y.line]}),y.alias&&r.jsxs("span",{style:{marginLeft:"4px",fontStyle:"italic"},children:["(",y.alias,")"]})]},b))})]})]})]})}function ot({path:t,label:e}){return r.jsxs("svg",{className:"pb-icon",viewBox:"0 0 16 16",fill:"none",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":e?void 0:!0,role:e?"img":void 0,children:[e&&r.jsx("title",{children:e}),r.jsx("path",{d:t})]})}const st={search:"M7 11.5a4.5 4.5 0 1 0 0-9 4.5 4.5 0 0 0 0 9ZM10.5 10.5 14 14",fit:"M2.5 6V2.5H6M10 2.5h3.5V6M13.5 10v3.5H10M6 13.5H2.5V10",minus:"M3.5 8h9",plus:"M8 3.5v9M3.5 8h9",play:"M5 3.5v9l7-4.5-7-4.5Z",pause:"M5.5 3.5v9M10.5 3.5v9",close:"M4 4l8 8M12 4l-8 8",open:"M9 3h4v4M13 3 7.5 8.5M11.5 9.5V13H3V4.5h3.5",centre:"M8 1.5v3M8 11.5v3M1.5 8h3M11.5 8h3M8 10a2 2 0 1 0 0-4 2 2 0 0 0 0 4Z",chevron:"M4.5 6.5 8 10l3.5-3.5",keyboard:"M2 4.5h12v7H2zM4.5 7h.01M7 7h.01M9.5 7h.01M12 7h.01M5 9.5h6",folder:"M2 4.5h4l1.5 1.5H14v6.5H2z",refresh:"M13 8a5 5 0 1 1-1.5-3.5M13 2.5v3h-3",burger:"M2 4h12M2 8h12M2 12h12",pencil:"M11 2.5 13.5 5 5 13.5H2.5V11L11 2.5Z",notes:"M3 2.5h10v11H3zM5 5.5h6M5 8.5h6M5 11.5h4",gear:"M6.7 1.5h2.6l.4 1.8a5.6 5.6 0 0 1 1.4.8l1.7-.8 1.8 1.8-.8 1.7c.3.4.6.9.8 1.4l1.8.4v2.6l-1.8.4a5.6 5.6 0 0 1-.8 1.4l.8 1.7-1.8 1.8-1.7-.8a5.6 5.6 0 0 1-1.4.8l-.4 1.8H6.7l-.4-1.8a5.6 5.6 0 0 1-1.4-.8l-1.7.8-1.8-1.8.8-1.7a5.6 5.6 0 0 1-.8-1.4l-1.8-.4V6.7l1.8-.4a5.6 5.6 0 0 1 .8-1.4l-.8-1.7 1.8-1.8 1.7.8a5.6 5.6 0 0 1 1.4-.8l.4-1.8ZM8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5Z",info:"M8 2a6 6 0 1 0 0 12 6 6 0 0 0 0-12ZM8 7.5v3M8 5v.01",file:"M3.5 1.5h6L13 5v9.5H3.5zM9.5 1.5V5H13",code:"M5.5 4.5 2 8l3.5 3.5M10.5 4.5 14 8l-3.5 3.5"};function S2(t){return!t||typeof t!="string"?"":t.replace(/\\/g,"/")}function M2(t){const e={name:"",path:"",isDir:!0,children:new Map};if(!Array.isArray(t))return e;for(const n of t){if(!n||typeof n.path!="string")continue;const i=n.path.split(/[\\/]/).filter(Boolean);if(i.length===0)continue;let a=e;for(let s=0;s<i.length;s++){const l=i[s],o=s===i.length-1,c=i.slice(0,s+1).join("/");if(o)a.children.set(l,{name:l,path:c,isDir:!1,children:new Map,file:{...n,path:c}});else{let u=a.children.get(l);u||(u={name:l,path:c,isDir:!0,children:new Map},a.children.set(l,u)),a=u}}}return e}function E2({workspaceName:t,files:e,activePath:n,onSelectFile:i}){const[a,s]=P.useState(""),[l,o]=P.useState(new Set),c=P.useMemo(()=>M2(e),[e]),u=P.useMemo(()=>{const m=[];function f(v){v.isDir&&v.path&&m.push(v.path);for(const y of v.children.values())y.isDir&&f(y)}return f(c),m},[c]),h=m=>{o(f=>{const v=new Set(f);return v.has(m)?v.delete(m):v.add(m),v})},g=()=>o(new Set),d=()=>o(new Set(u)),p=S2(n);function x(m,f){if(!f)return!0;const v=f.toLowerCase();if(!m.isDir)return m.name.toLowerCase().includes(v)||m.path.toLowerCase().includes(v);if(m.name&&m.name.toLowerCase().includes(v))return!0;for(const y of m.children.values())if(x(y,f))return!0;return!1}const _=(m,f=0)=>{var y,b,T;if(m.isDir){const M=l.has(m.path),A=Array.from(m.children.values()).sort((N,D)=>N.isDir!==D.isDir?N.isDir?-1:1:N.name.localeCompare(D.name)),S=a?A.filter(N=>x(N,a)):A;return a&&S.length===0&&(!m.name||!m.name.toLowerCase().includes(a.toLowerCase()))?null:r.jsxs("div",{className:"tree-dir-group",children:[m.name&&r.jsxs("div",{className:`tree-item tree-item--dir ${f===0?"tree-item--root":""}`,style:{paddingLeft:`${f*14+10}px`},onClick:()=>h(m.path),role:"treeitem","aria-expanded":!M,children:[r.jsx("span",{className:"tree-chevron",children:M?"▶":"▼"}),r.jsx("span",{className:"tree-icon",children:r.jsx(ot,{path:st.folder})}),r.jsx("span",{className:"tree-label",children:m.name}),r.jsx("span",{className:"tree-badge tree-badge--count",children:m.children.size})]}),(!M||a)&&r.jsx("div",{className:"tree-dir-children",children:S.map(N=>_(N,m.name?f+1:f))})]},m.path||"root")}const v=p!==""&&p===m.path;return a&&!m.path.toLowerCase().includes(a.toLowerCase())?null:r.jsxs("div",{className:`tree-item tree-item--file ${v?"tree-item--active":""}`,style:{paddingLeft:`${f*14+22}px`},onClick:()=>i(m.path),title:m.path,role:"treeitem",children:[r.jsx("span",{className:"tree-dot","aria-hidden":"true"}),r.jsx("span",{className:"tree-label mono",children:m.name}),((y=m.file)==null?void 0:y.lang)&&r.jsx("span",{className:"tree-badge tree-badge--lang",children:m.file.lang}),((b=m.file)==null?void 0:b.loc)!==void 0&&r.jsxs("span",{className:"tree-badge tree-badge--loc",children:[m.file.loc," L"]}),((T=m.file)==null?void 0:T.agent)&&r.jsx("span",{className:"tree-agent-dot",style:{background:m.file.agent.color},title:`Owner: ${m.file.agent.name}`})]},m.path)};return r.jsxs("div",{className:"explorer-view",children:[r.jsxs("div",{className:"explorer-header",children:[r.jsxs("div",{className:"explorer-title",children:[r.jsx(ot,{path:st.folder}),r.jsxs("strong",{children:[t||"Workspace"," Explorer"]})]}),r.jsx("div",{className:"explorer-stats",children:r.jsxs("span",{children:[e.length," Dateien aus Brain"]})})]}),r.jsxs("div",{className:"explorer-search",children:[r.jsx("input",{type:"text",placeholder:"Dateibaum filtern …",value:a,onChange:m=>s(m.target.value),className:"explorer-search__input"}),a&&r.jsx("button",{type:"button",className:"explorer-search__clear",onClick:()=>s(""),children:"✕"})]}),u.length>0&&r.jsxs("div",{className:"tree-actions",children:[r.jsx("button",{type:"button",className:"tree-action-btn",onClick:g,title:"Alle Ordner aufklappen",children:"Alle aufklappen"}),r.jsx("button",{type:"button",className:"tree-action-btn",onClick:d,title:"Alle Ordner einklappen",children:"Alle einklappen"})]}),r.jsx("div",{className:"explorer-tree",children:e.length===0?r.jsx("div",{className:"explorer-empty",children:"Keine Dateien im Snapshot vorhanden."}):_(c)})]})}function T2({workspaceId:t,onSelectHit:e}){const[n,i]=P.useState(()=>{const M=new URLSearchParams(window.location.search).get("mode");return M==="code"||M==="notes"||M==="prose"?M:"all"}),[a,s]=P.useState(()=>new URLSearchParams(window.location.search).get("q")||""),[l,o]=P.useState([]),[c,u]=P.useState([]),[h,g]=P.useState(null),[d,p]=P.useState(!1),[x,_]=P.useState(""),[m,f]=P.useState(""),[v,y]=P.useState(""),b=()=>{let M=`# Suchtreffer-Kontext: "${m}" +Workspace: ${t} + +`;if(n==="notes"){M+=`Gefundene Notizen (${c.length}): +`;for(const A of c)M+=`- **${A.title}** (\`${A.path}\`)${A.stand?` [${A.stand}]`:""} +`}else{M+=`Gefundene Treffer (${l.length}): +`;for(const A of l)M+=`- **${A.name}** (${A.kind}) — \`${A.path}${A.line?`:${A.line}`:""}\` +`,A.snippet&&(M+=` > ${A.snippet.replace(/\n/g," ")} +`)}navigator.clipboard.writeText(M).then(()=>{y("Suchergebnisse als Kontext kopiert (bereit für ChatGPT, Claude & Co.)"),setTimeout(()=>y(""),3e3)}).catch(()=>{y("Fehler beim Kopieren in die Zwischenablage")})},T=async(M,A,S)=>{M&&M.preventDefault();const N=S??n,D=(A??a).trim();if(!D)return;p(!0),_(""),g(null);const U=performance.now();try{if(N==="all"){const[I,Y]=await Promise.allSettled([pp(t,D),mp(t,D)]);if(I.status==="rejected"&&Y.status==="rejected")throw new Error("Code- und Notizsuche sind nicht erreichbar. Bitte Verbindung zum Brain prüfen.");I.status==="rejected"?_("Code-Suche nicht erreichbar. Angezeigt werden nur bestätigte Notiztreffer."):Y.status==="rejected"&&_("Notizsuche nicht erreichbar. Angezeigt werden nur bestätigte Codetreffer.");const k=[];if(I.status==="fulfilled")for(const B of I.value)k.push({id:`code-${B.path}-${B.name}-${B.line??0}`,type:"code",name:B.name,kind:B.kind,path:B.path,line:B.line});if(Y.status==="fulfilled")for(const B of Y.value.hits||[])k.push({id:`note-${B.path}-${B.line??0}`,type:"note",name:B.title,kind:"Notiz",path:B.path,line:B.line,snippet:B.snippet});o(k),u([])}else if(N==="code"){const I=await pp(t,D);o(I.map((Y,k)=>({id:`code-${Y.path}-${Y.name}-${Y.line??k}`,type:"code",name:Y.name,kind:Y.kind,path:Y.path,line:Y.line}))),u([])}else if(N==="prose"){const I=await mp(t,D);o((I.hits||[]).map((Y,k)=>({id:`note-${Y.path}-${Y.line??k}`,type:"note",name:Y.title,kind:"Notiz",path:Y.path,line:Y.line,snippet:Y.snippet}))),u([])}else{const I=await N1(t,D);u(I.notes||[]),o([])}g(Math.round(performance.now()-U)),f(D)}catch(I){_((I==null?void 0:I.message)||`Fehler bei der Suche (${N})`),o([]),u([]),f("")}finally{p(!1)}};return P.useEffect(()=>{const M=new URLSearchParams(window.location.search).get("q");M&&t&&T(void 0,M,n)},[t]),r.jsxs("div",{className:"search-view",children:[r.jsxs("div",{className:"search-view__header",children:[r.jsxs("div",{className:"search-view__title",children:[r.jsx(ot,{path:st.search}),r.jsx("strong",{children:n==="all"?"Suche über Notizen & Code":n==="code"?"Code- & Symbolsuche":n==="prose"?"Notiz-Volltextsuche":"Notizen- & Property-Filter"}),r.jsx("span",{className:"search-view__endpoint mono",children:n==="all"?"Notizen + Code":n==="code"?"/api/agent/search":n==="prose"?"/api/notes/search":"/api/notes/query"})]}),r.jsxs("div",{style:{display:"flex",gap:"6px",marginTop:"8px"},children:[r.jsx("button",{type:"button",className:`tb ${n==="all"?"on":""}`,onClick:()=>{i("all"),a.trim()&&T(void 0,a,"all")},children:"Alle"}),r.jsx("button",{type:"button",className:`tb ${n==="code"?"on":""}`,onClick:()=>{i("code"),a.trim()&&T(void 0,a,"code")},children:"Code & Symbole"}),r.jsx("button",{type:"button",className:`tb ${n==="prose"?"on":""}`,onClick:()=>{i("prose"),a.trim()&&T(void 0,a,"prose")},children:"Notizen-Volltext"}),r.jsx("button",{type:"button",className:`tb ${n==="notes"?"on":""}`,onClick:()=>{i("notes");const M=a.trim()||"typ=gate UND stand=offen";s(M),T(void 0,M,"notes")},children:"Properties (Bases)"})]})]}),r.jsx("form",{className:"search-form",onSubmit:T,style:{marginTop:"10px"},children:r.jsxs("div",{className:"search-input-group",children:[r.jsx("input",{type:"search",className:"search-input",placeholder:n==="all"?"Symbol, Datei oder Notiztext (z. B. workspaceIdFor, Brain) …":n==="code"?"Symbol, Variable, Klasse, Datei (z. B. authKey) …":n==="prose"?"Satz oder Stichwörter aus dem Notiztext …":"Bases-Filter: typ=gate UND stand=offen …",value:a,onChange:M=>s(M.target.value),autoFocus:!0}),r.jsx("button",{type:"submit",className:"search-submit-btn pb-button pb-button--primary",disabled:d||!a.trim(),children:d?"Suche …":"Suchen"})]})}),x&&r.jsx("div",{className:"search-error-alert",role:"alert",children:x}),r.jsxs("div",{className:"search-results",children:[m&&r.jsxs("div",{className:"search-results-summary",style:{display:"flex",alignItems:"center",justifyContent:"space-between",flexWrap:"wrap",gap:"8px"},children:[r.jsxs("div",{children:[n==="notes"?c.length===0?`Keine Notizen entsprechen dem Filter "${m}"`:`${c.length} Notiz(en) gefunden für "${m}":`:l.length===0?x?`Keine bestätigten Treffer für "${m}"; Suche unvollständig`:`Keine Treffer für "${m}" im Brain-Index`:`${l.length} Treffer für "${m}":`,h!==null&&r.jsxs("span",{className:"search-results-time mono",style:{marginLeft:"8px",opacity:.7},children:[h," ms"]})]}),(n==="notes"&&c.length>0||n!=="notes"&&l.length>0)&&r.jsx("button",{type:"button",className:"pb-button pb-button--secondary",onClick:()=>b(),title:"Alle Suchtreffer als Markdown-Kontext kopieren",style:{fontSize:"11px",padding:"4px 10px"},children:"Als Kontext kopieren"})]}),v&&r.jsx("div",{style:{margin:"8px 0",padding:"6px 12px",background:"var(--accent-soft)",border:"1px solid var(--pos)",borderRadius:"var(--r)",fontSize:"12px",color:"var(--ink)",fontFamily:"var(--mono)"},children:v}),n==="notes"?r.jsx("div",{className:"search-hits-list",children:c.map(M=>r.jsxs("button",{type:"button",className:"search-hit-card",onClick:()=>e(M.path),children:[r.jsxs("span",{className:"search-hit-card__head",children:[r.jsx("span",{className:"search-hit-name",children:M.title}),M.typ&&r.jsx("span",{className:"search-hit-kind search-hit-kind--class",children:M.typ}),M.stand&&r.jsx("span",{className:"source-badge",style:{fontSize:"11px",marginLeft:"6px"},children:M.stand})]}),r.jsx("span",{className:"search-hit-path mono",title:M.path,style:{marginTop:"4px"},children:M.path}),(M.inLinks!==void 0||M.outLinks!==void 0)&&r.jsxs("span",{style:{display:"block",fontSize:"11px",color:"var(--faint)",marginTop:"4px"},children:["Verlinkungen: → ",M.outLinks??0," ausgehend · ← ",M.inLinks??0," Rückverweise"]})]},M.path))}):r.jsx("div",{className:"search-hits-list",children:l.map(M=>r.jsxs("button",{type:"button",className:"search-hit-card",onClick:()=>e(M.path,M.line),children:[r.jsxs("span",{className:"search-hit-card__head",children:[r.jsx("span",{className:"search-hit-name mono",children:M.name}),r.jsx("span",{className:`search-hit-kind search-hit-kind--${M.kind.toLowerCase()}`,children:M.kind}),M.line!==null&&r.jsxs("span",{className:"search-hit-line mono",children:["Zeile ",M.line]})]}),M.snippet&&r.jsx("span",{className:"search-hit-snippet",children:M.snippet}),r.jsx("span",{className:"search-hit-path mono",title:M.path,style:{marginTop:"4px"},children:M.path})]},M.id))})]})]})}function A2({workspaceId:t,focus:e,onOpenNote:n}){const[i,a]=P.useState(1),[s,l]=P.useState(120),[o,c]=P.useState(null),[u,h]=P.useState(0),[g,d]=P.useState("");P.useEffect(()=>{let v=!0;return d(""),D1(t,{focus:e,depth:i,limit:s}).then(y=>{v&&(c(y),h(0))}).catch(y=>{v&&(c(null),d(y instanceof Error?y.message:String(y)))}),()=>{v=!1}},[t,e,i,s]);const p=P.useMemo(()=>((o==null?void 0:o.nodes)??[]).slice().sort((v,y)=>Number(y.focus)-Number(v.focus)||y.inLinks+y.outLinks-(v.inLinks+v.outLinks)||v.path.localeCompare(y.path)),[o]),x=p[u]??null,_=P.useMemo(()=>!x||!o?[]:o.edges.filter(v=>v.source===x.id||v.target===x.id).map(v=>p.find(y=>y.id===(v.source===x.id?v.target:v.source))).filter(v=>v!==void 0),[x,o,p]),m=v=>h(y=>Math.max(0,Math.min(p.length-1,y+v))),f=v=>{p.length&&(v.key==="ArrowDown"||v.key==="ArrowRight"?(v.preventDefault(),m(1)):v.key==="ArrowUp"||v.key==="ArrowLeft"?(v.preventDefault(),m(-1)):v.key==="Home"?(v.preventDefault(),h(0)):v.key==="End"?(v.preventDefault(),h(p.length-1)):v.key==="Enter"&&x&&(v.preventDefault(),n(x.path)))};return r.jsxs("section",{className:"knowledge-graph","aria-label":"Wissensgraph",onKeyDown:f,tabIndex:0,children:[r.jsxs("header",{className:"knowledge-graph__head",children:[r.jsxs("div",{children:[r.jsx("h2",{children:"Wissensgraph"}),r.jsx("p",{children:"Aus den indizierten Wiki-Links; keine berechneten Ersatzknoten."})]}),r.jsxs("label",{children:["Tiefe ",r.jsxs("select",{value:i,onChange:v=>a(Number(v.target.value)),children:[r.jsx("option",{value:0,children:"Nur Fokus"}),r.jsx("option",{value:1,children:"1 Hop"}),r.jsx("option",{value:2,children:"2 Hops"}),r.jsx("option",{value:3,children:"3 Hops"})]})]}),r.jsxs("label",{children:["LOD ",r.jsxs("select",{value:s,onChange:v=>l(Number(v.target.value)),children:[r.jsx("option",{value:60,children:"Kompakt (60)"}),r.jsx("option",{value:120,children:"Standard (120)"}),r.jsx("option",{value:300,children:"Detail (300)"})]})]})]}),g&&r.jsx("p",{className:"notes-conflict",role:"alert",children:g}),!g&&o&&r.jsxs("p",{className:"knowledge-graph__coverage",role:"status",children:[o.coverage.selected," von ",o.coverage.notesInScope," Notizen · ",o.edges.length," Verweise",o.coverage.truncated?" · für diese LOD begrenzt":""]}),r.jsxs("div",{className:"knowledge-graph__body",children:[r.jsxs("ol",{className:"knowledge-graph__list","aria-label":"Graphknoten, Pfeiltasten wählen, Eingabe öffnen",children:[p.map((v,y)=>{var b;return r.jsx("li",{children:r.jsxs("button",{type:"button",className:y===u?"on":"",onFocus:()=>h(y),onClick:()=>n(v.path),children:[r.jsx("i",{style:{background:(b=o==null?void 0:o.groups.find(T=>T.name===v.group))==null?void 0:b.color}}),r.jsxs("span",{children:[r.jsx("strong",{children:v.title}),r.jsx("small",{children:v.path})]}),r.jsx("b",{children:v.inLinks+v.outLinks})]})},v.id)}),!p.length&&r.jsx("li",{className:"knowledge-graph__empty",children:"Keine Knoten für diese Auswahl."})]}),r.jsx("aside",{className:"knowledge-graph__inspector","aria-live":"polite",children:x?r.jsxs(r.Fragment,{children:[r.jsx("h3",{children:"Inspector"}),r.jsx("strong",{children:x.title}),r.jsx("code",{children:x.path}),r.jsxs("dl",{children:[r.jsxs("div",{children:[r.jsx("dt",{children:"Typ"}),r.jsx("dd",{children:x.properties.typ??"—"})]}),r.jsxs("div",{children:[r.jsx("dt",{children:"Status"}),r.jsx("dd",{children:x.properties.stand??"—"})]}),r.jsxs("div",{children:[r.jsx("dt",{children:"Tags"}),r.jsx("dd",{children:x.properties.tags.map(v=>`#${v}`).join(" ")||"—"})]}),r.jsxs("div",{children:[r.jsx("dt",{children:"Verweise"}),r.jsxs("dd",{children:[x.outLinks," hinaus · ",x.inLinks," herein"]})]})]}),r.jsx("button",{type:"button",className:"primary",onClick:()=>n(x.path),children:"Notiz öffnen"}),_.length>0&&r.jsxs("section",{children:[r.jsx("h4",{children:"Verknüpft"}),_.map(v=>r.jsx("button",{type:"button",onClick:()=>n(v.path),children:v.title},v.id))]})]}):r.jsx("p",{children:"Wähle einen Knoten aus der Liste."})})]})]})}const L0={note:{type:"note",title:"HINWEIS",color:"var(--info)",border:"var(--info)",bg:"color-mix(in srgb, var(--info) 8%, transparent)",glyph:"[i]"},important:{type:"important",title:"WICHTIG",color:"var(--accent)",border:"var(--accent)",bg:"var(--accent-soft)",glyph:"[!]"},warning:{type:"warning",title:"WARNUNG",color:"var(--warn)",border:"var(--warn)",bg:"color-mix(in srgb, var(--warn) 8%, transparent)",glyph:"[!]"},tip:{type:"tip",title:"TIPP",color:"var(--pos)",border:"var(--pos)",bg:"var(--accent-soft)",glyph:"[*]"},caution:{type:"caution",title:"ACHTUNG",color:"var(--neg)",border:"var(--neg)",bg:"color-mix(in srgb, var(--neg) 8%, transparent)",glyph:"[!]"}};function w2(t,e){const n=[];let i=0;const a=new Set(["import","export","from","default","const","let","var","function","return","if","else","for","while","do","switch","case","break","continue","try","catch","finally","throw","new","typeof","instanceof","async","await","yield","class","extends","interface","type","enum","implements","public","private","protected","readonly","static","as","is","keyof","in","of","void","any","never","unknown","null","undefined","true","false","super","this","debugger"]),s=new Set(["string","number","boolean","symbol","bigint","object","Array","Record","Promise","Map","Set","Date","RegExp","Error","Function","React","Node","HTMLElement","Partial","Required","Readonly"]);for(;i<t.length;){if(t.slice(i,i+2)==="//"||t[i]==="#"&&(i===0||/\s/.test(t[i-1]))){n.push({type:"comment",text:t.slice(i)});break}if(t[i]==='"'||t[i]==="'"||t[i]==="`"){const l=t[i];let o=i+1;for(;o<t.length;)if(t[o]==="\\")o+=2;else if(t[o]===l){o++;break}else o++;n.push({type:"string",text:t.slice(i,o)}),i=o;continue}if(/\d/.test(t[i])&&(i===0||/[^\w$]/.test(t[i-1]))){let l=i;for(;l<t.length&&/[\d.xXbBoOa-fA-F_]/.test(t[l]);)l++;n.push({type:"number",text:t.slice(i,l)}),i=l;continue}if(/[a-zA-Z_$]/.test(t[i])){let l=i;for(;l<t.length&&/[a-zA-Z0-9_$]/.test(t[l]);)l++;const o=t.slice(i,l);a.has(o)?n.push({type:"keyword",text:o}):s.has(o)||/^[A-Z][a-zA-Z0-9_$]*$/.test(o)?n.push({type:"type",text:o}):l<t.length&&t[l]==="("?n.push({type:"fn",text:o}):n.push({type:"text",text:o}),i=l;continue}n.push({type:"text",text:t[i]}),i++}return n}function N2({code:t,language:e}){const[n,i]=P.useState(!1),a=()=>{var l;(l=navigator.clipboard)==null||l.writeText(t).then(()=>{i(!0),setTimeout(()=>i(!1),2e3)}).catch(()=>{})},s=t.split(` +`);return r.jsxs("div",{className:"md-code-block",children:[r.jsxs("div",{className:"md-code-header",children:[r.jsx("span",{className:"md-code-lang",children:e||"text"}),r.jsx("button",{type:"button",className:"md-code-copy",onClick:a,children:n?"Kopiert":"Kopieren"})]}),r.jsx("pre",{className:"md-code-pre",children:r.jsx("code",{children:s.map((l,o)=>{const c=w2(l);return r.jsxs("div",{className:"md-code-line",children:[r.jsx("span",{className:"md-line-num",children:o+1}),r.jsxs("span",{className:"md-line-code",children:[c.map((u,h)=>r.jsx("span",{className:`md-tok md-tok--${u.type}`,children:u.text},h)),c.length===0&&" "]})]},o)})})})]})}function C2(t){return/^[a-zA-Z0-9_.-]+(?:\/[a-zA-Z0-9_.-]+)+\.[a-zA-Z0-9]+(?::\d+)?$/.test(t)||/^[a-zA-Z0-9_.-]+\.(?:ts|tsx|js|mjs|cjs|json|css|html|rs|go|py|sh|nsi|nsh)(?::\d+)?$/.test(t)}function Hn(t,e,n,i="inline"){var u;const a=[];let s=0,l=0;const o=/(\[\[([^\]|#]+)(?:#[^\]|]+)?(?:\|([^\]]+))?\]\])|(\[([^\]]+)\]\(([^)]+)\))|(`([^`]+)`)|(\*\*([^*]+)\*\*)|(\*([^*]+)\*)|(__([^_]+)__)|(_([^_]+)_)|(~~([^~]+)~~)/g;let c;for(;(c=o.exec(t))!==null;){c.index>s&&a.push(t.slice(s,c.index));const h=`${i}-${l++}`;if(c[1]){const g=c[2].trim(),d=((u=c[3])==null?void 0:u.trim())||g;a.push(r.jsxs("button",{type:"button",className:"md-wikilink",onClick:()=>e==null?void 0:e(g),title:`Notiz öffnen: ${g}`,children:[r.jsx("span",{className:"md-wikilink__brackets",children:"[["}),r.jsx("span",{className:"md-wikilink__text",children:d}),r.jsx("span",{className:"md-wikilink__brackets",children:"]]"}),r.jsx("span",{className:"md-wikilink__arrow","aria-hidden":"true",children:"↗"})]},h))}else if(c[4]){const g=c[5],d=c[6],p=d.startsWith("http://")||d.startsWith("https://")||d.startsWith("mailto:")||d.startsWith("#")||d.startsWith("/")||d.startsWith("./")||d.startsWith("../")||!d.includes(":"),x=d.startsWith("http://")||d.startsWith("https://");p?a.push(r.jsx("a",{href:d,className:"md-link",target:x?"_blank":void 0,rel:x?"noopener noreferrer":void 0,children:g},h)):a.push(r.jsx("span",{className:"md-link-blocked",children:g},h))}else if(c[7]){const g=c[8];C2(g)&&n?a.push(r.jsxs("button",{type:"button",className:"md-code-link",onClick:()=>n(g),title:`Quelle öffnen: ${g}`,children:[r.jsx("code",{children:g}),r.jsx("span",{className:"md-code-link__glyph","aria-hidden":"true",children:"↗"})]},h)):a.push(r.jsx("code",{className:"md-inline-code",children:g},h))}else c[9]?a.push(r.jsx("strong",{children:Hn(c[10],e,n,`${h}-b`)},h)):c[11]?a.push(r.jsx("em",{children:Hn(c[12],e,n,`${h}-i`)},h)):c[13]?a.push(r.jsx("strong",{children:Hn(c[14],e,n,`${h}-b2`)},h)):c[15]?a.push(r.jsx("em",{children:Hn(c[16],e,n,`${h}-i2`)},h)):c[17]&&a.push(r.jsx("del",{children:Hn(c[18],e,n,`${h}-s`)},h));s=c.index+c[0].length}return s<t.length&&a.push(t.slice(s)),a}function R2({content:t,onNavigateNote:e,onOpenSource:n,className:i=""}){const a=P.useMemo(()=>t?t.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/,""):"",[t]),s=P.useMemo(()=>{if(!a.trim())return[r.jsx("p",{className:"md-empty",children:"Leere Notiz"},"empty")];const l=a.split(/\r?\n/),o=[];let c=0;for(;c<l.length;){const u=l[c];if(!u.trim()){c++;continue}if(u.trim().startsWith("```")){const d=u.trim().slice(3).trim(),p=[];for(c++;c<l.length&&!l[c].trim().startsWith("```");)p.push(l[c]),c++;c<l.length&&c++,o.push(r.jsx(N2,{code:p.join(` +`),language:d},`code-${o.length}`));continue}if(u.trim().startsWith(">")){const d=[];for(;c<l.length&&l[c].trim().startsWith(">");)d.push(l[c].trim().replace(/^>\s?/,"")),c++;const x=(d[0]||"").match(/^\[!(note|important|warning|tip|caution)\]/i);if(x){const _=x[1].toLowerCase(),m=L0[_]||L0.note,f=d.slice(1).join(` +`);o.push(r.jsxs("div",{className:`md-callout md-callout--${m.type}`,style:{borderLeft:`4px solid ${m.border}`,backgroundColor:m.bg},children:[r.jsxs("div",{className:"md-callout-header",style:{color:m.color},children:[r.jsx("span",{className:"md-callout-glyph",children:m.glyph}),r.jsx("span",{className:"md-callout-title",children:m.title})]}),f&&r.jsx("div",{className:"md-callout-content",children:f.split(` +`).map((v,y)=>r.jsx("p",{children:Hn(v,e,n,`callout-${o.length}-${y}`)},y))})]},`callout-${o.length}`))}else o.push(r.jsx("blockquote",{className:"md-blockquote",children:d.map((_,m)=>r.jsx("p",{children:Hn(_,e,n,`bq-${o.length}-${m}`)},m))},`bq-${o.length}`));continue}if(u.includes("|")&&c+1<l.length&&l[c+1].includes("|")&&/[-:]+/.test(l[c+1])){const d=[];for(;c<l.length&&l[c].includes("|");)d.push(l[c]),c++;const p=v=>{let y=v.trim();return y.startsWith("|")&&(y=y.slice(1)),y.endsWith("|")&&(y=y.slice(0,-1)),y.split("|").map(b=>b.trim())},x=p(d[0]),m=p(d[1]).map(v=>{const y=v.startsWith(":"),b=v.endsWith(":");return y&&b?"center":b?"right":"left"}),f=d.slice(2).map(p);o.push(r.jsx("div",{className:"md-table-wrap",children:r.jsxs("table",{className:"md-table",children:[r.jsx("thead",{children:r.jsx("tr",{children:x.map((v,y)=>r.jsx("th",{style:{textAlign:m[y]||"left"},children:Hn(v,e,n,`th-${o.length}-${y}`)},y))})}),r.jsx("tbody",{children:f.map((v,y)=>r.jsx("tr",{children:v.map((b,T)=>r.jsx("td",{style:{textAlign:m[T]||"left"},children:Hn(b,e,n,`td-${o.length}-${y}-${T}`)},T))},y))})]})},`table-${o.length}`));continue}const h=u.match(/^(#{1,6})\s+(.*)$/);if(h){const d=h[1].length,p=h[2],x=Hn(p,e,n,`h-${o.length}`),_=`h-${o.length}`;switch(d){case 1:o.push(r.jsx("h1",{className:"md-heading md-heading--1",children:x},_));break;case 2:o.push(r.jsx("h2",{className:"md-heading md-heading--2",children:x},_));break;case 3:o.push(r.jsx("h3",{className:"md-heading md-heading--3",children:x},_));break;case 4:o.push(r.jsx("h4",{className:"md-heading md-heading--4",children:x},_));break;case 5:o.push(r.jsx("h5",{className:"md-heading md-heading--5",children:x},_));break;default:o.push(r.jsx("h6",{className:"md-heading md-heading--6",children:x},_));break}c++;continue}if(/^(\*{3,}|-{3,}|_{3,})$/.test(u.trim())){o.push(r.jsx("hr",{className:"md-hr"},`hr-${o.length}`)),c++;continue}if(/^\s*[-*]\s+\[([ xX])\]\s+(.*)$/.test(u)){const d=[];for(;c<l.length&&/^\s*[-*]\s+\[([ xX])\]\s+(.*)$/.test(l[c]);){const p=l[c].match(/^\s*[-*]\s+\[([ xX])\]\s+(.*)$/);d.push({checked:p[1].toLowerCase()==="x",text:p[2]}),c++}o.push(r.jsx("ul",{className:"md-checklist",children:d.map((p,x)=>r.jsxs("li",{className:`md-checklist-item ${p.checked?"is-checked":""}`,children:[r.jsx("input",{type:"checkbox",checked:p.checked,readOnly:!0,className:"md-checkbox"}),r.jsx("span",{className:"md-checklist-label",children:Hn(p.text,e,n,`check-${o.length}-${x}`)})]},x))},`checklist-${o.length}`));continue}if(/^\s*[-*+]\s+(.*)$/.test(u)){const d=[];for(;c<l.length&&/^\s*[-*+]\s+(.*)$/.test(l[c])&&!/^\s*[-*+]\s+\[[ xX]\]/.test(l[c]);){const p=l[c].match(/^\s*[-*+]\s+(.*)$/);d.push(p[1]),c++}o.push(r.jsx("ul",{className:"md-ul",children:d.map((p,x)=>r.jsx("li",{children:Hn(p,e,n,`ul-${o.length}-${x}`)},x))},`ul-${o.length}`));continue}if(/^\s*\d+\.\s+(.*)$/.test(u)){const d=[];for(;c<l.length&&/^\s*\d+\.\s+(.*)$/.test(l[c]);){const p=l[c].match(/^\s*\d+\.\s+(.*)$/);d.push(p[1]),c++}o.push(r.jsx("ol",{className:"md-ol",children:d.map((p,x)=>r.jsx("li",{children:Hn(p,e,n,`ol-${o.length}-${x}`)},x))},`ol-${o.length}`));continue}const g=[];for(;c<l.length&&l[c].trim()&&!l[c].trim().startsWith("#")&&!l[c].trim().startsWith("```")&&!l[c].trim().startsWith(">")&&!/^(\*{3,}|-{3,}|_{3,})$/.test(l[c].trim())&&!/^\s*[-*+]\s+/.test(l[c])&&!/^\s*\d+\.\s+/.test(l[c])&&!(l[c].includes("|")&&c+1<l.length&&/[-:]+/.test(l[c+1]));)g.push(l[c]),c++;g.length>0&&o.push(r.jsx("p",{className:"md-paragraph",children:g.map((d,p)=>r.jsxs(yM.Fragment,{children:[Hn(d,e,n,`p-${o.length}-${p}`),p<g.length-1&&r.jsx("br",{})]},p))},`p-${o.length}`))}return o},[a,e,n]);return r.jsx("article",{className:`markdown-preview ${i}`,children:s})}function D2(t){const e=t.trim(),n=e.match(/^([^:#]+):(\d+)(?::\d+)?$/);if(n)return{path:n[1],line:parseInt(n[2],10)};const i=e.match(/^([^:#]+)#(\d+)$/);return i?{path:i[1],line:parseInt(i[2],10)}:{path:e,line:null}}function L2({workspaceId:t,onOpenSource:e,onOpenRevision:n,onOpenAgentRun:i,onNavigateTab:a}){const[s,l]=P.useState([]),[o,c]=P.useState(null),[u,h]=P.useState(""),[g,d]=P.useState(""),[p,x]=P.useState(!1),[_,m]=P.useState(""),[f,v]=P.useState(""),[y,b]=P.useState(""),[T,M]=P.useState([]),[A,S]=P.useState([]),[N,D]=P.useState([]),[U,I]=P.useState(!1),[Y,k]=P.useState(""),[B,z]=P.useState(""),[j,X]=P.useState("saved"),[q,ve]=P.useState(!0),[he,Ae]=P.useState(!0),[$,pe]=P.useState(!1),[we,ne]=P.useState(null),[ge,ue]=P.useState("editor"),[Le,Ie]=P.useState("edit"),[Xe,dt]=P.useState("notiz"),[re,H]=P.useState("entwurf"),[le,se]=P.useState(()=>{try{return localStorage.getItem("plugbrain.notes_meta_collapsed")==="1"}catch{return!1}}),[Me,Oe]=P.useState(!1),[De,Re]=P.useState(!1),[Fe,ye]=P.useState(""),[O,Ye]=P.useState(!1),[$e,R]=P.useState(""),[E,V]=P.useState(0),K=()=>{se(F=>{const de=!F;try{localStorage.setItem("plugbrain.notes_meta_collapsed",de?"1":"0")}catch{}return de})},J=P.useRef(null),Se=async()=>{const F=await R1(t);return l(F),F},_e=async F=>{I(!0),z(""),k(""),pe(!1);try{const de=await wc(t,F);c(de),h(de.content),X("saved"),D(await mv(t,de.path))}catch(de){k(de instanceof Error?de.message:String(de))}finally{I(!1)}};P.useEffect(()=>{Se().then(F=>{if(F[0])return _e(F[0].path)}).catch(F=>k(String(F)))},[t]);const te=P.useMemo(()=>[...new Set(s.flatMap(F=>F.tags||[]))].sort(),[s]),Q=P.useMemo(()=>{const F={};for(const de of s)if(de.tags)for(const Pe of de.tags)F[Pe]=(F[Pe]||0)+1;return F},[s]),oe=P.useMemo(()=>{if(!_.trim())return te;const F=_.toLowerCase();return te.filter(de=>de.toLowerCase().includes(F))},[te,_]),me=P.useMemo(()=>s.filter(F=>{var de;return!g||((de=F.tags)==null?void 0:de.includes(g))}),[s,g]);P.useEffect(()=>{o&&(u!==o.content?j!=="conflict"&&j!=="saving"&&X("dirty"):j==="dirty"&&X("saved"))},[u,o==null?void 0:o.content,j]);const xe=async(F=!1)=>{if(!(!o||U)){I(!0),z(""),k(""),X("saving");try{const de=o.content,Pe=await Gl(t,o.path,u,o.hash||void 0,void 0,o.hash==="");ne({path:o.path,content:de,savedHash:Pe.hash});const ht=await wc(t,o.path);c(ht),h(ht.content),X("saved"),await Se(),k(F?"Automatisch gespeichert und im Brain indiziert.":Pe.created?"Notiz angelegt und im Brain indiziert.":"Gespeichert und im Brain indiziert.")}catch(de){de!=null&&de.conflict||(de==null?void 0:de.status)===409?(z(de!=null&&de.conflict?`${de.message} Bitte neu laden und die Änderungen zusammenführen.`:"Konflikt beim Speichern: Die Notiz wurde extern geändert."),X("conflict")):(k(de instanceof Error?de.message:String(de)),X("dirty"))}finally{I(!1)}}},Te=async()=>{if(!(!o||U)){I(!0),z(""),k(""),X("saving");try{const F=o.content,de=await Gl(t,o.path,u,void 0,void 0,!1);ne({path:o.path,content:F,savedHash:de.hash});const Pe=await wc(t,o.path);c(Pe),h(Pe.content),X("saved"),pe(!1),await Se(),k("Änderungen erzwungen und im Brain indiziert.")}catch(F){k(F instanceof Error?F.message:String(F)),X("conflict")}finally{I(!1)}}};P.useEffect(()=>{if(!q||!o||o.hash===""||U||B||u===o.content)return;const F=setTimeout(()=>{!U&&!B&&u!==o.content&&o&&o.hash!==""&&xe(!0)},600);return()=>clearTimeout(F)},[u,o,q,U,B]);const Be=async()=>{if(!(!we||!o||we.path!==o.path||U)){I(!0),z(""),k("");try{const F=await Gl(t,we.path,we.content,we.savedHash),de=await wc(t,we.path);c(de),h(de.content),ne(null),X("saved"),await Se(),k("Wiederhergestellt.")}catch(F){k(F instanceof Error?F.message:String(F))}finally{I(!1)}}},Ve=()=>{const F=document.querySelector(".notes-editor textarea");if(!F)return;const de=F.selectionStart,Pe=F.selectionEnd,ht=u.substring(de,Pe),$t=`[[${ht||"Notizname"}]]`,ln=u.substring(0,de)+$t+u.substring(Pe);h(ln),setTimeout(()=>{F.focus();const Ft=de+(ht?$t.length:2);F.setSelectionRange(Ft,Ft+(ht?0:9))},10)},Ke=async F=>{if(!o||!F.trim())return;let de=F.trim();if(de.endsWith(".md")||(de+=".md"),de===o.path){Re(!1);return}I(!0);try{await Gl(t,de,u,void 0,void 0,!0),await Se(),await _e(de),Re(!1),k(`Notiz umbenannt nach "${de}".`)}catch(Pe){k(Pe instanceof Error?Pe.message:String(Pe))}finally{I(!1)}};P.useEffect(()=>{const F=de=>{(de.ctrlKey||de.metaKey)&&de.key.toLowerCase()==="o"?(de.preventDefault(),Ye(!0),R(""),V(0)):de.key==="Escape"&&(O&&Ye(!1),De&&Re(!1),Me&&Oe(!1))};return window.addEventListener("keydown",F),()=>window.removeEventListener("keydown",F)},[O,De,Me]);const G=P.useMemo(()=>{if(!$e.trim())return s.slice(0,15);const F=$e.toLowerCase();return s.filter(de=>de.title.toLowerCase().includes(F)||de.path.toLowerCase().includes(F)||(de.tags||[]).some(Pe=>Pe.toLowerCase().includes(F))).slice(0,20)},[s,$e]),Ne=async()=>{const F=prompt("Dateiname der neuen Notiz (z.B. Architektur.md):");if(!F)return;const de=F.endsWith(".md")?F:`${F}.md`,Pe=de.replace(/\.md$/,"").split("/").pop()||de,ht=Xe==="notiz"?"":Xe,$t=ht?ht==="entscheidung"?`--- +typ: ${ht} +stand: ${re} +bezug: +code: +revision: +--- + +# ${Pe} + +## Kontext + +## Entscheidung + +## Konsequenzen +`:`--- +typ: ${ht} +stand: ${re} +bezug: +code: +--- + +# ${Pe} + +## Befund + +## Ursache + +## Lösungsvorschlag +`:`# ${Pe} + +`;I(!0);try{await Gl(t,de,$t,void 0,void 0,!0),await Se(),await _e(de)}catch(ln){k(ln instanceof Error?ln.message:String(ln))}finally{I(!1)}},ce=F=>{const de=(o==null?void 0:o.title)||"Notiz";let Pe="";F==="entscheidung"?Pe=`--- +typ: entscheidung +stand: entwurf +bezug: +code: +revision: +--- + +# ${de} + +## Kontext + +## Entscheidung + +## Konsequenzen + +`:Pe=`--- +typ: widerspruch +stand: offen +bezug: +code: +--- + +# ${de} + +## Befund + +## Ursache + +## Lösungsvorschlag + +`,h(ht=>Pe+ht)},Ce=F=>{const{path:de,line:Pe}=D2(F);e==null||e(de,Pe)},Ue=F=>{const de=F.trim(),Pe=s.find(ht=>ht.path===de||ht.path===`Notizen/${de}.md`||ht.path.replace(/\.md$/,"").endsWith(de)||ht.title.toLowerCase()===de.toLowerCase());_e(Pe?Pe.path:`Notizen/${de}.md`)},be=async F=>{if(F.preventDefault(),!y.trim()){M([]),S([]);return}I(!0);try{const de=await mp(t,y);M(de.hits||[]);try{const Pe=await pp(t,y);S(Pe)}catch{S([])}}catch(de){k(de instanceof Error?de.message:String(de))}finally{I(!1)}},qe=async F=>{if(!(!F||!o)){I(!0);try{await L1(t,o.path,F),D(await mv(t,o.path)),k(`Anhang ${F.name} hochgeladen.`)}catch(de){k(de instanceof Error?de.message:String(de))}finally{I(!1)}}},je=F=>{(F.ctrlKey||F.metaKey)&&F.key==="s"&&(F.preventDefault(),xe(!1))},xt=F=>{var de;return((de=o==null?void 0:o.properties.find(Pe=>Pe.key.toLowerCase()===F))==null?void 0:de.value.trim())||null},vt=xt("bezug"),Jt=xt("code"),rn=xt("revision"),Ua=xt("agentenlauf"),Ct=P.useMemo(()=>{var ht;if(!u)return null;const F=u.match(/^---\r?\n([\s\S]*?)\r?\n---/);if(!F){if(!((ht=o==null?void 0:o.properties)!=null&&ht.length))return null;const $t=ln=>{var Ft;return((Ft=o.properties.find(cr=>cr.key.toLowerCase()===ln))==null?void 0:Ft.value.trim())||null};return{typ:o.typ||$t("typ"),stand:o.stand||$t("stand"),bezug:$t("bezug"),code:$t("code"),revision:$t("revision"),agentenlauf:$t("agentenlauf")}}const de=F[1].split(/\r?\n/),Pe={};for(const $t of de){const ln=$t.match(/^([a-zA-Z0-9_-]+)\s*:\s*(.*)$/);ln&&(Pe[ln[1].toLowerCase()]=ln[2].trim().replace(/^['"]|['"]$/g,""))}return{typ:Pe.typ||(o==null?void 0:o.typ)||null,stand:Pe.stand||(o==null?void 0:o.stand)||null,bezug:Pe.bezug||xt("bezug"),code:Pe.code||xt("code"),revision:Pe.revision||xt("revision"),agentenlauf:Pe.agentenlauf||xt("agentenlauf")}},[u,o]),ea=P.useMemo(()=>u?[...u.matchAll(/\[\[([^\]|#]+)(?:#[^\]|]+)?(?:\|([^\]]+))?\]\]/g)].map(de=>{var ln;const Pe=de[1].trim(),ht=((ln=de[2])==null?void 0:ln.trim())||null,$t=s.find(Ft=>Ft.path===Pe||Ft.path===`Notizen/${Pe}.md`||Ft.path.replace(/\.md$/,"").endsWith(Pe)||Ft.title.toLowerCase()===Pe.toLowerCase());return{target:Pe,alias:ht,resolvedPath:$t?$t.path:null}}):[],[u,s]),ta=P.useMemo(()=>{if(!(o!=null&&o.backlinks))return[];const F=new Set;for(const de of o.backlinks){const Pe=s.find(ht=>ht.path===de.path);if(Pe!=null&&Pe.tags)for(const ht of Pe.tags)F.add(ht)}return[...F].sort()},[o==null?void 0:o.backlinks,s]),Pi=P.useMemo(()=>o!=null&&o.backlinks?f?o.backlinks.filter(F=>{var Pe;const de=s.find(ht=>ht.path===F.path);return(Pe=de==null?void 0:de.tags)==null?void 0:Pe.includes(f)}):o.backlinks:[],[o==null?void 0:o.backlinks,s,f]);return r.jsxs("main",{className:"notes-workbench",children:[r.jsxs("aside",{className:"notes-sidebar",children:[r.jsxs("div",{className:"notes-sidebar__head",children:[r.jsx("strong",{children:"Wissen"}),r.jsxs("span",{children:[r.jsx("button",{type:"button",className:ge==="editor"?"on":"",onClick:()=>ue("editor"),children:"Editor"}),r.jsx("button",{type:"button",className:ge==="graph"?"on":"",onClick:()=>ue("graph"),"aria-label":"Graph-Ansicht",children:"Graph-Ansicht"})]})]}),r.jsxs("div",{className:"notes-create",children:[r.jsxs("select",{"aria-label":"Typ der neuen Wissensnotiz",value:Xe,onChange:F=>dt(F.target.value),children:[r.jsx("option",{value:"notiz",children:"Notiz"}),r.jsx("option",{value:"entscheidung",children:"Entscheidung"}),r.jsx("option",{value:"widerspruch",children:"Widerspruch"})]}),Xe!=="notiz"&&r.jsxs("select",{"aria-label":"Status der neuen Wissensnotiz",value:re,onChange:F=>H(F.target.value),children:[r.jsx("option",{value:"entwurf",children:"Entwurf"}),r.jsx("option",{value:"offen",children:"Offen"}),r.jsx("option",{value:"entschieden",children:"Entschieden"}),r.jsx("option",{value:"geklärt",children:"Geklärt"})]}),r.jsx("button",{type:"button",onClick:Ne,children:"Neu"})]}),r.jsxs("form",{className:"notes-search",onSubmit:be,children:[r.jsx("input",{value:y,onChange:F=>b(F.target.value),placeholder:"Notizen & Code suchen …"}),r.jsx("button",{disabled:U,type:"submit",children:"Suchen"})]}),r.jsxs("div",{className:"notes-tag-drawer",children:[r.jsxs("div",{className:"notes-tag-drawer__head",children:[r.jsx("button",{type:"button",className:"notes-tag-drawer__toggle",onClick:()=>x(F=>!F),children:r.jsxs("span",{children:[p?"▾":"▸"," Tags (",te.length,")"]})}),g&&r.jsxs("div",{className:"notes-tag-drawer__active",children:[r.jsxs("span",{className:"notes-tag-chip",children:["#",g]}),r.jsx("button",{type:"button",className:"notes-tag-clear",onClick:()=>d(""),title:"Tag-Filter aufheben",children:"✕"})]})]}),p&&r.jsxs("div",{className:"notes-tag-drawer__body",children:[r.jsx("input",{type:"search",className:"notes-tag-search",placeholder:"Tags filtern...",value:_,onChange:F=>m(F.target.value)}),r.jsxs("div",{className:"notes-tags notes-tag-drawer__list",children:[r.jsxs("button",{type:"button",className:g?"":"on",onClick:()=>d(""),children:["Alle (",s.length,")"]}),oe.map(F=>r.jsxs("button",{type:"button",className:g===F?"on":"",onClick:()=>d(g===F?"":F),children:["#",F," (",Q[F]||0,")"]},F))]})]}),!p&&r.jsxs("div",{className:"notes-tags notes-tags--compact",children:[r.jsx("button",{type:"button",className:g?"":"on",onClick:()=>d(""),children:"Alle"}),te.slice(0,8).map(F=>r.jsxs("button",{type:"button",className:g===F?"on":"",onClick:()=>d(F),children:["#",F]},F)),te.length>8&&r.jsxs("button",{type:"button",className:"notes-tags-more",onClick:()=>x(!0),children:["+",te.length-8," weitere …"]})]})]}),(T.length>0||A.length>0)&&r.jsxs("div",{className:"notes-results",children:[T.length>0&&r.jsxs("div",{children:[r.jsxs("div",{className:"notes-results__section-head",children:["Notizen (",T.length,")"]}),T.map(F=>r.jsxs("button",{type:"button",onClick:()=>void _e(F.path),children:[r.jsx("strong",{children:F.title}),r.jsxs("span",{children:[F.path,F.line?`:${F.line}`:""]}),F.snippet&&r.jsx("small",{children:F.snippet})]},`${F.path}:${F.line}`))]}),A.length>0&&r.jsxs("div",{children:[r.jsxs("div",{className:"notes-results__section-head",children:["Code-Symbole (",A.length,")"]}),A.map((F,de)=>r.jsxs("button",{type:"button",onClick:()=>Ce(`${F.path}${F.line?`:${F.line}`:""}`),children:[r.jsxs("div",{className:"notes-results__sym-row",children:[r.jsx("strong",{children:F.name}),r.jsx("span",{className:"notes-results__sym-kind",children:F.kind})]}),r.jsxs("span",{children:[F.path,F.line?`:${F.line}`:""]})]},`${F.path}:${F.line}:${de}`))]})]}),r.jsx("div",{className:"notes-list",children:s.length===0?r.jsxs("div",{className:"notes-empty-list",children:[r.jsx("p",{children:"Noch keine Notizen"}),r.jsx("button",{type:"button",className:"pb-button pb-button--primary",onClick:Ne,children:"Erste Notiz anlegen"})]}):me.map(F=>r.jsxs("button",{type:"button",className:(o==null?void 0:o.path)===F.path?"on":"",onClick:()=>void _e(F.path),children:[r.jsx("strong",{children:F.title}),r.jsx("span",{children:F.path}),r.jsxs("small",{children:[(F.tags||[]).map(de=>`#${de}`).join(" ")," ",F.inLinks?`←${F.inLinks}`:""]})]},F.path))})]}),r.jsxs("section",{className:"notes-editor",children:[ge==="graph"&&r.jsx(A2,{workspaceId:t,focus:o==null?void 0:o.path,onOpenNote:F=>{ue("editor"),_e(F)}}),ge==="editor"&&(o?r.jsxs(r.Fragment,{children:[r.jsxs("header",{className:"notes-editor__head",children:[r.jsxs("div",{className:"notes-editor__title",children:[r.jsxs("div",{style:{display:"flex",alignItems:"center",gap:"8px"},children:[r.jsx("strong",{title:o.title,children:o.title}),r.jsx("button",{type:"button",className:"pb-tool pb-tool--icon",style:{width:"24px",height:"24px",padding:0},onClick:()=>{ye(o.path),Re(!0)},title:"Notiz umbenennen",children:r.jsx(ot,{path:st.pencil})})]}),r.jsx("span",{children:o.path}),o.typ&&r.jsxs("small",{children:[o.typ,o.stand?` · ${o.stand}`:""]})]}),r.jsxs("div",{className:"notes-editor__actions",children:[r.jsxs("div",{className:"notes-view-mode-toggle",role:"group","aria-label":"Editor-Ansichtsmodus",children:[r.jsx("button",{type:"button",className:`notes-toggle-btn ${Le==="edit"?"on":""}`,onClick:()=>Ie("edit"),title:"Reiner Texteditor",children:"Edit"}),r.jsx("button",{type:"button",className:`notes-toggle-btn ${Le==="split"?"on":""}`,onClick:()=>Ie("split"),title:"Editor und Live-Vorschau nebeneinander",children:"Split"}),r.jsx("button",{type:"button",className:`notes-toggle-btn ${Le==="lesen"?"on":""}`,onClick:()=>Ie("lesen"),title:"Reine Leseansicht (Markdown)",children:"Lesen"})]}),r.jsx("button",{type:"button",className:"pb-tool",onClick:Ve,title:"Wiki-Link [[...]] an Cursorposition einfügen",children:"[[ Link ]]"}),r.jsxs("span",{className:"pb-status","data-tone":j==="saved"?"ok":j==="saving"?"idle":j==="conflict"?"bad":"warn",title:j==="conflict"?"Konflikt beim Speichern":j==="dirty"?"Ungespeicherte Änderungen":"Gespeichert",children:[r.jsx("i",{"aria-hidden":"true"}),r.jsx("span",{children:j==="saving"?"SPEICHERT":j==="dirty"?"GEÄNDERT":j==="conflict"?"KONFLIKT":"GESPEICHERT"})]}),r.jsx("button",{type:"button",className:"pb-button pb-button--primary",disabled:U||j==="saved",onClick:()=>void xe(!1),title:"Notiz speichern (Strg+S)",children:"Speichern"}),r.jsxs("button",{type:"button",className:`pb-tool ${he?"is-active":""}`,onClick:()=>Ae(F=>!F),title:"Rückverweise und Eigenschaften anzeigen/verbergen",children:["Rückverweise (",o.backlinks.length,")"]}),r.jsxs("div",{className:"notes-overflow-wrap",style:{position:"relative"},children:[r.jsx("button",{type:"button",className:"pb-tool pb-tool--icon",onClick:()=>Oe(F=>!F),title:"Weitere Aktionen","aria-expanded":Me,children:"⋯"}),Me&&r.jsxs("div",{className:"notes-overflow-dropdown",onClick:()=>Oe(!1),children:[r.jsxs("button",{type:"button",onClick:()=>ve(F=>!F),children:["Autosave: ",q?"Aktiv":"Aus"]}),r.jsx("button",{type:"button",disabled:U||!we||we.path!==o.path,onClick:()=>void Be(),children:"Rückgängig"}),r.jsx("button",{type:"button",onClick:()=>{ye(o.path),Re(!0)},children:"Notiz umbenennen …"}),r.jsx("a",{href:gv(t,o.path),download:!0,children:"Notiz exportieren"}),r.jsx("a",{href:gv(t),download:!0,children:"Vault exportieren"})]})]})]})]}),Ct&&(Ct.typ||Ct.stand||Ct.bezug||Ct.code||Ct.revision)&&r.jsxs("div",{className:"notes-frontmatter-bar",children:[Ct.typ&&r.jsxs("span",{className:`notes-fm-pill notes-fm-pill--${Ct.typ.toLowerCase()}`,children:["TYP: ",Ct.typ.toUpperCase()]}),Ct.stand&&r.jsxs("span",{className:"notes-fm-pill notes-fm-pill--stand",children:["STATUS: ",Ct.stand.toUpperCase()]}),Ct.bezug&&r.jsxs("button",{type:"button",className:"notes-fm-btn notes-fm-btn--link",onClick:()=>Ue(Ct.bezug.replace(/^\[\[|\]\]$/g,"")),title:`Notiz öffnen: ${Ct.bezug}`,children:["BEZUG: ",Ct.bezug," ↗"]}),Ct.code&&r.jsxs("button",{type:"button",className:"notes-fm-btn notes-fm-btn--code",onClick:()=>Ce(Ct.code),title:`Datei im Editor öffnen: ${Ct.code}`,children:["CODE: ",Ct.code," ↗"]}),Ct.revision&&r.jsxs("button",{type:"button",className:"notes-fm-btn notes-fm-btn--rev",onClick:()=>n==null?void 0:n(Ct.revision),title:`Revision prüfen: ${Ct.revision}`,children:["GIT: ",Ct.revision.slice(0,8)," ↗"]})]}),B&&r.jsxs("div",{style:{display:"flex",alignItems:"center",gap:"8px",background:"rgba(239, 68, 68, 0.15)",padding:"6px 12px",borderRadius:"4px",margin:"4px 0"},children:[r.jsx("p",{className:"notes-conflict",role:"alert",style:{margin:0,flex:1},children:B}),r.jsx("button",{type:"button",style:{fontSize:"11px",padding:"2px 8px"},onClick:()=>o&&void _e(o.path),children:"Neu laden"}),r.jsx("button",{type:"button",style:{fontSize:"11px",padding:"2px 8px"},onClick:()=>void Te(),children:"Trotzdem überschreiben"}),r.jsx("button",{type:"button",style:{fontSize:"11px",padding:"2px 8px"},onClick:()=>pe(F=>!F),children:$?"Diff schließen":"Diff anzeigen"})]}),$&&r.jsxs("div",{style:{display:"grid",gridTemplateColumns:"1fr 1fr",gap:"8px",padding:"8px 12px",background:"var(--panel)",borderBottom:"1px solid var(--border)",maxHeight:"180px",overflowY:"auto",fontSize:"11px",fontFamily:"var(--mono)"},children:[r.jsxs("div",{children:[r.jsxs("strong",{style:{display:"block",color:"var(--faint)",marginBottom:"4px"},children:["Server-Version (",o.path,"):"]}),r.jsx("pre",{style:{margin:0,whiteSpace:"pre-wrap",color:"var(--neg)"},children:o.content})]}),r.jsxs("div",{children:[r.jsx("strong",{style:{display:"block",color:"var(--faint)",marginBottom:"4px"},children:"Lokale Version (Entwurf):"}),r.jsx("pre",{style:{margin:0,whiteSpace:"pre-wrap",color:"var(--pos)"},children:u})]})]}),Y&&r.jsx("p",{className:"notes-notice",role:"status",children:Y}),!u.startsWith("---")&&r.jsxs("div",{style:{display:"flex",gap:"6px",margin:"4px 0",flex:"none"},children:[r.jsx("button",{type:"button",style:{fontSize:"10.5px",padding:"2px 7px"},onClick:()=>ce("entscheidung"),children:"+ Vorlage: Entscheidung"}),r.jsx("button",{type:"button",style:{fontSize:"10.5px",padding:"2px 7px"},onClick:()=>ce("widerspruch"),children:"+ Vorlage: Widerspruch"})]}),r.jsxs("div",{className:"notes-content-grid",style:{display:"grid",gridTemplateColumns:he?Le==="split"?"1fr 1fr 280px":"1fr 280px":Le==="split"?"1fr 1fr":"1fr",height:"100%",minHeight:0,overflow:"hidden"},children:[r.jsx("div",{className:"notes-editor-pane",style:{display:Le==="lesen"?"none":"flex",flexDirection:"column",height:"100%",minHeight:0,overflow:"hidden",borderRight:Le==="split"||he?"1px solid var(--border)":"none"},children:r.jsx("textarea",{"aria-label":"Notizinhalt",value:u,onChange:F=>h(F.target.value),onKeyDown:je,spellCheck:!1,style:{height:"100%",width:"100%",boxSizing:"border-box",resize:"none",border:"none",outline:"none"}})}),Le==="lesen"&&r.jsx("textarea",{"aria-label":"Notizinhalt",value:u,onChange:F=>h(F.target.value),onKeyDown:je,spellCheck:!1,style:{position:"absolute",opacity:0,pointerEvents:"none",width:0,height:0}}),(Le==="split"||Le==="lesen")&&r.jsx("div",{className:"notes-preview-pane",style:{height:"100%",minHeight:0,overflowY:"auto",padding:"16px 20px",background:"var(--bg)",borderRight:he?"1px solid var(--border)":"none"},children:r.jsx(R2,{content:u,onNavigateNote:Ue,onOpenSource:Ce})}),he&&r.jsxs("aside",{className:"notes-backlinks-pane","aria-label":"Wissensnetz & Backlinks",style:{overflowY:"auto",padding:"12px",background:"var(--panel)",display:"flex",flexDirection:"column",gap:"12px",fontSize:"12px"},children:[r.jsxs("div",{children:[r.jsxs("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:"6px"},children:[r.jsxs("strong",{style:{fontSize:"11px",textTransform:"uppercase",letterSpacing:"0.08em",color:"var(--faint)"},children:["Rückverweise (",Pi.length,")"]}),ta.length>0&&r.jsxs("select",{value:f,onChange:F=>v(F.target.value),style:{fontSize:"11px",padding:"1px 4px",background:"transparent",color:"var(--fg)",border:"1px solid var(--border)",borderRadius:"3px"},"aria-label":"Backlinks nach Tag filtern",children:[r.jsx("option",{value:"",children:"Alle Tags"}),ta.map(F=>r.jsxs("option",{value:F,children:["#",F]},F))]})]}),Pi.length>0?r.jsx("div",{style:{display:"flex",flexDirection:"column",gap:"4px"},children:Pi.map(F=>r.jsxs("button",{type:"button",onClick:()=>void _e(F.path),style:{textAlign:"left",background:"rgba(255,255,255,0.03)",border:"1px solid var(--border)",borderRadius:"4px",padding:"6px 8px",color:"var(--fg)",cursor:"pointer"},children:[r.jsxs("div",{style:{fontWeight:600,color:"var(--info)"},children:["← ",F.title]}),r.jsxs("div",{style:{fontSize:"11px",color:"var(--faint)",display:"flex",justifyContent:"space-between",marginTop:"2px"},children:[r.jsx("span",{children:F.path}),r.jsxs("span",{children:["Z. ",F.line]})]})]},`${F.path}:${F.line}`))}):r.jsx("div",{style:{color:"var(--faint)",fontStyle:"italic",fontSize:"11px"},children:"Keine Rückverweise für aktuelle Auswahl."})]}),r.jsxs("div",{children:[r.jsxs("strong",{style:{display:"block",fontSize:"11px",textTransform:"uppercase",letterSpacing:"0.08em",color:"var(--faint)",marginBottom:"6px"},children:["Ausgehende Wiki-Links (",ea.length,")"]}),ea.length>0?r.jsx("div",{style:{display:"flex",flexDirection:"column",gap:"4px"},children:ea.map((F,de)=>r.jsxs("button",{type:"button",disabled:!F.resolvedPath,onClick:()=>F.resolvedPath&&void _e(F.resolvedPath),style:{textAlign:"left",background:"rgba(255,255,255,0.03)",border:"1px solid var(--border)",borderRadius:"4px",padding:"6px 8px",color:F.resolvedPath?"var(--fg)":"var(--faint)",cursor:F.resolvedPath?"pointer":"default"},children:[r.jsxs("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between"},children:[r.jsx("span",{style:{fontWeight:600},children:F.alias||F.target}),r.jsx("span",{style:{fontSize:"10px",color:F.resolvedPath?"var(--pos)":"var(--faint)"},children:F.resolvedPath?"im Vault":"unaufgelöst"})]}),F.resolvedPath&&r.jsx("div",{style:{fontSize:"11px",color:"var(--faint)",marginTop:"2px"},children:F.resolvedPath})]},`${F.target}:${de}`))}):r.jsx("div",{style:{color:"var(--faint)",fontStyle:"italic",fontSize:"11px"},children:"Keine [[Wiki-Links]] im Text."})]}),Jt&&r.jsxs("div",{style:{borderTop:"1px solid var(--border)",paddingTop:"8px"},children:[r.jsx("strong",{style:{display:"block",fontSize:"11px",textTransform:"uppercase",letterSpacing:"0.08em",color:"var(--faint)",marginBottom:"6px"},children:"Code-Bindung"}),r.jsxs("button",{type:"button",disabled:!e,onClick:()=>Ce(Jt),style:{width:"100%",textAlign:"left",background:"color-mix(in srgb, var(--info) 8%, transparent)",border:"1px solid color-mix(in srgb, var(--info) 30%, transparent)",borderRadius:"4px",padding:"6px 8px",color:"var(--info)",cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"space-between"},children:[r.jsx("span",{style:{fontFamily:"var(--mono)",fontSize:"11px"},children:Jt}),r.jsx("span",{style:{fontSize:"11px"},children:"Quelle öffnen →"})]})]})]})]}),r.jsx("div",{className:"notes-meta-bar",children:r.jsxs("button",{type:"button",className:"notes-meta-toggle",onClick:K,"aria-expanded":!le,title:le?"Metadaten und Verknüpfungen aufklappen":"Metadaten und Verknüpfungen einklappen",children:[r.jsxs("span",{children:[le?"▸":"▾"," Verknüpfungen & Anhänge"]}),r.jsxs("span",{className:"notes-meta-counts",children:[o.links.length," Links · ",o.backlinks.length," Backlinks",N.length>0?` · ${N.length} Anhänge`:""]})]})}),!le&&r.jsxs("footer",{className:"notes-editor__meta",children:[r.jsxs("section",{children:[r.jsxs("h3",{children:["Links (",o.links.length,")"]}),o.links.length?o.links.map(F=>r.jsxs("button",{type:"button",disabled:!F.path,onClick:()=>F.path&&void _e(F.path),children:[F.alias||F.target,F.path?"":" (nicht aufgelöst)"]},`${F.target}:${F.line}`)):r.jsx("span",{children:"Keine Wiki-Links."})]}),r.jsxs("section",{children:[r.jsxs("h3",{children:["Backlinks (",o.backlinks.length,")"]}),o.backlinks.length?o.backlinks.map(F=>r.jsxs("button",{type:"button",onClick:()=>void _e(F.path),children:["← ",F.title," · Zeile ",F.line]},`${F.path}:${F.line}`)):r.jsx("span",{children:"Keine Rückverweise."})]}),(vt||Jt||rn||Ua)&&r.jsxs("section",{children:[r.jsx("h3",{children:"Verknüpfungen"}),vt&&r.jsxs("button",{type:"button",onClick:()=>void _e(vt.replace(/^\[\[|\]\]$/g,"")),children:["Entscheidung/Widerspruch: ",vt]}),Jt&&r.jsxs("button",{type:"button",disabled:!e,onClick:()=>Ce(Jt),children:["Datei: ",Jt]}),rn&&r.jsxs("button",{type:"button",disabled:!n,onClick:()=>n==null?void 0:n(rn),children:["Revision: ",rn]}),Ua&&r.jsxs("button",{type:"button",disabled:!i,onClick:()=>i==null?void 0:i(Ua),children:["Agentenlauf: ",Ua]})]}),r.jsxs("section",{children:[r.jsxs("h3",{children:["Anhänge (",N.length,")"]}),r.jsx("input",{ref:J,type:"file",hidden:!0,onChange:F=>{var de;return void qe((de=F.target.files)==null?void 0:de[0])}}),r.jsx("button",{type:"button",disabled:U||o.hash==="",title:o.hash===""?"Die Notiz zuerst speichern":void 0,onClick:()=>{var F;return(F=J.current)==null?void 0:F.click()},children:"Datei anhängen"}),o.hash===""&&r.jsx("span",{children:"Notiz zuerst speichern."}),N.map(F=>r.jsxs("a",{href:U1(t,o.path,F.name),children:[F.name," · ",F.bytes," B"]},F.path))]})]})]}):s.length===0?r.jsxs("div",{className:"notes-empty--initial",children:[r.jsx("div",{className:"notes-empty__icon",children:r.jsx(ot,{path:st.notes})}),r.jsx("h3",{children:"Dieser Vault ist noch leer"}),r.jsx("p",{children:"Erstelle deine erste Notiz mit Wiki-Links, Tags und Eigenschaften — genau wie in Obsidian."}),r.jsx("button",{type:"button",className:"pb-button pb-button--primary",onClick:Ne,children:"Erste Notiz anlegen"})]}):r.jsxs("div",{className:"notes-empty--initial",children:[r.jsx("div",{className:"notes-empty__icon",children:r.jsx(ot,{path:st.notes})}),r.jsx("h3",{children:"Keine Notiz ausgewählt"}),r.jsx("p",{children:"Wähle eine Notiz in der linken Seitenleiste aus oder erstelle eine neue."}),r.jsx("button",{type:"button",className:"pb-button pb-button--primary",onClick:Ne,children:"Neue Notiz anlegen"})]}))]}),O&&r.jsx("div",{className:"brain-modal-backdrop",onClick:()=>Ye(!1),children:r.jsxs("div",{className:"brain-modal brain-quick-switcher",onClick:F=>F.stopPropagation(),children:[r.jsxs("div",{className:"brain-modal__header",children:[r.jsx("h3",{children:"Schnellwechsler (Notiz öffnen)"}),r.jsx("button",{type:"button",className:"brain-modal__close",onClick:()=>Ye(!1),children:"✕"})]}),r.jsx("div",{className:"quick-switcher-input-wrap",children:r.jsx("input",{type:"text",className:"brain-modal__input",autoFocus:!0,placeholder:"Notiz nach Titel, Pfad oder #Tag suchen …",value:$e,onChange:F=>{R(F.target.value),V(0)},onKeyDown:F=>{F.key==="ArrowDown"?(F.preventDefault(),V(de=>Math.min(G.length-1,de+1))):F.key==="ArrowUp"?(F.preventDefault(),V(de=>Math.max(0,de-1))):F.key==="Enter"?(F.preventDefault(),G[E]&&(_e(G[E].path),Ye(!1))):F.key==="Escape"&&Ye(!1)}})}),r.jsx("div",{className:"quick-switcher-list",role:"listbox",children:G.length===0?r.jsx("p",{className:"quick-switcher-empty",children:"Keine passende Notiz gefunden."}):G.map((F,de)=>r.jsxs("button",{type:"button",className:`quick-switcher-item ${de===E?"is-selected":""}`,onClick:()=>{_e(F.path),Ye(!1)},onMouseEnter:()=>V(de),children:[r.jsx("div",{className:"quick-switcher-item__title",children:F.title}),r.jsx("div",{className:"quick-switcher-item__path",children:F.path}),F.tags&&F.tags.length>0&&r.jsx("div",{className:"quick-switcher-item__tags",children:F.tags.map(Pe=>`#${Pe}`).join(" ")})]},F.path))}),r.jsxs("div",{className:"quick-switcher-hint",children:[r.jsxs("span",{children:[r.jsx("kbd",{children:"↑"}),r.jsx("kbd",{children:"↓"})," Auswählen"]}),r.jsxs("span",{children:[r.jsx("kbd",{children:"Enter"})," Öffnen"]}),r.jsxs("span",{children:[r.jsx("kbd",{children:"Esc"})," Schließen"]})]})]})}),De&&r.jsx("div",{className:"brain-modal-backdrop",onClick:()=>Re(!1),children:r.jsxs("div",{className:"brain-modal",onClick:F=>F.stopPropagation(),children:[r.jsxs("div",{className:"brain-modal__header",children:[r.jsx("h3",{children:"Notiz umbenennen"}),r.jsx("button",{type:"button",className:"brain-modal__close",onClick:()=>Re(!1),children:"✕"})]}),r.jsxs("form",{onSubmit:F=>{F.preventDefault(),Ke(Fe)},children:[r.jsxs("div",{className:"brain-modal__field",children:[r.jsx("label",{children:"Neuer Pfad / Dateiname (im Vault):"}),r.jsx("input",{type:"text",className:"brain-modal__input",autoFocus:!0,value:Fe,onChange:F=>ye(F.target.value),placeholder:"Ordner/NeueNotiz.md"})]}),r.jsx("p",{style:{fontSize:"11px",color:"var(--muted)",margin:"0 0 16px"},children:"Die Notiz wird unter dem neuen Pfad gespeichert und im Wissensgraphen indiziert."}),r.jsxs("div",{className:"brain-modal__actions",children:[r.jsx("button",{type:"button",onClick:()=>Re(!1),children:"Abbrechen"}),r.jsx("button",{type:"submit",className:"pb-button pb-button--primary",disabled:U||!Fe.trim(),children:U?"Speichert …":"Umbenennen"})]})]})]})})]})}function U2(t){const e=[],n=t.split(` +`);for(const i of n){const a=i.match(/^-\s*`([^`]+)`\s*—\s*(.*)$/);a&&e.push({path:a[1],reasons:a[2]})}return e}function O2({workspaceId:t,onSelectSource:e}){const[n,i]=P.useState(()=>new URLSearchParams(window.location.search).get("goal")||"authKey security tests"),[a,s]=P.useState(!1),[l,o]=P.useState(""),[c,u]=P.useState(null),[h,g]=P.useState(null),[d,p]=P.useState(!1),[x,_]=P.useState(!1);P.useEffect(()=>{const M=new URLSearchParams(window.location.search).get("goal");M&&t&&(s(!0),pv(t,M).then(A=>{u(A),A.id&&f(A.id)}).catch(A=>o((A==null?void 0:A.message)||"Fehler beim Erzeugen")).finally(()=>s(!1)))},[t]);const m=async M=>{M&&M.preventDefault();const A=n.trim();if(A){s(!0),o(""),u(null),g(null);try{const S=await pv(t,A);u(S),S.id&&f(S.id)}catch(S){o((S==null?void 0:S.message)||"Fehler beim Erzeugen des Context Packs")}finally{s(!1)}}},f=async M=>{p(!0);try{const A=await w1(M);g(A)}catch(A){console.error("Staleness check error:",A)}finally{p(!1)}},[v,y]=P.useState(""),b=(M="chatgpt")=>{if(!(c!=null&&c.body))return;let A=c.body;M==="chatgpt"&&(A=`# Kontext-Paket für Agenten / LLM +Ziel: ${n} +Workspace-ID: ${t} +Version: ${c.version} +Quellen: ${c.sources} + +${c.body}`),navigator.clipboard.writeText(A).then(()=>{y("Als Kontext kopiert (bereit für ChatGPT, Claude & Co.)"),setTimeout(()=>y(""),3e3)}).catch(()=>{y("Fehler beim Kopieren in die Zwischenablage")})},T=c!=null&&c.body?U2(c.body):[];return r.jsxs("div",{className:"pack-view",children:[r.jsxs("div",{className:"pack-view__header",children:[r.jsxs("div",{className:"pack-view__title",children:[r.jsx(ot,{path:st.open}),r.jsx("strong",{children:"Context-Pack-Inspector"}),r.jsx("span",{className:"pack-view__endpoint mono",children:"/api/context/pack"})]}),r.jsx("p",{className:"pack-view__lead",style:{fontSize:"12px",color:"var(--muted)",margin:"4px 0 0"},children:"Stellt einen deterministischen Kontext-Ausschnitt aus Code und Notizen für eine Agenten-Aufgabe zusammen."})]}),r.jsx("form",{className:"pack-form",onSubmit:m,children:r.jsxs("div",{className:"pack-form__field",children:[r.jsx("label",{htmlFor:"pack-goal-input",children:"Aufgabe / Ziel für den Agenten:"}),r.jsxs("div",{className:"pack-input-row",children:[r.jsx("input",{id:"pack-goal-input",type:"text",className:"pack-input",value:n,onChange:M=>i(M.target.value),placeholder:"z. B. authKey security tests"}),r.jsx("button",{type:"submit",className:"pack-create-btn pb-button pb-button--primary",disabled:a||!n.trim(),children:a?"Erzeuge …":"Pack erzeugen"})]})]})}),l&&r.jsx("div",{className:"pack-error-alert",role:"alert",children:l}),c&&r.jsx("div",{className:"pack-details",children:r.jsxs("div",{className:"pack-card",children:[r.jsxs("div",{className:"pack-card__header",children:[r.jsxs("div",{className:"pack-card__meta",children:[r.jsx("span",{className:"pack-id mono",children:c.id}),r.jsxs("span",{className:"pack-badge pack-badge--version",children:["v",c.version]}),r.jsxs("span",{className:"pack-badge pack-badge--sources",children:[c.sources," Quellen"]})]}),r.jsxs("div",{className:"pack-card__actions",style:{display:"flex",alignItems:"center",gap:"8px",flexWrap:"wrap"},children:[r.jsx("button",{type:"button",className:"pb-button pb-button--primary",onClick:()=>b("chatgpt"),title:"Markdown-Kontext für ChatGPT oder Claude in die Zwischenablage kopieren","aria-label":"Als Kontext kopieren",style:{fontSize:"12px",padding:"6px 14px"},children:"Als Kontext kopieren"}),r.jsxs("div",{className:"pack-card__staleness",children:[d?r.jsx("span",{className:"pack-staleness-badge pack-staleness-badge--loading",children:"Prüfe …"}):h?r.jsxs("span",{className:"pb-status","data-tone":h.stale?"bad":"ok",style:{marginRight:"8px"},children:[r.jsx("i",{"aria-hidden":"true"}),r.jsx("span",{children:h.stale?"VERALTET":"AKTUELL"})]}):null,r.jsx("button",{type:"button",className:"pack-staleness-btn",onClick:()=>f(c.id),disabled:d,title:"Staleness gegen aktuellen Brain-Index prüfen",children:"Neu prüfen"})]})]})]}),v&&r.jsx("div",{className:"pack-copy-feedback",style:{margin:"10px 0",padding:"8px 12px",background:"var(--accent-soft)",border:"1px solid var(--pos)",borderRadius:"var(--r)",fontSize:"12px",color:"var(--ink)",fontFamily:"var(--mono)"},children:v}),h&&h.stale&&r.jsxs("div",{className:"pack-stale-warning",children:[r.jsx("strong",{children:"Quellen haben sich geändert:"}),h.changed.length>0&&r.jsxs("div",{children:["Geändert: ",h.changed.join(", ")]}),h.missing.length>0&&r.jsxs("div",{children:["Fehlt: ",h.missing.join(", ")]})]}),r.jsxs("div",{className:"pack-sources-section",children:[r.jsx("h4",{children:"Extrahierte Quellen aus dem Index:"}),T.length===0?r.jsx("div",{className:"pack-sources-empty",children:"Keine spezifischen Quelltreffer für dieses Ziel gefunden."}):r.jsx("div",{className:"pack-sources-list",children:T.map(M=>r.jsxs("div",{className:"pack-source-item",onClick:()=>e(M.path),title:`Klicken, um ${M.path} in Quellansicht zu öffnen`,children:[r.jsx("div",{className:"pack-source-path mono",children:M.path}),r.jsx("div",{className:"pack-source-why",children:M.reasons})]},M.path))})]}),r.jsx("div",{className:"pack-body-toggle",children:r.jsx("button",{type:"button",className:"pack-toggle-raw-btn",onClick:()=>_(M=>!M),children:x?"Markdown-Text verbergen":"Vollständigen Pack-Markdown anzeigen"})}),x&&r.jsx("div",{className:"pack-raw-markdown mono",children:r.jsx("pre",{children:c.body})})]})})]})}function P2(t){const e=+this._x.call(null,t),n=+this._y.call(null,t);return XS(this.cover(e,n),e,n,t)}function XS(t,e,n,i){if(isNaN(e)||isNaN(n))return t;var a,s=t._root,l={data:i},o=t._x0,c=t._y0,u=t._x1,h=t._y1,g,d,p,x,_,m,f,v;if(!s)return t._root=l,t;for(;s.length;)if((_=e>=(g=(o+u)/2))?o=g:u=g,(m=n>=(d=(c+h)/2))?c=d:h=d,a=s,!(s=s[f=m<<1|_]))return a[f]=l,t;if(p=+t._x.call(null,s.data),x=+t._y.call(null,s.data),e===p&&n===x)return l.next=s,a?a[f]=l:t._root=l,t;do a=a?a[f]=new Array(4):t._root=new Array(4),(_=e>=(g=(o+u)/2))?o=g:u=g,(m=n>=(d=(c+h)/2))?c=d:h=d;while((f=m<<1|_)===(v=(x>=d)<<1|p>=g));return a[v]=s,a[f]=l,t}function z2(t){var e,n,i=t.length,a,s,l=new Array(i),o=new Array(i),c=1/0,u=1/0,h=-1/0,g=-1/0;for(n=0;n<i;++n)isNaN(a=+this._x.call(null,e=t[n]))||isNaN(s=+this._y.call(null,e))||(l[n]=a,o[n]=s,a<c&&(c=a),a>h&&(h=a),s<u&&(u=s),s>g&&(g=s));if(c>h||u>g)return this;for(this.cover(c,u).cover(h,g),n=0;n<i;++n)XS(this,l[n],o[n],t[n]);return this}function j2(t,e){if(isNaN(t=+t)||isNaN(e=+e))return this;var n=this._x0,i=this._y0,a=this._x1,s=this._y1;if(isNaN(n))a=(n=Math.floor(t))+1,s=(i=Math.floor(e))+1;else{for(var l=a-n||1,o=this._root,c,u;n>t||t>=a||i>e||e>=s;)switch(u=(e<i)<<1|t<n,c=new Array(4),c[u]=o,o=c,l*=2,u){case 0:a=n+l,s=i+l;break;case 1:n=a-l,s=i+l;break;case 2:a=n+l,i=s-l;break;case 3:n=a-l,i=s-l;break}this._root&&this._root.length&&(this._root=o)}return this._x0=n,this._y0=i,this._x1=a,this._y1=s,this}function k2(){var t=[];return this.visit(function(e){if(!e.length)do t.push(e.data);while(e=e.next)}),t}function I2(t){return arguments.length?this.cover(+t[0][0],+t[0][1]).cover(+t[1][0],+t[1][1]):isNaN(this._x0)?void 0:[[this._x0,this._y0],[this._x1,this._y1]]}function Rn(t,e,n,i,a){this.node=t,this.x0=e,this.y0=n,this.x1=i,this.y1=a}function B2(t,e,n){var i,a=this._x0,s=this._y0,l,o,c,u,h=this._x1,g=this._y1,d=[],p=this._root,x,_;for(p&&d.push(new Rn(p,a,s,h,g)),n==null?n=1/0:(a=t-n,s=e-n,h=t+n,g=e+n,n*=n);x=d.pop();)if(!(!(p=x.node)||(l=x.x0)>h||(o=x.y0)>g||(c=x.x1)<a||(u=x.y1)<s))if(p.length){var m=(l+c)/2,f=(o+u)/2;d.push(new Rn(p[3],m,f,c,u),new Rn(p[2],l,f,m,u),new Rn(p[1],m,o,c,f),new Rn(p[0],l,o,m,f)),(_=(e>=f)<<1|t>=m)&&(x=d[d.length-1],d[d.length-1]=d[d.length-1-_],d[d.length-1-_]=x)}else{var v=t-+this._x.call(null,p.data),y=e-+this._y.call(null,p.data),b=v*v+y*y;if(b<n){var T=Math.sqrt(n=b);a=t-T,s=e-T,h=t+T,g=e+T,i=p.data}}return i}function F2(t){if(isNaN(h=+this._x.call(null,t))||isNaN(g=+this._y.call(null,t)))return this;var e,n=this._root,i,a,s,l=this._x0,o=this._y0,c=this._x1,u=this._y1,h,g,d,p,x,_,m,f;if(!n)return this;if(n.length)for(;;){if((x=h>=(d=(l+c)/2))?l=d:c=d,(_=g>=(p=(o+u)/2))?o=p:u=p,e=n,!(n=n[m=_<<1|x]))return this;if(!n.length)break;(e[m+1&3]||e[m+2&3]||e[m+3&3])&&(i=e,f=m)}for(;n.data!==t;)if(a=n,!(n=n.next))return this;return(s=n.next)&&delete n.next,a?(s?a.next=s:delete a.next,this):e?(s?e[m]=s:delete e[m],(n=e[0]||e[1]||e[2]||e[3])&&n===(e[3]||e[2]||e[1]||e[0])&&!n.length&&(i?i[f]=n:this._root=n),this):(this._root=s,this)}function H2(t){for(var e=0,n=t.length;e<n;++e)this.remove(t[e]);return this}function G2(){return this._root}function V2(){var t=0;return this.visit(function(e){if(!e.length)do++t;while(e=e.next)}),t}function W2(t){var e=[],n,i=this._root,a,s,l,o,c;for(i&&e.push(new Rn(i,this._x0,this._y0,this._x1,this._y1));n=e.pop();)if(!t(i=n.node,s=n.x0,l=n.y0,o=n.x1,c=n.y1)&&i.length){var u=(s+o)/2,h=(l+c)/2;(a=i[3])&&e.push(new Rn(a,u,h,o,c)),(a=i[2])&&e.push(new Rn(a,s,h,u,c)),(a=i[1])&&e.push(new Rn(a,u,l,o,h)),(a=i[0])&&e.push(new Rn(a,s,l,u,h))}return this}function X2(t){var e=[],n=[],i;for(this._root&&e.push(new Rn(this._root,this._x0,this._y0,this._x1,this._y1));i=e.pop();){var a=i.node;if(a.length){var s,l=i.x0,o=i.y0,c=i.x1,u=i.y1,h=(l+c)/2,g=(o+u)/2;(s=a[0])&&e.push(new Rn(s,l,o,h,g)),(s=a[1])&&e.push(new Rn(s,h,o,c,g)),(s=a[2])&&e.push(new Rn(s,l,g,h,u)),(s=a[3])&&e.push(new Rn(s,h,g,c,u))}n.push(i)}for(;i=n.pop();)t(i.node,i.x0,i.y0,i.x1,i.y1);return this}function q2(t){return t[0]}function Y2(t){return arguments.length?(this._x=t,this):this._x}function $2(t){return t[1]}function K2(t){return arguments.length?(this._y=t,this):this._y}function Ng(t,e,n){var i=new Cg(e??q2,n??$2,NaN,NaN,NaN,NaN);return t==null?i:i.addAll(t)}function Cg(t,e,n,i,a,s){this._x=t,this._y=e,this._x0=n,this._y0=i,this._x1=a,this._y1=s,this._root=void 0}function U0(t){for(var e={data:t.data},n=e;t=t.next;)n=n.next={data:t.data};return e}var zn=Ng.prototype=Cg.prototype;zn.copy=function(){var t=new Cg(this._x,this._y,this._x0,this._y0,this._x1,this._y1),e=this._root,n,i;if(!e)return t;if(!e.length)return t._root=U0(e),t;for(n=[{source:e,target:t._root=new Array(4)}];e=n.pop();)for(var a=0;a<4;++a)(i=e.source[a])&&(i.length?n.push({source:i,target:e.target[a]=new Array(4)}):e.target[a]=U0(i));return t};zn.add=P2;zn.addAll=z2;zn.cover=j2;zn.data=k2;zn.extent=I2;zn.find=B2;zn.remove=F2;zn.removeAll=H2;zn.root=G2;zn.size=V2;zn.visit=W2;zn.visitAfter=X2;zn.x=Y2;zn.y=K2;function Ln(t){return function(){return t}}function rs(t){return(t()-.5)*1e-6}function Z2(t){return t.x+t.vx}function Q2(t){return t.y+t.vy}function J2(t){var e,n,i,a=1,s=1;typeof t!="function"&&(t=Ln(t==null?1:+t));function l(){for(var u,h=e.length,g,d,p,x,_,m,f=0;f<s;++f)for(g=Ng(e,Z2,Q2).visitAfter(o),u=0;u<h;++u)d=e[u],_=n[d.index],m=_*_,p=d.x+d.vx,x=d.y+d.vy,g.visit(v);function v(y,b,T,M,A){var S=y.data,N=y.r,D=_+N;if(S){if(S.index>d.index){var U=p-S.x-S.vx,I=x-S.y-S.vy,Y=U*U+I*I;Y<D*D&&(U===0&&(U=rs(i),Y+=U*U),I===0&&(I=rs(i),Y+=I*I),Y=(D-(Y=Math.sqrt(Y)))/Y*a,d.vx+=(U*=Y)*(D=(N*=N)/(m+N)),d.vy+=(I*=Y)*D,S.vx-=U*(D=1-D),S.vy-=I*D)}return}return b>p+D||M<p-D||T>x+D||A<x-D}}function o(u){if(u.data)return u.r=n[u.data.index];for(var h=u.r=0;h<4;++h)u[h]&&u[h].r>u.r&&(u.r=u[h].r)}function c(){if(e){var u,h=e.length,g;for(n=new Array(h),u=0;u<h;++u)g=e[u],n[g.index]=+t(g,u,e)}}return l.initialize=function(u,h){e=u,i=h,c()},l.iterations=function(u){return arguments.length?(s=+u,l):s},l.strength=function(u){return arguments.length?(a=+u,l):a},l.radius=function(u){return arguments.length?(t=typeof u=="function"?u:Ln(+u),c(),l):t},l}function eD(t){return t.index}function O0(t,e){var n=t.get(e);if(!n)throw new Error("node not found: "+e);return n}function tD(t){var e=eD,n=g,i,a=Ln(30),s,l,o,c,u,h=1;t==null&&(t=[]);function g(m){return 1/Math.min(o[m.source.index],o[m.target.index])}function d(m){for(var f=0,v=t.length;f<h;++f)for(var y=0,b,T,M,A,S,N,D;y<v;++y)b=t[y],T=b.source,M=b.target,A=M.x+M.vx-T.x-T.vx||rs(u),S=M.y+M.vy-T.y-T.vy||rs(u),N=Math.sqrt(A*A+S*S),N=(N-s[y])/N*m*i[y],A*=N,S*=N,M.vx-=A*(D=c[y]),M.vy-=S*D,T.vx+=A*(D=1-D),T.vy+=S*D}function p(){if(l){var m,f=l.length,v=t.length,y=new Map(l.map((T,M)=>[e(T,M,l),T])),b;for(m=0,o=new Array(f);m<v;++m)b=t[m],b.index=m,typeof b.source!="object"&&(b.source=O0(y,b.source)),typeof b.target!="object"&&(b.target=O0(y,b.target)),o[b.source.index]=(o[b.source.index]||0)+1,o[b.target.index]=(o[b.target.index]||0)+1;for(m=0,c=new Array(v);m<v;++m)b=t[m],c[m]=o[b.source.index]/(o[b.source.index]+o[b.target.index]);i=new Array(v),x(),s=new Array(v),_()}}function x(){if(l)for(var m=0,f=t.length;m<f;++m)i[m]=+n(t[m],m,t)}function _(){if(l)for(var m=0,f=t.length;m<f;++m)s[m]=+a(t[m],m,t)}return d.initialize=function(m,f){l=m,u=f,p()},d.links=function(m){return arguments.length?(t=m,p(),d):t},d.id=function(m){return arguments.length?(e=m,d):e},d.iterations=function(m){return arguments.length?(h=+m,d):h},d.strength=function(m){return arguments.length?(n=typeof m=="function"?m:Ln(+m),x(),d):n},d.distance=function(m){return arguments.length?(a=typeof m=="function"?m:Ln(+m),_(),d):a},d}var nD={value:()=>{}};function qS(){for(var t=0,e=arguments.length,n={},i;t<e;++t){if(!(i=arguments[t]+"")||i in n||/[\s.]/.test(i))throw new Error("illegal type: "+i);n[i]=[]}return new Uu(n)}function Uu(t){this._=t}function iD(t,e){return t.trim().split(/^|\s+/).map(function(n){var i="",a=n.indexOf(".");if(a>=0&&(i=n.slice(a+1),n=n.slice(0,a)),n&&!e.hasOwnProperty(n))throw new Error("unknown type: "+n);return{type:n,name:i}})}Uu.prototype=qS.prototype={constructor:Uu,on:function(t,e){var n=this._,i=iD(t+"",n),a,s=-1,l=i.length;if(arguments.length<2){for(;++s<l;)if((a=(t=i[s]).type)&&(a=aD(n[a],t.name)))return a;return}if(e!=null&&typeof e!="function")throw new Error("invalid callback: "+e);for(;++s<l;)if(a=(t=i[s]).type)n[a]=P0(n[a],t.name,e);else if(e==null)for(a in n)n[a]=P0(n[a],t.name,null);return this},copy:function(){var t={},e=this._;for(var n in e)t[n]=e[n].slice();return new Uu(t)},call:function(t,e){if((a=arguments.length-2)>0)for(var n=new Array(a),i=0,a,s;i<a;++i)n[i]=arguments[i+2];if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(s=this._[t],i=0,a=s.length;i<a;++i)s[i].value.apply(e,n)},apply:function(t,e,n){if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(var i=this._[t],a=0,s=i.length;a<s;++a)i[a].value.apply(e,n)}};function aD(t,e){for(var n=0,i=t.length,a;n<i;++n)if((a=t[n]).name===e)return a.value}function P0(t,e,n){for(var i=0,a=t.length;i<a;++i)if(t[i].name===e){t[i]=nD,t=t.slice(0,i).concat(t.slice(i+1));break}return n!=null&&t.push({name:e,value:n}),t}var Ml=0,co=0,eo=0,YS=1e3,xd,uo,vd=0,er=0,Wd=0,qo=typeof performance=="object"&&performance.now?performance:Date,$S=typeof window=="object"&&window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(t){setTimeout(t,17)};function KS(){return er||($S(sD),er=qo.now()+Wd)}function sD(){er=0}function cm(){this._call=this._time=this._next=null}cm.prototype=ZS.prototype={constructor:cm,restart:function(t,e,n){if(typeof t!="function")throw new TypeError("callback is not a function");n=(n==null?KS():+n)+(e==null?0:+e),!this._next&&uo!==this&&(uo?uo._next=this:xd=this,uo=this),this._call=t,this._time=n,um()},stop:function(){this._call&&(this._call=null,this._time=1/0,um())}};function ZS(t,e,n){var i=new cm;return i.restart(t,e,n),i}function rD(){KS(),++Ml;for(var t=xd,e;t;)(e=er-t._time)>=0&&t._call.call(void 0,e),t=t._next;--Ml}function z0(){er=(vd=qo.now())+Wd,Ml=co=0;try{rD()}finally{Ml=0,oD(),er=0}}function lD(){var t=qo.now(),e=t-vd;e>YS&&(Wd-=e,vd=t)}function oD(){for(var t,e=xd,n,i=1/0;e;)e._call?(i>e._time&&(i=e._time),t=e,e=e._next):(n=e._next,e._next=null,e=t?t._next=n:xd=n);uo=t,um(i)}function um(t){if(!Ml){co&&(co=clearTimeout(co));var e=t-er;e>24?(t<1/0&&(co=setTimeout(z0,t-qo.now()-Wd)),eo&&(eo=clearInterval(eo))):(eo||(vd=qo.now(),eo=setInterval(lD,YS)),Ml=1,$S(z0))}}const cD=1664525,uD=1013904223,j0=4294967296;function dD(){let t=1;return()=>(t=(cD*t+uD)%j0)/j0}function hD(t){return t.x}function fD(t){return t.y}var pD=10,mD=Math.PI*(3-Math.sqrt(5));function gD(t){var e,n=1,i=.001,a=1-Math.pow(i,1/300),s=0,l=.6,o=new Map,c=ZS(g),u=qS("tick","end"),h=dD();t==null&&(t=[]);function g(){d(),u.call("tick",e),n<i&&(c.stop(),u.call("end",e))}function d(_){var m,f=t.length,v;_===void 0&&(_=1);for(var y=0;y<_;++y)for(n+=(s-n)*a,o.forEach(function(b){b(n)}),m=0;m<f;++m)v=t[m],v.fx==null?v.x+=v.vx*=l:(v.x=v.fx,v.vx=0),v.fy==null?v.y+=v.vy*=l:(v.y=v.fy,v.vy=0);return e}function p(){for(var _=0,m=t.length,f;_<m;++_){if(f=t[_],f.index=_,f.fx!=null&&(f.x=f.fx),f.fy!=null&&(f.y=f.fy),isNaN(f.x)||isNaN(f.y)){var v=pD*Math.sqrt(.5+_),y=_*mD;f.x=v*Math.cos(y),f.y=v*Math.sin(y)}(isNaN(f.vx)||isNaN(f.vy))&&(f.vx=f.vy=0)}}function x(_){return _.initialize&&_.initialize(t,h),_}return p(),e={tick:d,restart:function(){return c.restart(g),e},stop:function(){return c.stop(),e},nodes:function(_){return arguments.length?(t=_,p(),o.forEach(x),e):t},alpha:function(_){return arguments.length?(n=+_,e):n},alphaMin:function(_){return arguments.length?(i=+_,e):i},alphaDecay:function(_){return arguments.length?(a=+_,e):+a},alphaTarget:function(_){return arguments.length?(s=+_,e):s},velocityDecay:function(_){return arguments.length?(l=1-_,e):1-l},randomSource:function(_){return arguments.length?(h=_,o.forEach(x),e):h},force:function(_,m){return arguments.length>1?(m==null?o.delete(_):o.set(_,x(m)),e):o.get(_)},find:function(_,m,f){var v=0,y=t.length,b,T,M,A,S;for(f==null?f=1/0:f*=f,v=0;v<y;++v)A=t[v],b=_-A.x,T=m-A.y,M=b*b+T*T,M<f&&(S=A,f=M);return S},on:function(_,m){return arguments.length>1?(u.on(_,m),e):u.on(_)}}}function xD(){var t,e,n,i,a=Ln(-30),s,l=1,o=1/0,c=.81;function u(p){var x,_=t.length,m=Ng(t,hD,fD).visitAfter(g);for(i=p,x=0;x<_;++x)e=t[x],m.visit(d)}function h(){if(t){var p,x=t.length,_;for(s=new Array(x),p=0;p<x;++p)_=t[p],s[_.index]=+a(_,p,t)}}function g(p){var x=0,_,m,f=0,v,y,b;if(p.length){for(v=y=b=0;b<4;++b)(_=p[b])&&(m=Math.abs(_.value))&&(x+=_.value,f+=m,v+=m*_.x,y+=m*_.y);p.x=v/f,p.y=y/f}else{_=p,_.x=_.data.x,_.y=_.data.y;do x+=s[_.data.index];while(_=_.next)}p.value=x}function d(p,x,_,m){if(!p.value)return!0;var f=p.x-e.x,v=p.y-e.y,y=m-x,b=f*f+v*v;if(y*y/c<b)return b<o&&(f===0&&(f=rs(n),b+=f*f),v===0&&(v=rs(n),b+=v*v),b<l&&(b=Math.sqrt(l*b)),e.vx+=f*p.value*i/b,e.vy+=v*p.value*i/b),!0;if(p.length||b>=o)return;(p.data!==e||p.next)&&(f===0&&(f=rs(n),b+=f*f),v===0&&(v=rs(n),b+=v*v),b<l&&(b=Math.sqrt(l*b)));do p.data!==e&&(y=s[p.data.index]*i/b,e.vx+=f*y,e.vy+=v*y);while(p=p.next)}return u.initialize=function(p,x){t=p,n=x,h()},u.strength=function(p){return arguments.length?(a=typeof p=="function"?p:Ln(+p),h(),u):a},u.distanceMin=function(p){return arguments.length?(l=p*p,u):Math.sqrt(l)},u.distanceMax=function(p){return arguments.length?(o=p*p,u):Math.sqrt(o)},u.theta=function(p){return arguments.length?(c=p*p,u):Math.sqrt(c)},u}function vD(t){var e=Ln(.1),n,i,a;typeof t!="function"&&(t=Ln(t==null?0:+t));function s(o){for(var c=0,u=n.length,h;c<u;++c)h=n[c],h.vx+=(a[c]-h.x)*i[c]*o}function l(){if(n){var o,c=n.length;for(i=new Array(c),a=new Array(c),o=0;o<c;++o)i[o]=isNaN(a[o]=+t(n[o],o,n))?0:+e(n[o],o,n)}}return s.initialize=function(o){n=o,l()},s.strength=function(o){return arguments.length?(e=typeof o=="function"?o:Ln(+o),l(),s):e},s.x=function(o){return arguments.length?(t=typeof o=="function"?o:Ln(+o),l(),s):t},s}function _D(t){var e=Ln(.1),n,i,a;typeof t!="function"&&(t=Ln(t==null?0:+t));function s(o){for(var c=0,u=n.length,h;c<u;++c)h=n[c],h.vy+=(a[c]-h.y)*i[c]*o}function l(){if(n){var o,c=n.length;for(i=new Array(c),a=new Array(c),o=0;o<c;++o)i[o]=isNaN(a[o]=+t(n[o],o,n))?0:+e(n[o],o,n)}}return s.initialize=function(o){n=o,l()},s.strength=function(o){return arguments.length?(e=typeof o=="function"?o:Ln(+o),l(),s):e},s.y=function(o){return arguments.length?(t=typeof o=="function"?o:Ln(+o),l(),s):t},s}const k0=.04,I0=6,bD=.0016,yD=4,SD=4,MD=2.6,ED=1.35,TD=13,ff=1.4,AD=.13,wD=.035,ND=.8,CD=.13,RD=.7,DD=1.3,B0=12,F0=.75,LD=1.25,UD=.06,OD=280,ru=3,PD=4,zD=40,H0=56,jD=1.6,kD=260;var $0;const G0=typeof window<"u"&&(($0=window.matchMedia)==null?void 0:$0.call(window,"(prefers-reduced-motion: reduce)").matches)===!0;function ID(t){return Math.min(TD,MD+ED*Math.sqrt(t))}function BD(t){return 1-Math.pow(1-t,3)}function V0(t){const e=getComputedStyle(t),n=(a,s)=>e.getPropertyValue(a).trim()||s,i=[];for(let a=0;a<8;a+=1)i.push(n(`--graph-g${a}`,n("--graph-other","#6b6e76")));return{bg:n("--graph-bg","#0b0c0e"),edge:n("--graph-edge","#8b8e96"),accent:n("--graph-accent","#9dc4ff"),label:n("--graph-label","#d6d8dd"),labelHalo:n("--graph-label-halo","#0b0c0e"),other:n("--graph-other","#6b6e76"),groups:i,font:e.fontFamily||"ui-sans-serif, system-ui, sans-serif"}}function FD(t,e){const n=t.getContext("2d");if(n===null)throw new Error("Canvas 2D ist in diesem Browser nicht verfügbar.");const i=n;let a=V0(t.parentElement??t),s=null,l=[],o=new Map,c=[],u=new Map,h=null,g=()=>!1,d=new Set,p="auto",x=null,_=null;const m={x:0,y:0,k:1};let f=0,v=0,y=1,b=0,T=!0,M=!1,A=!1,S=null,N=-1;const D=(re,H)=>[(re-m.x)/m.k,(H-m.y)/m.k],U=re=>{const H=u.get(re.group)??-1;return H>=0?a.groups[H]??a.other:a.other},I=re=>!g(re.node);function Y(re,H){const[le,se]=D(re,H);let Me=null,Oe=1/0;for(const De of l){if(!I(De)||De.x===void 0||De.y===void 0)continue;const Re=Math.max(De.r,ff/m.k)+SD/m.k,Fe=De.x-le,ye=De.y-se,O=Fe*Fe+ye*ye;O<=Re*Re&&O<Oe&&(Me=De,Oe=O)}return Me}function k(){const re=_??x;if(re===null&&d.size>0)return new Set(d);if(re===null||s===null)return null;const H=new Set([re]);for(const le of s.neighbours.get(re)??[])H.add(le);return H}function B(){if(i.setTransform(1,0,0,1,0,0),i.fillStyle=a.bg,i.fillRect(0,0,t.width,t.height),l.length===0)return;i.setTransform(y*m.k,0,0,y*m.k,y*m.x,y*m.y);const re=k();i.lineCap="round",i.strokeStyle=a.edge,i.globalAlpha=re?wD:AD,i.lineWidth=RD/m.k,i.beginPath();const H=[];for(const se of c){const Me=se.source,Oe=se.target;if(!(!I(Me)||!I(Oe))){if(re&&(Me.id===(_??x)||Oe.id===(_??x))){H.push(se);continue}i.moveTo(Me.x??0,Me.y??0),i.lineTo(Oe.x??0,Oe.y??0)}}if(i.stroke(),H.length>0){i.strokeStyle=a.accent,i.globalAlpha=ND,i.lineWidth=DD/m.k,i.beginPath();for(const se of H){const Me=se.source,Oe=se.target;i.moveTo(Me.x??0,Me.y??0),i.lineTo(Oe.x??0,Oe.y??0)}i.stroke()}const le=ff/m.k;for(const se of l){if(!I(se))continue;const Me=Math.max(se.r,le);i.globalAlpha=re&&!re.has(se.id)?CD:1,i.fillStyle=U(se),i.beginPath(),i.arc(se.x??0,se.y??0,Me,0,Math.PI*2),i.fill()}i.globalAlpha=1,i.strokeStyle=a.accent;for(const se of l){if(!I(se))continue;const Me=se.id===x?2.2:se.id===_?1.6:d.has(se.id)?1.4:0;Me!==0&&(i.lineWidth=Me/m.k,i.beginPath(),i.arc(se.x??0,se.y??0,Math.max(se.r,le)+3/m.k,0,Math.PI*2),i.stroke())}z(re)}function z(re){const H=_??x,le=De=>De.id===x||De.id===_?4:re!=null&&re.has(De.id)?3:d.has(De.id)?2:0,se=[];for(const De of l){if(!I(De))continue;const Re=le(De);let Fe=0;if(Re>0)Fe=1;else if(re)Fe=0;else if(p==="always")Fe=1;else if(p==="auto"){const Ye=m.k+De.r*UD;Fe=Math.max(0,Math.min(1,(Ye-F0)/(LD-F0)))}if(Fe<=.02)continue;const ye=(De.x??0)*m.k+m.x,O=(De.y??0)*m.k+m.y;ye<-200||ye>f+200||O<-40||O>v+40||se.push({node:De,alpha:Fe,rank:Re*1e5+De.node.degree})}se.sort((De,Re)=>Re.rank-De.rank),i.setTransform(y,0,0,y,0,0),i.font=`500 ${B0}px ${a.font}`,i.textAlign="center",i.textBaseline="top",i.lineJoin="round";const Me=[];let Oe=0;for(const{node:De,alpha:Re}of se){if(Oe>=OD)break;const Fe=De.node.label,ye=i.measureText(Fe).width,O=Math.max(De.r,ff/m.k)*m.k,Ye=(De.x??0)*m.k+m.x,$e=(De.y??0)*m.k+m.y+O+PD,R={x0:Ye-ye/2-ru,y0:$e-ru,x1:Ye+ye/2+ru,y1:$e+B0+ru};!(De.id===H)&&Me.some(V=>R.x0<V.x1&&R.x1>V.x0&&R.y0<V.y1&&R.y1>V.y0)||(Me.push(R),Oe+=1,i.globalAlpha=Re,i.lineWidth=3,i.strokeStyle=a.labelHalo,i.strokeText(Fe,Ye,$e),i.fillStyle=De.id===x||De.id===_?a.accent:a.label,i.fillText(Fe,Ye,$e))}i.globalAlpha=1}function j(re){b=0;let H=!1;if(S!==null){const le=Math.min(1,(re-S.start)/kD),se=BD(le);m.x=S.from.x+(S.to.x-S.from.x)*se,m.y=S.from.y+(S.to.y-S.from.y)*se,m.k=S.from.k+(S.to.k-S.from.k)*se,le>=1&&(S=null),T=!0,H=!0}h!==null&&h.alpha()>h.alphaMin()?(h.tick(),T=!0,H=!0):h!==null&&!A&&(A=!0,M||he(),H=!0),T&&(B(),T=!1,m.k!==N&&(N=m.k,e.onZoom(m.k))),H&&X()}function X(){b===0&&(b=requestAnimationFrame(j))}function q(){T=!0,X()}function ve(re){if(G0){m.x=re.x,m.y=re.y,m.k=re.k,S=null,q();return}S={from:{...m},to:re,start:performance.now()},X()}function he(re=!1){let H=1/0,le=1/0,se=-1/0,Me=-1/0;for(const Re of l)!I(Re)||Re.x===void 0||Re.y===void 0||(H=Math.min(H,Re.x-Re.r),le=Math.min(le,Re.y-Re.r),se=Math.max(se,Re.x+Re.r),Me=Math.max(Me,Re.y+Re.r));if(!Number.isFinite(H)||f===0||v===0)return;const Oe=Math.max(k0,Math.min(I0,Math.min((f-H0*2)/Math.max(1,se-H),(v-H0*2)/Math.max(1,Me-le)))),De={k:Oe,x:f/2-(H+se)/2*Oe,y:v/2-(le+Me)/2*Oe};if(re){m.x=De.x,m.y=De.y,m.k=De.k,S=null,q();return}ve(De)}function Ae(re,H,le){const se=Math.max(k0,Math.min(I0,m.k*le)),[Me,Oe]=D(re,H);m.x=re-Me*se,m.y=H-Oe*se,m.k=se,S=null,M=!0,q()}let $=null;const pe=re=>{const H=t.getBoundingClientRect();return[re.clientX-H.left,re.clientY-H.top]};function we(re){if(re.button!==0)return;const[H,le]=pe(re);$={x:H,y:le,node:Y(H,le),moved:!1,pointerId:re.pointerId},t.setPointerCapture(re.pointerId),S=null}function ne(re){const[H,le]=pe(re);if($===null){const Oe=Y(H,le),De=(Oe==null?void 0:Oe.id)??null;t.style.cursor=Oe?"pointer":"grab",De!==_&&(_=De,q());return}const se=H-$.x,Me=le-$.y;if(!(!$.moved&&Math.hypot(se,Me)<yD))if($.moved=!0,M=!0,t.style.cursor="grabbing",$.node!==null&&h!==null){const[Oe,De]=D(H,le);$.node.fx=Oe,$.node.fy=De,h.alphaTarget(.25),h.alpha()<.25&&h.alpha(.25),q()}else m.x+=re.movementX,m.y+=re.movementY,q()}function ge(re){if($===null)return;const H=$.moved,le=$.node;if(t.hasPointerCapture($.pointerId)&&t.releasePointerCapture($.pointerId),$=null,t.style.cursor=le?"pointer":"grab",le!==null&&h!==null&&(le.fx=null,le.fy=null,h.alphaTarget(0)),!H){const[se,Me]=pe(re),Oe=Y(se,Me);x=(Oe==null?void 0:Oe.id)??null,e.onSelect(x)}q()}function ue(re){const[H,le]=pe(re),se=Y(H,le);se!==null&&e.onOpen(se.id)}function Le(re){re.preventDefault();const[H,le]=pe(re);Ae(H,le,Math.exp(-re.deltaY*bD))}function Ie(){$===null&&_!==null&&(_=null,q())}t.addEventListener("pointerdown",we),t.addEventListener("pointermove",ne),t.addEventListener("pointerup",ge),t.addEventListener("pointercancel",ge),t.addEventListener("pointerleave",Ie),t.addEventListener("dblclick",ue),t.addEventListener("wheel",Le,{passive:!1}),t.style.cursor="grab",t.style.touchAction="none";function Xe(re){s=re,u=new Map(re.groups.map(Re=>[Re.name,Re.slot]));const H=o,le=H.size===0,se=re.groups.map(Re=>Re.name),Me=18*Math.sqrt(re.nodes.length),Oe=new Map;se.forEach((Re,Fe)=>{const ye=Fe/Math.max(1,se.length)*Math.PI*2,O=Fe===0?0:Me;Oe.set(Re,[Math.cos(ye)*O,Math.sin(ye)*O])});const De=re.nodes.map(Re=>{const Fe=H.get(Re.id),ye={id:Re.id,r:ID(Re.degree),group:Re.group,node:Re};if((Fe==null?void 0:Fe.x)!==void 0&&Fe.y!==void 0)ye.x=Fe.x,ye.y=Fe.y,ye.vx=Fe.vx??0,ye.vy=Fe.vy??0;else{const[O,Ye]=Oe.get(Re.group)??[0,0],$e=6*Math.sqrt(re.nodes.length/Math.max(1,se.length));ye.x=O+(Math.random()-.5)*$e,ye.y=Ye+(Math.random()-.5)*$e}return ye});if(l=De,o=new Map(De.map(Re=>[Re.id,Re])),c=re.edges.filter(Re=>o.has(Re.source)&&o.has(Re.target)).map(Re=>({source:Re.source,target:Re.target})),h==null||h.stop(),h=gD(De).force("link",tD(c).id(Re=>Re.id).distance(30)).force("charge",xD().strength(-42).distanceMax(420).theta(.9)).force("x",vD(0).strength(.04)).force("y",_D(0).strength(.04)).force("collide",J2(Re=>Re.r+1.5).strength(.7).iterations(1)).stop(),G0){const Re=Math.ceil(Math.log(h.alphaMin())/Math.log(1-h.alphaDecay()));h.tick(Re)}else le?h.tick(zD):h.alpha(.3);x!==null&&!o.has(x)&&(x=null,e.onSelect(null)),le&&(M=!1,A=!1,he(!0)),q()}function dt(){const re=t.getBoundingClientRect();y=window.devicePixelRatio||1;const H=f>0;f=re.width,v=re.height,t.width=Math.max(1,Math.round(f*y)),t.height=Math.max(1,Math.round(v*y)),!H&&l.length>0&&he(!0),q()}return{setModel:Xe,setHidden(re){g=re,q()},setQuery(re){d=re,q()},setLabelMode(re){p=re,q()},setSelected(re){x=re,q()},setHovered(re){_=re,q()},focusNode(re){const H=o.get(re);if((H==null?void 0:H.x)===void 0||H.y===void 0)return;M=!0;const le=Math.max(m.k,jD);ve({k:le,x:f/2-H.x*le,y:v/2-H.y*le})},fit(){M=!0,he()},zoomBy(re){Ae(f/2,v/2,re)},resize:dt,refreshColours(){a=V0(t.parentElement??t),q()},dispose(){b!==0&&cancelAnimationFrame(b),h==null||h.stop(),t.removeEventListener("pointerdown",we),t.removeEventListener("pointermove",ne),t.removeEventListener("pointerup",ge),t.removeEventListener("pointercancel",ge),t.removeEventListener("pointerleave",Ie),t.removeEventListener("dblclick",ue),t.removeEventListener("wheel",Le)}}}const HD=8,rl="Weitere",GD=new Set(["file","class","interface","note"]);function VD(t){return typeof t=="string"&&GD.has(t)?t:"other"}function Ii(t){return typeof t=="string"&&t!==""?t:null}function WD(t){return typeof t=="number"&&Number.isFinite(t)?t:null}function XD(t){if(t===null)return rl;const e=t.split("/").filter(Boolean);return e.length>=2&&e[0]==="Code"?e[1]:e.length>=2?e[0]:"Workspace"}function qD(t){var u,h,g;const e=new Map,n={file:0,class:0,interface:0,note:0,other:0};for(const d of t.nodes){const p=Ii(d==null?void 0:d.id);if(p===null||e.has(p))continue;const x=Ii((u=d.properties)==null?void 0:u.path),_=VD(d.type),m=Ii(d.label)??Ii(d.name)??(x==null?void 0:x.split("/").pop())??p;e.set(p,{id:p,label:m,kind:_,path:x,group:XD(x),lines:WD((h=d.properties)==null?void 0:h.lines),lang:Ii((g=d.properties)==null?void 0:g.lang),degree:0}),n[_]+=1}const i=[],a=new Map,s=new Set;for(const d of t.edges){const p=Ii(d==null?void 0:d.sourceId)??Ii(d==null?void 0:d.source),x=Ii(d==null?void 0:d.targetId)??Ii(d==null?void 0:d.target);if(p===null||x===null||p===x)continue;const _=e.get(p),m=e.get(x);if(_===void 0||m===void 0)continue;const f=Ii(d.type)??"link",v=`${p}\0${x}\0${f}`;s.has(v)||(s.add(v),i.push({source:p,target:x,kind:f}),_.degree+=1,m.degree+=1,a.has(p)||a.set(p,new Set),a.has(x)||a.set(x,new Set),a.get(p).add(x),a.get(x).add(p))}const l=new Map;for(const d of e.values())l.set(d.group,(l.get(d.group)??0)+1);const c=[...l.entries()].sort((d,p)=>p[1]-d[1]||d[0].localeCompare(p[0])).map(([d,p],x)=>({name:d,count:p,slot:x<HD&&d!==rl?x:-1}));return{nodes:[...e.values()],edges:i,groups:c,byId:e,neighbours:a,kindCounts:n}}function YD(t,e){var i;const n=e.trim().toLowerCase();return n===""?!1:t.label.toLowerCase().includes(n)||(((i=t.path)==null?void 0:i.toLowerCase().includes(n))??!1)}function QS(t){return[...t].sort((e,n)=>n.degree-e.degree||e.label.localeCompare(n.label))}const Wa=60;function JS({timeline:t}){return r.jsxs("div",{className:"pb-timeline",children:[r.jsx("button",{type:"button",className:"pb-tool pb-tool--icon",onClick:t.onTogglePlay,"aria-label":t.playing?"Wachstum anhalten":"Wachstum abspielen",title:t.playing?"Anhalten":"Wachstum abspielen",children:r.jsx(ot,{path:t.playing?st.pause:st.play})}),r.jsx("input",{type:"range",min:0,max:Wa,step:1,value:t.step,"aria-label":"Zeitpunkt des Index","aria-valuetext":t.label,onChange:e=>t.onScrub(Number(e.target.value))}),r.jsx("span",{className:"pb-timeline__label pb-num",children:t.label})]})}const pf=120,lu=1.25,W0={file:"Dateien",class:"Klassen",interface:"Interfaces",note:"Notizen",other:"Sonstige"},$D={file:"Datei",class:"Klasse",interface:"Interface",note:"Notiz",other:"Objekt"},KD={imports:["Importiert","Wird importiert von"],defines:["Definiert","Definiert in"],links:["Verweist auf","Verwiesen von"]},Fi=new Intl.NumberFormat("de-DE");function ZD({graph:t,coverage:e,error:n,onRetry:i,onOpenSource:a,timeline:s}){const l=P.useMemo(()=>t?qD(t):null,[t]),o=P.useRef(null),c=P.useRef(null),u=P.useRef(null),h=P.useRef(null),g=P.useRef(null),[d,p]=P.useState(""),[x,_]=P.useState(""),[m,f]=P.useState(new Set),[v,y]=P.useState(new Set),[b,T]=P.useState("auto"),[M,A]=P.useState(null),[S,N]=P.useState(!0),D=P.useCallback($=>{const pe=l==null?void 0:l.byId.get($);pe!=null&&pe.path&&a(pe.path)},[l,a]),U=P.useRef(D);U.current=D,P.useEffect(()=>{const $=o.current,pe=c.current;if($===null||pe===null)return;let we;try{we=FD($,{onHover:()=>{},onSelect:ue=>A(ue),onOpen:ue=>U.current(ue),onZoom:ue=>{h.current&&(h.current.textContent=`${Math.round(ue*100)} %`)}})}catch(ue){p(ue instanceof Error?ue.message:String(ue));return}g.current=we;const ne=new ResizeObserver(()=>we.resize());ne.observe(pe),we.resize();const ge=new MutationObserver(()=>we.refreshColours());return ge.observe(document.documentElement,{attributes:!0,attributeFilter:["data-theme"]}),()=>{ge.disconnect(),ne.disconnect(),we.dispose(),g.current=null}},[]),P.useEffect(()=>{var $;l!==null&&(($=g.current)==null||$.setModel(l))},[l]),P.useEffect(()=>{var $;($=g.current)==null||$.setHidden(pe=>m.has(pe.kind)||v.has(X0(l,pe)))},[m,v,l]);const I=P.useMemo(()=>l===null||x.trim()===""?new Set:new Set(l.nodes.filter($=>YD($,x)).map($=>$.id)),[l,x]);P.useEffect(()=>{var $;($=g.current)==null||$.setQuery(I)},[I]),P.useEffect(()=>{var $;($=g.current)==null||$.setLabelMode(b)},[b]),P.useEffect(()=>{var $;($=g.current)==null||$.setSelected(M)},[M]);const Y=P.useCallback(($,pe=!0)=>{var we;A($),pe&&((we=g.current)==null||we.focusNode($))},[]),k=P.useMemo(()=>{if(l===null)return[];const $=x.trim()===""?l.nodes:l.nodes.filter(pe=>I.has(pe.id));return QS($.filter(pe=>!m.has(pe.kind)&&!v.has(X0(l,pe))))},[l,x,I,m,v]),B=M!==null?(l==null?void 0:l.byId.get(M))??null:null;P.useEffect(()=>{const $=pe=>{var ge,ue,Le,Ie;const we=pe.target,ne=we!==null&&(we.tagName==="INPUT"||we.tagName==="TEXTAREA"||we.isContentEditable);if(pe.key==="Escape"){if(ne&&we===u.current&&x!==""){_("");return}if(M!==null){A(null);return}ne&&we.blur();return}ne||pe.metaKey||pe.ctrlKey||pe.altKey||(pe.key==="/"?(pe.preventDefault(),(ge=u.current)==null||ge.focus()):pe.key==="f"||pe.key==="F"?(ue=g.current)==null||ue.fit():pe.key==="+"||pe.key==="="?(Le=g.current)==null||Le.zoomBy(lu):pe.key==="-"&&((Ie=g.current)==null||Ie.zoomBy(1/lu)))};return window.addEventListener("keydown",$),()=>window.removeEventListener("keydown",$)},[x,M]);const z=($,pe)=>{const we=new Set($);return we.has(pe)?we.delete(pe):we.add(pe),we},j=$=>{if($.key!=="ArrowDown"&&$.key!=="ArrowUp")return;$.preventDefault();const pe=[...$.currentTarget.querySelectorAll("button")],we=pe.indexOf(document.activeElement),ne=pe[Math.max(0,Math.min(pe.length-1,we+($.key==="ArrowDown"?1:-1)))];ne==null||ne.focus()},X=$=>{var pe;$.key==="Enter"&&k[0]&&Y(k[0].id),$.key==="ArrowDown"&&($.preventDefault(),(pe=document.querySelector(".pb-graph__list button"))==null||pe.focus())},q=n?"offline":l===null?"loading":l.nodes.length===0?"empty":"ready",ve=(l==null?void 0:l.groups.filter($=>$.slot>=0))??[],he=(l==null?void 0:l.groups.filter($=>$.slot<0))??[],Ae=he.reduce(($,pe)=>$+pe.count,0);return r.jsxs("section",{className:"pb-graph","data-inspector":B?"open":"closed","aria-label":"Wissensgraph",children:[r.jsxs("aside",{className:"pb-graph__side","aria-label":"Objekte im Graph",children:[r.jsxs("label",{className:"pb-search",children:[r.jsx(ot,{path:st.search}),r.jsx("input",{ref:u,type:"search",value:x,placeholder:"Datei oder Symbol suchen","aria-label":"Datei oder Symbol im Graph suchen",spellCheck:!1,autoComplete:"off",onChange:$=>_($.target.value),onKeyDown:X}),r.jsx("kbd",{"aria-hidden":"true",children:"/"})]}),l!==null&&r.jsxs(r.Fragment,{children:[r.jsx("div",{className:"pb-chips",role:"group","aria-label":"Objektarten anzeigen",children:Object.keys(W0).filter($=>l.kindCounts[$]>0).map($=>r.jsxs("button",{type:"button",className:"pb-chip","aria-pressed":!m.has($),onClick:()=>f(pe=>z(pe,$)),children:[W0[$]," ",r.jsx("span",{className:"pb-num",children:Fi.format(l.kindCounts[$])})]},$))}),r.jsx("h2",{className:"pb-heading",children:"Bereiche"}),r.jsxs("ul",{className:"pb-groups","aria-label":"Bereiche anzeigen oder ausblenden",children:[ve.map($=>r.jsx("li",{children:r.jsxs("button",{type:"button","aria-pressed":!v.has($.name),onClick:()=>y(pe=>z(pe,$.name)),children:[r.jsx("i",{className:"pb-dot","data-slot":$.slot,"aria-hidden":"true"}),r.jsx("span",{className:"pb-groups__name",title:$.name,children:$.name}),r.jsx("span",{className:"pb-num",children:Fi.format($.count)})]})},$.name)),he.length>0&&r.jsx("li",{children:r.jsxs("button",{type:"button","aria-pressed":!v.has(rl),onClick:()=>y($=>z($,rl)),children:[r.jsx("i",{className:"pb-dot","data-slot":"-1","aria-hidden":"true"}),r.jsxs("span",{className:"pb-groups__name",children:[rl," · ",he.length," Bereiche"]}),r.jsx("span",{className:"pb-num",children:Fi.format(Ae)})]})})]}),r.jsx("h2",{className:"pb-heading",children:x.trim()===""?"Am stärksten vernetzt":`${Fi.format(k.length)} Treffer`}),k.length===0?r.jsx("p",{className:"pb-quiet",children:x.trim()===""?"Alle Objekte sind ausgeblendet.":"Kein Objekt passt zur Suche."}):r.jsxs("ol",{className:"pb-graph__list",onKeyDown:j,children:[k.slice(0,pf).map($=>r.jsx("li",{children:r.jsxs("button",{type:"button","aria-current":$.id===M?"true":void 0,onClick:()=>Y($.id),onDoubleClick:()=>D($.id),onMouseEnter:()=>{var pe;return(pe=g.current)==null?void 0:pe.setHovered($.id)},onMouseLeave:()=>{var pe;return(pe=g.current)==null?void 0:pe.setHovered(null)},onFocus:()=>{var pe;return(pe=g.current)==null?void 0:pe.setHovered($.id)},onBlur:()=>{var pe;return(pe=g.current)==null?void 0:pe.setHovered(null)},children:[r.jsx("i",{className:"pb-dot","data-slot":Rg(l,$),"aria-hidden":"true"}),r.jsxs("span",{className:"pb-row__text",children:[r.jsx("span",{className:"pb-row__label",children:$.label}),$.path&&r.jsx("span",{className:"pb-row__path",children:JD($.path)})]}),r.jsx("span",{className:"pb-num",title:`${$.degree} Verbindungen`,children:$.degree})]})},$.id)),k.length>pf&&r.jsxs("li",{className:"pb-quiet",children:[Fi.format(k.length-pf)," weitere — Suche verfeinern"]})]})]})]}),r.jsxs("div",{className:"pb-graph__stage",ref:c,children:[r.jsx("canvas",{ref:o,className:"pb-graph__canvas",role:"img","aria-label":l?`Graph mit ${l.nodes.length} Objekten und ${l.edges.length} Kanten. Die Liste links ist die Tastaturansicht.`:"Graph","data-state":q}),q==="ready"&&r.jsxs("div",{className:"pb-toolbar",role:"toolbar","aria-label":"Graph-Ansicht",children:[r.jsxs("button",{type:"button",className:"pb-tool",onClick:()=>{var $;return($=g.current)==null?void 0:$.fit()},title:"Alles einpassen (F)",children:[r.jsx(ot,{path:st.fit}),r.jsx("span",{children:"Einpassen"})]}),r.jsx("span",{className:"pb-toolbar__sep","aria-hidden":"true"}),r.jsx("button",{type:"button",className:"pb-tool pb-tool--icon",onClick:()=>{var $;return($=g.current)==null?void 0:$.zoomBy(1/lu)},"aria-label":"Herauszoomen",title:"Herauszoomen (−)",children:r.jsx(ot,{path:st.minus})}),r.jsx("span",{className:"pb-toolbar__zoom pb-num",ref:h,"aria-live":"off",children:"100 %"}),r.jsx("button",{type:"button",className:"pb-tool pb-tool--icon",onClick:()=>{var $;return($=g.current)==null?void 0:$.zoomBy(lu)},"aria-label":"Hineinzoomen",title:"Hineinzoomen (+)",children:r.jsx(ot,{path:st.plus})}),r.jsx("span",{className:"pb-toolbar__sep","aria-hidden":"true"}),r.jsx("div",{className:"pb-segmented",role:"radiogroup","aria-label":"Beschriftungen",children:[["auto","Auto"],["always","Alle"],["off","Aus"]].map(([$,pe])=>r.jsx("button",{type:"button",role:"radio","aria-checked":b===$,onClick:()=>T($),children:pe},$))}),r.jsx("span",{className:"pb-toolbar__sep","aria-hidden":"true"}),r.jsx("button",{type:"button",className:`pb-tool ${S?"pb-tool--active":""}`,onClick:()=>N($=>!$),title:"Graph-Legende ein-/ausblenden",children:r.jsx("span",{children:"Legende"})}),s&&r.jsxs(r.Fragment,{children:[r.jsx("span",{className:"pb-toolbar__sep","aria-hidden":"true"}),r.jsx(JS,{timeline:s})]})]}),q==="ready"&&S&&r.jsxs("aside",{className:"pb-graph-legend","aria-label":"Graph-Legende",children:[r.jsxs("header",{className:"pb-graph-legend__head",children:[r.jsx("strong",{children:"Graph-Legende"}),r.jsx("button",{type:"button",className:"pb-tool pb-tool--icon",style:{width:"20px",height:"20px",padding:0},onClick:()=>N(!1),"aria-label":"Legende ausblenden",title:"Ausblenden",children:"✕"})]}),r.jsxs("div",{className:"pb-graph-legend__body",children:[r.jsxs("div",{className:"pb-graph-legend__row",children:[r.jsx("span",{className:"pb-legend-dot-sample"}),r.jsxs("div",{children:[r.jsx("strong",{children:"Knotengröße:"})," Vernetzungsgrad (mehr Verbindungen = größerer Knoten)"]})]}),r.jsxs("div",{className:"pb-graph-legend__row",children:[r.jsx("span",{className:"pb-legend-line-sample"}),r.jsxs("div",{children:[r.jsx("strong",{children:"Kanten:"})," Beziehungen (Code-Importe, Symbol-Definitionen, Wiki-Links)"]})]}),r.jsxs("div",{className:"pb-graph-legend__row",children:[r.jsx("span",{className:"pb-legend-color-sample"}),r.jsxs("div",{children:[r.jsx("strong",{children:"Farben:"})," Repositories & Wissensbereiche (Farben links zugeordnet)"]})]})]})]}),q==="ready"&&l&&r.jsxs("p",{className:"pb-graph__status",role:"status",children:[r.jsx("span",{className:"pb-num",children:Fi.format(l.nodes.length)})," Objekte ·"," ",r.jsx("span",{className:"pb-num",children:Fi.format(l.edges.length)})," Kanten",e&&e.totalFiles>e.shownFiles&&r.jsxs("span",{title:"Der Graph zeigt die am stärksten vernetzten Dateien; Suche und Explorer umfassen den ganzen Index.",children:[" ","· Ausschnitt aus ",r.jsx("span",{className:"pb-num",children:Fi.format(e.totalFiles)})," Dateien"]}),e&&!e.indexComplete&&r.jsxs("span",{className:"pb-graph__stale",children:[" · ",Fi.format(e.staleFiles??0)," Dateien warten auf den Index"]})]}),q!=="ready"&&r.jsxs("div",{className:"pb-state",role:q==="offline"?"alert":"status",children:[q==="loading"&&r.jsxs(r.Fragment,{children:[r.jsx("div",{className:"pb-state__bar","aria-hidden":"true",children:r.jsx("span",{})}),r.jsx("h2",{children:"Graph wird geladen"}),r.jsx("p",{children:"Der aktuelle Stand wird aus dem Brain gelesen."})]}),q==="empty"&&r.jsxs(r.Fragment,{children:[r.jsx("h2",{children:"Noch keine indizierten Objekte"}),r.jsx("p",{children:"Sobald der Index Dateien enthält, erscheinen sie hier."})]}),q==="offline"&&r.jsxs(r.Fragment,{children:[r.jsx("h2",{children:"PlugBrain ist nicht erreichbar"}),r.jsx("p",{children:n}),r.jsx("button",{type:"button",className:"pb-button pb-button--primary",onClick:i,children:"Erneut verbinden"})]})]}),d&&r.jsxs("div",{className:"pb-state",role:"alert",children:[r.jsx("h2",{children:"Der Graph kann hier nicht gezeichnet werden"}),r.jsxs("p",{children:[d," Die Liste links bleibt vollständig nutzbar."]})]})]}),B&&l&&r.jsx(QD,{node:B,model:l,onClose:()=>A(null),onSelect:$=>Y($),onOpen:()=>D(B.id),onCentre:()=>{var $;return($=g.current)==null?void 0:$.focusNode(B.id)}})]})}function QD({node:t,model:e,onClose:n,onSelect:i,onOpen:a,onCentre:s}){const l=P.useMemo(()=>{const c=new Map,u=(h,g)=>{if(g===void 0)return;const d=c.get(h)??[];d.push(g),c.set(h,d)};for(const h of e.edges){const[g,d]=KD[h.kind]??["Verbunden mit","Verbunden mit"];h.source===t.id?u(g,e.byId.get(h.target)):h.target===t.id&&u(d,e.byId.get(h.source))}return[...c.entries()].map(([h,g])=>[h,QS(g)])},[t,e]),o=e.groups.find(c=>c.name===t.group);return r.jsxs("aside",{className:"pb-inspector","aria-label":`Details zu ${t.label}`,children:[r.jsxs("header",{className:"pb-inspector__head",children:[r.jsxs("div",{className:"pb-inspector__kicker",children:[r.jsx("i",{className:"pb-dot","data-slot":(o==null?void 0:o.slot)??-1,"aria-hidden":"true"}),r.jsx("span",{children:$D[t.kind]}),r.jsx("span",{"aria-hidden":"true",children:"·"}),r.jsx("span",{className:"pb-inspector__group",title:t.group,children:t.group})]}),r.jsx("button",{type:"button",className:"pb-tool pb-tool--icon",onClick:n,"aria-label":"Details schließen",title:"Schließen (Esc)",children:r.jsx(ot,{path:st.close})})]}),r.jsx("h2",{className:"pb-inspector__title",children:t.label}),t.path&&r.jsx("p",{className:"pb-inspector__path",children:t.path}),r.jsxs("dl",{className:"pb-facts",children:[r.jsxs("div",{children:[r.jsx("dt",{children:"Verbindungen"}),r.jsx("dd",{className:"pb-num",children:t.degree})]}),t.lines!==null&&r.jsxs("div",{children:[r.jsx("dt",{children:"Zeilen"}),r.jsx("dd",{className:"pb-num",children:Fi.format(t.lines)})]}),t.lang&&r.jsxs("div",{children:[r.jsx("dt",{children:"Sprache"}),r.jsx("dd",{children:t.lang})]})]}),r.jsxs("div",{className:"pb-inspector__actions",children:[t.path&&r.jsxs("button",{type:"button",className:"pb-button pb-button--primary",onClick:a,children:[r.jsx(ot,{path:st.open})," ",t.kind==="note"?"Notiz öffnen":"Quelle öffnen"]}),r.jsxs("button",{type:"button",className:"pb-button",onClick:s,children:[r.jsx(ot,{path:st.centre})," Zentrieren"]})]}),l.length===0?r.jsx("p",{className:"pb-quiet",children:"Dieses Objekt hat im gezeigten Ausschnitt keine Verbindungen."}):l.map(([c,u])=>r.jsxs("section",{className:"pb-inspector__section",children:[r.jsxs("h3",{className:"pb-heading",children:[c," ",r.jsx("span",{className:"pb-num",children:u.length})]}),r.jsxs("ul",{children:[u.slice(0,40).map(h=>r.jsx("li",{children:r.jsxs("button",{type:"button",onClick:()=>i(h.id),children:[r.jsx("i",{className:"pb-dot","data-slot":Rg(e,h),"aria-hidden":"true"}),r.jsx("span",{className:"pb-row__label",children:h.label})]})},h.id)),u.length>40&&r.jsxs("li",{className:"pb-quiet",children:[u.length-40," weitere"]})]})]},c))]})}function Rg(t,e){var n;return((n=t.groups.find(i=>i.name===e.group))==null?void 0:n.slot)??-1}function X0(t,e){return t===null||Rg(t,e)>=0?e.group:rl}function JD(t){const e=t.split("/");return e.pop(),e.slice(-3).join("/")}function eL({workspaceId:t,workspaceName:e,onOpenFile:n,onOpenNotes:i,initialData:a}){var m,f,v,y,b;const[s,l]=P.useState(a??null),[o,c]=P.useState(!a),[u,h]=P.useState(!1),[g,d]=P.useState(!1),[p,x]=P.useState(null),_=()=>{if(a)return;c(!0),h(!1),d(!1),x(null);let T=!0;return O1(t).then(M=>{T&&(c(!1),"routeMissing"in M?h(!0):"unreachable"in M?d(!0):"serverError"in M?x(`Fehler ${M.status}: ${M.error}`):l(M))}).catch(M=>{T&&(c(!1),x(M instanceof Error?M.message:String(M)))}),()=>{T=!1}};return P.useEffect(()=>{if(a){l(a),c(!1),h(!1),d(!1);return}return _()},[t,a]),r.jsxs("div",{className:"briefing-view",children:[r.jsxs("div",{className:"pb-view-header",children:[r.jsx("h2",{children:"Projekt-Briefing"}),r.jsx("p",{children:"Projektüberblick, Kennzahlen, Einstiegspunkte und Hotspots"})]}),r.jsxs("div",{className:"briefing-body",children:[o&&r.jsx("div",{className:"briefing-card briefing-card--loading",role:"status",children:r.jsx("p",{children:"Briefing wird geladen …"})}),u&&!o&&r.jsxs("div",{className:"briefing-card briefing-card--unavailable",role:"status",children:[r.jsx("div",{className:"briefing-unavailable-icon",children:r.jsx(ot,{path:st.info})}),r.jsx("h3",{children:"Briefing in dieser Version noch nicht verfügbar"}),r.jsxs("p",{children:["Die Route ",r.jsx("code",{children:"GET /api/briefing"})," ist im angebundenen Kern noch nicht aktiv. Sobald Lane ASK-01 bereitgestellt ist, erscheinen hier automatisch Projekt-Zusammenfassung, Kennzahlen, Einstiegspunkte und Code-Hotspots."]})]}),g&&!o&&r.jsxs("div",{className:"briefing-card briefing-card--error",role:"alert",children:[r.jsx("h3",{children:"Brain nicht erreichbar"}),r.jsxs("p",{children:["Keine Verbindung zum Brain-Server — läuft ",r.jsx("code",{children:"plugbrain serve"}),"?"]}),r.jsx("button",{type:"button",className:"btn btn-secondary",onClick:_,children:"Erneut versuchen"})]}),p&&!o&&r.jsxs("div",{className:"briefing-card briefing-card--error",role:"alert",children:[r.jsx("h3",{children:"Fehler beim Laden des Briefings"}),r.jsx("p",{children:p}),r.jsx("button",{type:"button",className:"btn btn-secondary",onClick:_,children:"Erneut versuchen"})]}),s&&s.unavailable&&s.unavailable.length>0&&r.jsx("div",{className:"briefing-card briefing-card--notice",role:"status",children:r.jsxs("p",{children:["Hinweis: Folgende Bereiche sind nicht verfügbar: ",s.unavailable.join(", ")]})}),s&&!o&&r.jsxs("div",{className:"briefing-grid",children:[r.jsxs("section",{className:"briefing-card briefing-card--summary",children:[r.jsx("header",{className:"briefing-card__head",children:r.jsx("h3",{children:s.name||e||"Workspace"})}),r.jsx("p",{className:"briefing-summary-text",children:s.summary||"Keine Beschreibung hinterlegt."})]}),r.jsxs("section",{className:"briefing-card briefing-card--stats",children:[r.jsx("header",{className:"briefing-card__head",children:r.jsx("h3",{children:"Kennzahlen"})}),r.jsxs("div",{className:"briefing-stats-row",children:[r.jsxs("div",{className:"briefing-stat",children:[r.jsx("span",{className:"briefing-stat__num",children:((m=s.stats)==null?void 0:m.repos)??0}),r.jsx("span",{className:"briefing-stat__label",children:"Repos"})]}),r.jsxs("div",{className:"briefing-stat",children:[r.jsx("span",{className:"briefing-stat__num",children:((f=s.stats)==null?void 0:f.files)??0}),r.jsx("span",{className:"briefing-stat__label",children:"Dateien"})]}),r.jsxs("div",{className:"briefing-stat",children:[r.jsx("span",{className:"briefing-stat__num",children:((v=s.stats)==null?void 0:v.symbols)??0}),r.jsx("span",{className:"briefing-stat__label",children:"Symbole"})]}),r.jsxs("div",{className:"briefing-stat",children:[r.jsx("span",{className:"briefing-stat__num",children:((y=s.stats)==null?void 0:y.notes)??0}),r.jsx("span",{className:"briefing-stat__label",children:"Notizen"})]})]}),((b=s.stats)==null?void 0:b.languages)&&s.stats.languages.length>0&&r.jsxs("div",{className:"briefing-languages",children:[r.jsx("strong",{className:"briefing-subhead",children:"Sprachen:"}),r.jsx("div",{className:"briefing-lang-chips",children:s.stats.languages.map(T=>r.jsxs("span",{className:"briefing-lang-chip",children:[T.name," (",T.files,")"]},T.name))})]})]}),r.jsxs("section",{className:"briefing-card briefing-card--entrypoints",children:[r.jsx("header",{className:"briefing-card__head",children:r.jsx("h3",{children:"Einstiegspunkte"})}),!s.entrypoints||s.entrypoints.length===0?r.jsx("p",{className:"briefing-empty",children:"Keine Einstiegspunkte erkannt."}):r.jsx("ul",{className:"briefing-list",children:s.entrypoints.map(T=>r.jsxs("li",{className:"briefing-list-item",children:[r.jsxs("button",{type:"button",className:"briefing-link-btn",onClick:()=>n(T.path),children:[r.jsx(ot,{path:st.file}),r.jsx("span",{className:"briefing-path",children:T.path})]}),T.why&&r.jsx("span",{className:"briefing-desc",children:T.why})]},T.path))})]}),r.jsxs("section",{className:"briefing-card briefing-card--changes",children:[r.jsx("header",{className:"briefing-card__head",children:r.jsx("h3",{children:"Zuletzt geändert"})}),!s.recentChanges||s.recentChanges.length===0?r.jsx("p",{className:"briefing-empty",children:"Keine aktuellen Änderungen verzeichnet."}):r.jsx("ul",{className:"briefing-list",children:s.recentChanges.map(T=>r.jsxs("li",{className:"briefing-list-item",children:[r.jsx("button",{type:"button",className:"briefing-link-btn",onClick:()=>n(T.path),children:r.jsx("span",{className:"briefing-path",children:T.path})}),r.jsxs("div",{className:"briefing-change-meta",children:[r.jsx("span",{className:"briefing-kind-badge",children:T.kind}),r.jsx("time",{className:"briefing-time",children:new Date(T.when).toLocaleString()})]})]},T.path+T.when))})]}),r.jsxs("section",{className:"briefing-card briefing-card--hotspots",children:[r.jsx("header",{className:"briefing-card__head",children:r.jsx("h3",{children:"Code-Hotspots (hohe Vernetzung)"})}),!s.hotspots||s.hotspots.length===0?r.jsx("p",{className:"briefing-empty",children:"Keine Hotspots identifiziert."}):r.jsx("ul",{className:"briefing-list",children:s.hotspots.map(T=>r.jsxs("li",{className:"briefing-list-item",children:[r.jsxs("button",{type:"button",className:"briefing-link-btn",onClick:()=>n(T.path),children:[r.jsx(ot,{path:st.code}),r.jsx("span",{className:"briefing-path",children:T.path})]}),r.jsxs("span",{className:"briefing-degree-badge",children:["Grad ",T.degree]})]},T.path))})]})]})]})]})}function wo(t){return t==="risk"?"hygiene-risk":t==="attention"?"hygiene-attention":"hygiene-ok"}function No(t){return t==="risk"?"● Risiko":t==="attention"?"◌ Achtung":"● Ok"}function _d({cmd:t,label:e}){const[n,i]=P.useState(!1),a=P.useCallback(()=>{navigator.clipboard.writeText(t).then(()=>{i(!0),setTimeout(()=>i(!1),2e3)}).catch(()=>{})},[t]);return r.jsxs("div",{className:"hygiene-cmd-row",children:[r.jsx("code",{className:"hygiene-cmd",children:t}),r.jsx("button",{type:"button",className:"hygiene-copy-btn",onClick:a,title:e??"Befehl in Zwischenablage kopieren","aria-label":e??"Befehl kopieren",children:n?"Kopiert":"Kopieren"})]})}function tL({co:t}){const[e,n]=P.useState(!1),i=t.dirtyFiles>0||t.untrackedFiles>0||t.stashes>0||t.unpushed.length>0||t.orphan,a=t.orphan||t.dirtyFiles>5?"risk":i?"attention":"ok";return r.jsxs("div",{className:`hygiene-checkout-card ${wo(a)}`,children:[r.jsxs("button",{type:"button",className:"hygiene-checkout-header",onClick:()=>n(s=>!s),"aria-expanded":e,children:[r.jsx("span",{className:`hygiene-badge hygiene-badge-${a}`,children:No(a)}),r.jsx("span",{className:"hygiene-checkout-path",children:t.path}),r.jsxs("span",{className:"hygiene-checkout-meta",children:[t.repo," · ",t.branch]}),r.jsx("span",{className:"hygiene-toggle",children:e?"▲":"▼"})]}),e&&r.jsxs("div",{className:"hygiene-checkout-detail",children:[r.jsxs("div",{className:"hygiene-stat-grid",children:[r.jsxs("div",{className:"hygiene-stat",children:[r.jsx("span",{className:"hygiene-stat-label",children:"Geänderte Dateien"}),r.jsx("span",{className:`hygiene-stat-value ${t.dirtyFiles>0?"hygiene-stat-warn":""}`,children:t.dirtyFiles})]}),r.jsxs("div",{className:"hygiene-stat",children:[r.jsx("span",{className:"hygiene-stat-label",children:"Unverfolgte Dateien"}),r.jsx("span",{className:`hygiene-stat-value ${t.untrackedFiles>0?"hygiene-stat-warn":""}`,children:t.untrackedFiles})]}),r.jsxs("div",{className:"hygiene-stat",children:[r.jsx("span",{className:"hygiene-stat-label",children:"Stashes"}),r.jsx("span",{className:`hygiene-stat-value ${t.stashes>0?"hygiene-stat-warn":""}`,children:t.stashes})]}),r.jsxs("div",{className:"hygiene-stat",children:[r.jsx("span",{className:"hygiene-stat-label",children:"Größe"}),r.jsxs("span",{className:"hygiene-stat-value",children:[t.sizeMb.toFixed(0)," MB"]})]}),r.jsxs("div",{className:"hygiene-stat",children:[r.jsx("span",{className:"hygiene-stat-label",children:"Zuletzt aktiv"}),r.jsxs("span",{className:`hygiene-stat-value ${t.staleDays>30?"hygiene-stat-warn":""}`,children:[t.staleDays," Tage"]})]}),t.orphan&&r.jsx("div",{className:"hygiene-stat hygiene-stat-full",children:r.jsx("span",{className:"hygiene-badge hygiene-badge-risk",children:"Verwaister Checkout / Worktree"})})]}),t.unpushed.length>0&&r.jsxs("div",{className:"hygiene-section",children:[r.jsx("div",{className:"hygiene-section-title",children:"Ungepushte Branches"}),t.unpushed.map((s,l)=>r.jsxs("div",{className:"hygiene-unpushed-row",children:[r.jsx("span",{className:"hygiene-branch",children:s.branch}),r.jsxs("span",{className:"hygiene-ahead",children:[s.ahead," Commits voraus"]}),s.upstream?r.jsxs("span",{className:"hygiene-upstream",children:["→ ",s.upstream]}):r.jsx("span",{className:"hygiene-no-upstream",children:"Kein Upstream konfiguriert"})]},l))]}),t.claims&&t.claims.length>0&&r.jsxs("div",{className:"hygiene-section",children:[r.jsx("div",{className:"hygiene-section-title",children:"Aktive Sperren (Claims)"}),t.claims.map((s,l)=>r.jsxs("div",{className:"hygiene-claim-row",children:[r.jsx("span",{className:"hygiene-agent",children:s.agent}),r.jsx("span",{className:"hygiene-claim-paths",children:s.paths.join(", ")}),s.conflictsWith.length>0&&r.jsxs("span",{className:"hygiene-conflicts",children:["Konflikt mit: ",s.conflictsWith.join(", ")]})]},l))]}),(t.dirtyFiles>0||t.stashes>0)&&r.jsxs("div",{className:"hygiene-section",children:[r.jsx("div",{className:"hygiene-section-title",children:"WIP sichern — Befehl zum Kopieren"}),r.jsx(_d,{cmd:`cd "${t.path}" && git stash push -m "wip-$(date +%Y%m%d-%H%M%S)"`})]})]})]})}function nL({f:t}){return r.jsxs("div",{className:`hygiene-finding-row ${wo(t.level)}`,children:[r.jsx("span",{className:`hygiene-badge hygiene-badge-${t.level}`,children:No(t.level)}),r.jsxs("div",{className:"hygiene-finding-body",children:[r.jsx("div",{className:"hygiene-finding-text",children:t.text}),t.paths.length>0&&r.jsx("div",{className:"hygiene-finding-paths",children:t.paths.join(", ")}),t.fix&&r.jsx(_d,{cmd:t.fix})]})]})}function iL({workspaceId:t}){const[e,n]=P.useState("workspace"),[i,a]=P.useState(null),[s,l]=P.useState("loading"),[o,c]=P.useState(""),[u,h]=P.useState(null),[g,d]=P.useState("loading"),[p,x]=P.useState(null),[_,m]=P.useState("loading"),[f,v]=P.useState(!1),y=P.useCallback(()=>{if(!t){l("unavailable");return}l("loading"),z1(t).then(M=>{"routeMissing"in M?l("unavailable"):"unreachable"in M?(c("Brain nicht erreichbar — läuft plugbrain serve?"),l("unreachable")):"serverError"in M?(c(`Fehler ${M.status}: ${M.error}`),l("error")):(a(M),l("ready"))}).catch(M=>{c((M==null?void 0:M.message)??"Unbekannter Fehler"),l("error")})},[t]),b=P.useCallback(()=>{d("loading"),j1().then(M=>{"routeMissing"in M?d("unavailable"):"unreachable"in M?d("unreachable"):"serverError"in M?d("error"):(h(M),d("ready"))}).catch(()=>{d("unreachable")})},[]),T=P.useCallback(()=>{m("loading"),k1().then(M=>{"routeMissing"in M?m("unavailable"):"unreachable"in M?m("unreachable"):"serverError"in M?m("error"):(x(M),m("ready"))}).catch(()=>{m("unreachable")})},[]);return P.useEffect(()=>{y(),b(),T()},[y,b,T]),r.jsxs("div",{className:"hygiene-view",children:[r.jsxs("div",{className:"view-header",children:[r.jsxs("div",{className:"view-header-text",children:[r.jsx("h2",{className:"view-title",children:"Aufräumen"}),r.jsx("p",{className:"view-subtitle",children:"Checkouts, ungepushte Branches und ungesicherte Arbeit auf einen Blick"})]}),r.jsxs("div",{className:"view-header-actions",children:[r.jsx("button",{type:"button",className:"btn btn-secondary",onClick:()=>v(!0),title:"Befehl zum Sichern ungesicherter Arbeit anzeigen",children:"WIP sichern"}),r.jsx("button",{type:"button",className:"btn btn-secondary",onClick:()=>{y(),b(),T()},title:"Hygiene-Status neu laden",children:"↻ Aktualisieren"})]})]}),r.jsxs("div",{className:"hygiene-subnav",role:"tablist","aria-label":"Hygiene-Bereiche",children:[r.jsx("button",{type:"button",role:"tab",className:`hygiene-subnav-btn ${e==="workspace"?"active":""}`,"aria-selected":e==="workspace",onClick:()=>n("workspace"),children:"Workspace-Checkouts"}),r.jsx("button",{type:"button",role:"tab",className:`hygiene-subnav-btn ${e==="machine"?"active":""}`,"aria-selected":e==="machine",onClick:()=>n("machine"),children:"System & Festplatten"}),r.jsx("button",{type:"button",role:"tab",className:`hygiene-subnav-btn ${e==="repos"?"active":""}`,"aria-selected":e==="repos",onClick:()=>n("repos"),children:"Globale Git-Inventur"})]}),f&&r.jsx("div",{className:"hygiene-modal-backdrop",onClick:()=>v(!1),children:r.jsxs("div",{className:"hygiene-modal-content",onClick:M=>M.stopPropagation(),children:[r.jsxs("div",{className:"hygiene-modal-header",children:[r.jsx("h3",{className:"hygiene-modal-title",children:"WIP sichern (Ungesicherte Arbeit)"}),r.jsx("button",{type:"button",className:"hygiene-modal-close",onClick:()=>v(!1),"aria-label":"Schließen",children:"✕"})]}),r.jsxs("div",{className:"hygiene-modal-body",children:[r.jsxs("p",{children:["Der folgende Befehl legt automatisch WIP-Snapshots über einen temporären Index an.",r.jsx("strong",{children:" Dein Arbeitsbaum und Index bleiben unverändert"}),":"]}),r.jsx(_d,{cmd:"plugbrain hygiene --wip-snapshot",label:"CLI-Befehl kopieren"}),r.jsxs("div",{className:"hygiene-modal-note",children:[r.jsx("p",{className:"hygiene-note-title",children:"Was bewirkt dieser Befehl?"}),r.jsxs("p",{children:["Er erstellt WIP-Commits unter ",r.jsx("code",{children:"refs/wip/<datum>/<name>"})," für alle schmutzigen Checkouts. Deine lokalen Dateien und uncommitted Changes werden dabei nicht berührt."]})]})]}),r.jsx("div",{className:"hygiene-modal-footer",children:r.jsx("button",{type:"button",className:"btn btn-secondary",onClick:()=>v(!1),children:"Schließen"})})]})}),e==="workspace"&&r.jsxs("div",{className:"hygiene-tab-content",children:[s==="loading"&&r.jsxs("div",{className:"hygiene-status-box",children:[r.jsx("div",{className:"hygiene-spinner","aria-label":"Wird geladen…"}),r.jsx("p",{className:"hygiene-status-text",children:"Hygiene-Daten werden abgerufen…"})]}),s==="unavailable"&&r.jsxs("div",{className:"hygiene-status-box hygiene-offline",children:[r.jsx("p",{className:"hygiene-status-title",children:"Hygiene-Daten nicht verfügbar"}),r.jsx("p",{className:"hygiene-status-text",children:t?"Die Route /api/hygiene ist auf diesem Kern noch nicht aktiv (Lane HYG-01).":"Kein Workspace ausgewählt. Öffne zunächst einen Vault."}),t&&r.jsx("button",{type:"button",className:"btn btn-secondary",onClick:y,children:"Erneut versuchen"})]}),s==="unreachable"&&r.jsxs("div",{className:"hygiene-status-box hygiene-error",children:[r.jsx("p",{className:"hygiene-status-title",children:"Brain nicht erreichbar"}),r.jsxs("p",{className:"hygiene-status-text",children:["Keine Verbindung zum Brain-Server — läuft ",r.jsx("code",{children:"plugbrain serve"}),"?"]}),r.jsx("button",{type:"button",className:"btn btn-secondary",onClick:y,children:"Erneut versuchen"})]}),s==="error"&&r.jsxs("div",{className:"hygiene-status-box hygiene-error",children:[r.jsx("p",{className:"hygiene-status-title",children:"Fehler beim Laden"}),r.jsx("p",{className:"hygiene-status-text",children:o}),r.jsx("button",{type:"button",className:"btn btn-secondary",onClick:y,children:"Erneut versuchen"})]}),s==="ready"&&i&&r.jsxs(r.Fragment,{children:[r.jsxs("div",{className:`hygiene-summary-banner ${wo(i.summary.level)}`,children:[r.jsx("span",{className:`hygiene-badge hygiene-badge-${i.summary.level}`,children:No(i.summary.level)}),r.jsx("span",{className:"hygiene-summary-text",children:i.summary.text}),r.jsxs("span",{className:"hygiene-summary-meta",children:["Freier Speicher: ",i.diskFreeGb.toFixed(1)," GB · Geprüft: ",new Date(i.checkedAt).toLocaleTimeString("de-DE",{hour:"2-digit",minute:"2-digit"})]})]}),i.findings.length>0&&r.jsxs("section",{className:"hygiene-section-container",children:[r.jsxs("h3",{className:"hygiene-section-heading",children:["Befunde (",i.findings.length,")"]}),r.jsx("div",{className:"hygiene-findings-list",children:i.findings.map((M,A)=>r.jsx(nL,{f:M},A))})]}),r.jsxs("section",{className:"hygiene-section-container",children:[r.jsxs("h3",{className:"hygiene-section-heading",children:["Checkouts (",i.checkouts.length,")"]}),i.checkouts.length===0?r.jsx("div",{className:"hygiene-empty",children:r.jsx("p",{children:"Keine Checkouts im aktuellen Workspace gefunden."})}):r.jsx("div",{className:"hygiene-checkouts-list",children:i.checkouts.map((M,A)=>r.jsx(tL,{co:M},A))})]}),i.unavailable&&i.unavailable.length>0&&r.jsx("div",{className:"hygiene-unavailable-note",children:r.jsxs("span",{children:["Teile der Daten nicht verfügbar: ",i.unavailable.join(", ")]})})]})]}),e==="machine"&&r.jsxs("div",{className:"hygiene-tab-content",children:[g==="loading"&&r.jsxs("div",{className:"hygiene-status-box",children:[r.jsx("div",{className:"hygiene-spinner","aria-label":"Wird geladen…"}),r.jsx("p",{className:"hygiene-status-text",children:"System-Ressourcen werden ermittelt…"})]}),g==="unavailable"&&r.jsxs("div",{className:"hygiene-status-box hygiene-offline",children:[r.jsx("p",{className:"hygiene-status-title",children:"System-Status nicht verfügbar"}),r.jsxs("p",{className:"hygiene-status-text",children:["Die Schnittstelle ",r.jsx("code",{children:"/api/machine"})," ist in dieser Version des Kerns noch nicht aktiv (Lane HYG-02)."]})]}),g==="unreachable"&&r.jsxs("div",{className:"hygiene-status-box hygiene-error",children:[r.jsx("p",{className:"hygiene-status-title",children:"Brain nicht erreichbar"}),r.jsxs("p",{className:"hygiene-status-text",children:["Keine Verbindung zum Brain-Server — läuft ",r.jsx("code",{children:"plugbrain serve"}),"?"]}),r.jsx("button",{type:"button",className:"btn btn-secondary",onClick:b,children:"Erneut versuchen"})]}),g==="error"&&r.jsxs("div",{className:"hygiene-status-box hygiene-error",children:[r.jsx("p",{className:"hygiene-status-title",children:"Fehler beim Laden"}),r.jsx("p",{className:"hygiene-status-text",children:"System-Daten konnten nicht geladen werden."}),r.jsx("button",{type:"button",className:"btn btn-secondary",onClick:b,children:"Erneut versuchen"})]}),g==="ready"&&u&&r.jsxs("div",{className:"hygiene-machine-container",children:[u.drives&&u.drives.length>0&&r.jsxs("section",{className:"hygiene-section-container",children:[r.jsx("h3",{className:"hygiene-section-heading",children:"Festplatten & Partitionen"}),r.jsx("div",{className:"hygiene-drives-grid",children:u.drives.map((M,A)=>r.jsxs("div",{className:`hygiene-drive-card ${wo(M.level)}`,children:[r.jsxs("div",{className:"hygiene-drive-header",children:[r.jsx("span",{className:"hygiene-drive-mount",children:M.mount}),r.jsx("span",{className:`hygiene-badge hygiene-badge-${M.level}`,children:No(M.level)})]}),r.jsxs("div",{className:"hygiene-drive-numbers",children:[r.jsxs("span",{className:"hygiene-drive-free",children:[M.freeGb.toFixed(1)," GB frei"]}),r.jsxs("span",{className:"hygiene-drive-total",children:["von ",M.totalGb.toFixed(0)," GB"]})]}),r.jsx("div",{className:"hygiene-progress-bar",children:r.jsx("div",{className:"hygiene-progress-fill",style:{width:`${Math.min(100,Math.max(0,(M.totalGb-M.freeGb)/M.totalGb*100))}%`}})})]},A))})]}),u.forecast&&u.forecast.length>0&&r.jsxs("section",{className:"hygiene-section-container",children:[r.jsx("h3",{className:"hygiene-section-heading",children:"Speicher-Prognose"}),r.jsx("div",{className:"hygiene-forecast-list",children:u.forecast.map((M,A)=>r.jsxs("div",{className:"hygiene-forecast-card",children:[r.jsx("span",{className:"hygiene-forecast-mount",children:M.mount}),r.jsxs("span",{className:"hygiene-forecast-text",children:["Voll in ca. ",r.jsxs("strong",{children:[M.fullInHours.toFixed(1)," Stunden"]})," (",M.trendGbPerHour>0?"+":"",M.trendGbPerHour.toFixed(1)," GB/h, Basis: ",M.basis,")"]})]},A))})]}),u.findings&&u.findings.length>0&&r.jsxs("section",{className:"hygiene-section-container",children:[r.jsx("h3",{className:"hygiene-section-heading",children:"System-Befunde"}),r.jsx("div",{className:"hygiene-findings-list",children:u.findings.map((M,A)=>r.jsxs("div",{className:`hygiene-finding-row ${wo(M.level)}`,children:[r.jsx("span",{className:`hygiene-badge hygiene-badge-${M.level}`,children:No(M.level)}),r.jsxs("div",{className:"hygiene-finding-body",children:[r.jsx("div",{className:"hygiene-finding-text",children:M.text}),M.fix&&r.jsx(_d,{cmd:M.fix})]})]},A))})]})]})]}),e==="repos"&&r.jsxs("div",{className:"hygiene-tab-content",children:[_==="loading"&&r.jsxs("div",{className:"hygiene-status-box",children:[r.jsx("div",{className:"hygiene-spinner","aria-label":"Wird geladen…"}),r.jsx("p",{className:"hygiene-status-text",children:"Globale Git-Inventur wird abgerufen…"})]}),_==="unavailable"&&r.jsxs("div",{className:"hygiene-status-box hygiene-offline",children:[r.jsx("p",{className:"hygiene-status-title",children:"Globale Git-Inventur nicht verfügbar"}),r.jsxs("p",{className:"hygiene-status-text",children:["Die Schnittstelle ",r.jsx("code",{children:"/api/repos"})," ist in dieser Version des Kerns noch nicht aktiv (Lane HYG-02)."]})]}),_==="unreachable"&&r.jsxs("div",{className:"hygiene-status-box hygiene-error",children:[r.jsx("p",{className:"hygiene-status-title",children:"Brain nicht erreichbar"}),r.jsxs("p",{className:"hygiene-status-text",children:["Keine Verbindung zum Brain-Server — läuft ",r.jsx("code",{children:"plugbrain serve"}),"?"]}),r.jsx("button",{type:"button",className:"btn btn-secondary",onClick:T,children:"Erneut versuchen"})]}),_==="error"&&r.jsxs("div",{className:"hygiene-status-box hygiene-error",children:[r.jsx("p",{className:"hygiene-status-title",children:"Fehler beim Laden"}),r.jsx("p",{className:"hygiene-status-text",children:"Globale Git-Inventur konnte nicht geladen werden."}),r.jsx("button",{type:"button",className:"btn btn-secondary",onClick:T,children:"Erneut versuchen"})]}),_==="ready"&&p&&r.jsxs("div",{className:"hygiene-repos-container",children:[r.jsxs("div",{className:"hygiene-summary-banner hygiene-ok",children:[r.jsxs("span",{className:"hygiene-summary-text",children:["Gefunden: ",p.totals.repos," Repositories (",p.totals.dirty," mit Änderungen, ",p.totals.unpushedBranches," ungepushte Branches, ",p.totals.orphanWorktrees," verwaiste Worktrees)"]}),r.jsxs("span",{className:"hygiene-summary-meta",children:["Geprüfte Roots: ",p.roots.join(", ")]})]}),r.jsxs("section",{className:"hygiene-section-container",children:[r.jsxs("h3",{className:"hygiene-section-heading",children:["Gefundene Repositories (",p.repos.length,")"]}),r.jsx("div",{className:"hygiene-checkouts-list",children:p.repos.map((M,A)=>r.jsxs("div",{className:`hygiene-checkout-card ${M.dirtyFiles>0?"hygiene-attention":"hygiene-ok"}`,children:[r.jsxs("div",{className:"hygiene-checkout-header",children:[r.jsx("span",{className:`hygiene-badge ${M.dirtyFiles>0?"hygiene-badge-attention":"hygiene-badge-ok"}`,children:M.dirtyFiles>0?"◌ Achtung":"● Ok"}),r.jsx("span",{className:"hygiene-checkout-path",children:M.path}),r.jsx("span",{className:"hygiene-checkout-meta",children:M.branch})]}),r.jsx("div",{className:"hygiene-checkout-detail",style:{display:"block"},children:r.jsxs("div",{className:"hygiene-stat-grid",children:[r.jsxs("div",{className:"hygiene-stat",children:[r.jsx("span",{className:"hygiene-stat-label",children:"Geändert"}),r.jsx("span",{className:"hygiene-stat-value",children:M.dirtyFiles})]}),r.jsxs("div",{className:"hygiene-stat",children:[r.jsx("span",{className:"hygiene-stat-label",children:"Worktrees"}),r.jsx("span",{className:"hygiene-stat-value",children:M.worktrees})]}),r.jsxs("div",{className:"hygiene-stat",children:[r.jsx("span",{className:"hygiene-stat-label",children:"Größe"}),r.jsxs("span",{className:"hygiene-stat-value",children:[(M.gitSizeMb+M.workTreeSizeMb).toFixed(0)," MB"]})]}),r.jsxs("div",{className:"hygiene-stat",children:[r.jsx("span",{className:"hygiene-stat-label",children:"Letzter Commit vor"}),r.jsxs("span",{className:"hygiene-stat-value",children:[M.lastCommitDays," Tagen"]})]})]})})]},A))})]})]})]})]})}const q0=["blocked","still","awaiting-commit","working","needs-task","resting"];function dm(t,e,n){const i=t.lastHeartbeat===null?Number.NaN:Date.parse(t.lastHeartbeat);return Number.isFinite(i)&&i>=e&&i<=n}function eM(t,e=Date.now()){if(t.turnState!=="working")return!1;const n=t.lastHeartbeat===null?Number.NaN:Date.parse(t.lastHeartbeat);return Number.isFinite(n)&&e-n>30*6e4}function ls(t,e,n,i=Date.now()){return t.retired||t.turnState==="paused"||t.turnState==="offline"||!dm(t,e,n)?"resting":t.turnState==="blocked"?"blocked":eM(t,i)?"still":t.turnState==="awaiting-commit"?"awaiting-commit":t.turnState==="working"?"working":t.turnState==="needs-task"?"needs-task":"resting"}function aL(t,e,n,i=Date.now()){return[...t].sort((a,s)=>q0.indexOf(ls(a,e,n,i))-q0.indexOf(ls(s,e,n,i))||(a.account??"").localeCompare(s.account??"")||a.name.localeCompare(s.name))}function Dr(t,e=Date.now()){const n=t===null?Number.NaN:Date.parse(t);if(!Number.isFinite(n))return"unbekannt";const i=Math.max(0,Math.floor((e-n)/6e4));if(i<60)return`${i} min`;const a=Math.floor(i/60);if(a<48)return`${a} Std.`;const s=Math.floor(a/24);return s===1?"1 Tag":`${s} Tagen`}function sL(t,e,n,i=Date.now()){return t.reduce((a,s)=>{const l=ls(s,e,n,i);return l==="working"&&a.working++,l==="still"&&a.still++,s.turnState==="awaiting-commit"&&l!=="resting"&&a.awaiting++,a.unread+=s.unread,a},{working:0,awaiting:0,still:0,unread:0})}function Y0(t,e,n,i,a=Date.now()){if(e.size===0)return!0;const s=ls(t,n,i,a);return e.has("working")&&s==="working"||e.has("awaiting")&&t.turnState==="awaiting-commit"&&s!=="resting"||e.has("still")&&s==="still"||e.has("unread")&&t.unread>0}function mf(t){switch(t){case"working":return"running";case"needs-task":return"needs-task";case"awaiting-commit":return"awaiting-commit";case"blocked":return"blocked";case"paused":return"paused";case"commit-approved":return"approved";default:return"unknown"}}function rL(t,e,n=Date.now()){const i=t===null?Number.NaN:Date.parse(t);return e===null&&Number.isFinite(i)&&n-i>30*6e4}function Rs(t,e,n){const i=Date.parse(t);return!Number.isFinite(i)||n<=e?0:Math.min(100,Math.max(0,(i-e)/(n-e)*100))}const lL=[{label:"1 Std.",hours:1},{label:"6 Std.",hours:6},{label:"24 Std.",hours:24}],Lr={working:"Läuft","needs-task":"Braucht Aufgabe","awaiting-commit":"Wartet auf Freigabe","commit-approved":"Freigegeben",blocked:"Blockiert",paused:"Pausiert",offline:"Offline"};function oL({workspaceId:t,data:e,resources:n,onRefresh:i}){var dt,re;const[a,s]=P.useState(6),[l,o]=P.useState(100),[c,u]=P.useState(Date.now()),[h,g]=P.useState(null),[d,p]=P.useState(null),[x,_]=P.useState(""),[m,f]=P.useState(!1),[v,y]=P.useState(!1),[b,T]=P.useState(()=>new Set),[M,A]=P.useState(!1);P.useEffect(()=>{const H=window.setInterval(()=>u(Date.now()),3e4);return()=>clearInterval(H)},[]);const S=P.useMemo(()=>(e==null?void 0:e.board.agents)??[],[e]),N=P.useMemo(()=>{var se;if(!e)return[];const H=e.turns.map(Me=>({...Me})),le=new Set(H.filter(Me=>Me.endedAt===null).map(Me=>Me.agentId));for(const Me of S)Me.turnState==="working"&&Me.turnStateAt&&!le.has(Me.id)&&H.push({id:`current-${Me.id}`,agentId:Me.id,taskId:((se=Me.task)==null?void 0:se.id)??null,startedAt:Me.turnStateAt,endedAt:null,state:"working",summary:Me.turnSummary,legacy:!0});return H},[e,S]),D=P.useMemo(()=>N.find(H=>H.id===h)??null,[N,h]),U=D?S.find(H=>H.id===D.agentId)??null:null,I=D!=null&&D.taskId?(e==null?void 0:e.tasks.find(H=>H.id===D.taskId))??null:null,Y=Ti(),k=U!=null&&U.account?n==null?void 0:n.quotas.find(H=>H.account===U.account):null,B=N.filter(H=>H.legacy).length,z=c-(100-l)/100*a*60*6e4,j=z-a*60*6e4,X=aL(S,j,z,c),q=sL(S,j,z,c),ve=X.filter(H=>ls(H,j,z,c)!=="resting"&&Y0(H,b,j,z,c)),he=X.filter(H=>ls(H,j,z,c)==="resting"),Ae=he.filter(H=>Y0(H,b,j,z,c)),$=["blocked","still","awaiting-commit","working","needs-task"],pe=new Map(ve.map((H,le)=>[H.id,le])),we=H=>{T(le=>{const se=new Set(le);return se.has(H)?se.delete(H):se.add(H),se})},ne=(H,le)=>{const se=ls(H,j,z,c),Me=N.filter(O=>O.agentId===H.id&&dm(H,j,z)&&Date.parse(O.startedAt)<=z&&(O.endedAt===null||Date.parse(O.endedAt)>=j)),Oe=H.leases.filter(O=>Date.parse(O.createdAt)<=z&&Date.parse(O.expiresAt)>=j),De=Le(H.id).filter(O=>Date.parse(O.createdAt)>=j&&Date.parse(O.createdAt)<=z),Re=Ie(H.id).filter(O=>Date.parse(O.approvedAt)>=j&&Date.parse(O.approvedAt)<=z),Fe=eM(H,c),ye=H.turnState==="working"&&!dm(H,j,z);return r.jsxs("div",{className:"turns-lane","data-priority":se,children:[r.jsxs("div",{className:"turns-worker",children:[r.jsx("strong",{children:H.name}),r.jsxs("span",{children:[H.surface??"Oberfläche offen"," · ",H.model??"Modell offen"]}),r.jsx("small",{children:ye?`Zustand veraltet seit ${Dr(H.lastHeartbeat,c)}`:Fe?`still? seit ${Dr(H.lastHeartbeat,c)}`:H.turnState?Lr[H.turnState]??H.turnState:"Kein Turn-Status"})]}),r.jsxs("div",{className:"turns-track",style:{"--row":le},children:[Array.from({length:6},(O,Ye)=>r.jsx("i",{className:"turns-gridline",style:{left:`${Ye*20}%`}},Ye)),Me.map(O=>{const Ye=Rs(O.startedAt,j,z),$e=O.endedAt?Rs(O.endedAt,j,z):100,R=O.endedAt?O.state:"working";return r.jsx("button",{type:"button",id:`turn-${O.id}`,className:"turns-block","data-state":mf(R),"data-still":Fe?"true":void 0,"data-selected":h===O.id?"true":void 0,style:{left:`${Ye}%`,width:`${Math.max(.65,$e-Ye)}%`},onClick:()=>{g(O.id),p(null),_("")},"aria-label":`${H.name}, ${Lr[R??""]??"laufend"}${Fe?", still?":""}`,title:`${O.taskId??"Ohne Aufgabe"} · ${new Date(O.startedAt).toLocaleString()}${Fe?` · still? seit ${Dr(H.lastHeartbeat,c)}`:""}`,children:Fe&&r.jsxs("span",{className:"turns-still",children:["still? seit ",Dr(H.lastHeartbeat,c)]})},O.id)}),Oe.map(O=>r.jsx("i",{className:"turns-claim",style:{left:`${Rs(O.createdAt,j,z)}%`,width:`${Math.max(.8,Rs(O.expiresAt,j,z)-Rs(O.createdAt,j,z))}%`},title:`Claim: ${O.paths.join(", ")}`},O.id)),De.map(O=>r.jsx("button",{type:"button",className:"turns-mark turns-mark--message",style:{left:`${Rs(O.createdAt,j,z)}%`},onClick:()=>{const Ye=N.find($e=>$e.agentId===H.id);Ye&&g(Ye.id)},"aria-label":`Nachricht ${O.fromAgent} an ${O.toAgent??"alle"}: ${O.subject}`,title:`${O.fromAgent} → ${O.toAgent??"alle"}: ${O.subject}`},O.id)),Re.map(O=>r.jsx("button",{type:"button",className:"turns-mark turns-mark--approval",style:{left:`${Rs(O.approvedAt,j,z)}%`},onClick:()=>{const Ye=N.find($e=>$e.agentId===H.id);Ye&&g(Ye.id)},"aria-label":`Freigabe von ${O.byAgent}`,title:`Freigabe von ${O.byAgent}`},O.id))]})]},H.id)};if(P.useEffect(()=>{const H=le=>{var Re;if(le.target instanceof HTMLElement&&le.target.matches("input,textarea,select,[contenteditable=true]")||le.key!=="ArrowLeft"&&le.key!=="ArrowRight"&&le.key!=="Enter")return;const se=N.filter(Fe=>Date.parse(Fe.startedAt)<=z&&(Fe.endedAt===null||Date.parse(Fe.endedAt)>=j));if(se.length===0)return;if(le.key==="Enter"){h&&(le.preventDefault(),(Re=document.getElementById(`turn-${h}`))==null||Re.focus());return}le.preventDefault();const Me=se.findIndex(Fe=>Fe.id===h),Oe=le.key==="ArrowRight"?1:-1,De=se[(Me+Oe+se.length)%se.length];De&&g(De.id)};return window.addEventListener("keydown",H),()=>window.removeEventListener("keydown",H)},[z,h,N,j]),!e)return r.jsx("section",{className:"turns-empty",role:"status",children:"Fleet-Daten werden geladen oder sind nicht erreichbar."});const ge=async H=>{if(H.preventDefault(),!U)return;const le=new FormData(H.currentTarget);f(!0),_("");try{if(d==="message")await b1({workspace:t,fromAgent:Y,toAgent:String(le.get("toAgent")??""),subject:String(le.get("subject")??""),body:String(le.get("body")??"")});else if(d==="task"){const se=String(le.get("title")??"").trim(),Me=e.tasks.find(De=>(De.state==="pending"||De.state==="claimed")&&De.addressed_to===U.id&&De.title.trim().toLocaleLowerCase()===se.toLocaleLowerCase());if(Me){_(`Nicht eingereiht: ${Me.id} ist bereits ${Me.state} für ${U.id} („${Me.title}“). Prüfe die bestehende Aufgabe in der Queue.`);return}const Oe=await y1({workspace:t,requestedBy:Y,addressedTo:U.id,title:se,body:String(le.get("body")??"")});p(null),_(`Brain-Aufgabe ${Oe.id} · ${Oe.state} · an ${U.id}`),i();return}p(null),_("Gespeichert."),i()}catch(se){_(se instanceof Error?se.message:"Aktion fehlgeschlagen.")}finally{f(!1)}},ue=async()=>{if(!(!U||U.turnState!=="awaiting-commit")){y(!0),_("");try{await f1({workspaceId:t,agentId:U.id,by:Y}),_(`Commit für ${U.id} freigegeben.`),i()}catch(H){_(H instanceof Error?H.message:"Commit-Freigabe fehlgeschlagen.")}finally{y(!1)}}},Le=H=>e.messages.filter(le=>le.fromAgent===H||le.toAgent===H),Ie=H=>e.approvals.filter(le=>le.agentId===H),Xe=Array.from({length:7},(H,le)=>new Date(j+a*60*6e4*le/6).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"}));return r.jsxs("section",{className:"turns-view","aria-label":"Agenten-Turn-Zeitleiste",children:[r.jsxs("header",{className:"turns-toolbar",children:[r.jsxs("div",{children:[r.jsx("h2",{children:"Turns"}),r.jsx("p",{children:"Agenten, Aufgaben und Übergaben nach tatsächlichem Brain-Stand"})]}),r.jsxs("div",{className:"turns-zoom","aria-label":"Zeitfenster",children:[lL.map(H=>r.jsx("button",{type:"button","aria-pressed":a===H.hours,onClick:()=>{s(H.hours),o(100)},children:H.label},H.hours)),r.jsx("button",{type:"button",onClick:i,children:"Aktualisieren"})]})]}),r.jsxs("div",{className:"turns-summary","aria-label":"Fleet-Zusammenfassung",children:[r.jsxs("button",{type:"button","aria-pressed":b.has("working"),onClick:()=>we("working"),children:[q.working," arbeiten"]}),r.jsxs("button",{type:"button","aria-pressed":b.has("awaiting"),onClick:()=>we("awaiting"),children:[q.awaiting," warten auf Freigabe"]}),r.jsxs("button",{type:"button","aria-pressed":b.has("still"),onClick:()=>we("still"),children:[q.still," still?"]}),r.jsxs("button",{type:"button","aria-pressed":b.has("unread"),onClick:()=>we("unread"),children:[q.unread," ungelesen"]})]}),r.jsx("p",{className:"turns-history-note",role:"status",children:"Turn-Paare werden ab diesem Upgrade gespeichert. Ältere Starts und Enden wurden nicht erfasst; laufende Profile ohne Turn-Eintrag sind separat markiert."}),r.jsxs("div",{className:"turns-key","aria-label":"Zeitleisten-Legende",children:[["working","needs-task","awaiting-commit","blocked","paused"].map(H=>r.jsxs("span",{children:[r.jsx("i",{"data-state":mf(H)}),Lr[H]]},H)),r.jsxs("span",{children:[r.jsx("i",{"data-mark":"claim"}),"Claim"]}),r.jsxs("span",{children:[r.jsx("i",{"data-mark":"message"}),"Nachricht"]}),r.jsxs("span",{children:[r.jsx("i",{"data-mark":"approval"}),"Freigabe"]})]}),r.jsxs("label",{className:"turns-scrubber",children:["Zeitpunkt durchsuchen",r.jsx("input",{"aria-label":"Zeitpunkt durchsuchen",type:"range",min:"0",max:"100",value:l,onChange:H=>o(Number(H.target.value))}),r.jsx("span",{children:new Date(z).toLocaleString()})]}),r.jsxs("div",{className:"turns-layout",children:[r.jsxs("div",{className:"turns-scroll","aria-label":"Worker-Spuren",children:[r.jsxs("div",{className:"turns-ruler",children:[r.jsx("span",{children:"Konto / Worker"}),r.jsx("div",{children:Xe.map((H,le)=>r.jsx("span",{children:H},le))})]}),ve.length===0&&he.length===0&&r.jsx("p",{className:"turns-empty",children:"Keine registrierten Worker in diesem Workspace."}),$.map(H=>{const le=ve.filter(se=>ls(se,j,z,c)===H);return le.length===0?null:r.jsxs("section",{className:"turns-priority","data-priority":H,children:[r.jsx("h3",{children:H==="still"?"still?":Lr[H]??H}),Array.from(new Set(le.map(se=>se.account??"Ohne Konto"))).map(se=>r.jsxs("section",{className:"turns-account",children:[r.jsx("h4",{children:se}),le.filter(Me=>(Me.account??"Ohne Konto")===se).map(Me=>ne(Me,pe.get(Me.id)??0))]},se))]},H)}),he.length>0&&r.jsxs("section",{className:"turns-resting",children:[r.jsxs("button",{className:"turns-resting__toggle",type:"button","aria-expanded":M||b.size>0&&Ae.length>0,onClick:()=>A(H=>!H),children:[he.length," ruhende Worker ",M?"−":"+"]}),(M||b.size>0&&Ae.length>0)&&r.jsx("div",{children:he.filter(H=>b.size===0||Ae.includes(H)).map((H,le)=>r.jsxs("div",{className:"turns-resting__worker",children:[r.jsx("strong",{children:H.name}),r.jsxs("span",{children:[H.turnState==="working"?`Zustand veraltet seit ${Dr(H.lastHeartbeat,c)}`:Lr[H.turnState??""]??H.turnState??"Kein Status"," · ",H.unread," ungelesen"]}),H.turnState==="working"&&r.jsx("i",{className:"turns-stale-hatch","aria-label":"Veralteter Status"})]},H.id))})]})]}),r.jsx("aside",{className:"turns-detail","aria-label":"Turn-Details","aria-live":"polite",children:D&&U?r.jsxs(r.Fragment,{children:[r.jsxs("div",{className:"turns-detail__head",children:[r.jsxs("div",{children:[r.jsx("span",{children:"TURN"}),r.jsx("h3",{children:U.name})]}),r.jsx("button",{type:"button",onClick:()=>g(null),"aria-label":"Details schließen",children:"Schließen"})]}),r.jsxs("dl",{children:[r.jsx("dt",{children:"Zustand"}),r.jsxs("dd",{children:[r.jsx("span",{className:"turns-state","data-state":mf(D.endedAt?D.state:"working"),children:D.endedAt?Lr[D.state??""]??D.state:"Läuft"}),rL(U.lastHeartbeat,D.endedAt,c)&&r.jsxs("b",{className:"turns-still-label",children:["still? seit ",Dr(U.lastHeartbeat,c)]})]}),r.jsx("dt",{children:"Beginn"}),r.jsx("dd",{children:new Date(D.startedAt).toLocaleString()}),r.jsx("dt",{children:"Ende"}),r.jsx("dd",{children:D.endedAt?new Date(D.endedAt).toLocaleString():"Noch offen"}),r.jsx("dt",{children:"Aufgabe"}),r.jsx("dd",{children:(I==null?void 0:I.title)??((dt=U.task)==null?void 0:dt.title)??D.taskId??"Keine aktuelle Aufgabe"}),r.jsx("dt",{children:"Handlungsbedarf"}),r.jsx("dd",{children:(re=U.attention)!=null&&re.length?U.attention.join(", "):"Keiner gemeldet"}),r.jsx("dt",{children:"Kontingent"}),r.jsx("dd",{children:k?`${k.remaining} ${k.unit}${k.resetsAt?` · Reset ${new Date(k.resetsAt).toLocaleString()}`:""}`:U.account?`Für ${U.account} kein Kontingent gemeldet`:"Konto nicht bekannt"}),(I==null?void 0:I.body)&&r.jsxs(r.Fragment,{children:[r.jsx("dt",{children:"Aufgabenbeschreibung"}),r.jsx("dd",{children:I.body})]}),r.jsx("dt",{children:"Zusammenfassung"}),r.jsx("dd",{children:D.summary??U.turnSummary??"Keine Zusammenfassung gespeichert."})]}),D.legacy&&r.jsx("p",{className:"turns-history-note",children:"Dieser laufende Status stammt aus dem Worker-Profil; die Start-/Ende-Paarung liegt noch nicht in der neuen Historie."}),r.jsx("h4",{children:"Claims"}),r.jsx("ul",{children:U.leases.length?U.leases.flatMap(H=>H.paths.map(le=>r.jsx("li",{children:le},`${H.id}:${le}`))):r.jsx("li",{children:"Keine aktiven Claims."})}),r.jsx("h4",{children:"Nachrichten dieses Workers"}),r.jsx("ul",{children:Le(D.agentId).slice(-8).map(H=>r.jsxs("li",{children:[r.jsxs("b",{children:[H.fromAgent," → ",H.toAgent??"alle"]}),r.jsxs("span",{children:[H.subject," · ",H.readAt?"quittiert":H.deliveredAt?"zugestellt":"offen"]})]},H.id))}),r.jsx("h4",{children:"Branch / Commit"}),r.jsx("ul",{children:U.worktrees.length?U.worktrees.map(H=>r.jsxs("li",{children:[r.jsxs("b",{children:[H.branch??"Branch nicht lesbar"," · ",H.head??"Commit nicht lesbar"]}),r.jsx("span",{children:H.path})]},H.path)):r.jsx("li",{children:"Kein Worktree registriert."})}),r.jsxs("div",{className:"turns-actions",children:[r.jsx("button",{type:"button",onClick:()=>{p(d==="message"?null:"message"),_("")},children:"Nachricht senden"}),r.jsx("button",{type:"button",onClick:()=>{p(d==="task"?null:"task"),_("")},children:"Aufgabe einreihen"}),U.turnState==="awaiting-commit"&&r.jsx("button",{type:"button",disabled:v,onClick:()=>void ue(),children:v?"Gebe frei …":`Commit für ${U.id} freigeben`})]}),d&&r.jsxs("form",{className:"turns-composer",onSubmit:ge,children:[r.jsx("h4",{children:d==="message"?"Nachricht an Worker":"Aufgabe einreihen"}),d==="message"?r.jsxs(r.Fragment,{children:[r.jsxs("label",{children:["Absender (Shell-Profil)",r.jsx("input",{value:Y,readOnly:!0})]}),r.jsxs("label",{children:["Empfänger-ID",r.jsx("input",{name:"toAgent",required:!0})]}),r.jsxs("label",{children:["Betreff",r.jsx("input",{name:"subject",required:!0})]}),r.jsxs("label",{children:["Nachricht",r.jsx("textarea",{name:"body",required:!0})]})]}):r.jsxs(r.Fragment,{children:[r.jsxs("p",{children:["Auftraggeber (Shell-Profil): ",r.jsx("strong",{children:Y})," · Ziel-Lane: ",r.jsx("strong",{children:U.id})]}),r.jsxs("label",{children:["Titel",r.jsx("input",{name:"title",required:!0})]}),r.jsxs("label",{children:["Beschreibung",r.jsx("textarea",{name:"body"})]})]}),r.jsx("button",{type:"submit",disabled:m,children:m?"Wird gesendet …":"Senden"})]}),x&&r.jsx("p",{role:"status",children:x})]}):r.jsxs("div",{className:"turns-detail__empty",children:[r.jsx("h3",{children:"Turn auswählen"}),r.jsx("p",{children:"Wähle einen Block, um Zusammenfassung, Claims, Nachrichten, Branch und Commit zu sehen."}),r.jsx("p",{children:"Pfeiltasten wechseln zwischen sichtbaren Turns, Enter fokussiert den ausgewählten Turn."}),r.jsxs("p",{children:[e.turns.length," gespeicherte Turns · ",B," laufende Profilstatus · ",S.length," Worker · ",e.messages.length," Nachrichten · ",e.approvals.length," Freigaben"]})]})})]})]})}const Bi=t=>`${(t/1024**3).toFixed(1)} GB`;function cL(){var d,p,x,_,m,f,v,y,b;const[t,e]=P.useState(null),[n,i]=P.useState(null),[a,s]=P.useState(""),[l,o]=P.useState(!1),c=P.useCallback(async()=>{try{e(await d1()),s("")}catch(T){s(T instanceof Error?T.message:String(T))}},[]);P.useEffect(()=>{c(),nS().then(i).catch(()=>i(null))},[c]),P.useEffect(()=>{var M;if(!((M=t==null?void 0:t.scan)!=null&&M.running))return;const T=window.setInterval(()=>{c()},2500);return()=>clearInterval(T)},[(d=t==null?void 0:t.scan)==null?void 0:d.running,c]);const u=async()=>{o(!0),s("");try{await h1(),await c()}catch(T){s(T instanceof Error?T.message:String(T))}finally{o(!1)}},h=((p=t==null?void 0:t.recommendations)==null?void 0:p.recommendations)??[],g=((x=t==null?void 0:t.wipeCheck)==null?void 0:x.items)??[];return r.jsxs("section",{className:"pb-domain-view","aria-label":"Festplatten",children:[r.jsxs("header",{className:"pb-view-header",children:[r.jsx("h2",{children:"Festplatten"}),r.jsx("p",{children:"Reale Laufwerke, Inventur und geprüfte Aufräumkandidaten"}),r.jsx("button",{className:"pb-button",type:"button",onClick:()=>void c(),children:"Aktualisieren"}),r.jsx("button",{className:"pb-button pb-button--primary",type:"button",disabled:l||((_=t==null?void 0:t.scan)==null?void 0:_.running),onClick:()=>void u(),children:(m=t==null?void 0:t.scan)!=null&&m.running?"Inventur läuft …":l?"Starte …":"Inventur starten"})]}),a&&r.jsxs("p",{className:"pb-domain-error",role:"alert",children:["Festplatten-Daten nicht verfügbar: ",a]}),!t&&!a&&r.jsx("p",{role:"status",children:"Festplatten-Daten werden geladen …"}),t&&r.jsxs(r.Fragment,{children:[r.jsxs("section",{className:"pb-domain-card",children:[r.jsx("h3",{children:"Laufwerke"}),n!=null&&n.host.drives.length?r.jsx("div",{className:"pb-drive-grid",children:n.host.drives.map(T=>r.jsxs("article",{children:[r.jsx("strong",{children:T.root}),r.jsxs("span",{children:[Bi(T.freeBytes)," frei von ",Bi(T.totalBytes)]})]},T.root))}):r.jsx("p",{children:(v=(f=t.scan)==null?void 0:f.roots)!=null&&v.length?"Laufwerkskapazitäten sind aktuell nicht verfügbar.":"Für die Laufwerke liegt noch keine Inventur vor. Starte eine metadata-only Inventur, um Verzeichnisse und Platzbelegung zu erfassen."})]}),r.jsxs("section",{className:"pb-domain-card",children:[r.jsx("h3",{children:"Letzte Inventur"}),t.scan?r.jsxs("p",{role:"status",children:[t.scan.running?"Läuft":t.scan.complete?"Abgeschlossen":"Unvollständig"," · ",t.scan.directories.toLocaleString()," Verzeichnisse · ",t.scan.files.toLocaleString()," Dateien · ",Bi(t.scan.bytes)," erfasst · ",t.scan.inaccessible," unzugänglich"]}):r.jsx("p",{children:"Noch keine Inventur vorhanden."}),((b=(y=t.scan)==null?void 0:y.errors)==null?void 0:b.length)>0&&r.jsx("ul",{children:t.scan.errors.slice(0,8).map((T,M)=>r.jsx("li",{children:T},`${M}-${T}`))})]}),r.jsxs("section",{className:"pb-domain-card",children:[r.jsx("h3",{children:"Erfasste Verzeichnisse auf der obersten Ebene"}),t.directories.length?r.jsx("ol",{className:"pb-domain-list",children:t.directories.slice(0,12).map(T=>r.jsxs("li",{children:[r.jsx("code",{children:T.path}),r.jsxs("span",{children:[Bi(T.bytes)," · ",T.files.toLocaleString()," Dateien"]})]},T.path))}):r.jsx("p",{children:"Keine Verzeichnisdaten vorhanden. Erst nach abgeschlossener Inventur kann eine Größenliste gezeigt werden."})]}),r.jsxs("section",{className:"pb-domain-card",children:[r.jsx("h3",{children:"Verlauf und größte Zuwächse"}),t.history.length?r.jsx("ol",{className:"pb-domain-list",children:t.history.slice(0,10).map(T=>r.jsxs("li",{children:[r.jsx("strong",{children:T.complete?"Abgeschlossen":T.running?"Läuft":"Unvollständig"}),r.jsxs("span",{children:[new Date(T.startedAt).toLocaleString()," · ",T.directories.toLocaleString()," Verzeichnisse · ",Bi(T.bytes)]})]},T.scanId))}):r.jsx("p",{children:"Mit dieser Version beginnt der Verlauf. Ältere Scans wurden vom Server nicht aufbewahrt."}),t.largestGrowth.length?r.jsx("ul",{className:"pb-domain-list",children:t.largestGrowth.map(T=>r.jsxs("li",{children:[r.jsx("code",{children:T.path}),r.jsxs("span",{children:["+",Bi(T.deltaBytes)," seit dem vorherigen vollständigen Scan · vorher ",Bi(T.previousBytes),", jetzt ",Bi(T.bytes)]})]},T.path))}):r.jsx("p",{children:"Größte Zuwächse benötigen zwei vollständige Inventuren mit überlappenden Verzeichnissen."})]}),r.jsxs("section",{className:"pb-domain-card",children:[r.jsx("h3",{children:"Aufräumkandidaten"}),r.jsx("p",{children:"Nur eine Prüfliste für den Owner. PlugBrain löscht hier nichts."}),h.length?r.jsx("ul",{className:"pb-domain-list",children:h.slice(0,20).map((T,M)=>{var A,S;return r.jsxs("li",{children:[r.jsx("code",{children:(A=T.paths)==null?void 0:A.join(", ")}),r.jsxs("span",{children:[Bi(T.bytes)," · ",T.risk," · ",T.reason]})]},`${(S=T.paths)==null?void 0:S[0]}-${M}`)})}):r.jsx("p",{children:"Keine Empfehlungen aus der vorhandenen Inventur."}),r.jsx("h4",{children:"Geschützte / zu prüfende Pfade"}),g.length?r.jsx("ul",{className:"pb-domain-list",children:g.slice(0,20).map(T=>r.jsxs("li",{children:[r.jsx("code",{children:T.path}),r.jsxs("span",{children:[Bi(T.bytes)," · ",T.reason]})]},T.path))}):r.jsx("p",{children:"Die aktuelle Prüfliste enthält keine geschützten Pfade."})]})]})]})}function uL(t){if(!t)return"kein Lebenszeichen erfasst";const e=Math.max(0,Date.now()-Date.parse(t));return Number.isFinite(e)?e<6e4?`vor ${Math.floor(e/1e3)} s`:e<36e5?`vor ${Math.floor(e/6e4)} min`:`vor ${Math.floor(e/36e5)} h`:"Zeitstempel ungültig"}function dL({data:t,onRefresh:e}){const n=(t==null?void 0:t.board.agents.filter(i=>/^cx\d\d$/.test(i.id)))??[];return r.jsxs("section",{className:"pb-domain-view","aria-label":"Dauerbetrieb und Nachtschicht",children:[r.jsxs("header",{className:"pb-view-header",children:[r.jsx("h2",{children:"Dauerbetrieb / Nachtschicht"}),r.jsx("p",{children:"Fleet-Zustand aus dem Brain-Koordinationstrace"}),r.jsx("button",{className:"pb-button pb-button--primary",type:"button",onClick:e,children:"Fleet aktualisieren"})]}),t?r.jsxs(r.Fragment,{children:[r.jsxs("section",{className:"pb-domain-card",children:[r.jsx("h3",{children:"Codex-Lanes cx01–cx15"}),n.length?r.jsx("div",{className:"pb-table-wrap",children:r.jsxs("table",{className:"pb-data-table",children:[r.jsx("thead",{children:r.jsxs("tr",{children:[r.jsx("th",{children:"Lane"}),r.jsx("th",{children:"Turn"}),r.jsx("th",{children:"Letztes Lebenszeichen"}),r.jsx("th",{children:"Aufgabe"}),r.jsx("th",{children:"Bedarf"})]})}),r.jsx("tbody",{children:n.map(i=>r.jsxs("tr",{children:[r.jsx("th",{scope:"row",children:i.id}),r.jsx("td",{children:i.turnState??i.presence}),r.jsx("td",{children:uL(i.lastHeartbeat)}),r.jsx("td",{children:i.task?`${i.task.id}: ${i.task.title}`:"Keine aktive Aufgabe"}),r.jsx("td",{children:i.unread?`${i.unread} neue Nachricht(en)`:i.leases.length?`${i.leases.length} Claim(s)`:"—"})]},i.id))})]})}):r.jsx("p",{children:"Die Brain-Projektion enthält derzeit keine cx01–cx15-Worker."})]}),r.jsxs("section",{className:"pb-domain-card",children:[r.jsx("h3",{children:"Queue und letzte Ereignisse"}),r.jsxs("p",{children:[t.tasks.length," Queue-Aufgaben · ",t.turns.length," gespeicherte Turns · ",t.messages.length," Nachrichten · ",t.approvals.length," Freigaben"]}),!t.historyAvailable&&r.jsx("p",{className:"pb-domain-note",children:"Ältere Turns sind in dieser Brain-Version nicht vollständig gespeichert."}),t.tasks.length?r.jsx("ul",{className:"pb-domain-list",children:t.tasks.slice(0,12).map(i=>r.jsxs("li",{children:[r.jsx("strong",{children:i.state}),r.jsxs("span",{children:[i.id,": ",i.title," · ",i.claimed_by??i.addressed_to??"nicht zugewiesen"]})]},i.id))}):r.jsx("p",{children:"Die Brain-Queue ist leer."})]}),r.jsxs("section",{className:"pb-domain-card",children:[r.jsx("h3",{children:"Supervisor-Belege"}),r.jsx("p",{children:"HALT-Zustand, Aussetzer, utilization.log und Morgenbericht sind noch nicht über eine authentifizierte UI-Projektion angebunden. Diese Bereiche werden deshalb als nicht verfügbar angezeigt."}),r.jsxs("ul",{children:[r.jsx("li",{children:"HALT-Status: nicht über die API verfügbar"}),r.jsx("li",{children:"fleetd utilization.log: nicht über die API verfügbar"}),r.jsx("li",{children:"Aussetzer und Morgenbericht: nicht über die API verfügbar"})]})]})]}):r.jsx("p",{role:"status",children:"Fleet-Daten werden geladen oder sind nicht verfügbar."})]})}function hL({workspaceId:t}){var o,c,u,h,g,d,p,x,_;const[e,n]=P.useState(null),[i,a]=P.useState(""),s=P.useCallback(async()=>{try{n(await u1(t)),a("")}catch(m){n(null),a(m instanceof Error?m.message:String(m))}},[t]);P.useEffect(()=>{s()},[s]);const l=e==null?void 0:e.progress;return r.jsxs("section",{className:"pb-domain-view","aria-label":"Plan und Master",children:[r.jsxs("header",{className:"pb-view-header",children:[r.jsx("h2",{children:"Plan / Master"}),r.jsx("p",{children:"Projektion aus dem Master-Ledger und der Brain-Queue"}),r.jsx("button",{className:"pb-button pb-button--primary",type:"button",onClick:()=>void s(),children:"Plan aktualisieren"})]}),i&&r.jsxs("p",{className:"pb-domain-error",role:"alert",children:["Plan nicht verfügbar: ",i]}),!e&&!i&&r.jsx("p",{role:"status",children:"Plan-Daten werden geladen …"}),e&&r.jsxs(r.Fragment,{children:[r.jsxs("section",{className:"pb-domain-card",children:[r.jsx("h3",{children:((o=e.ledger)==null?void 0:o.master)??"Master-Ledger"}),r.jsxs("p",{children:["Stand: ",((c=e.ledger)==null?void 0:c.updated)??"unbekannt"," · ",((u=e.ledger)==null?void 0:u.ageHours)==null?"Alter unbekannt":`${e.ledger.ageHours.toFixed(1)} Stunden alt`," · Gesamtstatus: ",((h=e.ledger)==null?void 0:h.statusOverall)??"nicht gesetzt"]}),r.jsxs("div",{className:"pb-metric-grid",children:[r.jsxs("article",{children:[r.jsxs("strong",{children:[(l==null?void 0:l.tasksDone)??0," / ",(l==null?void 0:l.tasksTotal)??0]}),r.jsx("span",{children:"Aufgaben abgeschlossen"})]}),r.jsxs("article",{children:[r.jsx("strong",{children:(l==null?void 0:l.tasksInProgress)??0}),r.jsx("span",{children:"in Arbeit"})]}),r.jsxs("article",{children:[r.jsxs("strong",{children:[(l==null?void 0:l.gatesVerified)??0," / ",(l==null?void 0:l.gatesTotal)??0]}),r.jsx("span",{children:"Gates verifiziert"})]}),r.jsxs("article",{children:[r.jsx("strong",{children:(l==null?void 0:l.gatesPartial)??0}),r.jsx("span",{children:"teilweise verifiziert"})]})]})]}),r.jsxs("section",{className:"pb-domain-card",children:[r.jsx("h3",{children:"Nächste startbare Master-Aufgaben"}),(g=e.next)!=null&&g.length?r.jsx("ol",{className:"pb-domain-list",children:e.next.map(m=>{var f;return r.jsxs("li",{children:[r.jsxs("strong",{children:[m.id," · ",m.status]}),r.jsxs("span",{children:[m.title," ",(f=m.queue)!=null&&f.length?`· Queue: ${m.queue.map(v=>v.taskId).join(", ")}`:""]})]},m.id)})}):r.jsx("p",{children:"Das Ledger meldet aktuell keine startbaren Aufgaben."})]}),r.jsxs("section",{className:"pb-domain-card",children:[r.jsx("h3",{children:"Gates"}),(d=e.gates)!=null&&d.length?r.jsx("ul",{className:"pb-domain-list",children:e.gates.map(m=>r.jsxs("li",{children:[r.jsxs("strong",{children:[m.id," · ",m.status]}),r.jsxs("span",{children:[m.title," · ",m.owner??"ohne Owner"]})]},m.id))}):r.jsx("p",{children:"Das Ledger enthält keine Gate-Einträge."})]}),r.jsxs("section",{className:"pb-domain-card",children:[r.jsx("h3",{children:"Fortschrittsdimensionen und offene Entscheidungen"}),(p=e.dimensions)!=null&&p.length?r.jsx("ul",{children:e.dimensions.map(m=>r.jsxs("li",{children:[r.jsxs("strong",{children:[m.dimension,":"]})," ",m.current]},m.dimension))}):r.jsx("p",{children:"Keine Fortschrittsdimensionen im Ledger."}),(x=e.openDecisions)!=null&&x.length?r.jsx("ul",{children:e.openDecisions.map(m=>r.jsxs("li",{children:[r.jsxs("strong",{children:[m.ref,":"]})," ",m.decision]},m.ref))}):r.jsx("p",{children:"Keine offenen Entscheidungen im Ledger."}),((_=e.unplanned)==null?void 0:_.length)>0&&r.jsxs("p",{children:[e.unplanned.length," Queue-Aufgabe(n) ohne Master-Referenz."]})]})]})]})}function fL({workspaceId:t,isOpen:e,onClose:n,onOpenSource:i,onNavigateToSearch:a,initialQuestion:s=""}){const[l,o]=P.useState(s),[c,u]=P.useState(!1),[h,g]=P.useState(null),[d,p]=P.useState(!1),[x,_]=P.useState(!1),[m,f]=P.useState(null),v=P.useRef(null);if(P.useEffect(()=>{e&&(o(s),g(null),p(!1),_(!1),f(null),setTimeout(()=>{var A;return(A=v.current)==null?void 0:A.focus()},50))},[e,s]),!e)return null;const y=async A=>{const S=A.trim();if(!(!S||c)){u(!0),f(null),p(!1),_(!1);try{const N=await P1(t,S);"routeMissing"in N?(p(!0),g(null)):"unreachable"in N?(_(!0),g(null)):"serverError"in N?(f(`Fehler ${N.status}: ${N.error}`),g(null)):g(N)}catch(N){f(N instanceof Error?N.message:String(N))}finally{u(!1)}}},b=A=>{o(A),y(A)},T=A=>{i(A.path,A.line),n()},M=()=>{a&&(a(l),n())};return r.jsx("div",{className:"brain-modal-backdrop",onClick:n,role:"presentation",children:r.jsxs("div",{className:"brain-modal ask-modal",role:"dialog","aria-modal":"true","aria-labelledby":"ask-modal-title",onClick:A=>A.stopPropagation(),children:[r.jsxs("header",{className:"ask-modal__header",children:[r.jsxs("div",{className:"ask-modal__input-wrap",children:[r.jsx("span",{className:"ask-modal__search-icon",children:r.jsx(ot,{path:st.search})}),r.jsx("input",{ref:v,id:"ask-modal-title",type:"text",className:"ask-modal__input",placeholder:"Frag das Projekt … (z. B. 'Wo ist workspaceIdFor definiert?')",value:l,onChange:A=>o(A.target.value),onKeyDown:A=>{A.key==="Escape"&&n()}}),l&&r.jsx("button",{type:"button",className:"ask-modal__clear-btn",onClick:()=>{var A;o(""),g(null),(A=v.current)==null||A.focus()},"aria-label":"Eingabe leeren",children:"✕"})]}),r.jsx("button",{type:"button",className:"brain-modal__close",onClick:n,"aria-label":"Dialog schließen",children:"✕"})]}),r.jsxs("div",{className:"ask-modal__body",children:[c&&r.jsx("div",{className:"ask-modal__state ask-modal__state--busy",role:"status",children:r.jsx("p",{children:"Durchsuche Wissensgraphen und Code …"})}),d&&!c&&r.jsxs("div",{className:"ask-modal__state ask-modal__state--unavailable",role:"status",children:[r.jsx("div",{className:"ask-modal__state-icon",children:r.jsx(ot,{path:st.info})}),r.jsx("h4",{children:"Frag-Funktion in dieser Version noch nicht verfügbar"}),r.jsxs("p",{children:["Die Backend-Route ",r.jsx("code",{children:"POST /api/ask"})," ist auf diesem Kern noch nicht aktiv (wird in Lane ASK-01 bereitgestellt)."]}),a&&l&&r.jsxs("button",{type:"button",className:"pb-button pb-button--primary",onClick:M,style:{marginTop:"12px"},children:[r.jsx(ot,{path:st.search})," Suche nach „",l,'" öffnen']})]}),x&&!c&&r.jsxs("div",{className:"ask-modal__state ask-modal__state--error",role:"alert",children:[r.jsx("h4",{children:"Brain nicht erreichbar"}),r.jsxs("p",{children:["Keine Verbindung zum Brain-Server — läuft ",r.jsx("code",{children:"plugbrain serve"}),"?"]})]}),m&&!c&&r.jsxs("div",{className:"ask-modal__state ask-modal__state--error",role:"alert",children:[r.jsx("h4",{children:"Fehler bei der Abfrage"}),r.jsx("p",{children:m})]}),!c&&!d&&!x&&!m&&!h&&r.jsxs("div",{className:"ask-modal__empty",children:[r.jsx("p",{className:"ask-modal__hint",children:"Typische Fragen an den Brain:"}),r.jsxs("div",{className:"ask-modal__suggestions",children:[r.jsx("button",{type:"button",className:"ask-modal__suggestion-chip",onClick:()=>{const A="Wo ist workspaceIdFor definiert?";o(A),y(A)},children:"„Wo ist workspaceIdFor definiert?“"}),r.jsx("button",{type:"button",className:"ask-modal__suggestion-chip",onClick:()=>{const A="Welche Einstiegspunkte hat das Projekt?";o(A),y(A)},children:"„Welche Einstiegspunkte hat das Projekt?“"}),r.jsx("button",{type:"button",className:"ask-modal__suggestion-chip",onClick:()=>{const A="Was hat sich zuletzt geändert?";o(A),y(A)},children:"„Was hat sich zuletzt geändert?“"})]})]}),h&&!c&&r.jsxs("div",{className:"ask-modal__response",children:[r.jsxs("div",{className:"ask-modal__answer-box",children:[r.jsx("p",{className:"ask-modal__answer",children:h.answer}),r.jsxs("div",{className:"ask-modal__meta",children:[r.jsxs("span",{className:`ask-modal__confidence ask-modal__confidence--${h.confidence}`,title:`Konfidenz: ${h.confidence}`,children:["Konfidenz: ",h.confidence]}),h.intent&&r.jsxs("span",{className:"ask-modal__intent-badge",children:["Fokus: ",h.intent]})]})]}),h.unavailable&&h.unavailable.length>0&&r.jsxs("div",{className:"ask-modal__unavailable-note",style:{fontSize:"12px",color:"var(--muted)",margin:"8px 0"},children:["Hinweis: Folgende Bereiche sind nicht verfügbar: ",h.unavailable.join(", ")]}),h.sources&&h.sources.length>0&&r.jsxs("div",{className:"ask-modal__sources",children:[r.jsxs("h5",{children:["Gefundene Quellen (",h.sources.length,"):"]}),r.jsx("div",{className:"ask-modal__sources-list",children:h.sources.map((A,S)=>r.jsxs("button",{type:"button",className:"ask-modal__source-card",onClick:()=>T(A),children:[r.jsxs("div",{className:"ask-modal__source-top",children:[r.jsx(ot,{path:st.file}),r.jsx("strong",{className:"ask-modal__source-path",children:A.path}),r.jsxs("span",{className:"ask-modal__source-line",children:["Zeile ",A.line]})]}),A.symbol&&r.jsxs("span",{className:"ask-modal__source-symbol",children:["Symbol: ",r.jsx("code",{children:A.symbol})]}),A.why&&r.jsx("span",{className:"ask-modal__source-why",children:A.why})]},`${A.path}-${A.line}-${S}`))})]}),h.confidence==="low"&&a&&r.jsxs("div",{className:"ask-modal__low-conf-alert",children:[r.jsx("p",{children:"Die Antwort hat eine niedrige Konfidenz."}),r.jsx("button",{type:"button",className:"pb-button",onClick:M,children:"Reguläre Code-Suche öffnen"})]}),h.followUps&&h.followUps.length>0&&r.jsxs("div",{className:"ask-modal__followups",children:[r.jsx("h5",{children:"Folgefragen:"}),r.jsx("div",{className:"ask-modal__followup-list",children:h.followUps.map(A=>r.jsxs("button",{type:"button",className:"ask-modal__followup-btn",onClick:()=>b(A),children:["↳ ",A]},A))})]})]})]}),r.jsxs("footer",{className:"ask-modal__footer",children:[r.jsxs("span",{children:[r.jsx("kbd",{children:"Enter"})," Fragen absenden"]}),r.jsxs("span",{children:[r.jsx("kbd",{children:"Esc"})," Schließen"]}),r.jsxs("span",{children:[r.jsx("kbd",{children:"/"})," Immer verfügbar"]})]})]})})}const pL=[{id:"briefing",label:"Briefing",testName:"Briefing",hint:"Projekt-Briefing: Zusammenfassung, Kennzahlen, Hotspots"},{id:"notes",label:"Notizen",testName:"Wissen",hint:"Notizen lesen, schreiben und verknüpfen"},{id:"atlas",label:"Graph",testName:"Atlas",hint:"Wissensgraph: Symbole, Notizen und Verbindungen"},{id:"search",label:"Suche",hint:"Code und Notizen durchsuchen"},{id:"explorer",label:"Dateien",testName:"Explorer",hint:"Quelldateien mit echtem Inhalt und Zeilennummern"},{id:"hygiene",label:"Aufräumen",testName:"Hygiene",hint:"Checkouts, ungepushte Branches und ungesicherte Arbeit auf einen Blick"}],mL=[{id:"packs",label:"Kontext-Pakete",hint:"Context-Packs für Agenten-Aufgaben zusammenstellen"},{id:"queue",label:"Aufgaben",hint:"Wartende Aufgaben; der nächste freie Agent nimmt sie"},{id:"mesh",label:"Agenten-Netz",hint:"Nachweisbare Arbeit und Übergaben aus dem Core-Trace"},{id:"turns",label:"Turns",hint:"Worker-Turns, Nachrichten, Claims und Freigaben auf einer Zeitachse"},{id:"city",label:"Code-Stadt",hint:"Workspace als Stadt — Repos als Distrikte, Dateien als Gebäude"}],tM=[{id:"turns",label:"Agenten",hint:"Flottenboard, Nachrichten und Freigaben"},{id:"durable",label:"Dauerbetrieb",hint:"Nachtschicht, Supervisor und Fleet-Lebenszeichen"},{id:"disk",label:"Festplatten",hint:"Laufwerke, Inventur und Löschliste"},{id:"plan",label:"Plan",hint:"Master-Aufgaben, Gates und Fortschritt"}],gL={briefing:"▧",atlas:"⌘",notes:"▤",explorer:"▱",search:"⌕",packs:"▣",city:"▦",mesh:"◉",queue:"☷",turns:"◎",hygiene:"⌁",durable:"↻",disk:"▤",plan:"☷"},hm=[...pL,...mL,...tM],ou=Qy,xL=2e3;function vL(){const t=new URLSearchParams(location.search).get("view"),e=(()=>{try{return localStorage.getItem("plugbrain.view")}catch{return null}})(),n=t||e;return hm.some(i=>i.id===n)?n:"briefing"}function _L(){const t=new URLSearchParams(location.search);return t.has("workspace")?t.get("workspace")??"":t.has("workspaceRoot")?"":n1()}function bL(){var oc,cc,uc,C,W,ee;const[t,e]=P.useState(null),[n,i]=P.useState(null),[a,s]=P.useState(null),[l,o]=P.useState(null),[c,u]=P.useState(""),[h,g]=P.useState({depth:0,tasks:[]}),[d,p]=P.useState(""),[x,_]=P.useState(0),[m,f]=P.useState(vL),[v,y]=P.useState(_L),b=P.useMemo(()=>{const w=new URLSearchParams(location.search);return w.has("workspace")?null:w.get("workspaceRoot")},[]),[T,M]=P.useState([]),[A,S]=P.useState(!1),[N,D]=P.useState(""),[U,I]=P.useState(!1),[Y,k]=P.useState(""),[B,z]=P.useState(""),[j,X]=P.useState(""),[q,ve]=P.useState(null),[he,Ae]=P.useState(null),[$,pe]=P.useState(!1),[we,ne]=P.useState([]),[ge,ue]=P.useState(()=>{const w=new URLSearchParams(location.search).get("file"),L=Number(new URLSearchParams(location.search).get("line"));return w?{path:w,line:Number.isFinite(L)?L:null}:null}),[Le,Ie]=P.useState(null),[Xe,dt]=P.useState(null),re=P.useCallback(()=>{if(!v){s(null);return}fv(v).then(s).catch(()=>s(null))},[v]);P.useEffect(()=>{if(!v)return;let w=!0;const L=()=>{fv(v).then(ae=>{w&&s(ae)}).catch(()=>{w&&s(null)})};L();const ie=window.setInterval(L,15e3);return()=>{w=!1,clearInterval(ie)}},[v]),P.useEffect(()=>{if(!v){o(null);return}let w=!0;const L=()=>{nS().then(ae=>{w&&(o(ae),u(""))}).catch(ae=>{w&&u(ae instanceof Error?ae.message:String(ae))})};L();const ie=window.setInterval(L,15e3);return()=>{w=!1,clearInterval(ie)}},[v]);const[H,le]=P.useState(!1),[se,Me]=P.useState("appearance"),[Oe,De]=P.useState(()=>{try{const w=localStorage.getItem("plugbrain.theme");if(w==="dark"||w==="light"||w==="system")return w}catch{}return"dark"});P.useEffect(()=>{try{localStorage.setItem("plugbrain.theme",Oe)}catch{}const w=L=>{let ie=L;L==="system"&&(ie=typeof window<"u"&&window.matchMedia&&window.matchMedia("(prefers-color-scheme: light)").matches?"light":"dark"),document.documentElement.dataset.theme=ie};if(w(Oe),Oe==="system"&&typeof window<"u"&&window.matchMedia){const L=window.matchMedia("(prefers-color-scheme: light)"),ie=()=>w("system");return L.addEventListener("change",ie),()=>L.removeEventListener("change",ie)}},[Oe]);const[Re,Fe]=P.useState(!1),[ye,O]=P.useState(aS()),[Ye,$e]=P.useState(Ti()),[R,E]=P.useState(!1),[V,K]=P.useState(null),[J,Se]=P.useState([]),[_e,te]=P.useState(!1),[Q,oe]=P.useState(""),[me,xe]=P.useState(!1),[Te,Be]=P.useState(!1),[Ve,Ke]=P.useState(!1),[G,Ne]=P.useState(""),[ce,Ce]=P.useState(0),Ue=P.useRef(null),be=P.useRef(null);P.useEffect(()=>{const w=L=>{if(L.key==="Escape"){Ke(!1),xe(!1),le(!1),E(!1),Be(!1);return}const ie=L.target;ie!=null&&ie.matches('input, textarea, select, [contenteditable="true"]')||((L.ctrlKey||L.metaKey)&&L.key.toLowerCase()==="k"?(L.preventDefault(),f("search"),window.setTimeout(()=>{var ae;return(ae=Ue.current)==null?void 0:ae.focus()},0)):L.key==="/"&&!L.shiftKey?(L.preventDefault(),Be(!0)):L.key==="?"||L.key==="/"&&L.shiftKey?(L.preventDefault(),xe(!0)):L.key.toLowerCase()==="o"&&!L.ctrlKey&&!L.metaKey&&(L.preventDefault(),S(!0)))};return window.addEventListener("keydown",w),()=>window.removeEventListener("keydown",w)},[]),P.useEffect(()=>{if(!Ve)return;const w=L=>{var ie;(ie=be.current)!=null&&ie.contains(L.target)||Ke(!1)};return document.addEventListener("pointerdown",w),document.addEventListener("focusin",w),()=>{document.removeEventListener("pointerdown",w),document.removeEventListener("focusin",w)}},[Ve]),P.useEffect(()=>{try{localStorage.setItem("plugbrain.view",m)}catch{}},[m]),P.useEffect(()=>{(m==="city"||m==="mesh"||m==="queue")&&ue(null),m==="mesh"&&(pe(!1),Ae(null))},[m]);const qe=P.useRef(null);P.useEffect(()=>{qe.current=he},[he]);const je=w=>{y(w),i1(w);const L=new URL(location.href);w?L.searchParams.set("workspace",w):L.searchParams.delete("workspace"),w&&L.searchParams.delete("workspaceRoot"),history.replaceState(null,"",L.toString())};P.useEffect(()=>{if(!v)return;let w=!0;return fetch(`/api/graph?workspace=${encodeURIComponent(v)}&limit=5000`,{headers:Lt()}).then(L=>L.json()).then(L=>{if(!w||!(L!=null&&L.nodes))return;const ie=L.nodes.filter(ae=>{var Ee;return ae.type==="file"&&(ae.path||((Ee=ae.properties)==null?void 0:Ee.path))}).map(ae=>{var Ee,ke,We,Je;return{id:ae.id,path:ae.path||((Ee=ae.properties)==null?void 0:Ee.path),label:ae.label||ae.path,lang:ae.lang||((ke=ae.properties)==null?void 0:ke.lang),loc:ae.loc??((We=ae.properties)==null?void 0:We.lines)??0,agent:ae.agent||((Je=ae.properties)!=null&&Je.agentId?{id:ae.properties.agentId,name:ae.properties.agentName,color:ae.properties.agentColor}:null)}});ne(ie)}).catch(()=>{}),()=>{w=!1}},[v,x]),P.useEffect(()=>{let w=!0;return dv().then(L=>{if(w){if(M(L),!v&&b!==null){const ie=a1(L,b);ie&&je(ie);return}if(!v&&L.length>0){const ie=[...L].sort((ae,Ee)=>(Ee.indexedAt??"").localeCompare(ae.indexedAt??""))[0];ie&&je(ie.id)}}}).catch(()=>{w&&k("Die Galaxie ist nicht erreichbar — läuft plugbrain serve?")}),()=>{w=!1}},[]);const xt=async w=>{w.preventDefault();const L=N.trim();if(L!==""){I(!0),k(""),z(""),X("Vault registriert — Indexlauf wird vorbereitet …");try{const ie=await s1(L,void 0,ae=>X(hp(ae)));dv().then(ae=>{ae.length>0&&M(ae)}).catch(()=>{}),z("Vault registriert und indiziert."),D(""),S(!1),je(ie),f("notes"),_(ae=>ae+1)}catch(ie){const ae=ie instanceof Error?ie.message:String(ie);let Ee=ae;/ENOENT|not found|nicht gefunden/i.test(ae)?Ee=`Ordner nicht gefunden oder nicht lesbar: "${L}". Bitte überprüfe den Pfad.`:/fetch|network|connection refused|econnrefused/i.test(ae)&&(Ee='Brain-Kern nicht erreichbar. Bitte prüfe, ob "plugbrain serve" im Terminal läuft.'),k(Ee)}finally{I(!1),X("")}}},vt=async()=>{if(!(!v||U)){I(!0),k(""),z(""),X("Indexlauf wird vorbereitet …");try{const w=await tS(v,L=>X(hp(L)));z(`Neu indiziert: ${(w==null?void 0:w.files)??0} Dateien, ${(w==null?void 0:w.symbols)??0} Symbole, ${(w==null?void 0:w.edges)??0} Kanten.`),_(L=>L+1)}catch(w){k(w instanceof Error?w.message:String(w))}finally{I(!1),X("")}}};P.useEffect(()=>{if(!v)return;let w=!0,L;const ie=async()=>{try{const ae=await fetch(`/api/index/progress?workspace=${encodeURIComponent(v)}`,{headers:Lt()});if(ae.ok){const Ee=await ae.json();if(!w)return;X(ke=>r1(Ee,ke))}}catch{}w&&(L=setTimeout(()=>void ie(),1500))};return ie(),()=>{w=!1,clearTimeout(L)}},[v]);const Jt=w=>{w.preventDefault(),sS(ye.trim()),m1(Ye.trim()),T1(),_(L=>L+1),Fe(!1)},rn=async(w=!1)=>{if(!(!v||_e)){te(!0),oe("");try{const L=await g1(v);K(L),Se([...L.indexSelection.checkoutIds]),w&&E(!0)}catch(L){oe(L instanceof Error?L.message:String(L)),w&&E(!0)}finally{te(!1)}}},Ua=w=>{Se(L=>L.includes(w)?L.filter(ie=>ie!==w):[...L,w])},Ct=async w=>{if(w.preventDefault(),!(!v||_e)){te(!0),oe("");try{const L=await x1(v,J);K(L),Se([...L.indexSelection.checkoutIds]),z(L.indexSelection.checkoutIds.length===0?"Code-Auswahl gespeichert: bewusst keine Code-Checkouts aktiv.":`Code-Auswahl gespeichert: ${L.indexSelection.checkoutIds.length} Checkout(s) aktiv.`),E(!1),_(ie=>ie+1)}catch(L){oe(L instanceof Error?L.message:String(L))}finally{te(!1)}}},ea=r.jsxs("form",{className:"brain-vault",onSubmit:xt,children:[r.jsxs("div",{className:"brain-vault__row",children:[r.jsx("input",{className:"brain-vault__path",value:N,onChange:w=>D(w.target.value),placeholder:"Pfad eines Ordners (auch ohne .git), z. B. C:\\Notizen\\vault",spellCheck:!1,"aria-label":"Vault-Pfad"}),r.jsx("button",{type:"submit",className:"brain-vault__open",disabled:U||N.trim()==="",children:U?"Indiziere …":"Als Vault öffnen"})]}),v&&r.jsx("div",{className:"brain-vault__row brain-vault__row--tools",children:r.jsx("button",{type:"button",className:"brain-vault__reindex",disabled:U,onClick:()=>void vt(),children:U?"läuft …":"Neu indizieren"})}),j&&r.jsx("p",{className:"brain-vault__progress",role:"status","aria-live":"polite",children:j}),Y&&r.jsx("p",{className:"brain-vault__error",role:"alert",children:Y}),B&&r.jsx("p",{className:"brain-vault__done",role:"status",children:B})]});P.useEffect(()=>{const w=v||void 0;fetch("/api/timeline"+(w?"?workspace="+encodeURIComponent(w):""),{headers:Lt()}).then(L=>L.json()).then(L=>{var ie;(ie=L==null?void 0:L.bounds)!=null&&ie.first&&ve(L.bounds)}).catch(()=>{})},[v]),P.useEffect(()=>{if(!$||!q)return;const w=new Date(q.first).getTime(),L=new Date(q.last).getTime(),ie=Math.max(1,L-w);let ae=he?Math.round((new Date(he).getTime()-w)/ie*Wa):0;const Ee=setInterval(()=>{if(ae+=1,ae>=Wa){Ae(null),pe(!1);return}Ae(new Date(w+ie*ae/Wa).toISOString())},220);return()=>clearInterval(Ee)},[$,q]),P.useEffect(()=>{const w=new AbortController;let L,ie="";const ae=v||void 0;async function Ee(){var ke,We,Je;try{const Ze=new URLSearchParams;ae&&Ze.set("workspace",ae),Ze.set("limit",String(xL)),qe.current&&Ze.set("until",qe.current);const He=await fetch("/api/atlas/snapshot"+(Ze.toString()?`?${Ze}`:""),{headers:Lt(),signal:w.signal});if(!He.ok)throw new Error(`Brain-Verbindung: HTTP ${He.status}`);const rt=await He.json();if(!((ke=rt.workspace)!=null&&ke.canonicalPath)||!Array.isArray((We=rt.graph)==null?void 0:We.nodes)||!Array.isArray((Je=rt.graph)==null?void 0:Je.edges))throw new Error("Der Brain-Snapshot ist unvollständig.");const Ht=`${rt.workspace.id}:${rt.indexGeneration??""}:${he??""}:${rt.graph.nodes.length}:${rt.graph.edges.length}`;Ht!==ie&&(e(rt),ie=Ht),p("")}catch(Ze){w.signal.aborted||p(Ze instanceof Error?Ze.message:String(Ze))}if(ae)try{const Ze=await v1(ae);w.signal.aborted||i(Ze)}catch{w.signal.aborted||i(null)}else w.signal.aborted||i(null);try{const Ze=await fetch("/api/queue"+(ae?"?workspace="+encodeURIComponent(ae):""),{headers:Lt(),signal:w.signal});if(Ze.ok){const He=await Ze.json();(He==null?void 0:He.ok)===!0&&Array.isArray(He.tasks)&&g({depth:Number(He.depth??0),tasks:He.tasks})}}catch{}w.signal.aborted||(L=setTimeout(Ee,3e3))}return Ee(),()=>{w.abort(),clearTimeout(L)}},[x,he,v]);const ta=t!=null&&t.coverage?!t.coverage.indexComplete:!1,Pi=((oc=t==null?void 0:t.coverage)==null?void 0:oc.staleFiles)??0,F=d?{tone:"bad",text:"Offline",title:d}:t?ta?{tone:"warn",text:`${Pi} warten`,title:`${Pi} Datei(en) warten auf den Index`}:{tone:"ok",text:"Aktuell",title:"Der Index ist auf dem neuesten Stand."}:{tone:"idle",text:"Verbinde …",title:"Der aktuelle Stand wird aus dem Brain gelesen."},de=P.useMemo(()=>{var w;return we.length>0?we:(w=t==null?void 0:t.graph)!=null&&w.nodes?t.graph.nodes.filter(L=>{var ie;return L.type==="file"&&(((ie=L.properties)==null?void 0:ie.path)||L.path)}).map(L=>{var ae,Ee,ke,We;const ie=((ae=L.properties)==null?void 0:ae.path)||L.path||"";return{id:L.id,path:ie,label:L.label||L.name||ie,lang:((Ee=L.properties)==null?void 0:Ee.lang)??L.lang??null,loc:((ke=L.properties)==null?void 0:ke.lines)??L.loc??0,agent:(We=L.properties)!=null&&We.agentId?{id:L.properties.agentId,name:L.properties.agentName||L.properties.agentId,color:L.properties.agentColor||"#60a5fa"}:null}}):[]},[we,t]),Pe=(w,L)=>{w&&(Ie(null),ue({path:w,line:L}))},ht=w=>{Pe(w),f("explorer")},$t=w=>{ue(null),Ie(w),f("explorer")},ln=w=>{dt(w),f("mesh")},Ft=P.useCallback(()=>_(w=>w+1),[]),cr=P.useCallback(w=>{Ie(null),ue({path:w,line:null}),w.endsWith(".md")?f("notes"):f("explorer")},[]),Oa=P.useMemo(()=>{if(!q||!(m==="atlas"||m==="city"))return null;const w=new Date(q.first).getTime(),L=Math.max(1,new Date(q.last).getTime()-w),ie=he?Math.round((new Date(he).getTime()-w)/L*Wa):Wa;return{playing:$,step:ie,label:he?new Date(he).toLocaleString("de-DE",{day:"2-digit",month:"2-digit",hour:"2-digit",minute:"2-digit"}):"jetzt",onTogglePlay:()=>pe(ae=>!ae),onScrub:ae=>{if(pe(!1),ae>=Wa){Ae(null);return}Ae(new Date(w+L*ae/Wa).toISOString())}}},[q,he,$]),Es=ge&&v?r.jsx("div",{className:"atlas-source-overlay",children:r.jsx(D0,{workspaceId:v,path:ge.path,highlightLine:ge.line,onClose:()=>ue(null),onNavigateFile:(w,L)=>Pe(w,L)})}):null,ur=w=>ge&&v?r.jsx(D0,{workspaceId:v,path:ge.path,highlightLine:ge.line,onClose:()=>ue(null),onNavigateFile:(L,ie)=>Pe(L,ie)}):w,Pl=t?ou(t.workspace.name):((cc=T.find(w=>w.id===v))==null?void 0:cc.name)??"";return r.jsxs("div",{className:"pb-app",children:[r.jsxs("header",{className:"pb-topbar",children:[r.jsxs("div",{className:"pb-topbar__brand",children:[r.jsx("span",{className:"pb-logo","aria-hidden":"true"}),r.jsx("span",{className:"pb-topbar__product",children:"PlugBrain"}),v&&T.length>1?r.jsxs("label",{className:"pb-select",title:"Vault wechseln",children:[r.jsx("span",{className:"pb-sr",children:"Vault"}),r.jsx("select",{value:v,onChange:w=>{const L=w.target.value;L&&je(L)},children:T.map(w=>r.jsx("option",{value:w.id,children:ou(w.name)},w.id))}),r.jsx(ot,{path:st.chevron})]}):Pl!==""&&r.jsx("span",{className:"pb-topbar__workspace",title:(t==null?void 0:t.workspace.canonicalPath)??"",children:Pl})]}),r.jsxs("div",{className:"pb-topbar__actions",children:[v&&r.jsxs("div",{className:"pb-mobile-nav",ref:be,children:[r.jsx("button",{type:"button",className:"pb-tool pb-tool--icon",title:"Menü","aria-label":"Menü","aria-controls":"mobile-menu","aria-expanded":Ve,onClick:()=>Ke(w=>!w),children:r.jsx(ot,{path:st.burger})}),r.jsxs("div",{id:"mobile-menu",className:"pb-mobile-menu",hidden:!Ve,children:[hm.map(w=>r.jsx("button",{type:"button",className:"pb-tab-menu-item",title:w.hint,"aria-current":w.id===m?"page":void 0,onClick:()=>{f(w.id),Ke(!1)},children:w.label},w.id)),r.jsx("button",{type:"button",className:"pb-tab-menu-item",onClick:()=>{f("search"),Ke(!1)},children:"Suche (Strg+K)"}),r.jsx("button",{type:"button",className:"pb-tab-menu-item",onClick:()=>{Be(!0),Ke(!1)},children:"Fragen"}),r.jsx("button",{type:"button",className:"pb-tab-menu-item",onClick:()=>{S(!0),Ke(!1)},children:"Vault öffnen"}),r.jsx("button",{type:"button",className:"pb-tab-menu-item",disabled:_e,onClick:()=>{Me("repos"),le(!0),rn(),Ke(!1)},children:_e?"Repos laden …":"Repos wählen"})]})]}),v&&r.jsxs("span",{className:"pb-status","data-tone":F.tone,role:"status",title:F.title,children:[r.jsx("i",{"aria-hidden":"true"}),r.jsx("span",{children:F.text})]}),v&&r.jsxs("form",{className:"pb-global-search",role:"search",onSubmit:w=>{var ae;w.preventDefault();const L=((ae=Ue.current)==null?void 0:ae.value.trim())??"",ie=new URL(location.href);ie.searchParams.set("view","search"),L?ie.searchParams.set("q",L):ie.searchParams.delete("q"),history.replaceState(null,"",ie.toString()),Ce(Ee=>Ee+1),f("search")},children:[r.jsx(ot,{path:st.search}),r.jsx("input",{ref:Ue,"aria-label":"Code und Notizen durchsuchen",placeholder:"Code und Notizen durchsuchen …"}),r.jsx("kbd",{children:"Ctrl K"})]}),v&&r.jsx("button",{type:"button",className:"pb-tool pb-topbar__secondary-action",onClick:()=>Be(!0),title:"Frag das Projekt … (/)","aria-label":"Frag das Projekt",children:r.jsx("span",{children:"Fragen"})}),v&&r.jsxs("button",{type:"button",className:"pb-tool pb-topbar__secondary-action","aria-expanded":A,onClick:()=>{S(w=>!w),k(""),z("")},title:"Einen Ordner als neuen Vault öffnen (O)",children:[r.jsx(ot,{path:st.folder}),r.jsx("span",{children:A?"Schließen":"Vault öffnen"})]}),v&&r.jsx("button",{type:"button",className:"pb-tool pb-topbar__secondary-action",onClick:()=>{Me("repos"),le(!0),rn()},disabled:_e,title:"Aktive Code-Checkouts auswählen (Repos wählen)",children:r.jsx("span",{children:_e?"Lädt …":"Repos wählen"})}),r.jsx("button",{type:"button",className:"pb-tool pb-tool--icon",onClick:()=>xe(!0),"aria-label":"Tastenkürzel anzeigen",title:"Tastenkürzel (?)",children:r.jsx(ot,{path:st.keyboard})}),r.jsx("button",{type:"button",className:"pb-tool pb-tool--icon",onClick:()=>{Me("appearance"),le(!0)},"aria-label":"Einstellungen",title:"Einstellungen",children:r.jsx(ot,{path:st.gear})})]})]}),d&&r.jsxs("div",{className:"pb-banner",role:"alert",children:[r.jsx("strong",{children:"Brain-Server offline."}),r.jsxs("span",{children:["Der Kern antwortet nicht (",d,"). Bitte prüfe, ob ",r.jsx("code",{children:"plugbrain serve"})," läuft."]}),r.jsxs("button",{type:"button",className:"pb-button pb-button--primary",onClick:Ft,children:[r.jsx(ot,{path:st.refresh})," Erneut verbinden"]})]}),A&&v&&r.jsx("div",{className:"pb-drawer",children:ea}),r.jsxs("main",{className:`pb-main${v?" pb-main--rail":""}`,children:[v&&r.jsxs("aside",{className:"pb-activity","aria-label":"Arbeitsbereiche",children:[r.jsxs("button",{type:"button",className:"pb-activity__item","aria-current":m==="atlas"?"page":void 0,onClick:()=>f("atlas"),title:"Code-Graph","aria-label":"Code",children:[r.jsx(ot,{path:st.code}),r.jsx("span",{children:"Code"})]}),r.jsxs("button",{type:"button",className:"pb-activity__item","aria-current":m==="explorer"?"page":void 0,onClick:()=>f("explorer"),title:"Dateien durchsuchen","aria-label":"Explorer",children:[r.jsx(ot,{path:st.folder}),r.jsx("span",{children:"Dateien"})]}),r.jsxs("button",{type:"button",className:"pb-activity__item","aria-current":m==="notes"?"page":void 0,onClick:()=>f("notes"),title:"Notizen / Wissen","aria-label":"Wissen",children:[r.jsx(ot,{path:st.notes}),r.jsx("span",{children:"Notizen"})]}),r.jsx("div",{className:"pb-activity__divider"}),tM.map(w=>r.jsxs("button",{type:"button",className:"pb-activity__item pb-activity__system","aria-current":m===w.id?"page":void 0,onClick:()=>f(w.id),title:w.hint,"aria-label":w.label,children:[r.jsx("span",{className:"pb-activity__glyph","aria-hidden":"true",children:gL[w.id]}),r.jsx("span",{children:w.label})]},w.id)),r.jsx("div",{className:"pb-activity__divider"}),r.jsxs("details",{className:"pb-activity__views",open:!1,children:[r.jsxs("summary",{className:"pb-activity__item",title:"Alle vorhandenen Ansichten","aria-label":"Ansichten",children:[r.jsxs("span",{className:"pb-activity__grid","aria-hidden":"true",children:["•••",r.jsx("br",{}),"•••"]}),r.jsx("span",{children:"Ansichten"})]}),r.jsx("div",{className:"pb-activity__menu","aria-label":"Vorhandene Ansichten",children:hm.map(w=>{const L="testName"in w&&typeof w.testName=="string"?w.testName:w.label;return r.jsx("button",{type:"button","aria-current":m===w.id?"page":void 0,onClick:()=>f(w.id),children:L},w.id)})})]}),r.jsxs("button",{type:"button",className:"pb-activity__item pb-activity__settings",onClick:()=>{Me("appearance"),le(!0)},title:"Einstellungen","aria-label":"Einstellungen",children:[r.jsx(ot,{path:st.gear}),r.jsx("span",{children:"Einstellungen"})]})]}),v?r.jsxs(r.Fragment,{children:[m==="briefing"&&r.jsxs("div",{className:"pb-view",children:[r.jsx(eL,{workspaceId:v,workspaceName:Pl,onOpenFile:w=>Pe(w),onOpenNotes:()=>f("notes")}),Es]}),m==="atlas"&&r.jsxs("div",{className:"pb-view",children:[r.jsxs("div",{className:"pb-view-header",children:[r.jsx("h2",{children:"Graph"}),r.jsx("p",{children:"Wissensgraph: Symbole, Notizen und Verbindungen"}),r.jsx("button",{type:"button",className:"pb-button pb-button--primary",onClick:Ft,children:"Graph aktualisieren"})]}),r.jsx("div",{id:"app",className:"atlas-app",children:r.jsx(ZD,{graph:(t==null?void 0:t.graph)??null,coverage:(t==null?void 0:t.coverage)??null,error:t?"":d,onRetry:Ft,onOpenSource:cr,timeline:Oa})})]}),m==="notes"&&r.jsxs("div",{className:"pb-view",children:[r.jsxs("div",{className:"pb-view-header",children:[r.jsx("h2",{children:"Notizen"}),r.jsx("p",{children:"Notizen lesen, schreiben und verknüpfen wie in Obsidian"}),r.jsx("button",{type:"button",className:"pb-button pb-button--primary",onClick:()=>{const w=document.querySelector(".notes-create button, .notes-empty-list button, .notes-empty--initial button");w==null||w.click()},children:"Neue Notiz"})]}),r.jsx(L2,{workspaceId:v,onOpenSource:ht,onOpenRevision:$t,onOpenAgentRun:ln,onNavigateTab:w=>f(w)})]}),m==="explorer"&&r.jsxs("div",{className:"pb-view",children:[r.jsxs("div",{className:"pb-view-header",children:[r.jsx("h2",{children:"Dateien"}),r.jsx("p",{children:"Quelldateien mit echtem Inhalt und Zeilennummern"}),r.jsx("button",{type:"button",className:"pb-button pb-button--primary",onClick:Ft,children:"Dateien aktualisieren"})]}),r.jsxs("div",{className:"workbench-split",children:[r.jsx("div",{className:"workbench-pane workbench-pane--side",children:r.jsx(E2,{workspaceName:(t==null?void 0:t.workspace.name)??(((uc=T.find(w=>w.id===v))==null?void 0:uc.name)||"Workspace"),files:de,activePath:ge==null?void 0:ge.path,onSelectFile:w=>Pe(w)})}),r.jsx("div",{className:"workbench-pane workbench-pane--main",children:Le&&!ge?r.jsx(yL,{workspaceId:v,revision:Le,onClose:()=>Ie(null)}):ur(r.jsxs("div",{className:"source-placeholder",children:[r.jsx("div",{className:"source-placeholder__icon",children:r.jsx(ot,{path:st.folder})}),r.jsx("h3",{children:"Datei im Explorer auswählen"}),r.jsx("p",{children:"Wähle eine Datei im linken Baum, um den echten Inhalt mit Zeilennummern und Revision anzuzeigen."})]}))})]})]}),m==="search"&&r.jsxs("div",{className:"pb-view",children:[r.jsxs("div",{className:"pb-view-header",children:[r.jsx("h2",{children:"Suche"}),r.jsx("p",{children:"Code und Notizen durchsuchen"}),r.jsx("button",{type:"button",className:"pb-button pb-button--primary",onClick:()=>{const w=document.querySelector(".search-input, .notes-search input");w==null||w.focus()},children:"Suche fokussieren"})]}),r.jsxs("div",{className:"workbench-split",children:[r.jsx("div",{className:"workbench-pane workbench-pane--side",children:r.jsx(T2,{workspaceId:v,onSelectHit:(w,L)=>Pe(w,L)},ce)}),r.jsx("div",{className:"workbench-pane workbench-pane--main",children:ur(r.jsxs("div",{className:"source-placeholder",children:[r.jsx("div",{className:"source-placeholder__icon",children:r.jsx(ot,{path:st.search})}),r.jsx("h3",{children:"Code- und Symbolsuche"}),r.jsxs("p",{children:["Gib einen Suchbegriff ein, zum Beispiel ",r.jsx("code",{children:"authKey"}),". Ein Klick auf einen Treffer öffnet die Quelle."]})]}))})]})]}),m==="packs"&&r.jsxs("div",{className:"pb-view",children:[r.jsxs("div",{className:"pb-view-header",children:[r.jsx("h2",{children:"Kontext-Pakete"}),r.jsx("p",{children:"Context-Packs für Agenten-Aufgaben zusammenstellen"}),r.jsx("button",{type:"button",className:"pb-button pb-button--primary",onClick:Ft,children:"Pakete aktualisieren"})]}),r.jsxs("div",{className:"workbench-split",children:[r.jsx("div",{className:"workbench-pane workbench-pane--side",children:r.jsx(O2,{workspaceId:v,onSelectSource:w=>Pe(w)})}),r.jsx("div",{className:"workbench-pane workbench-pane--main",children:ur(r.jsxs("div",{className:"source-placeholder",children:[r.jsx("div",{className:"source-placeholder__icon",children:r.jsx(ot,{path:st.open})}),r.jsx("h3",{children:"Context-Pack-Inspector"}),r.jsx("p",{children:"Erzeuge einen Context Pack für eine Aufgabe. Ein Klick auf eine extrahierte Quelle zeigt ihren Inhalt."})]}))})]})]}),m==="city"&&r.jsxs("div",{className:"pb-view",children:[r.jsxs("div",{className:"pb-view-header",children:[r.jsx("h2",{children:"Code-Stadt"}),r.jsx("p",{children:"Workspace als Stadt — Repos als Distrikte, Dateien als Gebäude"}),r.jsx("button",{type:"button",className:"pb-button pb-button--primary",onClick:Ft,children:"Stadt aktualisieren"})]}),r.jsxs("div",{className:"pb-city",children:[r.jsx(m2,{snapshot:t,onSelectFile:Pe}),Oa&&r.jsx("div",{className:"pb-overlay-tools",children:r.jsx(JS,{timeline:Oa})}),Es]})]}),m==="queue"&&r.jsxs("div",{className:"pb-view pb-view--scroll",children:[r.jsxs("div",{className:"pb-view-header",children:[r.jsx("h2",{children:"Aufgaben"}),r.jsx("p",{children:"Wartende Aufgaben; der nächste freie Agent nimmt sie"}),r.jsx("button",{type:"button",className:"pb-button pb-button--primary",onClick:Ft,children:"Aufgaben prüfen"})]}),r.jsx(v2,{tasks:h.tasks,depth:h.depth})]}),m==="mesh"&&r.jsxs("div",{className:"pb-view",children:[r.jsxs("div",{className:"pb-view-header",children:[r.jsx("h2",{children:"Agenten-Netz"}),r.jsx("p",{children:"Nachweisbare Arbeit und Übergaben aus dem Core-Trace"}),r.jsx("button",{type:"button",className:"pb-button pb-button--primary",onClick:Ft,children:"Netz aktualisieren"})]}),r.jsxs("div",{className:"pb-mesh",children:[r.jsx(x2,{mesh:n,workspaceId:v,onSelectFile:Pe,focusAgentId:Xe}),Es]})]}),m==="turns"&&r.jsx(oL,{workspaceId:v,data:a,resources:l,onRefresh:re}),m==="durable"&&r.jsx(dL,{data:a,onRefresh:re}),m==="disk"&&r.jsx(cL,{}),m==="plan"&&r.jsx(hL,{workspaceId:v}),m==="hygiene"&&r.jsxs("div",{className:"pb-view",children:[r.jsx(iL,{workspaceId:v}),Es]})]}):r.jsxs("div",{className:"brain-landing",children:[r.jsx("h1",{className:"brain-landing__title",children:"PlugBrain"}),r.jsxs("p",{className:"brain-landing__lead",children:["Einen Ordner als Vault öffnen — auch einen Wissensordner ohne ",r.jsx("code",{children:".git"}),". Der Brain indiziert ihn einmal und hält ihn über den Daemon automatisch aktuell. Wiki-Links, Überschriften, Tags und Code-Symbole werden zu einem durchsuchbaren Graphen."]}),r.jsxs("ol",{className:"brain-first-run","aria-label":"Erste Schritte",children:[r.jsxs("li",{children:[r.jsx("strong",{children:"Ordner wählen"}),r.jsx("span",{children:"Notiz- oder Projektordner angeben; Git ist nicht erforderlich."})]}),r.jsxs("li",{children:[r.jsx("strong",{children:"Index abwarten"}),r.jsx("span",{children:"Der echte Fortschritt bleibt sichtbar, bis Notizen und Graph bereit sind."})]}),r.jsxs("li",{children:[r.jsx("strong",{children:"Wissen öffnen"}),r.jsx("span",{children:"Leere Vaults bleiben ehrlich leer und bieten direkt „Erste Notiz anlegen“."})]})]}),ea,T.length>0&&r.jsxs("div",{className:"brain-vault__known",children:[r.jsx("span",{children:"Oder einen bekannten Vault öffnen:"}),T.map(w=>r.jsxs("button",{type:"button",className:"brain-vault__known-item",onClick:()=>je(w.id),children:[ou(w.name)," ",r.jsx("em",{title:w.root,children:w.indexedAt?"indiziert":"nicht indiziert"})]},w.id))]})]})]}),v&&r.jsxs("footer",{className:"pb-statusbar","aria-label":"Systemstatus",children:[r.jsxs("span",{title:t!=null&&t.coverage?`${Pi} Dateien warten auf den Index`:"Indexstatus nicht verfügbar",children:["Index: ",t!=null&&t.coverage?ta?`${Pi} warten`:"aktuell":"unbekannt"]}),r.jsxs("span",{title:c||"Live-Ressourcenprojektion",children:["Brain: ",d?"Fehler":t?"verbunden":"lädt"]}),r.jsxs("span",{children:["Platte: ",l!=null&&l.host.drives.length?`${(Math.min(...l.host.drives.map(w=>w.freeBytes))/1024**3).toFixed(1)} GB frei`:c?"nicht verfügbar":"lädt"]}),r.jsxs("span",{children:["RAM: ",l?`${Math.round(l.host.memory.freeBytes/l.host.memory.totalBytes*100)} % frei`:c?"nicht verfügbar":"lädt"]}),r.jsxs("span",{children:["Lanes: ",a?`${a.board.agents.filter(w=>w.turnState==="working").length} aktiv`:"—"]}),r.jsxs("span",{title:c||(l==null?void 0:l.quotas.map(w=>`${w.account}: ${w.remaining} ${w.unit}`).join(" · "))||"Kontingente",children:["Kontingente: ",l?l.quotas.length?l.quotas.map(w=>`${w.account} ${w.remaining} ${w.unit}`).join(" · "):"keine gemeldet":c?"nicht verfügbar":"lädt"]})]}),H&&r.jsx("div",{className:"brain-modal-backdrop",onClick:()=>le(!1),children:r.jsxs("section",{className:"brain-modal brain-settings-modal",role:"dialog","aria-modal":"true","aria-label":"Einstellungen",onClick:w=>w.stopPropagation(),children:[r.jsxs("div",{className:"brain-modal__header",children:[r.jsx("h3",{children:"Einstellungen"}),r.jsx("button",{type:"button",className:"brain-modal__close",onClick:()=>le(!1),"aria-label":"Einstellungen schließen",children:"✕"})]}),r.jsxs("div",{className:"brain-settings-layout",children:[r.jsxs("nav",{className:"brain-settings-nav","aria-label":"Einstellungskategorien",children:[r.jsx("button",{type:"button",className:"brain-settings-nav-item","data-active":se==="appearance"?"true":"false",onClick:()=>Me("appearance"),children:"Erscheinungsbild"}),r.jsx("button",{type:"button",className:"brain-settings-nav-item","data-active":se==="vaults"?"true":"false",onClick:()=>Me("vaults"),children:"Vault-Verwaltung"}),r.jsx("button",{type:"button",className:"brain-settings-nav-item","data-active":se==="repos"?"true":"false",onClick:()=>{Me("repos"),V||rn()},children:"Repos wählen"}),r.jsx("button",{type:"button",className:"brain-settings-nav-item","data-active":se==="shortcuts"?"true":"false",onClick:()=>Me("shortcuts"),children:"Tastenkürzel"}),r.jsx("button",{type:"button",className:"brain-settings-nav-item","data-active":se==="advanced"?"true":"false",onClick:()=>Me("advanced"),children:"Erweitert"})]}),r.jsxs("div",{className:"brain-settings-content",children:[se==="appearance"&&r.jsxs("div",{children:[r.jsx("h4",{className:"brain-settings-section-title",children:"Erscheinungsbild"}),r.jsx("p",{className:"brain-settings-section-desc",children:"Wähle dein bevorzugtes Farbschema für PlugBrain."}),r.jsxs("div",{className:"brain-theme-options",children:[r.jsxs("button",{type:"button",className:"brain-theme-card","data-active":Oe==="dark"?"true":"false",onClick:()=>De("dark"),children:[r.jsx("strong",{children:"Dunkel"}),r.jsx("div",{style:{fontSize:"11px",color:"var(--muted)",marginTop:"4px"},children:"Standard"})]}),r.jsxs("button",{type:"button",className:"brain-theme-card","data-active":Oe==="light"?"true":"false",onClick:()=>De("light"),children:[r.jsx("strong",{children:"Hell"}),r.jsx("div",{style:{fontSize:"11px",color:"var(--muted)",marginTop:"4px"},children:"Hoher Kontrast"})]}),r.jsxs("button",{type:"button",className:"brain-theme-card","data-active":Oe==="system"?"true":"false",onClick:()=>De("system"),children:[r.jsx("strong",{children:"System"}),r.jsx("div",{style:{fontSize:"11px",color:"var(--muted)",marginTop:"4px"},children:"Automatisch"})]})]})]}),se==="vaults"&&r.jsxs("div",{children:[r.jsx("h4",{className:"brain-settings-section-title",children:"Vault-Verwaltung"}),r.jsx("p",{className:"brain-settings-section-desc",children:"Verwalte den aktiven Wissensordner oder wechsle zu einem bestehenden Vault."}),v?r.jsx("div",{className:"brain-settings-vault-card",children:r.jsxs("dl",{children:[r.jsx("dt",{children:"Aktiver Vault:"}),r.jsx("dd",{children:((C=T.find(w=>w.id===v))==null?void 0:C.name)||v}),r.jsx("dt",{children:"Pfad:"}),r.jsx("dd",{children:((W=T.find(w=>w.id===v))==null?void 0:W.root)||"—"}),r.jsx("dt",{children:"Status:"}),r.jsx("dd",{children:(ee=T.find(w=>w.id===v))!=null&&ee.indexedAt?"Indiziert":"Bereit"})]})}):r.jsx("p",{style:{color:"var(--muted)",fontSize:"12px"},children:"Kein Vault geöffnet."}),r.jsxs("div",{style:{marginTop:"16px"},children:[r.jsx("strong",{style:{fontSize:"12px",display:"block",marginBottom:"8px"},children:"Bekannte Vaults:"}),r.jsx("div",{style:{display:"grid",gap:"6px"},children:T.map(w=>r.jsxs("button",{type:"button",className:"pb-button",style:{justifyContent:"space-between",background:w.id===v?"var(--panel)":"transparent",borderColor:w.id===v?"var(--accent)":"var(--line)"},onClick:()=>{je(w.id),le(!1)},children:[r.jsx("span",{children:ou(w.name)}),r.jsx("small",{style:{color:"var(--muted)",fontFamily:"var(--mono)"},children:w.root})]},w.id))})]})]}),se==="repos"&&r.jsxs("div",{children:[r.jsx("h4",{className:"brain-settings-section-title",children:"Repos wählen"}),r.jsxs("p",{className:"brain-settings-section-desc",children:["Wähle aus, welche Checkouts indiziert werden sollen. Nur markierte Repos werden gescannt (",V?`${V.checkouts.filter(w=>J.includes(w.id)).length} von ${V.checkouts.length} aktiv`:"lädt …",")."]}),r.jsxs("form",{onSubmit:Ct,children:[Q&&r.jsx("p",{className:"brain-vault__error",role:"alert",children:Q}),V===null?r.jsx("p",{className:"brain-selection-modal__hint",children:"Planet-Inventar wird geladen …"}):V.checkouts.length===0?r.jsx("p",{className:"brain-selection-modal__hint",children:"Dieser Workspace hat keine discoverbaren Code-Checkouts."}):r.jsxs("fieldset",{className:"brain-selection-list",disabled:_e,children:[r.jsx("legend",{children:"Checkout-Inventar"}),V.checkouts.map(w=>r.jsxs("label",{className:w.retiredAt?"is-retired":void 0,children:[r.jsx("input",{type:"checkbox",checked:J.includes(w.id),disabled:w.retiredAt!==null,onChange:()=>Ua(w.id)}),r.jsxs("span",{children:[r.jsx("strong",{children:w.relPrefix}),r.jsxs("small",{children:[w.id," · ",w.branch??"detached",w.retiredAt?" · retired":""]})]})]},w.id))]}),r.jsx("div",{className:"brain-modal__actions",style:{marginTop:"16px"},children:r.jsx("button",{type:"submit",className:"primary",disabled:_e||V===null,children:_e?"Speichert …":"Auswahl speichern"})})]})]}),se==="shortcuts"&&r.jsxs("div",{children:[r.jsx("h4",{className:"brain-settings-section-title",children:"Tastenkürzel"}),r.jsx("p",{className:"brain-settings-section-desc",children:"Tastenkombinationen für schnelle Navigation und Bearbeitung."}),r.jsx("table",{className:"brain-settings-shortcuts-table",children:r.jsxs("tbody",{children:[r.jsxs("tr",{children:[r.jsx("td",{children:r.jsx("kbd",{children:"?"})}),r.jsx("td",{children:"Tastenkürzel-Übersicht öffnen"})]}),r.jsxs("tr",{children:[r.jsxs("td",{children:[r.jsx("kbd",{children:"Strg"}),"+",r.jsx("kbd",{children:"O"})]}),r.jsx("td",{children:"Schnellwechsler (Notiz öffnen)"})]}),r.jsxs("tr",{children:[r.jsxs("td",{children:[r.jsx("kbd",{children:"Strg"}),"+",r.jsx("kbd",{children:"S"})]}),r.jsx("td",{children:"Notiz im Editor speichern"})]}),r.jsxs("tr",{children:[r.jsx("td",{children:r.jsx("kbd",{children:"O"})}),r.jsx("td",{children:"Ordner als Vault öffnen"})]}),r.jsxs("tr",{children:[r.jsx("td",{children:r.jsx("kbd",{children:"/"})}),r.jsx("td",{children:"Im Graph suchen"})]}),r.jsxs("tr",{children:[r.jsx("td",{children:r.jsx("kbd",{children:"F"})}),r.jsx("td",{children:"Graph auf Fenster einpassen"})]}),r.jsxs("tr",{children:[r.jsxs("td",{children:[r.jsx("kbd",{children:"+"})," / ",r.jsx("kbd",{children:"−"})]}),r.jsx("td",{children:"Graph vergrößern / verkleinern"})]}),r.jsxs("tr",{children:[r.jsx("td",{children:r.jsx("kbd",{children:"Esc"})}),r.jsx("td",{children:"Dialog oder Menü schließen"})]})]})})]}),se==="advanced"&&r.jsxs("div",{children:[r.jsx("h4",{className:"brain-settings-section-title",children:"Erweitert (Authentifizierung)"}),r.jsxs("p",{className:"brain-settings-section-desc",children:["Der Brain-Kern authentifiziert den lokalen Desktop automatisch über ",r.jsx("code",{children:"auth.token"}),". Manuelle Konfiguration ist nur für externe Agenten oder Debugging erforderlich."]}),r.jsxs("form",{onSubmit:Jt,children:[r.jsxs("div",{className:"brain-modal__field",children:[r.jsxs("label",{children:["Bearer Token (aus ",r.jsx("code",{children:"auth.token"}),"):"]}),r.jsx("input",{type:"password",className:"brain-modal__input mono",value:ye,onChange:w=>O(w.target.value),placeholder:"plug-..."})]}),r.jsxs("div",{className:"brain-modal__field",children:[r.jsx("label",{children:"Agent ID:"}),r.jsx("input",{type:"text",className:"brain-modal__input mono",value:Ye,onChange:w=>$e(w.target.value),placeholder:"agy"})]}),r.jsx("div",{className:"brain-modal__actions",style:{marginTop:"16px"},children:r.jsx("button",{type:"submit",className:"primary",children:"Zugangsdaten speichern"})})]})]})]})]})]})}),me&&r.jsx("div",{className:"brain-modal-backdrop",onClick:()=>xe(!1),children:r.jsxs("section",{className:"brain-modal brain-shortcuts",role:"dialog","aria-modal":"true","aria-labelledby":"shortcut-title",onClick:w=>w.stopPropagation(),children:[r.jsxs("div",{className:"brain-modal__header",children:[r.jsx("h3",{id:"shortcut-title",children:"Tastenkürzel"}),r.jsx("button",{type:"button",className:"brain-modal__close",onClick:()=>xe(!1),"aria-label":"Tastenkürzel schließen",children:"✕"})]}),r.jsxs("dl",{children:[r.jsxs("div",{children:[r.jsx("dt",{children:r.jsx("kbd",{children:"Strg+K"})}),r.jsx("dd",{children:"Code und Notizen durchsuchen"})]}),r.jsxs("div",{children:[r.jsx("dt",{children:r.jsx("kbd",{children:"?"})}),r.jsx("dd",{children:"Diese Übersicht öffnen"})]}),r.jsxs("div",{children:[r.jsx("dt",{children:r.jsx("kbd",{children:"O"})}),r.jsx("dd",{children:"Ordner als Vault öffnen"})]}),r.jsxs("div",{children:[r.jsx("dt",{children:r.jsx("kbd",{children:"Esc"})}),r.jsx("dd",{children:"Übersicht oder Dialog schließen"})]}),r.jsxs("div",{children:[r.jsx("dt",{children:r.jsx("kbd",{children:"/"})}),r.jsx("dd",{children:"Frag das Projekt"})]}),r.jsxs("div",{children:[r.jsx("dt",{children:r.jsx("kbd",{children:"F"})}),r.jsx("dd",{children:"Graph einpassen"})]}),r.jsxs("div",{children:[r.jsxs("dt",{children:[r.jsx("kbd",{children:"+"}),r.jsx("kbd",{children:"−"})]}),r.jsx("dd",{children:"Graph zoomen"})]}),r.jsxs("div",{children:[r.jsxs("dt",{children:[r.jsx("kbd",{children:"↑"}),r.jsx("kbd",{children:"↓"}),r.jsx("kbd",{children:"Enter"})]}),r.jsx("dd",{children:"In der Liste auswählen und zentrieren"})]})]}),r.jsx("p",{children:"In Eingabefeldern bleiben alle Zeichen Eingabe und lösen keine Kurzbefehle aus."})]})}),r.jsx(fL,{workspaceId:v,isOpen:Te,onClose:()=>Be(!1),onOpenSource:(w,L)=>Pe(w,L),onNavigateToSearch:w=>{f("search")},initialQuestion:G})]})}function yL({workspaceId:t,revision:e,onClose:n}){var c;const[i,a]=P.useState(null),[s,l]=P.useState("");P.useEffect(()=>{let u=!0;return a(null),l(""),rS(t).then(h=>{u&&a(h)}).catch(h=>{u&&l(h instanceof Error?h.message:String(h))}),()=>{u=!1}},[t,e]);const o=(c=i==null?void 0:i.commits)==null?void 0:c.find(u=>u.hash===e||u.hash.startsWith(e));return r.jsxs("section",{className:"source-placeholder revision-inspector","aria-live":"polite",children:[r.jsx("div",{className:"source-placeholder__icon",children:r.jsx(ot,{path:st.refresh})}),r.jsx("h3",{children:"Revision-Inspector"}),r.jsx("p",{children:r.jsx("code",{children:e})}),s&&r.jsx("p",{role:"alert",children:s}),!s&&!i&&r.jsx("p",{children:"Prüfe die reale Git-Historie …"}),i&&!i.isRepo&&r.jsx("p",{children:"Dieser Wissensordner ist absichtlich kein Git-Workspace; für diese Revision gibt es keine Git-Historie."}),(i==null?void 0:i.isRepo)&&o&&r.jsxs("dl",{children:[r.jsxs("div",{children:[r.jsx("dt",{children:"Hash"}),r.jsx("dd",{children:r.jsx("code",{children:o.hash})})]}),r.jsxs("div",{children:[r.jsx("dt",{children:"Autor"}),r.jsx("dd",{children:o.author})]}),r.jsxs("div",{children:[r.jsx("dt",{children:"Zeit"}),r.jsx("dd",{children:o.date})]}),r.jsxs("div",{children:[r.jsx("dt",{children:"Nachricht"}),r.jsx("dd",{children:o.message})]})]}),(i==null?void 0:i.isRepo)&&!o&&r.jsx("p",{children:"Die geladene Historie enthält diese Revision nicht. Der Link bleibt unverändert; keine Ersatzrevision wird behauptet."}),r.jsx("button",{type:"button",onClick:n,children:"Inspector schließen"})]})}e1.createRoot(document.getElementById("root")).render(r.jsx(P.StrictMode,{children:r.jsx(bL,{})})); diff --git a/ui-dist/index.html b/ui-dist/index.html index 2b41985..f436b51 100644 --- a/ui-dist/index.html +++ b/ui-dist/index.html @@ -4,8 +4,8 @@ <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>PlugBrain - - + +
diff --git a/ui/src/App.tsx b/ui/src/App.tsx index 7c90777..e5efd62 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -5,7 +5,7 @@ import { workspaceIdForRoot, } from './lib/workspaces.js' import { - fetchMesh, fetchPlanetInventory, getStoredToken, setStoredToken, getStoredAgentId, setStoredAgentId, + authHeaders, fetchMesh, fetchPlanetInventory, fetchSwarmSnapshot, fetchHostResources, getStoredToken, setStoredToken, getStoredAgentId, setStoredAgentId, resetAgentAttachments, setPlanetCheckoutSelection, fetchGitState, type GitState, type PlanetInventory, } from './lib/brain-client' import CityView from './views/CityView' @@ -19,10 +19,15 @@ import ContextPackView from './views/ContextPackView' import GraphView from './views/GraphView' import BriefingView from './views/BriefingView' import HygieneView from './views/HygieneView' +import TurnsView from './views/TurnsView' +import DiskView from './views/DiskView' +import DurableView from './views/DurableView' +import PlanView from './views/PlanView' import AskModal from './components/AskModal' import { Icon, ICON } from './ui/Icon' import { TimelineControl, TIMELINE_STEPS, type Timeline } from './ui/TimelineControl' -import type { MeshSnapshot, QueueTask, Snapshot, ViewId } from './types' +import type { MeshSnapshot, QueueTask, Snapshot, SwarmSnapshot, ViewId } from './types' +import type { HostResources } from './lib/brain-client' type Planet = { id: string; name: string; root: string; indexedAt: string | null } @@ -39,10 +44,21 @@ const AGENT_VIEWS: { id: ViewId; label: string; hint: string }[] = [ { id: 'packs', label: 'Kontext-Pakete', hint: 'Context-Packs für Agenten-Aufgaben zusammenstellen' }, { id: 'queue', label: 'Aufgaben', hint: 'Wartende Aufgaben; der nächste freie Agent nimmt sie' }, { id: 'mesh', label: 'Agenten-Netz', hint: 'Nachweisbare Arbeit und Übergaben aus dem Core-Trace' }, + { id: 'turns', label: 'Turns', hint: 'Worker-Turns, Nachrichten, Claims und Freigaben auf einer Zeitachse' }, { id: 'city', label: 'Code-Stadt', hint: 'Workspace als Stadt — Repos als Distrikte, Dateien als Gebäude' }, ] -const VIEWS = [...MAIN_VIEWS, ...AGENT_VIEWS] +const SYSTEM_VIEWS: { id: ViewId; label: string; hint: string }[] = [ + { id: 'turns', label: 'Agenten', hint: 'Flottenboard, Nachrichten und Freigaben' }, + { id: 'durable', label: 'Dauerbetrieb', hint: 'Nachtschicht, Supervisor und Fleet-Lebenszeichen' }, + { id: 'disk', label: 'Festplatten', hint: 'Laufwerke, Inventur und Löschliste' }, + { id: 'plan', label: 'Plan', hint: 'Master-Aufgaben, Gates und Fortschritt' }, +] +const ACTIVITY_GLYPH: Record = { + briefing: '▧', atlas: '⌘', notes: '▤', explorer: '▱', search: '⌕', packs: '▣', city: '▦', + mesh: '◉', queue: '☷', turns: '◎', hygiene: '⌁', durable: '↻', disk: '▤', plan: '☷', +} +const VIEWS = [...MAIN_VIEWS, ...AGENT_VIEWS, ...SYSTEM_VIEWS] const shortLabel = folderName const SNAPSHOT_FILE_LIMIT = 2000 @@ -66,6 +82,9 @@ function initialWorkspace(): string { export default function App() { const [snapshot, setSnapshot] = useState(null) const [mesh, setMesh] = useState(null) + const [swarm, setSwarm] = useState(null) + const [hostResources, setHostResources] = useState(null) + const [hostResourceError, setHostResourceError] = useState('') const [queue, setQueue] = useState<{ depth: number; tasks: QueueTask[] }>({ depth: 0, tasks: [] }) const [error, setError] = useState('') const [attempt, setAttempt] = useState(0) @@ -100,6 +119,29 @@ export default function App() { const [selectedRevision, setSelectedRevision] = useState(null) const [meshFocusAgent, setMeshFocusAgent] = useState(null) + const refreshSwarm = useCallback(() => { + if (!workspaceId) { setSwarm(null); return } + void fetchSwarmSnapshot(workspaceId).then(setSwarm).catch(() => setSwarm(null)) + }, [workspaceId]) + + useEffect(() => { + if (!workspaceId) return + let alive = true + const refresh = () => { void fetchSwarmSnapshot(workspaceId).then(data => { if (alive) setSwarm(data) }).catch(() => { if (alive) setSwarm(null) }) } + refresh() + const timer = window.setInterval(refresh, 15_000) + return () => { alive = false; clearInterval(timer) } + }, [workspaceId]) + + useEffect(() => { + if (!workspaceId) { setHostResources(null); return } + let alive = true + const refresh = () => { void fetchHostResources().then(data => { if (alive) { setHostResources(data); setHostResourceError('') } }).catch(cause => { if (alive) setHostResourceError(cause instanceof Error ? cause.message : String(cause)) }) } + refresh() + const timer = window.setInterval(refresh, 15_000) + return () => { alive = false; clearInterval(timer) } + }, [workspaceId]) + // Settings & Theme state const [settingsOpen, setSettingsOpen] = useState(false) const [settingsTab, setSettingsTab] = useState<'appearance' | 'vaults' | 'repos' | 'shortcuts' | 'advanced'>('appearance') @@ -108,7 +150,7 @@ export default function App() { const saved = localStorage.getItem('plugbrain.theme') if (saved === 'dark' || saved === 'light' || saved === 'system') return saved } catch {} - return 'system' + return 'dark' }) useEffect(() => { @@ -140,25 +182,33 @@ export default function App() { const [selectionError, setSelectionError] = useState('') const [shortcutsOpen, setShortcutsOpen] = useState(false) const [askModalOpen, setAskModalOpen] = useState(false) + const [headerMenuOpen, setHeaderMenuOpen] = useState(false) const [askInitialQuery, setAskInitialQuery] = useState('') + const [searchRequest, setSearchRequest] = useState(0) + const headerSearchRef = useRef(null) + const headerMenuRef = useRef(null) useEffect(() => { const onKey = (event: KeyboardEvent): void => { + if (event.key === 'Escape') { + setHeaderMenuOpen(false) + setShortcutsOpen(false) + setSettingsOpen(false) + setSelectionModalOpen(false) + setAskModalOpen(false) + return + } const target = event.target as HTMLElement | null if (target?.matches('input, textarea, select, [contenteditable="true"]')) return if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'k') { event.preventDefault() - setAskModalOpen(true) + setView('search') + window.setTimeout(() => headerSearchRef.current?.focus(), 0) } else if (event.key === '/' && !event.shiftKey) { event.preventDefault() setAskModalOpen(true) } else if (event.key === '?' || (event.key === '/' && event.shiftKey)) { event.preventDefault(); setShortcutsOpen(true) - } else if (event.key === 'Escape') { - setShortcutsOpen(false) - setSettingsOpen(false) - setSelectionModalOpen(false) - setAskModalOpen(false) } else if (event.key.toLowerCase() === 'o' && !event.ctrlKey && !event.metaKey) { event.preventDefault(); setVaultOpen(true) } @@ -167,6 +217,19 @@ export default function App() { return () => window.removeEventListener('keydown', onKey) }, []) + useEffect(() => { + if (!headerMenuOpen) return + const closeOutside = (event: Event): void => { + if (!headerMenuRef.current?.contains(event.target as Node)) setHeaderMenuOpen(false) + } + document.addEventListener('pointerdown', closeOutside) + document.addEventListener('focusin', closeOutside) + return () => { + document.removeEventListener('pointerdown', closeOutside) + document.removeEventListener('focusin', closeOutside) + } + }, [headerMenuOpen]) + useEffect(() => { try { localStorage.setItem('plugbrain.view', view) } catch { /* private mode */ } }, [view]) @@ -200,7 +263,7 @@ export default function App() { useEffect(() => { if (!workspaceId) return let alive = true - fetch(`/api/graph?workspace=${encodeURIComponent(workspaceId)}&limit=5000`) + fetch(`/api/graph?workspace=${encodeURIComponent(workspaceId)}&limit=5000`, { headers: authHeaders() }) .then(r => r.json()) .then(data => { if (!alive || !data?.nodes) return @@ -313,7 +376,7 @@ export default function App() { let timer: ReturnType const poll = async (): Promise => { try { - const response = await fetch(`/api/index/progress?workspace=${encodeURIComponent(workspaceId)}`) + const response = await fetch(`/api/index/progress?workspace=${encodeURIComponent(workspaceId)}`, { headers: authHeaders() }) if (response.ok) { const report = await response.json() if (!alive) return @@ -415,7 +478,7 @@ export default function App() { useEffect(() => { const requested = workspaceId || undefined - void fetch('/api/timeline' + (requested ? '?workspace=' + encodeURIComponent(requested) : '')) + void fetch('/api/timeline' + (requested ? '?workspace=' + encodeURIComponent(requested) : ''), { headers: authHeaders() }) .then(r => r.json()) .then(payload => { if (payload?.bounds?.first) setBounds(payload.bounds) }) .catch(() => {}) @@ -446,7 +509,7 @@ export default function App() { if (requested) params.set('workspace', requested) params.set('limit', String(SNAPSHOT_FILE_LIMIT)) if (untilRef.current) params.set('until', untilRef.current) - const response = await fetch('/api/atlas/snapshot' + (params.toString() ? `?${params}` : ''), { signal: controller.signal }) + const response = await fetch('/api/atlas/snapshot' + (params.toString() ? `?${params}` : ''), { headers: authHeaders(), signal: controller.signal }) if (!response.ok) throw new Error(`Brain-Verbindung: HTTP ${response.status}`) const next: Snapshot = await response.json() if (!next.workspace?.canonicalPath || !Array.isArray(next.graph?.nodes) || !Array.isArray(next.graph?.edges)) { @@ -479,7 +542,7 @@ export default function App() { try { const response = await fetch( '/api/queue' + (requested ? '?workspace=' + encodeURIComponent(requested) : ''), - { signal: controller.signal }) + { headers: authHeaders(), signal: controller.signal }) if (response.ok) { const payload = await response.json() if (payload?.ok === true && Array.isArray(payload.tasks)) { @@ -626,50 +689,28 @@ export default function App() {
{workspaceId && ( - - )} - - {workspaceId && ( -
- -
+
@@ -681,15 +722,29 @@ export default function App() { )} {workspaceId && ( - )} {workspaceId && ( - )} {workspaceId && ( - + + +
+ {SYSTEM_VIEWS.map(item => )} +
+
+ + Ansichten + +
+ {VIEWS.map(v => { + const label = 'testName' in v && typeof v.testName === 'string' ? v.testName : v.label + return + })} +
+
+ + + )} {!workspaceId ? (

PlugBrain

@@ -850,7 +937,7 @@ export default function App() {
- handleOpenSource(path, line)} /> + handleOpenSource(path, line)} />
{sourcePane( @@ -929,6 +1016,14 @@ export default function App() {
)} + {view === 'turns' && } + + {view === 'durable' && } + + {view === 'disk' && } + + {view === 'plan' && } + {view === 'hygiene' && (
@@ -939,6 +1034,15 @@ export default function App() { } + {workspaceId &&
+ Index: {snapshot?.coverage ? indexBehind ? `${staleFiles} warten` : 'aktuell' : 'unbekannt'} + Brain: {error ? 'Fehler' : snapshot ? 'verbunden' : 'lädt'} + Platte: {hostResources?.host.drives.length ? `${(Math.min(...hostResources.host.drives.map(drive => drive.freeBytes)) / (1024 ** 3)).toFixed(1)} GB frei` : hostResourceError ? 'nicht verfügbar' : 'lädt'} + RAM: {hostResources ? `${Math.round(hostResources.host.memory.freeBytes / hostResources.host.memory.totalBytes * 100)} % frei` : hostResourceError ? 'nicht verfügbar' : 'lädt'} + Lanes: {swarm ? `${swarm.board.agents.filter(worker => worker.turnState === 'working').length} aktiv` : '—'} + `${quota.account}: ${quota.remaining} ${quota.unit}`).join(' · ') || 'Kontingente'}>Kontingente: {hostResources ? (hostResources.quotas.length ? hostResources.quotas.map(quota => `${quota.account} ${quota.remaining} ${quota.unit}`).join(' · ') : 'keine gemeldet') : hostResourceError ? 'nicht verfügbar' : 'lädt'} +
} + {settingsOpen && (
setSettingsOpen(false)}>
e.stopPropagation()}> @@ -1179,7 +1283,7 @@ export default function App() { {shortcutsOpen &&
setShortcutsOpen(false)}>
event.stopPropagation()}>

Tastenkürzel

-
Strg+K
Frag das Projekt
?
Diese Übersicht öffnen
O
Ordner als Vault öffnen
Esc
Übersicht oder Dialog schließen
/
Frag das Projekt / Suche
F
Graph einpassen
+−
Graph zoomen
↑↓Enter
In der Liste auswählen und zentrieren
+
Strg+K
Code und Notizen durchsuchen
?
Diese Übersicht öffnen
O
Ordner als Vault öffnen
Esc
Übersicht oder Dialog schließen
/
Frag das Projekt
F
Graph einpassen
+−
Graph zoomen
↑↓Enter
In der Liste auswählen und zentrieren

In Eingabefeldern bleiben alle Zeichen Eingabe und lösen keine Kurzbefehle aus.

} diff --git a/ui/src/components/AskModal.tsx b/ui/src/components/AskModal.tsx index 746aaf5..e027d6a 100644 --- a/ui/src/components/AskModal.tsx +++ b/ui/src/components/AskModal.tsx @@ -311,7 +311,7 @@ export default function AskModal({
Enter Fragen absenden Esc Schließen - Strg+K Immer verfügbar + / Immer verfügbar
diff --git a/ui/src/lib/brain-client.test.ts b/ui/src/lib/brain-client.test.ts index 578efdf..f4d0ea0 100644 --- a/ui/src/lib/brain-client.test.ts +++ b/ui/src/lib/brain-client.test.ts @@ -8,6 +8,8 @@ import { fetchMachine, fetchRepos, attachAgent, + enqueueSwarmTask, + sendSwarmMessage, } from './brain-client.ts' // While an index run holds the store's writer lock, the daemon refuses every @@ -253,3 +255,56 @@ describe('API error classes: unreachable vs server errors (UX-04)', () => { assert.equal('serverError' in res501, false) }) }) + +describe('enqueueSwarmTask dispatch contract', () => { + const originalFetch = globalThis.fetch + + afterEach(() => { + globalThis.fetch = originalFetch + }) + + test('sends the stored actor and selected lane and returns the Brain task identity', async () => { + let sent: unknown + globalThis.fetch = mock.fn(async (_url: string | URL | Request, init?: RequestInit) => { + sent = JSON.parse(String(init?.body)) + return { + ok: true, + status: 200, + json: async () => ({ ok: true, task: { id: 'task-abc123', title: 'Fix queue dispatch', state: 'pending', addressed_to: 'cx07' } }), + } as unknown as Response + }) as unknown as typeof fetch + + const task = await enqueueSwarmTask({ workspace: 'ws-test', requestedBy: 'shell-actor', addressedTo: 'cx07', title: 'Fix queue dispatch', body: 'Details' }) + + assert.deepEqual(sent, { workspace: 'ws-test', requestedBy: 'shell-actor', addressedTo: 'cx07', title: 'Fix queue dispatch', body: 'Details' }) + assert.deepEqual(task, { id: 'task-abc123', title: 'Fix queue dispatch', state: 'pending', addressed_to: 'cx07' }) + }) + + test('reports Brain errors and rejects success envelopes without a real task id or state', async () => { + globalThis.fetch = mock.fn(async () => ({ + ok: false, + status: 409, + json: async () => ({ ok: false, error: 'Unknown target lane' }), + } as unknown as Response)) as unknown as typeof fetch + await assert.rejects(enqueueSwarmTask({ workspace: 'ws-test', requestedBy: 'shell-actor', addressedTo: 'missing', title: 'Task', body: '' }), /Unknown target lane/) + + globalThis.fetch = mock.fn(async () => ({ + ok: true, + status: 200, + json: async () => ({ ok: true, task: { title: 'Task' } }), + } as unknown as Response)) as unknown as typeof fetch + await assert.rejects(enqueueSwarmTask({ workspace: 'ws-test', requestedBy: 'shell-actor', addressedTo: 'cx07', title: 'Task', body: '' }), /Task-ID oder Status fehlt/) + }) + + test('keeps the existing message action addressed from the Shell profile', async () => { + let sent: unknown + globalThis.fetch = mock.fn(async (_url: string | URL | Request, init?: RequestInit) => { + sent = JSON.parse(String(init?.body)) + return { ok: true, status: 200 } as unknown as Response + }) as unknown as typeof fetch + + await sendSwarmMessage({ workspace: 'ws-test', fromAgent: 'shell-actor', toAgent: 'cx07', subject: 'Hello', body: 'Message' }) + + assert.deepEqual(sent, { workspace: 'ws-test', fromAgent: 'shell-actor', toAgent: 'cx07', subject: 'Hello', body: 'Message' }) + }) +}) diff --git a/ui/src/lib/brain-client.ts b/ui/src/lib/brain-client.ts index 5d1bba7..1938614 100644 --- a/ui/src/lib/brain-client.ts +++ b/ui/src/lib/brain-client.ts @@ -3,7 +3,7 @@ * Connects directly to PlugBrain-Core daemon endpoints. * Handles bearer token authentication and error states. */ -import type { MeshSnapshot, MeshTimelineEntry, BriefingData, AskResponse, HygieneData, MachineData, ReposData, RouteMissing, ApiNetworkError, ApiServerError, ApiError } from '../types' +import type { MeshSnapshot, MeshTimelineEntry, BriefingData, AskResponse, HygieneData, MachineData, ReposData, RouteMissing, ApiNetworkError, ApiServerError, ApiError, SwarmSnapshot } from '../types' export type { RouteMissing, ApiNetworkError, ApiServerError, ApiError } from '../types' export interface GalaxyPlanet { @@ -71,6 +71,54 @@ export interface ContextPackResult { error?: string } +export interface HostResources { + host: { measuredAt: string; drives: Array<{ root: string; freeBytes: number; totalBytes: number }>; memory: { freeBytes: number; totalBytes: number }; cpuBusyFraction: number | null } + quotas: Array<{ account: string; remaining: number; unit: string; resetsAt: string | null; reportedAt: string }> + admission: Array<{ kind: string; allowed: boolean; reasons: string[] }> +} + +export async function fetchHostResources(): Promise { + const res = await fetch('/api/resources', { headers: authHeaders() }) + if (!res.ok) throw new Error(`Ressourcen HTTP ${res.status}`) + const data = await res.json() + if (!data?.ok || !data.host || !Array.isArray(data.quotas) || !Array.isArray(data.admission)) throw new Error('Ressourcen-Antwort unvollständig') + return { host: data.host, quotas: data.quotas, admission: data.admission } +} + +export async function fetchPlan(workspaceId: string): Promise> { + const res = await fetch(`/api/plan?workspace=${encodeURIComponent(workspaceId)}`, { headers: authHeaders() }) + const data = await res.json().catch(() => null) + if (!res.ok || !data?.ok || !data.plan) throw new Error(data?.error ?? `Plan HTTP ${res.status}`) + return data.plan +} + +export async function fetchDiskProjection(path?: string): Promise<{ scan: any; directories: any[]; history: any[]; largestGrowth: any[]; recommendations: any; wipeCheck: any }> { + const headers = authHeaders() + const query = path ? `?path=${encodeURIComponent(path)}` : '' + const [scanRes, treeRes, historyRes, recRes, wipeRes] = await Promise.all([ + fetch('/api/disk/scan', { headers }), fetch(`/api/disk/tree${query}`, { headers }), fetch('/api/disk/history', { headers }), + fetch('/api/disk/recommendations', { headers }), fetch('/api/disk/wipe-check', { headers }), + ]) + const responses = [scanRes, treeRes, historyRes, recRes, wipeRes] + if (responses.some(response => !response.ok)) throw new Error(`Festplatten-Projektion HTTP ${responses.find(response => !response.ok)?.status}`) + const [scan, tree, history, recommendations, wipeCheck] = await Promise.all(responses.map(response => response.json())) + if (![scan, tree, history, recommendations, wipeCheck].every(data => data?.ok)) throw new Error('Festplatten-Projektion unvollständig') + return { scan: scan.scan, directories: Array.isArray(tree.directories) ? tree.directories : [], history: history.scans ?? [], largestGrowth: history.largestGrowth ?? [], recommendations, wipeCheck } +} + +export async function startDiskScan(): Promise { + const response = await fetch('/api/disk/scan', { method: 'POST', headers: authHeaders(), body: JSON.stringify({}) }) + if (!response.ok) throw new Error(`Inventur konnte nicht gestartet werden (HTTP ${response.status})`) +} + +export async function approveAgentCommit(input: { workspaceId: string; agentId: string; by: string }): Promise { + const response = await fetch('/api/agent/approve-commit', { + method: 'POST', headers: authHeaders(), body: JSON.stringify(input), + }) + const data = await response.json().catch(() => null) + if (!response.ok || !data?.ok) throw new Error(data?.error ?? `Commit-Freigabe HTTP ${response.status}`) +} + export interface PackStalenessResult { ok: boolean packId: string @@ -177,7 +225,7 @@ export function setStoredAgentId(agentId: string): void { } } -function authHeaders(): Record { +export function authHeaders(): Record { const token = getStoredToken() const headers: Record = { 'Content-Type': 'application/json', @@ -273,6 +321,68 @@ export async function fetchMeshTimeline( return data.timeline as MeshTimelineEntry[] } +export async function fetchSwarmSnapshot(workspaceId: string): Promise { + const query = `?workspace=${encodeURIComponent(workspaceId)}` + const headers = authHeaders() + const [board, turns, messages, approvals, queue] = await Promise.all([ + fetch(`/api/swarm/board${query}`, { headers }), + fetch(`/api/swarm/turns${query}`, { headers }), + fetch(`/api/swarm/messages${query}`, { headers }), + fetch(`/api/swarm/approvals${query}`, { headers }), + fetch(`/api/swarm/queue${query}`, { headers }), + ]) + if ([board, turns, messages, approvals, queue].some(response => !response.ok)) { + throw new Error('Die Fleet-Zeitleiste ist nicht verfügbar.') + } + const [boardData, turnData, messageData, approvalData, queueData] = await Promise.all([ + board.json(), turns.json(), messages.json(), approvals.json(), queue.json(), + ]) + if (!boardData?.ok || !Array.isArray(boardData.board?.agents) + || !turnData?.ok || !Array.isArray(turnData.turns) + || !messageData?.ok || !Array.isArray(messageData.messages) + || !approvalData?.ok || !Array.isArray(approvalData.approvals) + || !queueData?.ok || !Array.isArray(queueData.tasks)) { + throw new Error('Die Fleet-Antwort ist unvollständig.') + } + return { + board: boardData.board, turns: turnData.turns, historyAvailable: turnData.historyAvailable === true, + messages: messageData.messages, approvals: approvalData.approvals, tasks: queueData.tasks, + } +} + +export async function sendSwarmMessage(input: { workspace: string; fromAgent: string; toAgent: string; subject: string; body: string }): Promise { + const response = await fetch('/api/agent/message', { method: 'POST', headers: authHeaders(), body: JSON.stringify(input) }) + if (!response.ok) throw new Error('Nachricht konnte nicht gesendet werden.') +} + +export type EnqueuedSwarmTask = { + id: string + title: string + state: 'pending' | 'claimed' | 'delivered' | 'cancelled' + addressed_to: string | null +} + +export async function enqueueSwarmTask(input: { workspace: string; requestedBy: string; addressedTo: string; title: string; body: string }): Promise { + const response = await fetch('/api/queue', { method: 'POST', headers: authHeaders(), body: JSON.stringify(input) }) + const data = await response.json().catch(() => null) + if (!response.ok || data?.ok !== true) { + const error = typeof data?.error === 'string' && data.error.trim() ? data.error : `Aufgabe konnte nicht eingereiht werden (HTTP ${response.status}).` + throw new Error(error) + } + const task = data.task + if (!task || typeof task.id !== 'string' || !task.id.trim() + || !['pending', 'claimed', 'delivered', 'cancelled'].includes(task.state) + || typeof task.title !== 'string') { + throw new Error('Brain-Antwort unvollständig: Task-ID oder Status fehlt.') + } + return { + id: task.id, + title: task.title, + state: task.state, + addressed_to: typeof task.addressed_to === 'string' ? task.addressed_to : null, + } +} + export async function fetchProvenance(workspaceId: string, path: string): Promise { const res = await fetch(`/api/provenance?workspace=${encodeURIComponent(workspaceId)}&path=${encodeURIComponent(path)}`) if (!res.ok) throw new Error(`Provenance HTTP ${res.status}`) diff --git a/ui/src/lib/turn-timeline.test.ts b/ui/src/lib/turn-timeline.test.ts new file mode 100644 index 0000000..41fad1e --- /dev/null +++ b/ui/src/lib/turn-timeline.test.ts @@ -0,0 +1,68 @@ +import { strict as assert } from 'node:assert' +import { test } from 'node:test' +import type { SwarmWorker } from '../types.ts' +import { isTurnStill, percentAt, sortTimelineWorkers, staleDuration, timelineBucket, timelineCounts, turnColor, workerMatchesFilters } from './turn-timeline.ts' + +test('turn timeline maps end states to stable style classes', () => { + assert.equal(turnColor('working'), 'running') + assert.equal(turnColor('awaiting-commit'), 'awaiting-commit') + assert.equal(turnColor('blocked'), 'blocked') + assert.equal(turnColor('paused'), 'paused') + assert.equal(turnColor('needs-task'), 'needs-task') +}) + +test('turn timeline marks only open turns older than thirty minutes as still', () => { + const now = Date.parse('2026-09-26T20:00:00.000Z') + assert.equal(isTurnStill('2026-09-26T19:29:59.000Z', null, now), true) + assert.equal(isTurnStill('2026-09-26T19:30:01.000Z', null, now), false) + assert.equal(isTurnStill('2026-09-26T19:00:00.000Z', '2026-09-26T19:20:00.000Z', now), false) +}) + +function worker(id: string, state: string, lastHeartbeat = '2026-09-26T19:59:00.000Z', account = 'acct'): SwarmWorker { + return { id, name: id, color: '', surface: null, account, model: null, presence: 'active', lastHeartbeat, + turnState: state, turnStateAt: lastHeartbeat, turnSummary: null, retired: false, unread: 0, task: null, + leases: [], worktrees: [] } +} + +test('timeline sorts urgent states first, then account and name within a state', () => { + const now = Date.parse('2026-09-26T20:00:00.000Z') + const windowStart = now - 60 * 60_000 + const sorted = sortTimelineWorkers([ + worker('working', 'working'), worker('blocked', 'blocked'), worker('waiting', 'awaiting-commit'), + worker('still', 'working', '2026-09-26T19:00:00.000Z'), worker('needs', 'needs-task'), + worker('paused', 'paused'), + ], windowStart, now, now) + assert.deepEqual(sorted.map(item => item.id), ['blocked', 'still', 'waiting', 'working', 'needs', 'paused']) +}) + +test('workers without contact in the selected range are folded and old working status is labeled stale', () => { + const now = Date.parse('2026-09-26T20:00:00.000Z') + const windowStart = now - 60 * 60_000 + const old = worker('old', 'working', '2026-09-23T20:00:00.000Z') + assert.equal(timelineBucket(old, windowStart, now, now), 'resting') + assert.equal(staleDuration(old.lastHeartbeat, now), '3 Tagen') + assert.equal(timelineBucket(worker('paused', 'paused'), windowStart, now, now), 'resting') + assert.equal(timelineBucket(worker('fresh', 'working'), windowStart, now, now), 'working') +}) + +test('summary filters select matching lanes and can be cleared by toggling off', () => { + const now = Date.parse('2026-09-26T20:00:00.000Z') + const windowStart = now - 60 * 60_000 + const working = worker('working', 'working') + const waiting = worker('waiting', 'awaiting-commit') + const unread = { ...worker('unread', 'needs-task'), unread: 2 } + assert.equal(workerMatchesFilters(working, new Set(['working']), windowStart, now, now), true) + assert.equal(workerMatchesFilters(waiting, new Set(['working']), windowStart, now, now), false) + assert.equal(workerMatchesFilters(waiting, new Set(['awaiting']), windowStart, now, now), true) + assert.equal(workerMatchesFilters(unread, new Set(['unread']), windowStart, now, now), true) + assert.equal(workerMatchesFilters(unread, new Set(), windowStart, now, now), true) + assert.equal(timelineCounts([working, waiting, unread], windowStart, now, now).unread, 2) +}) + +test('time coordinates stay bounded to the selected window', () => { + const start = Date.parse('2026-09-26T19:00:00.000Z') + const end = Date.parse('2026-09-26T20:00:00.000Z') + assert.equal(percentAt('2026-09-26T18:00:00.000Z', start, end), 0) + assert.equal(percentAt('2026-09-26T19:30:00.000Z', start, end), 50) + assert.equal(percentAt('2026-09-26T21:00:00.000Z', start, end), 100) +}) diff --git a/ui/src/lib/turn-timeline.ts b/ui/src/lib/turn-timeline.ts new file mode 100644 index 0000000..2467a16 --- /dev/null +++ b/ui/src/lib/turn-timeline.ts @@ -0,0 +1,88 @@ +import type { SwarmWorker } from '../types' + +export type TurnState = 'working' | 'needs-task' | 'awaiting-commit' | 'blocked' | 'paused' | 'commit-approved' | 'offline' +export type TimelineBucket = 'blocked' | 'still' | 'awaiting-commit' | 'working' | 'needs-task' | 'resting' + +const PRIORITY: TimelineBucket[] = ['blocked', 'still', 'awaiting-commit', 'working', 'needs-task', 'resting'] + +export function contactInWindow(worker: SwarmWorker, start: number, end: number): boolean { + const contact = worker.lastHeartbeat === null ? Number.NaN : Date.parse(worker.lastHeartbeat) + return Number.isFinite(contact) && contact >= start && contact <= end +} + +export function isWorkerStill(worker: SwarmWorker, now = Date.now()): boolean { + if (worker.turnState !== 'working') return false + const contact = worker.lastHeartbeat === null ? Number.NaN : Date.parse(worker.lastHeartbeat) + return Number.isFinite(contact) && now - contact > 30 * 60_000 +} + +export function timelineBucket(worker: SwarmWorker, start: number, end: number, now = Date.now()): TimelineBucket { + if (worker.retired || worker.turnState === 'paused' || worker.turnState === 'offline' || !contactInWindow(worker, start, end)) return 'resting' + if (worker.turnState === 'blocked') return 'blocked' + if (isWorkerStill(worker, now)) return 'still' + if (worker.turnState === 'awaiting-commit') return 'awaiting-commit' + if (worker.turnState === 'working') return 'working' + if (worker.turnState === 'needs-task') return 'needs-task' + return 'resting' +} + +export function sortTimelineWorkers(workers: SwarmWorker[], start: number, end: number, now = Date.now()): SwarmWorker[] { + return [...workers].sort((a, b) => { + const priority = PRIORITY.indexOf(timelineBucket(a, start, end, now)) - PRIORITY.indexOf(timelineBucket(b, start, end, now)) + return priority || (a.account ?? '').localeCompare(b.account ?? '') || a.name.localeCompare(b.name) + }) +} + +export function staleDuration(value: string | null, now = Date.now()): string { + const since = value === null ? Number.NaN : Date.parse(value) + if (!Number.isFinite(since)) return 'unbekannt' + const minutes = Math.max(0, Math.floor((now - since) / 60_000)) + if (minutes < 60) return `${minutes} min` + const hours = Math.floor(minutes / 60) + if (hours < 48) return `${hours} Std.` + const days = Math.floor(hours / 24) + return days === 1 ? '1 Tag' : `${days} Tagen` +} + +export function timelineCounts(workers: SwarmWorker[], start: number, end: number, now = Date.now()) { + return workers.reduce((counts, worker) => { + const bucket = timelineBucket(worker, start, end, now) + if (bucket === 'working') counts.working++ + if (bucket === 'still') counts.still++ + if (worker.turnState === 'awaiting-commit' && bucket !== 'resting') counts.awaiting++ + counts.unread += worker.unread + return counts + }, { working: 0, awaiting: 0, still: 0, unread: 0 }) +} + +export function workerMatchesFilters(worker: SwarmWorker, filters: Set<'working' | 'awaiting' | 'still' | 'unread'>, start: number, end: number, now = Date.now()): boolean { + if (filters.size === 0) return true + const bucket = timelineBucket(worker, start, end, now) + return (filters.has('working') && bucket === 'working') + || (filters.has('awaiting') && worker.turnState === 'awaiting-commit' && bucket !== 'resting') + || (filters.has('still') && bucket === 'still') + || (filters.has('unread') && worker.unread > 0) +} + +export function turnColor(state: string | null): string { + switch (state) { + case 'working': return 'running' + case 'needs-task': return 'needs-task' + case 'awaiting-commit': return 'awaiting-commit' + case 'blocked': return 'blocked' + case 'paused': return 'paused' + case 'commit-approved': return 'approved' + default: return 'unknown' + } +} + +export function isTurnStill(lastContact: string | null, endedAt: string | null, now = Date.now()): boolean { + const contact = lastContact === null ? Number.NaN : Date.parse(lastContact) + return endedAt === null && Number.isFinite(contact) && now - contact > 30 * 60_000 +} + +export function percentAt(value: string, start: number, end: number): number { + const at = Date.parse(value) + if (!Number.isFinite(at) || end <= start) return 0 + return Math.min(100, Math.max(0, ((at - start) / (end - start)) * 100)) +} diff --git a/ui/src/main.tsx b/ui/src/main.tsx index cacad38..fa75863 100644 --- a/ui/src/main.tsx +++ b/ui/src/main.tsx @@ -6,6 +6,7 @@ import './styles/app.css' import './styles/city.css' import './styles/mesh.css' import './styles/graph.css' +import './styles/turns.css' // Last: the shell owns layout and the shared atoms every view builds on. import './styles/shell.css' diff --git a/ui/src/styles/app.css b/ui/src/styles/app.css index 11d61cb..be2221c 100644 --- a/ui/src/styles/app.css +++ b/ui/src/styles/app.css @@ -1086,6 +1086,11 @@ gap: 8px; } .search-hit-card { + display: block; + width: 100%; + text-align: left; + font: inherit; + color: var(--fg); background: var(--card-bg); border: 1px solid var(--border); border-radius: 6px; @@ -1121,6 +1126,8 @@ margin-left: auto; } .search-hit-path { + display: block; + min-width: 0; font-size: 11.5px; color: var(--faint); overflow: hidden; @@ -1130,6 +1137,7 @@ /* The sentence that matched. Wraps, because a snippet cut off at the card edge is a snippet the reader has to open the file to understand. */ .search-hit-snippet { + display: block; font-size: 12px; line-height: 1.45; color: var(--fg); diff --git a/ui/src/styles/kit.css b/ui/src/styles/kit.css index ab4cc80..059a57f 100644 --- a/ui/src/styles/kit.css +++ b/ui/src/styles/kit.css @@ -382,14 +382,16 @@ td.n, th.n { text-align: right } background: var(--panel); border: 1px solid var(--line); border-radius: var(--r); padding: var(--s1); z-index: var(--z-modal); box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4); + min-width: 190px; max-height: min(75vh, 540px); overflow-y: auto; } +.pb-mobile-menu[hidden] { display: none; } .pb-mobile-menu .pb-tab-menu-item { display: block; width: 100%; text-align: left; padding: 8px 16px; border: none; background: transparent; cursor: pointer; color: var(--ink); font-size: var(--fs); } .pb-mobile-menu .pb-tab-menu-item:hover { background: var(--hover); } -@media (max-width: 640px) { +@media (max-width: 1180px) { .pb-tabs--desktop { display: none; } .pb-mobile-nav { display: block; position: relative; } } diff --git a/ui/src/styles/shell.css b/ui/src/styles/shell.css index 8ea7de0..612e06a 100644 --- a/ui/src/styles/shell.css +++ b/ui/src/styles/shell.css @@ -110,6 +110,15 @@ .pb-tab[aria-current="page"]::after { transform: scaleX(1) } .pb-topbar__actions { display: flex; align-items: center; gap: var(--s1); margin-left: auto; flex: none } +.pb-global-search { + display: flex; align-items: center; gap: 7px; width: clamp(190px, 25vw, 360px); height: 32px; + padding: 0 9px; border: 1px solid var(--line); border-radius: 7px; color: var(--faint); background: var(--bg); +} +.pb-global-search:focus-within { border-color: var(--accent); color: var(--ink); } +.pb-global-search .pb-icon { width: 15px; height: 15px; } +.pb-global-search input { width: 100%; min-width: 0; border: 0; outline: 0; color: var(--ink); background: transparent; font: inherit; font-size: var(--fs-sm); } +.pb-global-search input::placeholder { color: var(--faint); } +.pb-global-search kbd { flex: none; padding: 2px 4px; border: 1px solid var(--line); border-radius: 4px; color: var(--faint); font: 10px var(--mono); white-space: nowrap; } .pb-status { display: inline-flex; align-items: center; gap: 6px; @@ -124,6 +133,12 @@ .pb-status[data-tone="bad"] i { background: var(--neg) } .pb-status[data-tone="bad"] { color: var(--neg); border-color: color-mix(in srgb, var(--neg) 40%, var(--line)) } +@media (max-width: 1180px) { + .pb-topbar__secondary-action { display: none !important; } + .pb-global-search { width: clamp(150px, 25vw, 260px); } + .pb-select select { max-width: 150px; } +} + /* ── Messages under the header ─────────────────────────────────────────── */ .pb-banner { @@ -150,7 +165,7 @@ /* ── Main region ───────────────────────────────────────────────────────── */ .pb-main { position: relative; flex: 1; min-height: 0; overflow: hidden } -.pb-view { position: absolute; inset: 0; min-height: 0; display: flex; flex-direction: column; } +.pb-view { position: absolute; inset: 0 0 0 62px; min-height: 0; display: flex; flex-direction: column; } .pb-view--scroll { overflow: auto; display: block; } /* Views written for the old fixed header positioned themselves against it. @@ -162,6 +177,68 @@ .pb-main .pb-mesh { flex: 1; min-height: 0; position: relative; } .pb-main .brain-landing { position: absolute; inset: 0; overflow: auto; } +/* Compact activity rail. View destinations continue to use the same view state. */ +.pb-activity { + position: absolute; z-index: 12; inset: 0 auto 0 0; width: 62px; + display: flex; flex-direction: column; align-items: stretch; gap: 4px; + padding: 10px 6px; box-sizing: border-box; background: var(--panel); + border-right: 1px solid var(--line); +} +.pb-activity__item { + position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; + gap: 5px; width: 48px; min-height: 50px; padding: 5px 2px; border: 0; border-radius: 7px; + color: var(--muted); background: transparent; font: inherit; font-size: 9px; cursor: pointer; + text-decoration: none; list-style: none; +} +.pb-activity__item:hover, .pb-activity__item[aria-current="page"] { color: var(--ink); background: var(--hover); } +.pb-activity__item[aria-current="page"]::before { content: ""; position: absolute; inset: 9px auto 9px -6px; width: 2px; border-radius: 2px; background: var(--accent); } +.pb-activity__item .pb-icon { width: 18px; height: 18px; } +.pb-activity__item span { max-width: 48px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.pb-activity__divider { height: 1px; margin: 4px 2px; background: var(--line); } +.pb-activity__views { position: relative; } +.pb-activity__views summary { cursor: pointer; } +.pb-activity__views summary::-webkit-details-marker { display: none; } +.pb-activity__grid { font-size: 8px; line-height: 6px; letter-spacing: 1px; text-align: center; } +.pb-activity__menu { position: absolute; z-index: 20; left: 55px; top: 0; width: 190px; max-height: min(70vh, 520px); overflow: auto; padding: 5px; border: 1px solid var(--line); border-radius: 8px; background: var(--panel); box-shadow: 0 12px 32px #0005; } +.pb-activity__menu button { display: block; width: 100%; padding: 8px 10px; border: 0; border-radius: 5px; text-align: left; color: var(--muted); background: transparent; font: inherit; font-size: var(--fs-sm); cursor: pointer; } +.pb-activity__menu button:hover, .pb-activity__menu button[aria-current="page"] { color: var(--ink); background: var(--hover); } +.pb-activity__settings { margin-top: auto; } +.pb-activity__glyph { width: 18px; height: 18px; display: grid; place-items: center; font-size: 18px; line-height: 1; } +.pb-activity__system { min-height: 48px; } +.pb-statusbar { display: flex; align-items: center; gap: 14px; min-height: 25px; padding: 0 12px 0 70px; border-top: 1px solid var(--line); background: var(--panel); color: var(--muted); font: 10px var(--mono); white-space: nowrap; overflow-x: auto; } +.pb-domain-view { position: absolute; inset: 0 0 0 62px; overflow: auto; padding: 16px 20px 28px; box-sizing: border-box; } +.pb-domain-view .pb-view-header { position: sticky; top: -16px; z-index: 2; margin: -16px -20px 14px; padding: 12px 20px; background: var(--bg); } +.pb-domain-view h2 { margin: 0; font-size: 18px; } +.pb-domain-view h3 { margin: 0 0 8px; font-size: 14px; } +.pb-domain-view h4 { margin: 16px 0 6px; font-size: 12px; } +.pb-domain-card { margin: 0 0 12px; padding: 14px; border: 1px solid var(--line); border-radius: 8px; background: var(--panel); } +.pb-domain-card p { margin: 6px 0; color: var(--muted); } +.pb-domain-error { padding: 10px; border: 1px solid var(--neg); border-radius: 6px; color: var(--neg); } +.pb-domain-note { color: var(--faint); font-size: var(--fs-sm); } +.pb-domain-list { display: grid; gap: 8px; padding-left: 22px; } +.pb-domain-list li { padding-left: 3px; } +.pb-domain-list li code, .pb-domain-list li span { display: block; overflow-wrap: anywhere; } +.pb-domain-list li span { margin-top: 3px; color: var(--muted); font-size: var(--fs-sm); } +.pb-drive-grid, .pb-metric-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px; } +.pb-drive-grid article, .pb-metric-grid article { display: grid; gap: 5px; padding: 10px; border: 1px solid var(--line); border-radius: 6px; } +.pb-drive-grid span, .pb-metric-grid span { color: var(--muted); font-size: var(--fs-sm); } +.pb-table-wrap { overflow: auto; } +.pb-data-table { width: 100%; border-collapse: collapse; text-align: left; font-size: var(--fs-sm); } +.pb-data-table th, .pb-data-table td { padding: 8px; border-bottom: 1px solid var(--line); vertical-align: top; } +.pb-data-table th { color: var(--muted); font-weight: 600; } +@media (max-width: 760px) { + .pb-view { left: 52px; } + .pb-domain-view { left: 52px; padding-inline: 12px; } + .pb-domain-view .pb-view-header { margin-inline: -12px; padding-inline: 12px; } + .pb-statusbar { padding-left: 58px; } + .pb-activity { width: 52px; padding-inline: 2px; } + .pb-activity__item { width: 46px; } + .pb-activity__menu { left: 49px; } + .pb-topbar__workspace { display: none; } + .pb-global-search { width: clamp(110px, 28vw, 190px); } + .pb-global-search kbd { display: none; } +} + /* A map control on top of the City canvas: the one place a floating toolbar is right, because the canvas is the thing it controls. */ .pb-overlay-tools { @@ -204,7 +281,7 @@ gap: 4px !important; padding: 0 8px !important; max-width: 100vw !important; - overflow: hidden !important; + overflow: visible !important; } .pb-topbar__brand { gap: 4px !important; @@ -234,6 +311,8 @@ } } +@media (max-width: 760px) { .pb-statusbar { gap: 10px; } } + @media (prefers-reduced-motion: reduce) { .pb-tab::after, .pb-drawer { transition: none; animation: none } } diff --git a/ui/src/styles/turns.css b/ui/src/styles/turns.css new file mode 100644 index 0000000..e9515ca --- /dev/null +++ b/ui/src/styles/turns.css @@ -0,0 +1,83 @@ +.turns-view { min-height: 0; height: 100%; padding: 16px 20px 24px; overflow: auto; color: var(--ink); background: var(--canvas); } +.turns-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 16px; border-bottom: 1px solid var(--line); padding-bottom: 14px; } +.turns-toolbar h2 { margin: 0; font-size: 20px; font-weight: 620; } +.turns-toolbar p { margin: 5px 0 0; color: var(--muted); font-size: 13px; } +.turns-summary { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; margin: 10px 0 0; } +.turns-summary button { border: 0; border-radius: 4px; padding: 6px 8px; color: var(--ink); background: transparent; font: inherit; font-size: 12px; cursor: pointer; } +.turns-summary button:hover, .turns-summary button[aria-pressed="true"] { background: color-mix(in srgb, var(--accent) 16%, var(--panel)); } +.turns-summary button[aria-pressed="true"] { box-shadow: inset 0 -2px var(--accent); } +.turns-zoom, .turns-actions { display: flex; gap: 6px; flex-wrap: wrap; } +.turns-zoom button, .turns-actions button, .turns-detail__head button, .turns-composer button { color: var(--ink); background: var(--panel); border: 1px solid var(--line); border-radius: 5px; padding: 7px 10px; font: inherit; font-size: 12px; cursor: pointer; } +.turns-zoom button[aria-pressed="true"], .turns-actions button:hover, .turns-composer button:hover { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 13%, var(--panel)); } +.turns-view button:focus-visible, .turns-view input:focus-visible, .turns-view textarea:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } +.turns-history-note { margin: 10px 0; padding: 9px 12px; border-left: 3px solid #c49c52; background: color-mix(in srgb, #c49c52 10%, var(--panel)); color: var(--ink); font-size: 12px; } +.turns-key { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin: 12px 0; color: var(--muted); font-size: 11px; } +.turns-key span { display: inline-flex; gap: 6px; align-items: center; } +.turns-key i { display: inline-block; width: 13px; height: 8px; border-radius: 2px; background: #5eae88; } +.turns-key i[data-state="needs-task"] { background: #709cd3; } +.turns-key i[data-state="awaiting-commit"] { background: #c49c52; } +.turns-key i[data-state="blocked"] { background: #c46b6b; } +.turns-key i[data-state="paused"] { background: #8a82b7; } +.turns-key i[data-mark="claim"] { height: 3px; background: #84aab2; } +.turns-key i[data-mark="message"] { width: 8px; height: 8px; background: #e2a453; transform: rotate(45deg); } +.turns-key i[data-mark="approval"] { width: 8px; height: 8px; border-radius: 50%; background: #9acb7b; } +.turns-scrubber { display: grid; grid-template-columns: auto minmax(120px, 1fr) auto; align-items: center; gap: 10px; margin: 0 0 13px; color: var(--muted); font-size: 11px; } +.turns-scrubber input { width: 100%; accent-color: var(--accent); } +.turns-scrubber span { color: var(--ink); font-variant-numeric: tabular-nums; } +.turns-layout { display: grid; grid-template-columns: minmax(440px, 1.8fr) minmax(290px, .8fr); gap: 14px; align-items: start; } +.turns-scroll { min-width: 0; overflow: auto; border: 1px solid var(--line); border-radius: 6px; background: var(--panel); } +.turns-ruler, .turns-lane { display: grid; grid-template-columns: minmax(145px, 24%) minmax(420px, 1fr); align-items: center; } +.turns-ruler { position: sticky; z-index: 3; top: 0; color: var(--muted); background: var(--panel); border-bottom: 1px solid var(--line); font-size: 10px; } +.turns-ruler > span { padding: 9px; } +.turns-ruler > div { display: flex; justify-content: space-between; padding: 9px 3px; font-variant-numeric: tabular-nums; } +.turns-priority > h3 { margin: 0; padding: 8px 10px 5px; color: var(--ink); background: color-mix(in srgb, var(--accent) 8%, var(--panel)); border-bottom: 1px solid var(--line); font-size: 11px; font-weight: 650; } +.turns-account h4 { margin: 0; padding: 6px 10px 4px; color: var(--muted); background: color-mix(in srgb, var(--ink) 3%, var(--panel)); border-bottom: 1px solid var(--line); font-size: 10px; font-weight: 650; letter-spacing: .06em; } +.turns-lane { min-height: 54px; border-bottom: 1px solid var(--line); } +.turns-worker { display: flex; min-width: 0; flex-direction: column; gap: 2px; padding: 6px 9px; } +.turns-worker strong { overflow: hidden; font-size: 12px; text-overflow: ellipsis; white-space: nowrap; } +.turns-worker span, .turns-worker small { overflow: hidden; color: var(--muted); font-size: 10px; text-overflow: ellipsis; white-space: nowrap; } +.turns-track { position: relative; min-height: 39px; height: 100%; align-self: stretch; background: color-mix(in srgb, var(--canvas) 50%, transparent); } +.turns-gridline { position: absolute; inset-block: 0; width: 1px; background: color-mix(in srgb, var(--line) 80%, transparent); } +.turns-block { position: absolute; z-index: 1; top: 12px; min-width: 5px; height: 16px; padding: 0; border: 1px solid color-mix(in srgb, white 18%, transparent); border-radius: 3px; background: #477f68; cursor: pointer; } +.turns-block[data-state="needs-task"] { background: #557daf; } +.turns-block[data-state="awaiting-commit"] { background: #9f7e3e; } +.turns-block[data-state="blocked"] { background: #a75454; } +.turns-block[data-state="paused"] { background: #746c9c; } +.turns-block[data-state="approved"] { background: #668d49; } +.turns-block[data-still="true"] { background: repeating-linear-gradient(135deg, #9b623d 0 5px, #b17b4f 5px 8px); } +.turns-block[data-selected="true"] { outline: 2px solid var(--ink); outline-offset: 1px; } +.turns-still { position: absolute; left: 4px; top: 0; max-width: calc(100% - 8px); overflow: hidden; color: white; font-size: 9px; font-weight: 650; line-height: 14px; text-overflow: clip; text-shadow: 0 1px 2px #342115; white-space: nowrap; pointer-events: none; } +.turns-claim { position: absolute; z-index: 0; top: 33px; height: 3px; min-width: 3px; background: #84aab2; } +.turns-mark { position: absolute; z-index: 2; top: 4px; width: 8px; height: 8px; padding: 0; border: 1px solid var(--panel); cursor: pointer; } +.turns-mark--message { background: #e2a453; transform: rotate(45deg); } +.turns-mark--approval { top: 3px; width: 10px; height: 10px; border-radius: 50%; background: #9acb7b; } +.turns-detail { position: sticky; top: 0; max-height: calc(100vh - 150px); overflow: auto; border: 1px solid var(--line); border-radius: 6px; padding: 13px; background: var(--panel); } +.turns-detail__head { display: flex; justify-content: space-between; align-items: start; gap: 8px; border-bottom: 1px solid var(--line); padding-bottom: 10px; } +.turns-detail__head span { color: var(--muted); font-size: 9px; letter-spacing: .1em; } +.turns-detail__head h3, .turns-detail__empty h3 { margin: 3px 0 0; font-size: 16px; } +.turns-detail dl { display: grid; grid-template-columns: 92px 1fr; gap: 8px; margin: 12px 0; font-size: 11px; line-height: 1.45; } +.turns-detail dt { color: var(--muted); }.turns-detail dd { margin: 0; overflow-wrap: anywhere; } +.turns-state { display: inline-block; border-left: 3px solid #5eae88; padding-left: 6px; } +.turns-state[data-state="blocked"] { border-color: #c46b6b; }.turns-state[data-state="awaiting-commit"] { border-color: #c49c52; } +.turns-still-label { margin-left: 7px; color: #e29a62; } +.turns-resting { border-top: 1px solid var(--line); } +.turns-resting__toggle { width: 100%; padding: 10px; border: 0; text-align: left; color: var(--muted); background: color-mix(in srgb, var(--ink) 4%, var(--panel)); font: inherit; font-size: 12px; cursor: pointer; } +.turns-resting__toggle:hover { color: var(--ink); } +.turns-resting__worker { display: grid; grid-template-columns: minmax(120px, 24%) minmax(150px, 1fr) 36px; align-items: center; gap: 8px; min-height: 38px; padding: 5px 10px; border-top: 1px solid var(--line); font-size: 11px; } +.turns-resting__worker span { color: var(--muted); } +.turns-stale-hatch { justify-self: end; width: 28px; height: 12px; background: repeating-linear-gradient(135deg, #9b623d 0 4px, transparent 4px 7px); } +.turns-detail h4 { margin: 14px 0 5px; font-size: 11px; } +.turns-detail ul { display: grid; gap: 6px; margin: 0; padding: 0 0 0 15px; font-size: 10px; overflow-wrap: anywhere; } +.turns-detail li span { display: block; color: var(--muted); margin-top: 2px; } +.turns-actions { margin-top: 14px; } +.turns-composer { display: grid; gap: 8px; margin-top: 10px; padding: 10px; border: 1px solid var(--line); border-radius: 5px; } +.turns-composer h4 { margin: 0; } +.turns-composer label { display: grid; gap: 4px; color: var(--muted); font-size: 10px; } +.turns-composer input, .turns-composer textarea { min-width: 0; border: 1px solid var(--line); border-radius: 4px; padding: 7px; color: var(--ink); background: var(--canvas); font: inherit; font-size: 11px; } +.turns-composer textarea { min-height: 64px; resize: vertical; } +.turns-detail__empty { color: var(--muted); font-size: 12px; line-height: 1.5; } +.turns-detail__empty h3 { color: var(--ink); } +.turns-empty { padding: 30px; color: var(--muted); } +@media (max-width: 980px) { .turns-layout { grid-template-columns: minmax(0, 1fr); }.turns-detail { position: static; max-height: none; } } +@media (max-width: 620px) { .turns-view { padding: 10px; }.turns-toolbar { align-items: start; flex-direction: column; }.turns-scrubber { grid-template-columns: 1fr auto; }.turns-scrubber input { grid-column: 1 / -1; grid-row: 2; }.turns-ruler, .turns-lane { grid-template-columns: 120px minmax(420px, 1fr); } } +@media (prefers-reduced-motion: reduce) { .turns-view *, .turns-view *::before, .turns-view *::after { scroll-behavior: auto !important; animation-duration: .01ms !important; transition-duration: .01ms !important; } } diff --git a/ui/src/types.ts b/ui/src/types.ts index 2c0fd53..f71fdaf 100644 --- a/ui/src/types.ts +++ b/ui/src/types.ts @@ -89,7 +89,26 @@ export type BoardTask = { } /** Which of the renderings or tools of the same brain is on screen. */ -export type ViewId = 'briefing' | 'atlas' | 'notes' | 'explorer' | 'search' | 'packs' | 'city' | 'mesh' | 'queue' | 'hygiene' +export type ViewId = 'briefing' | 'atlas' | 'notes' | 'explorer' | 'search' | 'packs' | 'city' | 'mesh' | 'queue' | 'turns' | 'hygiene' | 'disk' | 'durable' | 'plan' + +export type SwarmWorker = { + id: string; name: string; color: string; surface: string | null; account: string | null + model: string | null; presence: string; lastHeartbeat: string | null; turnState: string | null + turnStateAt: string | null; turnSummary: string | null; retired: boolean; unread: number + silentMinutes?: number | null; attention?: string[] + task: { id: string; title: string; body: string } | null + leases: Array<{ id: string; taskId: string; paths: string[]; symbols: string[]; mode: 'write' | 'read'; createdAt: string; expiresAt: string }> + worktrees: Array<{ path: string; branch: string | null; head: string | null; dirtyFiles: number | null; error: string | null }> +} +export type SwarmTurn = { id: string; agentId: string; taskId: string | null; startedAt: string; endedAt: string | null; state: string | null; summary: string | null } +export type SwarmMessage = { id: string; fromAgent: string; toAgent: string | null; subject: string; body: string; createdAt: string; deliveredAt: string | null; readAt: string | null } +export type SwarmApproval = { id: string; byAgent: string; agentId: string | null; subject: string; body: string; approvedAt: string; acknowledgedAt: string | null } +export type SwarmQueueTask = QueueTask & { body?: string; created_at?: string; claimed_at?: string } +export type SwarmSnapshot = { + board: { agents: SwarmWorker[] } + turns: SwarmTurn[]; historyAvailable: boolean; messages: SwarmMessage[] + approvals: SwarmApproval[]; tasks: SwarmQueueTask[] +} /** A row of the workspace queue, as the Brain reports it. */ export type QueueTask = { diff --git a/ui/src/views/DiskView.tsx b/ui/src/views/DiskView.tsx new file mode 100644 index 0000000..6f57f83 --- /dev/null +++ b/ui/src/views/DiskView.tsx @@ -0,0 +1,61 @@ +import { useCallback, useEffect, useState } from 'react' +import { fetchDiskProjection, fetchHostResources, startDiskScan, type HostResources } from '../lib/brain-client' + +const gb = (bytes: number): string => `${(bytes / (1024 ** 3)).toFixed(1)} GB` + +export default function DiskView() { + const [data, setData] = useState> | null>(null) + const [resources, setResources] = useState(null) + const [error, setError] = useState('') + const [busy, setBusy] = useState(false) + const refresh = useCallback(async () => { + try { setData(await fetchDiskProjection()); setError('') } + catch (cause) { setError(cause instanceof Error ? cause.message : String(cause)) } + }, []) + useEffect(() => { void refresh(); void fetchHostResources().then(setResources).catch(() => setResources(null)) }, [refresh]) + useEffect(() => { + if (!data?.scan?.running) return + const timer = window.setInterval(() => { void refresh() }, 2500) + return () => clearInterval(timer) + }, [data?.scan?.running, refresh]) + const scan = async () => { + setBusy(true); setError('') + try { await startDiskScan(); await refresh() } + catch (cause) { setError(cause instanceof Error ? cause.message : String(cause)) } + finally { setBusy(false) } + } + const recommendations = data?.recommendations?.recommendations ?? [] + const candidates = data?.wipeCheck?.items ?? [] + return
+

Festplatten

Reale Laufwerke, Inventur und geprüfte Aufräumkandidaten

+ + +
+ {error &&

Festplatten-Daten nicht verfügbar: {error}

} + {!data && !error &&

Festplatten-Daten werden geladen …

} + {data && <> +

Laufwerke

+ {resources?.host.drives.length ?
{resources.host.drives.map(drive =>
{drive.root}{gb(drive.freeBytes)} frei von {gb(drive.totalBytes)}
)}
:

{data.scan?.roots?.length ? 'Laufwerkskapazitäten sind aktuell nicht verfügbar.' : 'Für die Laufwerke liegt noch keine Inventur vor. Starte eine metadata-only Inventur, um Verzeichnisse und Platzbelegung zu erfassen.'}

} +
+

Letzte Inventur

+ {data.scan ?

{data.scan.running ? 'Läuft' : data.scan.complete ? 'Abgeschlossen' : 'Unvollständig'} · {data.scan.directories.toLocaleString()} Verzeichnisse · {data.scan.files.toLocaleString()} Dateien · {gb(data.scan.bytes)} erfasst · {data.scan.inaccessible} unzugänglich

:

Noch keine Inventur vorhanden.

} + {data.scan?.errors?.length > 0 &&
    {data.scan.errors.slice(0, 8).map((item: string, index: number) =>
  • {item}
  • )}
} +
+

Erfasste Verzeichnisse auf der obersten Ebene

+ {data.directories.length ?
    {data.directories.slice(0, 12).map((row: any) =>
  1. {row.path}{gb(row.bytes)} · {row.files.toLocaleString()} Dateien
  2. )}
:

Keine Verzeichnisdaten vorhanden. Erst nach abgeschlossener Inventur kann eine Größenliste gezeigt werden.

} +
+

Verlauf und größte Zuwächse

+ {data.history.length ?
    {data.history.slice(0, 10).map((item: any) =>
  1. {item.complete ? 'Abgeschlossen' : item.running ? 'Läuft' : 'Unvollständig'}{new Date(item.startedAt).toLocaleString()} · {item.directories.toLocaleString()} Verzeichnisse · {gb(item.bytes)}
  2. )}
:

Mit dieser Version beginnt der Verlauf. Ältere Scans wurden vom Server nicht aufbewahrt.

} + {data.largestGrowth.length ?
    {data.largestGrowth.map((item: any) =>
  • {item.path}+{gb(item.deltaBytes)} seit dem vorherigen vollständigen Scan · vorher {gb(item.previousBytes)}, jetzt {gb(item.bytes)}
  • )}
:

Größte Zuwächse benötigen zwei vollständige Inventuren mit überlappenden Verzeichnissen.

} +
+

Aufräumkandidaten

+

Nur eine Prüfliste für den Owner. PlugBrain löscht hier nichts.

+ {recommendations.length ?
    {recommendations.slice(0, 20).map((item: any, index: number) =>
  • {item.paths?.join(', ')}{gb(item.bytes)} · {item.risk} · {item.reason}
  • )}
:

Keine Empfehlungen aus der vorhandenen Inventur.

} +

Geschützte / zu prüfende Pfade

+ {candidates.length ?
    {candidates.slice(0, 20).map((item: any) =>
  • {item.path}{gb(item.bytes)} · {item.reason}
  • )}
:

Die aktuelle Prüfliste enthält keine geschützten Pfade.

} +
+ } +
+} diff --git a/ui/src/views/DurableView.tsx b/ui/src/views/DurableView.tsx new file mode 100644 index 0000000..d8df61d --- /dev/null +++ b/ui/src/views/DurableView.tsx @@ -0,0 +1,36 @@ +import type { SwarmSnapshot } from '../types' + +function age(value: string | null): string { + if (!value) return 'kein Lebenszeichen erfasst' + const elapsed = Math.max(0, Date.now() - Date.parse(value)) + if (!Number.isFinite(elapsed)) return 'Zeitstempel ungültig' + if (elapsed < 60_000) return `vor ${Math.floor(elapsed / 1000)} s` + if (elapsed < 3_600_000) return `vor ${Math.floor(elapsed / 60_000)} min` + return `vor ${Math.floor(elapsed / 3_600_000)} h` +} + +export default function DurableView({ data, onRefresh }: { data: SwarmSnapshot | null; onRefresh: () => void }) { + const workers = data?.board.agents.filter(worker => /^cx\d\d$/.test(worker.id)) ?? [] + return
+

Dauerbetrieb / Nachtschicht

Fleet-Zustand aus dem Brain-Koordinationstrace

+ +
+ {!data ?

Fleet-Daten werden geladen oder sind nicht verfügbar.

: <> +

Codex-Lanes cx01–cx15

+ {workers.length ?
+ {workers.map(worker => + )} +
LaneTurnLetztes LebenszeichenAufgabeBedarf
{worker.id}{worker.turnState ?? worker.presence}{age(worker.lastHeartbeat)}{worker.task ? `${worker.task.id}: ${worker.task.title}` : 'Keine aktive Aufgabe'}{worker.unread ? `${worker.unread} neue Nachricht(en)` : worker.leases.length ? `${worker.leases.length} Claim(s)` : '—'}
:

Die Brain-Projektion enthält derzeit keine cx01–cx15-Worker.

} +
+

Queue und letzte Ereignisse

+

{data.tasks.length} Queue-Aufgaben · {data.turns.length} gespeicherte Turns · {data.messages.length} Nachrichten · {data.approvals.length} Freigaben

+ {!data.historyAvailable &&

Ältere Turns sind in dieser Brain-Version nicht vollständig gespeichert.

} + {data.tasks.length ?
    {data.tasks.slice(0, 12).map(task =>
  • {task.state}{task.id}: {task.title} · {task.claimed_by ?? task.addressed_to ?? 'nicht zugewiesen'}
  • )}
:

Die Brain-Queue ist leer.

} +
+

Supervisor-Belege

+

HALT-Zustand, Aussetzer, utilization.log und Morgenbericht sind noch nicht über eine authentifizierte UI-Projektion angebunden. Diese Bereiche werden deshalb als nicht verfügbar angezeigt.

+
  • HALT-Status: nicht über die API verfügbar
  • fleetd utilization.log: nicht über die API verfügbar
  • Aussetzer und Morgenbericht: nicht über die API verfügbar
+
+ } +
+} diff --git a/ui/src/views/PlanView.tsx b/ui/src/views/PlanView.tsx new file mode 100644 index 0000000..e900d3b --- /dev/null +++ b/ui/src/views/PlanView.tsx @@ -0,0 +1,37 @@ +import { useCallback, useEffect, useState } from 'react' +import { fetchPlan } from '../lib/brain-client' + +export default function PlanView({ workspaceId }: { workspaceId: string }) { + const [plan, setPlan] = useState | null>(null) + const [error, setError] = useState('') + const refresh = useCallback(async () => { + try { setPlan(await fetchPlan(workspaceId)); setError('') } + catch (cause) { setPlan(null); setError(cause instanceof Error ? cause.message : String(cause)) } + }, [workspaceId]) + useEffect(() => { void refresh() }, [refresh]) + const progress = plan?.progress + return
+

Plan / Master

Projektion aus dem Master-Ledger und der Brain-Queue

+ +
+ {error &&

Plan nicht verfügbar: {error}

} + {!plan && !error &&

Plan-Daten werden geladen …

} + {plan && <> +

{plan.ledger?.master ?? 'Master-Ledger'}

+

Stand: {plan.ledger?.updated ?? 'unbekannt'} · {plan.ledger?.ageHours == null ? 'Alter unbekannt' : `${plan.ledger.ageHours.toFixed(1)} Stunden alt`} · Gesamtstatus: {plan.ledger?.statusOverall ?? 'nicht gesetzt'}

+
{progress?.tasksDone ?? 0} / {progress?.tasksTotal ?? 0}Aufgaben abgeschlossen
{progress?.tasksInProgress ?? 0}in Arbeit
{progress?.gatesVerified ?? 0} / {progress?.gatesTotal ?? 0}Gates verifiziert
{progress?.gatesPartial ?? 0}teilweise verifiziert
+
+

Nächste startbare Master-Aufgaben

+ {plan.next?.length ?
    {plan.next.map((task: any) =>
  1. {task.id} · {task.status}{task.title} {task.queue?.length ? `· Queue: ${task.queue.map((q: any) => q.taskId).join(', ')}` : ''}
  2. )}
:

Das Ledger meldet aktuell keine startbaren Aufgaben.

} +
+

Gates

+ {plan.gates?.length ?
    {plan.gates.map((gate: any) =>
  • {gate.id} · {gate.status}{gate.title} · {gate.owner ?? 'ohne Owner'}
  • )}
:

Das Ledger enthält keine Gate-Einträge.

} +
+

Fortschrittsdimensionen und offene Entscheidungen

+ {plan.dimensions?.length ?
    {plan.dimensions.map((item: any) =>
  • {item.dimension}: {item.current}
  • )}
:

Keine Fortschrittsdimensionen im Ledger.

} + {plan.openDecisions?.length ?
    {plan.openDecisions.map((item: any) =>
  • {item.ref}: {item.decision}
  • )}
:

Keine offenen Entscheidungen im Ledger.

} + {plan.unplanned?.length > 0 &&

{plan.unplanned.length} Queue-Aufgabe(n) ohne Master-Referenz.

} +
+ } +
+} diff --git a/ui/src/views/SearchView.tsx b/ui/src/views/SearchView.tsx index 16f20a4..63d167d 100644 --- a/ui/src/views/SearchView.tsx +++ b/ui/src/views/SearchView.tsx @@ -78,6 +78,14 @@ export default function SearchView({ workspaceId, onSelectHit }: SearchViewProps searchAgent(workspaceId, q), searchNoteText(workspaceId, q), ]) + if (codeResults.status === 'rejected' && noteResults.status === 'rejected') { + throw new Error('Code- und Notizsuche sind nicht erreichbar. Bitte Verbindung zum Brain prüfen.') + } + if (codeResults.status === 'rejected') { + setError('Code-Suche nicht erreichbar. Angezeigt werden nur bestätigte Notiztreffer.') + } else if (noteResults.status === 'rejected') { + setError('Notizsuche nicht erreichbar. Angezeigt werden nur bestätigte Codetreffer.') + } const list: UnifiedHit[] = [] if (codeResults.status === 'fulfilled') { for (const c of codeResults.value) { @@ -140,6 +148,7 @@ export default function SearchView({ workspaceId, onSelectHit }: SearchViewProps setError(err?.message || `Fehler bei der Suche (${activeMode})`) setUnifiedHits([]) setNoteHits([]) + setSearchedQuery('') } finally { setLoading(false) } @@ -258,7 +267,8 @@ export default function SearchView({ workspaceId, onSelectHit }: SearchViewProps : `${noteHits.length} Notiz(en) gefunden für "${searchedQuery}":` ) : ( unifiedHits.length === 0 - ? `Keine Treffer für "${searchedQuery}" im Brain-Index` + ? error ? `Keine bestätigten Treffer für "${searchedQuery}"; Suche unvollständig` + : `Keine Treffer für "${searchedQuery}" im Brain-Index` : `${unifiedHits.length} Treffer für "${searchedQuery}":` )} {elapsedMs !== null && ( @@ -300,12 +310,12 @@ export default function SearchView({ workspaceId, onSelectHit }: SearchViewProps {mode === 'notes' ? (
{noteHits.map(note => ( -
onSelectHit(note.path)} > -
+ {note.title} {note.typ && {note.typ}} {note.stand && ( @@ -313,38 +323,38 @@ export default function SearchView({ workspaceId, onSelectHit }: SearchViewProps {note.stand} )} -
-
+ + {note.path} -
+ {(note.inLinks !== undefined || note.outLinks !== undefined) && ( -
+ Verlinkungen: → {note.outLinks ?? 0} ausgehend · ← {note.inLinks ?? 0} Rückverweise -
+ )} -
+ ))}
) : (
{unifiedHits.map(hit => ( -
onSelectHit(hit.path, hit.line)} > -
+ {hit.name} {hit.kind} {hit.line !== null && ( Zeile {hit.line} )} -
- {hit.snippet &&
{hit.snippet}
} -
+ + {hit.snippet && {hit.snippet}} + {hit.path} -
-
+ + ))}
)} diff --git a/ui/src/views/TurnsView.tsx b/ui/src/views/TurnsView.tsx new file mode 100644 index 0000000..07ab15e --- /dev/null +++ b/ui/src/views/TurnsView.tsx @@ -0,0 +1,253 @@ +import { useEffect, useMemo, useState } from 'react' +import type { SwarmMessage, SwarmSnapshot, SwarmTurn, SwarmWorker } from '../types' +import { approveAgentCommit, enqueueSwarmTask, getStoredAgentId, sendSwarmMessage, type HostResources } from '../lib/brain-client' +import { contactInWindow, isTurnStill, isWorkerStill, percentAt, staleDuration, sortTimelineWorkers, timelineBucket, timelineCounts, turnColor, workerMatchesFilters } from '../lib/turn-timeline' + +type Span = { id: string; agentId: string; taskId: string | null; startedAt: string; endedAt: string | null; state: string | null; summary: string | null; legacy?: boolean } +const ZOOMS = [{ label: '1 Std.', hours: 1 }, { label: '6 Std.', hours: 6 }, { label: '24 Std.', hours: 24 }] +const STATE_LABEL: Record = { + working: 'Läuft', 'needs-task': 'Braucht Aufgabe', 'awaiting-commit': 'Wartet auf Freigabe', + 'commit-approved': 'Freigegeben', blocked: 'Blockiert', paused: 'Pausiert', offline: 'Offline', +} + +export default function TurnsView({ workspaceId, data, resources, onRefresh }: { + workspaceId: string; data: SwarmSnapshot | null; resources: HostResources | null; onRefresh: () => void +}) { + const [zoom, setZoom] = useState(6) + const [scrub, setScrub] = useState(100) + const [clock, setClock] = useState(Date.now()) + const [selectedId, setSelectedId] = useState(null) + const [composer, setComposer] = useState<'message' | 'task' | null>(null) + const [feedback, setFeedback] = useState('') + const [busy, setBusy] = useState(false) + const [approving, setApproving] = useState(false) + const [filters, setFilters] = useState>(() => new Set()) + const [restingOpen, setRestingOpen] = useState(false) + + useEffect(() => { const timer = window.setInterval(() => setClock(Date.now()), 30_000); return () => clearInterval(timer) }, []) + + const workers = useMemo(() => data?.board.agents ?? [], [data]) + const spans = useMemo(() => { + if (!data) return [] + const rows: Span[] = data.turns.map((turn: SwarmTurn) => ({ ...turn })) + const byAgent = new Set(rows.filter(row => row.endedAt === null).map(row => row.agentId)) + for (const worker of workers) { + if (worker.turnState === 'working' && worker.turnStateAt && !byAgent.has(worker.id)) rows.push({ + id: `current-${worker.id}`, agentId: worker.id, taskId: worker.task?.id ?? null, + startedAt: worker.turnStateAt, endedAt: null, state: 'working', summary: worker.turnSummary, + legacy: true, + }) + } + return rows + }, [data, workers]) + + const selected = useMemo(() => spans.find(turn => turn.id === selectedId) ?? null, [spans, selectedId]) + const selectedWorker = selected ? workers.find(worker => worker.id === selected.agentId) ?? null : null + const selectedTask = selected?.taskId ? data?.tasks.find(task => task.id === selected.taskId) ?? null : null + const actorId = getStoredAgentId() + const selectedQuota = selectedWorker?.account ? resources?.quotas.find(quota => quota.account === selectedWorker.account) : null + const profileTurns = spans.filter(span => span.legacy).length + const end = clock - ((100 - scrub) / 100) * zoom * 60 * 60_000 + const start = end - zoom * 60 * 60_000 + const orderedWorkers = sortTimelineWorkers(workers, start, end, clock) + const counts = timelineCounts(workers, start, end, clock) + const visibleWorkers = orderedWorkers.filter(worker => timelineBucket(worker, start, end, clock) !== 'resting' + && workerMatchesFilters(worker, filters, start, end, clock)) + const restingWorkers = orderedWorkers.filter(worker => timelineBucket(worker, start, end, clock) === 'resting') + const matchingResting = restingWorkers.filter(worker => workerMatchesFilters(worker, filters, start, end, clock)) + const buckets = ['blocked', 'still', 'awaiting-commit', 'working', 'needs-task'] as const + const workerIndex = new Map(visibleWorkers.map((worker, index) => [worker.id, index])) + + const toggleFilter = (filter: 'working' | 'awaiting' | 'still' | 'unread') => { + setFilters(current => { + const next = new Set(current) + if (next.has(filter)) next.delete(filter) + else next.add(filter) + return next + }) + } + + const renderWorker = (worker: SwarmWorker, index: number) => { + const bucket = timelineBucket(worker, start, end, clock) + const workerSpans = spans.filter(span => span.agentId === worker.id && contactInWindow(worker, start, end) + && Date.parse(span.startedAt) <= end && (span.endedAt === null || Date.parse(span.endedAt) >= start)) + const claims = worker.leases.filter(lease => Date.parse(lease.createdAt) <= end && Date.parse(lease.expiresAt) >= start) + const messages = messagesFor(worker.id).filter(message => Date.parse(message.createdAt) >= start && Date.parse(message.createdAt) <= end) + const approvals = approvalsFor(worker.id).filter(approval => Date.parse(approval.approvedAt) >= start && Date.parse(approval.approvedAt) <= end) + const stale = isWorkerStill(worker, clock) + const expired = worker.turnState === 'working' && !contactInWindow(worker, start, end) + return
+
{worker.name}{worker.surface ?? 'Oberfläche offen'} · {worker.model ?? 'Modell offen'} + {expired ? `Zustand veraltet seit ${staleDuration(worker.lastHeartbeat, clock)}` : stale ? `still? seit ${staleDuration(worker.lastHeartbeat, clock)}` : worker.turnState ? STATE_LABEL[worker.turnState] ?? worker.turnState : 'Kein Turn-Status'}
+
+ {Array.from({ length: 6 }, (_, i) => )} + {workerSpans.map(span => { + const left = percentAt(span.startedAt, start, end) + const right = span.endedAt ? percentAt(span.endedAt, start, end) : 100 + const state = span.endedAt ? span.state : 'working' + return + })} + {claims.map(claim => )} + {messages.map(message =>
+
+ } + + useEffect(() => { + const onKey = (event: KeyboardEvent) => { + if (event.target instanceof HTMLElement && event.target.matches('input,textarea,select,[contenteditable=true]')) return + if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight' && event.key !== 'Enter') return + const list = spans.filter(span => Date.parse(span.startedAt) <= end && (span.endedAt === null || Date.parse(span.endedAt) >= start)) + if (list.length === 0) return + if (event.key === 'Enter') { if (selectedId) { event.preventDefault(); document.getElementById(`turn-${selectedId}`)?.focus() } return } + event.preventDefault() + const current = list.findIndex(span => span.id === selectedId) + const step = event.key === 'ArrowRight' ? 1 : -1 + const next = list[(current + step + list.length) % list.length] + if (next) setSelectedId(next.id) + } + window.addEventListener('keydown', onKey) + return () => window.removeEventListener('keydown', onKey) + }, [end, selectedId, spans, start]) + + if (!data) return
Fleet-Daten werden geladen oder sind nicht erreichbar.
+ + const submitAction = async (event: React.FormEvent) => { + event.preventDefault() + if (!selectedWorker) return + const form = new FormData(event.currentTarget) + setBusy(true); setFeedback('') + try { + if (composer === 'message') await sendSwarmMessage({ + workspace: workspaceId, fromAgent: actorId, + toAgent: String(form.get('toAgent') ?? ''), subject: String(form.get('subject') ?? ''), body: String(form.get('body') ?? ''), + }) + else if (composer === 'task') { + const title = String(form.get('title') ?? '').trim() + const duplicate = data.tasks.find(task => + (task.state === 'pending' || task.state === 'claimed') + && task.addressed_to === selectedWorker.id + && task.title.trim().toLocaleLowerCase() === title.toLocaleLowerCase(), + ) + if (duplicate) { + setFeedback(`Nicht eingereiht: ${duplicate.id} ist bereits ${duplicate.state} für ${selectedWorker.id} („${duplicate.title}“). Prüfe die bestehende Aufgabe in der Queue.`) + return + } + const task = await enqueueSwarmTask({ + workspace: workspaceId, requestedBy: actorId, addressedTo: selectedWorker.id, + title, body: String(form.get('body') ?? ''), + }) + setComposer(null) + setFeedback(`Brain-Aufgabe ${task.id} · ${task.state} · an ${selectedWorker.id}`) + onRefresh() + return + } + setComposer(null); setFeedback('Gespeichert.'); onRefresh() + } catch (error) { setFeedback(error instanceof Error ? error.message : 'Aktion fehlgeschlagen.') } + finally { setBusy(false) } + } + + const approveCommit = async () => { + if (!selectedWorker || selectedWorker.turnState !== 'awaiting-commit') return + setApproving(true); setFeedback('') + try { + await approveAgentCommit({ workspaceId, agentId: selectedWorker.id, by: actorId }) + setFeedback(`Commit für ${selectedWorker.id} freigegeben.`) + onRefresh() + } catch (error) { setFeedback(error instanceof Error ? error.message : 'Commit-Freigabe fehlgeschlagen.') } + finally { setApproving(false) } + } + + const messagesFor = (agentId: string): SwarmMessage[] => data.messages.filter(message => message.fromAgent === agentId || message.toAgent === agentId) + const approvalsFor = (agentId: string) => data.approvals.filter(approval => approval.agentId === agentId) + const tickLabels = Array.from({ length: 7 }, (_, index) => new Date(start + (zoom * 60 * 60_000 * index / 6)).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })) + + return
+
+

Turns

Agenten, Aufgaben und Übergaben nach tatsächlichem Brain-Stand

+
+ {ZOOMS.map(item => )} + +
+
+ +
+ + + + +
+ +

Turn-Paare werden ab diesem Upgrade gespeichert. Ältere Starts und Enden wurden nicht erfasst; laufende Profile ohne Turn-Eintrag sind separat markiert.

+
+ {['working','needs-task','awaiting-commit','blocked','paused'].map(state => {STATE_LABEL[state]})} + ClaimNachrichtFreigabe +
+ + + +
+
+
Konto / Worker
{tickLabels.map((label, index) => {label})}
+ {visibleWorkers.length === 0 && restingWorkers.length === 0 &&

Keine registrierten Worker in diesem Workspace.

} + {buckets.map(bucket => { + const bucketWorkers = visibleWorkers.filter(worker => timelineBucket(worker, start, end, clock) === bucket) + if (bucketWorkers.length === 0) return null + return
+

{bucket === 'still' ? 'still?' : STATE_LABEL[bucket] ?? bucket}

+ {Array.from(new Set(bucketWorkers.map(worker => worker.account ?? 'Ohne Konto'))).map(account =>
+

{account}

+ {bucketWorkers.filter(worker => (worker.account ?? 'Ohne Konto') === account).map(worker => renderWorker(worker, workerIndex.get(worker.id) ?? 0))} +
)} +
+ })} + {restingWorkers.length > 0 &&
+ + {(restingOpen || (filters.size > 0 && matchingResting.length > 0)) &&
+ {restingWorkers.filter(worker => filters.size === 0 || matchingResting.includes(worker)).map((worker, index) =>
+ {worker.name}{worker.turnState === 'working' ? `Zustand veraltet seit ${staleDuration(worker.lastHeartbeat, clock)}` : STATE_LABEL[worker.turnState ?? ''] ?? worker.turnState ?? 'Kein Status'} · {worker.unread} ungelesen + {worker.turnState === 'working' && } +
)} +
} +
} +
+ +