diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 507be66..887dffc 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.0" + "version": "0.3.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.0", + "version": "0.3.1", "license": "MIT", "category": "productivity", "keywords": [ diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 28df662..b4ca1ef 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "plugbrain", - "version": "0.3.0", + "version": "0.3.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/.github/workflows/ci.yml b/.github/workflows/ci.yml index a4fcccb..d9ec675 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,6 +29,12 @@ jobs: - name: Install UI dependencies run: npm ci --prefix ui + # GP-2 drives the real UI in Chromium. CI is the one place where the + # browser is downloaded (release.yml does the same). Without it GP-2 fails, + # and before v0.3.1 that failure also left the whole test run hanging. + - name: Install Chromium for the UI end-to-end test + run: node node_modules/playwright-core/cli.js install chromium + # Advisory, not blocking: the core runs on --experimental-strip-types # and carries pre-existing strict-tsc findings in src/ that REL-01 is # not allowed to touch. The gate makes the debt visible; the load-check @@ -47,5 +53,11 @@ jobs: - name: Run tests run: npm test + # Timing budgets need the machine to themselves: measured beside other + # test files they measure contention, not the product. `npm test` reports + # them as skipped; this serial step is the only place they run, on every OS. + - name: Run budget tests + run: npm run test:budget + - name: Secret scan run: npm run secret:scan diff --git a/CHANGELOG.md b/CHANGELOG.md index 157fcf5..37574ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,62 @@ 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.3.0] — 2026-09-26 +## [0.3.1] — 2026-09-26 + +The `v0.3.0` tag exists, but no 0.3.0 release was published: its cross-platform +verification failed on macOS and Windows. 0.3.1 is the first published build. + +### Fixed + +- **A vault opened through a linked path could not read its own notes.** When + the vault root was reached through a junction, a symlink or the macOS + `/var` → `/private/var` link, path containment compared each file's real path + with the unresolved root and refused the vault's own files. The root is now + resolved once. Traversal and links that point outside the vault are still + refused. +- **Opening a note during an index run failed silently in the web UI.** While an + index run holds the store's writer lock, the daemon answers store-writing + requests with `503` and `retryAfterMs`. The page gave up on the first `503` + when it attached its agent, so the following read went out without a + registered agent and the note stayed closed. The attach now waits and asks + again for up to 60 seconds. +- **The test suite could hang on a machine without Chromium.** The UI end-to-end + test launched the browser before its cleanup guard. A missing browser left the + test server and the store open, and the run never ended. The test now always + closes what it opened, and CI installs Chromium for it. +- **One folder, one spelling.** Hygiene, the checkout census, workspace + recognition and the daemon compared paths as text. On Windows a folder can + arrive as an 8.3 short name (`C:\Users\RUNNER~1\...`), on macOS as + `/var/...` or `/private/var/...`. Hygiene then missed checkouts and a + registered workspace was not recognised on the next run. Paths are now made + canonical before they are compared; what you typed is still what is shown. +- Two tests only passed in the author's checkout: one read a schema from + private git history, two probes needed PowerShell on Linux. Both are + portable now. + +### Changed + +- The release workflow can be dry-run on any branch (`workflow_dispatch`). + Publishing still happens only for `v*` tags. When the first UI check of + `verify:standalone` times out, it prints what the daemon and the page saw. +- The README now describes what PlugBrain is for: coordinating many agents on + one project, on top of a shared code and notes index. The screenshots are gone + from it, and it states what is not there yet. +- `docs/cli.md` documents every `swarm` subcommand with its real arguments. The + previous section had wrong signatures (for example `send`, `register`, + `release`) and left out `enqueue`, `ack`, `approve`, `deliver` and `quota`. +- New `docs/agent-protocol.md`: the per-turn rules as a block to paste into a + project's `AGENTS.md` or `CLAUDE.md`. + +### Known issues + +- **macOS:** when the brain store (`PLUGBRAIN_HOME`) lives inside a watched + vault, the file watcher reports a write to the store as a change of the vault + folder and the daemon starts a re-index that finds nothing to change. The + default store location, `~/.plugbrain`, is outside any vault and is not + affected. The test for this case is skipped on macOS until it is fixed. + +## [0.3.0] — 2026-09-26 (tagged, not released) First public release (MIT), first release prepared for Windows, macOS and Linux. diff --git a/README.md b/README.md index 65f0417..5a4be34 100644 --- a/README.md +++ b/README.md @@ -2,50 +2,95 @@ [![CI](https://github.com/litbitrim/plugbrain/actions/workflows/ci.yml/badge.svg)](https://github.com/litbitrim/plugbrain/actions/workflows/ci.yml) -**PlugBrain is a local project and code memory for AI agents** — real AST-based -code intelligence over your repositories plus an Obsidian-style note vault, -exposed to Claude Code (and any MCP client) as one set of tools. Everything -runs on your machine. No cloud, no telemetry. +**One local brain for all the coding agents working on your project.** -> The web interface is currently in German. An English interface is planned. -> The CLI, the MCP tools, their answers and all documentation are in English. +Claude Code, Codex, Gemini, GLM, a local model, several of each: any agent that +can run a shell command can join the same PlugBrain. Agents take tasks from a +shared queue, claim the files they are about to change, check in at the start +and end of every turn, message each other and wait for a commit approval. +Underneath sits one index of the project's code and notes, so every agent starts +from the same knowledge instead of re-reading the repository. -![Briefing — project summary, entry points and hotspots](docs/images/briefing.png) -![Ask — query the knowledge graph and code symbols](docs/images/ask.png) -![Graph — interactive knowledge graph and dependencies](docs/images/graph.png) -![Notes — Obsidian-style markdown vault with backlinks](docs/images/notes.png) -![Cleanup — git hygiene and uncommitted work protection](docs/images/cleanup.png) +The aim is simple: more agents should mean more work done, not overwritten +files, duplicated tasks or an agent silently stuck for an hour. -## Why PlugBrain? +Everything runs on your machine: one SQLite file, a daemon on `127.0.0.1`, no +cloud, no telemetry. MIT licensed. -Code assistants forget. Every session starts from zero: the agent re-reads -files it has already read, misses the note where you wrote down *why* a -decision was made, and steps on other agents editing the same files. -Existing code-graph tools help with the code half of that problem: +## Status -- **GitNexus** and **CodeGraph** are good local code-graph tools. Both index - a codebase and answer symbol, caller and impact questions, both run as a - CLI and both can serve those answers to an agent over MCP. +- 0.3.1 is the first published release. It is developed on Windows; macOS and + Linux run the same tests and packaging checks in CI. +- PlugBrain's own development runs on it. On 26 September 2026 one board + coordinated Claude Code, three Codex CLI workers and four Freebuff workers on + this repository. +- The web UI and the output of `plugbrain swarm` are in German for now. The + other commands, the MCP tools and all documentation are in English. +- Not there yet: the store keeps only each agent's latest turn, so there is no + turn history to replay, and worktrees of merged branches are not removed + automatically. Both are being built. -What PlugBrain adds is the other half: the project's written knowledge and -the agents working on it, in the same local brain as the code. +## How agents work together -| | GitNexus | CodeGraph | PlugBrain | -| --- | --- | --- | --- | -| Code intelligence (symbols, callers, impact) | ✓ | ✓ | ✓ | -| Local CLI and MCP server | ✓ | ✓ | ✓ | -| Obsidian-style notes vault with code bindings | – | – | ✓ | -| Multi-agent claims, fencing and awareness | – | – | ✓ | -| Plain-language `ask` with cited sources | – | – | ✓ | -| Git hygiene: unsaved work across all checkouts | – | – | ✓ | +```bash +# Once per agent: who it is and which account it spends +plugbrain swarm register codex-1 --surface other --account openai --model gpt-6-luna +plugbrain swarm register claude-1 --surface claude-code --account anthropic + +# Whoever plans, a person or an agent, queues work, optionally for one agent +plugbrain swarm enqueue "Fix the flaky login test" --body "Repro in issue #12" --to codex-1 + +# Every agent, at the start of every turn: read messages, take the offered task +plugbrain swarm turn codex-1 start --claim + +# Before writing: claim the paths. A path another agent holds is refused. +plugbrain swarm claim codex-1 src/auth/login.ts --task + +# Before tests, builds or new worktrees: is there room? Exit 5 means no. +plugbrain swarm admit test + +# At the end of every turn: exactly one state and a short summary +plugbrain swarm turn codex-1 end --state awaiting-commit --summary "Fixed the race, login tests pass" +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" --from integrator +``` + +A turn ends in one of four states: `needs-task` (done, give me work), +`awaiting-commit` (a tested change is ready), `blocked` (a real blocker, named in +the summary) or `paused`. `plugbrain swarm board` lists every agent once with its +account, turn state, unread messages, task, claims and worktree; with `--git` it +adds branch, head and uncommitted files, and it flags two agents writing in the +same worktree. + +Agents that speak MCP find the board, turns, claims and messages as tools +(`swarm_board`, `swarm_turn`, `claim`, `release`, `message_send`, `inbox_read`, +`heartbeat`, `awareness`). Queueing, approving and admission are CLI-only today. -The "–" marks reflect the tools' documented commands as of September 2026. -If one of them has since gained a feature, please open an issue and we will -correct the table. +The per-turn rules for agents are written down so you can paste them into your +project's `AGENTS.md` or `CLAUDE.md`: [docs/agent-protocol.md](docs/agent-protocol.md). +All `swarm` commands: [docs/cli.md](docs/cli.md#swarm). -### How it measures up +## The index underneath -We compared PlugBrain with CodeGraph and GitNexus on 120 questions (definitions, callers, impact, config keys, docs) across four of the author's own projects. PlugBrain itself is not one of them. We wrote the questions ourselves. 40 of them were frozen before any tuning and serve as the honest holdout. Every tool was driven through its documented command-line interface, and every raw answer is stored next to the results. +The index answers the questions agents ask all day: where is this defined, who +calls it, what breaks if I change it, and what did we decide about it. The goal +is to match or beat GitNexus and CodeGraph on code questions and Obsidian on +project notes, in one index every agent shares. + +**Code.** Symbols, callers and callees, impact of a change, changed files since a +revision, graph queries in Cypher, and `ask` for plain-language questions with +cited sources. + +We compared PlugBrain with CodeGraph and GitNexus on 120 questions (definitions, +callers, impact, config keys, docs) across four of the author's own projects; +PlugBrain itself is not one of them. We wrote the questions ourselves. 40 were +frozen before any tuning and serve as the holdout. Every tool was driven through +its documented command-line interface, and every raw answer is stored with the +results. | Tool | Holdout (40) | All questions (120) | Latency p50 | Latency p95 | | :--- | :---: | :---: | :---: | :---: | @@ -53,63 +98,59 @@ We compared PlugBrain with CodeGraph and GitNexus on 120 questions (definitions, | CodeGraph | 38 / 40 | 103 / 120 | 280 ms | 311 ms | | GitNexus | 36 / 40 | 90 / 120 | 1,134 ms | 3,465 ms | -In short, PlugBrain answers three more of the 120 questions than CodeGraph and ties it on the holdout. Its real edge is speed once the daemon is warm, and that it also searches your notes. CodeGraph's call edges are still more complete on deeply polymorphic class hierarchies. Full method, raw answers, hand-checked misses and how to reproduce the run: [bench/v2/RESULTS.md](bench/v2/RESULTS.md). +PlugBrain ties CodeGraph on the holdout and answers three more of all 120 +questions. CodeGraph's call edges are still more complete on deeply polymorphic +class hierarchies. Method, raw answers, hand-checked misses and how to rerun it: +[bench/v2/RESULTS.md](bench/v2/RESULTS.md). + +**Notes.** A Markdown vault with wiki-links, backlinks, tags, properties and +full-text search, in the same index as the code. A note can point at a file +(`code:`), a revision (`revision:`) and an agent run (`agentenlauf:`). Compared +with Obsidian there is no canvas, no mobile app and no plugin ecosystem. ## Quickstart **Windows:** download `PlugBrain--win-x64.exe` from [Releases](https://github.com/litbitrim/plugbrain/releases) and run it. The -installer bundles a portable Node.js runtime — nothing else to install. +installer brings its own Node.js runtime. -**macOS / Linux:** grab `plugbrain---.tar.gz` from the same -releases page, unpack it and put `bin/plugbrain` on your `PATH`. +**macOS / Linux:** download `plugbrain---.tar.gz` from the same +page, unpack it and put `bin/plugbrain` on your `PATH`. Then, inside your project folder: ```bash -# One-time: create the brain store and register this folder as a workspace -plugbrain init - -# Start the local brain (UI + API + MCP transport) -plugbrain serve +plugbrain init # register this folder, index it, add PlugBrain to your MCP clients +plugbrain serve # the local daemon: web UI and API on http://127.0.0.1:4310 ``` -Open `http://localhost:4310` for the cockpit. The data directory defaults to -`~/.plugbrain`; set `PLUGBRAIN_HOME` to choose another location. - -### Use it from Claude Code +`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. 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/). -PlugBrain ships as a Claude Code plugin with its own marketplace entry: +### Claude Code plugin ``` /plugin marketplace add litbitrim/plugbrain /plugin install plugbrain@plugbrain ``` -The plugin starts the brain when needed (session hook), and gives Claude the -`skills/brain` skill: *ask the brain first* — context packs before edits, -impact analysis before renames, notes before re-deriving decisions. - -## MCP tools - -`plugbrain mcp` speaks the Model Context Protocol over stdio, giving agents instant access to code intelligence and knowledge: - -- `ask` — Ask in plain language; routes to the right tool and returns one clear sentence with source citations. -- `context_pack` — Build goal-oriented context packs with relevant files, symbols, and dependencies. -- `query` — Concept search across indexed AST symbols, file paths, and notes. -- `context` — 360-degree symbol context: callers, callees, and execution flow memberships. -- `impact` — Blast-radius analysis showing upstream and downstream dependency changes before editing. - -For the full reference of all tools (including search, notes, git hygiene, and multi-agent coordination), see [MCP Tools Reference](docs/mcp-tools.md). +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. ## Privacy -- Everything stays on your machine: the store is a single SQLite database - under `PLUGBRAIN_HOME`, indexing runs locally, the UI is served locally. -- No telemetry, no analytics, no phone-home. The only network traffic is the +- The store is one SQLite database under `PLUGBRAIN_HOME`. Indexing runs locally + and the UI is served locally. +- No telemetry, no analytics, no phone-home. The only network listener is the local HTTP server on `127.0.0.1`. -- The HTTP API requires a bearer token that is generated on first start and - kept in `/auth.token`. +- The HTTP API requires a bearer token that is generated on first start and kept + in `/auth.token`. The `swarm` commands work on the local store + directly and need no token. ## Development @@ -117,7 +158,7 @@ For the full reference of all tools (including search, notes, git hygiene, and m git clone https://github.com/litbitrim/plugbrain cd plugbrain npm install -npm test # unit + integration tests (node:test) +npm test # unit and integration tests (node:test) npm run serve # start the brain against the current folder ``` diff --git a/desktop/PlugBrain.nsi b/desktop/PlugBrain.nsi index 5996644..2a17c58 100644 --- a/desktop/PlugBrain.nsi +++ b/desktop/PlugBrain.nsi @@ -1,7 +1,7 @@ Unicode true !define PRODUCT_NAME "PlugBrain" -!define PRODUCT_VERSION "0.3.0" +!define PRODUCT_VERSION "0.3.1" !define PRODUCT_PUBLISHER "PLUG" Name "${PRODUCT_NAME} ${PRODUCT_VERSION}" diff --git a/desktop/brain-url.test.cjs b/desktop/brain-url.test.cjs index e5b1d6a..f81edaa 100644 --- a/desktop/brain-url.test.cjs +++ b/desktop/brain-url.test.cjs @@ -61,7 +61,12 @@ test('desktop launcher fails closed rather than adopting an unrelated Core', () assert.doesNotMatch(launcher, /PlugPT-Shell|electron\\dist/i); }); -test('the launcher quoting form keeps a spaced Core bundle as one Node argument', () => { +// The desktop launcher is a Windows PowerShell script; these two probes run +// that PowerShell. Linux runners have no `powershell`, so they only mean +// something on Windows. +const WINDOWS_LAUNCHER_ONLY = process.platform === 'win32' ? false : 'the PowerShell launcher only runs on Windows'; + +test('the launcher quoting form keeps a spaced Core bundle as one Node argument', { skip: WINDOWS_LAUNCHER_ONLY }, () => { const expression = [ "$bundle = 'C:\\Program Files\\PlugBrain\\dist\\plugbrain.mjs'", "$bundleArgument = [string][char]34 + $bundle + [char]34", @@ -72,7 +77,7 @@ test('the launcher quoting form keeps a spaced Core bundle as one Node argument' assert.equal(probe.stdout.trim(), '"C:\\Program Files\\PlugBrain\\dist\\plugbrain.mjs"'); }); -test('PowerShell Start-Process delivers the quoted spaced bundle as one actual Node argv entry', () => { +test('PowerShell Start-Process delivers the quoted spaced bundle as one actual Node argv entry', { skip: WINDOWS_LAUNCHER_ONLY }, () => { const dir = mkdtempSync(join(tmpdir(), 'plugbrain-launcher-')); const probeScript = join(dir, 'argv-probe.cjs'); const probeOutput = join(dir, 'argv.json'); diff --git a/docs/agent-protocol.md b/docs/agent-protocol.md new file mode 100644 index 0000000..bfce61e --- /dev/null +++ b/docs/agent-protocol.md @@ -0,0 +1,93 @@ +# Agent protocol + +PlugBrain only coordinates agents that check in. This page holds the rules an +agent follows on every turn. Paste the block below into your project's +`AGENTS.md` (Codex, Cursor, OpenCode and others read it) or `CLAUDE.md` +(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, 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. 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 + +```markdown +## Working with PlugBrain + +You are one agent in a team coordinated by PlugBrain. Your agent id is ``. + +1. **Start of every turn:** run `plugbrain swarm turn start`. Read every + message it prints and acknowledge each one with + `plugbrain swarm ack `. If a task is offered to you, take it + with `plugbrain swarm turn start --claim`. +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 "..." --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. **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 `. + 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 percent|credits|requests|rpm|tokens`. + Never put keys or tokens into commands, summaries, messages or notes. +``` + +## For the integrator + +The integrator is whoever plans and merges: a person, or an agent given that +role. + +| Task | Command | +| --- | --- | +| See every agent, its turn, task, claims and worktree | `plugbrain swarm board --git` | +| Queue work, optionally for one agent, optionally after another task | `plugbrain swarm enqueue "" --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>"` | + +Two habits pay off: give every change an independent reviewer that runs on a +different model or key than its author, and write the brief into a file and +queue a pointer to it, so every agent can be restarted with the same one-line +prompt ("start your turn and take the offered task"). 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 88b5398..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` @@ -513,24 +560,209 @@ plugbrain agents --- ### `swarm` -Multi-agent coordination desk for managing leases, turn checkpoints, admissions, and messaging. +The coordination desk for a team of agents: registration, turns, the task +queue, messages, path claims, commit approval, quotas and machine admission. +It works on the local store directly, so an agent needs no HTTP token. The +per-turn rules for agents are in [agent-protocol.md](agent-protocol.md). +The command output is in German for now. ```bash -plugbrain swarm <subcommand> [args] +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. - -**Example:** +**Agents** +- `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. 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>] [--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>] [--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. 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] [--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. 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" --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/history/PROJECT.md b/docs/history/PROJECT.md new file mode 100644 index 0000000..8feab24 --- /dev/null +++ b/docs/history/PROJECT.md @@ -0,0 +1,90 @@ +# Project: PlugBrain Standalone Windows Desktop Application + +## Architecture +PlugBrain is a local-first desktop application combining AST code intelligence, an Obsidian-class Markdown knowledge base, multi-agent activity tracking, and a standalone portable Node.js runtime with an NSIS installer. + +- **Frontend (`ui/`)**: React 19 + TypeScript + Vite cockpit interface. Provides 8 core views: Graph (Atlas), Wissen (Notes), Explorer, Suche, Packs, City, Mesh, and Queue. Communicates with backend via REST endpoints (`/api/notes`, `/api/agent`, `/api/symbols`, `/api/files`, `/api/meta`). +- **Backend Core (`src/`)**: Node.js core running directly with `--experimental-strip-types`. Manages AST indexing (Tree-sitter / Babel / TypeScript parser), graph relationships, full-text search, and multi-agent coordination. +- **Standalone Runtime (`scripts/build-standalone.mjs` -> `dist/`)**: Self-contained runtime bundling portable `node.exe`, CLI daemon `plugbrain.mjs`, background worker `worker.mjs`, static UI assets `ui-dist/`, and SHA-256 release manifest `release.json`. +- **Desktop Packaging (`scripts/package-nsis.mjs` -> `release/`)**: Production Windows NSIS installer (`PlugBrain-0.2.4-win-x64.exe`) with silent/interactive install modes, desktop shortcuts, uninstaller, SHA-256 hash, and provenance metadata. + +## Feature Inventory +Every feature from the Survey phase is mapped to an assigned milestone: +| # | Feature | Description | Milestone | Source | +|---|---------|-------------|-----------|--------| +| 1 | Notes CRUD & Autosave | Markdown note creation, editing, debounced optimistic autosave, conflict detection, undo in NotesView.tsx | M1 | R1 | +| 2 | Wiki-links & Live Backlinks | Bidirectional [[Note]] parsing, dedicated live backlinks pane, tag filtering in NotesView.tsx | M1 | R1 | +| 3 | Frontmatter Code Binding | Structured frontmatter templates (typ: entscheidung, typ: widerspruch) with clickable code path links | M1 | R1 | +| 4 | Instant Dual Search | As-you-type instant search across note prose and AST symbol definitions | M1 | R1 | +| 5 | Explorer Sidebar Tree | Recursive search filtering (preserving parent paths), collapse/expand all, path normalization | M2 | R2 | +| 6 | "Quelle öffnen" Navigation | Centralized handleOpenSource in App.tsx parsing path:line with centered line highlighting in SourceView | M2 | R2 | +| 7 | Command-Center Design Tokens | Dark palette (--bg: #050706, --panel: #0B0F0D), eliminate amber/orange, mono uppercase status pills | M2 | R2 | +| 8 | Viewport Defect Elimination | Fix CSS layout collisions, eliminate unscaled viewport frames, operationalize all 8 views | M2 | R2 | +| 9 | Standalone Bundling & Build | Compile clean UI assets to ui-dist/ and bundle portable runtime with node.exe into dist/ | M3 | R3 | +| 10 | Standalone Verification Suite | Resolve Playwright strict-mode button ambiguity and execute verify-standalone.mjs smoke verification | M3 | R3 | +| 11 | Unit & Integration Test Green | Fix ui-mesh and ui-atlas-layout test regex assertions, achieve 100% pass on npm test | M3 | R3 | +| 12 | NSIS Installer Packaging | Package release/PlugBrain-0.2.4-win-x64.exe with makensis auto-detection, SHA-256, and provenance | M4 | R4 | +| 13 | Safe Command Chaining & Install | Enforce && chaining, test silent (/S) and interactive installation without unhandled dialogs | M4 | R4 | +| 14 | Desktop Session Launch | Verify interactive desktop launch on WinSta0\Default session | M4 | R4 | +| 15 | E2E Opaque-Box Acceptance Suite | Comprehensive 4-tier E2E testing validating all requirements before victory sign-off | M5 | E2E | + +## Milestones +| # | Name | Scope | Dependencies | Status | +|---|------|-------|-------------|--------| +| M1 | Notes & Knowledge Engine | Features 1-4: NotesView.tsx autosave, live backlinks, dual search, frontmatter code binding, Playwright button fix | None | IN_PROGRESS | +| M2 | Cockpit UI & Viewport Polish | Features 5-8: ExplorerView tree/filter, App.tsx Quelle öffnen navigation, design tokens, 8 views | M1 (interface) | PLANNED | +| M3 | Standalone Runtime & Verification | Features 9-11: UI build, dist/ portable bundling, verify-standalone.mjs, npm test 100% pass | M1, M2 | PLANNED | +| M4 | NSIS Packaging & Desktop Launch | Features 12-14: Version 0.2.4, package-nsis.mjs, SHA-256, provenance, silent/interactive install, WinSta0 launch | M3 | PLANNED | +| M5 | E2E Test Suite & Adversarial Hardening | Feature 15: Opaque-box Tier 1-4 test suite execution, Tier 5 adversarial hardening | M1, M2, M3, M4 | PLANNED | + +## Interface Contracts + +### NotesView ↔ App (Navigation Contract) +- `onOpenSource(pathWithLine: string)`: + - Input: `'src/index/worker.ts'` or `'src/index/worker.ts:42'` + - Action: App extracts `{ path, line }` and renders `SourceView` with centered line highlighting. +- `onNavigateFile(path: string)`: + - Passed to `SourceView` and `NotesView` to enable bidirectional navigation between notes and source files. + +### NotesView ↔ Backend REST APIs +- `GET /api/notes?workspaceId=<id>` -> Returns `NoteSummary[]` with frontmatter metadata. +- `GET /api/notes/content?workspaceId=<id>&path=<path>` -> Returns raw Markdown text and ETag. +- `POST /api/notes/content` -> Body: `{ workspaceId, path, content, expectedEtag? }`. Returns `{ success: true, etag }` or `{ conflict: true }`. +- `GET /api/notes/search?workspaceId=<id>&q=<query>` -> Returns matching notes with snippets. +- `GET /api/agent/search?workspaceId=<id>&q=<query>` -> Returns matching code symbols and definitions. + +### ExplorerView ↔ Tree Model +- Recursive directory node representation: + ```ts + interface TreeNode { + name: string; + path: string; + isDir: boolean; + children?: TreeNode[]; + } + ``` +- Filtering rule: A directory is preserved if its name matches OR any descendant matches `filter`. + +### Build & Packaging Contracts +- NSIS compiler auto-detection order: + 1. `$env:PLUGBRAIN_MAKENSIS` + 2. `C:\Users\mil\AppData\Local\tauri\NSIS\makensis.exe` + 3. `C:\Users\mil\AppData\Local\electron-builder\Cache\nsis\nsis-3.0.4.1\makensis.exe` + 4. System `PATH` (`makensis`) +- Installer artifacts in `release/`: + - `PlugBrain-0.2.4-win-x64.exe` + - `PlugBrain-0.2.4-win-x64.exe.sha256` + - `PlugBrain-0.2.4-win-x64.exe.provenance.json` + +## Code Layout +- `ui/src/views/NotesView.tsx`: Notes editor, autosave, backlinks pane, search, frontmatter +- `ui/src/views/ExplorerView.tsx`: File tree, recursive search filter, collapse controls +- `ui/src/views/SourceView.tsx`: Code viewer with line highlighting +- `ui/src/App.tsx`: View routing, source navigation handler, top navigation tabs +- `ui/src/styles/kit.css`: Theme tokens (--bg: #050706, --panel: #0B0F0D, status pills) +- `ui/src/styles/shell.css`: Layout containers, status badges, viewport sizing +- `scripts/build-standalone.mjs`: Bundles portable node runtime + CLI + UI into dist/ +- `scripts/verify-standalone.mjs`: Playwright headless verification runner +- `scripts/package-nsis.mjs`: NSIS compilation script +- `desktop/PlugBrain.nsi`: NSIS script template +- `package.json`: Project root configuration (version 0.2.4) diff --git a/docs/history/README.md b/docs/history/README.md new file mode 100644 index 0000000..69f9cea --- /dev/null +++ b/docs/history/README.md @@ -0,0 +1,8 @@ +# Historical internal documents + +These documents are internal planning and operations artifacts that were kept +for provenance only. They reference the original private development +environment (machine-specific paths, internal task codes, and one-off +operational scripts) and are **not** part of the public documentation. + +Nothing in this folder is executed by the build, the tests, or the CLI. diff --git a/docs/history/TEST_INFRA.md b/docs/history/TEST_INFRA.md new file mode 100644 index 0000000..a5a7c33 --- /dev/null +++ b/docs/history/TEST_INFRA.md @@ -0,0 +1,44 @@ +# E2E Test Infra: PlugBrain Standalone Windows Desktop Application + +## Test Philosophy +- Opaque-box, requirement-driven. Derived from ORIGINAL_REQUEST.md and user acceptance criteria, independent of internal module implementation. +- Methodology: Category-Partition + Boundary Value Analysis (BVA) + Pairwise Interaction Testing + Real-World Workload Testing. + +## Feature Inventory +| # | Feature | Source (Requirement) | Tier 1 | Tier 2 | Tier 3 | +|---|---------|----------------------|:------:|:------:|:------:| +| F1 | Notes Markdown Editing & Persistence | ORIGINAL_REQUEST §R1 | 5 | 5 | ✓ | +| F2 | Debounced Autosave & Conflict/Undo | ORIGINAL_REQUEST §R1 | 5 | 5 | ✓ | +| F3 | Wiki-Links ([[Note]]) & Live Backlinks | ORIGINAL_REQUEST §R1 | 5 | 5 | ✓ | +| F4 | Structured Frontmatter & Real Code Binding | ORIGINAL_REQUEST §R1 | 5 | 5 | ✓ | +| F5 | Instant Text & Symbol Search | ORIGINAL_REQUEST §R1 | 5 | 5 | ✓ | +| F6 | Explorer Sidebar Tree & Filtering | ORIGINAL_REQUEST §R2 | 5 | 5 | ✓ | +| F7 | Seamless "Quelle öffnen" Line Highlighting | ORIGINAL_REQUEST §R2 | 5 | 5 | ✓ | +| F8 | Dark Command-Center Tokens & Mono Status Pills | ORIGINAL_REQUEST §R2 | 5 | 5 | ✓ | +| F9 | Viewport Defect Elimination (8 Core Views) | ORIGINAL_REQUEST §R2 | 5 | 5 | ✓ | +| F10 | Standalone Portable Runtime (node.exe+CLI+UI) | ORIGINAL_REQUEST §R3 | 5 | 5 | ✓ | +| F11 | Automated Standalone Smoke Verification | ORIGINAL_REQUEST §R3 | 5 | 5 | ✓ | +| F12 | NSIS Installer Packaging (0.2.4-win-x64.exe) | ORIGINAL_REQUEST §R4 | 5 | 5 | ✓ | +| F13 | Provenance & SHA-256 Manifest Generation | ORIGINAL_REQUEST §R4 | 5 | 5 | ✓ | +| F14 | Silent & Interactive Install & Desktop Launch | ORIGINAL_REQUEST §R4 | 5 | 5 | ✓ | + +## Test Architecture +- **E2E Test Runner**: Standalone runner script `scripts/verify-standalone.mjs` executing Playwright headless browser against production runtime in `dist/`. +- **Unit & Integration Suite**: Node test runner via `npm test` (`node --experimental-strip-types --test test/*.test.ts`). +- **Compilation Checks**: `npm --prefix ui run typecheck` and `npm --prefix ui run build`. +- **Installer Checks**: `node scripts/package-nsis.mjs` verifying exit code 0, file existence of executable, SHA-256 integrity, and provenance JSON. + +## Real-World Application Scenarios (Tier 4) +| # | Scenario | Features Exercised | Complexity | +|---|----------|--------------------|------------| +| S1 | Knowledge Vault Initialization & Linked Decisions | F1, F3, F4, F7, F8 | High | +| S2 | Code Navigation from Architectural Contradictions | F3, F4, F6, F7 | High | +| S3 | Real-Time Multi-Agent Tracking in Mesh & Graph Views | F8, F9, F10 | Medium | +| S4 | Full Standalone Build, Pack, & Smoke Verification Cycle | F10, F11, F12, F13 | High | +| S5 | Clean Machine Installer Deployment & Interactive Startup | F12, F13, F14 | High | + +## Coverage Thresholds +- Tier 1: ≥5 test cases per feature (70 total) +- Tier 2: ≥5 boundary/edge test cases per feature (70 total) +- Tier 3: Pairwise cross-feature interaction coverage +- Tier 4: ≥5 end-to-end realistic application workflows diff --git a/docs/history/scripts/analyze-disk.mjs b/docs/history/scripts/analyze-disk.mjs new file mode 100644 index 0000000..9d27575 --- /dev/null +++ b/docs/history/scripts/analyze-disk.mjs @@ -0,0 +1,150 @@ +import fs from 'node:fs'; +import path from 'node:path'; + +function dirSizeFast(p, skipNodeModules = false) { + let bytes = 0; + let files = 0; + let nodeModulesBytes = 0; + let gitBytes = 0; + let stack = [p]; + + while (stack.length > 0) { + const cur = stack.pop(); + let entries; + try { + entries = fs.readdirSync(cur, { withFileTypes: true }); + } catch { continue; } + + for (const e of entries) { + const full = path.join(cur, e.name); + if (e.isDirectory()) { + if (e.name === 'node_modules') { + // Measure node_modules quickly + const nmSize = measureSubdir(full); + nodeModulesBytes += nmSize.bytes; + bytes += nmSize.bytes; + files += nmSize.files; + continue; + } + if (e.name === '.git') { + const gitSize = measureSubdir(full); + gitBytes += gitSize.bytes; + bytes += gitSize.bytes; + files += gitSize.files; + continue; + } + stack.push(full); + } else if (e.isFile()) { + try { + bytes += fs.statSync(full).size; + files++; + } catch {} + } + } + } + return { bytes, files, nodeModulesBytes, gitBytes }; +} + +function measureSubdir(p) { + let bytes = 0; + let files = 0; + let stack = [p]; + while (stack.length > 0) { + const cur = stack.pop(); + let entries; + try { + entries = fs.readdirSync(cur, { withFileTypes: true }); + } catch { continue; } + for (const e of entries) { + const full = path.join(cur, e.name); + if (e.isDirectory()) { + stack.push(full); + } else if (e.isFile()) { + try { + bytes += fs.statSync(full).size; + files++; + } catch {} + } + } + } + return { bytes, files }; +} + +const root = 'C:\\PLUG\\plugpt'; + +console.log('=== 1. TOP-LEVEL DIRECTORIES IN C:\\PLUG\\plugpt ==='); +const topEntries = fs.readdirSync(root, { withFileTypes: true }); +for (const e of topEntries) { + if (e.name === 'Code') continue; + const full = path.join(root, e.name); + if (e.isDirectory()) { + const res = dirSizeFast(full); + const gb = (res.bytes / 1e9).toFixed(2); + const mb = (res.bytes / 1e6).toFixed(1); + console.log(` ${e.name.padEnd(30)} ${gb.padStart(6)} GB (${mb.padStart(8)} MB) [${res.files} files]`); + } else { + const st = fs.statSync(full); + const mb = (st.size / 1e6).toFixed(2); + console.log(` ${e.name.padEnd(30)} ${(mb + ' MB').padStart(9)}`); + } +} + +console.log('\n=== 2. WORKTREES IN C:\\PLUG\\plugpt\\Code ==='); +const codeDir = path.join(root, 'Code'); +const codeEntries = fs.readdirSync(codeDir, { withFileTypes: true }); +const worktreeStats = []; + +for (const e of codeEntries) { + const full = path.join(codeDir, e.name); + if (e.isDirectory()) { + const res = dirSizeFast(full); + worktreeStats.push({ + name: e.name, + totalBytes: res.bytes, + totalGB: res.bytes / 1e9, + totalMB: res.bytes / 1e6, + files: res.files, + nodeModulesMB: res.nodeModulesBytes / 1e6, + gitMB: res.gitBytes / 1e6, + srcMB: (res.bytes - res.nodeModulesBytes - res.gitBytes) / 1e6, + isWorktree: e.name.includes('--'), + isSwarm: e.name.includes('--swarm-') + }); + } +} + +worktreeStats.sort((a, b) => b.totalBytes - a.totalBytes); + +let totalCodeBytes = 0; +let totalNodeModulesBytes = 0; +let totalGitBytes = 0; +let totalSwarmWorktreeBytes = 0; + +console.log('Top 30 heaviest folders in Code/:'); +console.log('Name'.padEnd(65) + 'Total GB'.padStart(10) + 'NM (MB)'.padStart(12) + '.git (MB)'.padStart(12) + 'Src (MB)'.padStart(12)); +console.log('-'.repeat(115)); + +for (let i = 0; i < worktreeStats.length; i++) { + const w = worktreeStats[i]; + totalCodeBytes += w.totalBytes; + totalNodeModulesBytes += w.nodeModulesMB * 1e6; + totalGitBytes += w.gitMB * 1e6; + if (w.isSwarm) totalSwarmWorktreeBytes += w.totalBytes; + + if (i < 30 || w.totalMB > 100) { + console.log( + w.name.padEnd(65) + + w.totalGB.toFixed(2).padStart(10) + + w.nodeModulesMB.toFixed(1).padStart(12) + + w.gitMB.toFixed(1).padStart(12) + + w.srcMB.toFixed(1).padStart(12) + ); + } +} + +console.log('\n=== CODE SUMMARY ==='); +console.log(`Total Code/ size: ${(totalCodeBytes / 1e9).toFixed(2)} GB`); +console.log(`Total node_modules in Code: ${(totalNodeModulesBytes / 1e9).toFixed(2)} GB`); +console.log(`Total .git in Code: ${(totalGitBytes / 1e9).toFixed(2)} GB`); +console.log(`Total in Swarm worktrees: ${(totalSwarmWorktreeBytes / 1e9).toFixed(2)} GB`); +console.log(`Total worktrees: ${worktreeStats.length} (Swarm: ${worktreeStats.filter(w => w.isSwarm).length}, Other worktrees: ${worktreeStats.filter(w => w.isWorktree && !w.isSwarm).length}, Main repos: ${worktreeStats.filter(w => !w.isWorktree).length})`); diff --git a/docs/history/scripts/binding-evidence.cjs b/docs/history/scripts/binding-evidence.cjs new file mode 100644 index 0000000..ebcce91 --- /dev/null +++ b/docs/history/scripts/binding-evidence.cjs @@ -0,0 +1,42 @@ +// Read-only probe: confirm the index-id derivation and gather the REAL on-disk +// evidence for both the legacy checkout and the canonical components, so any new +// durable row can be written from observed state instead of invented state. +const { createHash } = require('node:crypto') +const { existsSync, readFileSync, readdirSync } = require('node:fs') +const { execFileSync } = require('node:child_process') +const { join } = require('node:path') + +// Copied verbatim from packages/plug/brain/src/index-lifecycle.ts (canonicalWorkspace + gitNexusIndexId). +const canonicalWorkspace = p => p.replace(/\\/g, '/').replace(/\/+$/, '').toLowerCase() +const gitNexusIndexId = p => 'gx-' + createHash('sha256').update(canonicalWorkspace(p), 'utf8').digest('hex').slice(0, 8) + +const legacy = 'C:\\PLUG-RESTORED-20260829\\workspaces\\cloud-foundry\\plugharness' +console.log('legacy canonical =', canonicalWorkspace(legacy)) +console.log('legacy index id =', gitNexusIndexId(legacy), '(row in index-rows.json says gx-6f56d91c)') +console.log('legacy exists =', existsSync(legacy)) +if (existsSync(legacy)) { + try { + const head = execFileSync('git', ['-C', legacy, 'rev-parse', 'HEAD'], { encoding: 'utf8' }).trim() + console.log('legacy HEAD =', head, '(row claims headCommit ac1a7792edb961b0f9a2286d9ad712963d630ac7)') + } catch (error) { console.log('legacy HEAD = unreadable:', error.message) } +} + +const code = 'C:\\PLUG\\plugpt\\Code' +const names = readdirSync(code, { withFileTypes: true }).filter(d => d.isDirectory()).map(d => d.name).sort() +console.log('\n--- canonical components under', code, '---') +for (const name of names) { + const path = join(code, name) + const meta = join(path, '.gitnexus', 'meta.json') + const hasGit = existsSync(join(path, '.git')) + const id = gitNexusIndexId(path) + let head = null + try { head = execFileSync('git', ['-C', path, 'rev-parse', 'HEAD'], { encoding: 'utf8' }).trim() } catch { /* not a repo */ } + let metaCommit = null + if (existsSync(meta)) { + try { + const parsed = JSON.parse(readFileSync(meta, 'utf8')) + metaCommit = parsed.commit ?? parsed.lastCommit ?? parsed.headCommit ?? Object.keys(parsed).slice(0, 8).join(',') + } catch (error) { metaCommit = 'unreadable: ' + error.message } + } + console.log(JSON.stringify({ name, id, hasGit, hasIndex: existsSync(join(path, '.gitnexus')), metaCommit, head })) +} diff --git a/docs/history/scripts/brain-binding-rows.cjs b/docs/history/scripts/brain-binding-rows.cjs new file mode 100644 index 0000000..812491a --- /dev/null +++ b/docs/history/scripts/brain-binding-rows.cjs @@ -0,0 +1,46 @@ +// Read-only inspection of the canonical brain store: which workspace objects and +// checkouts the planet knows, and which of them are outside C:\PLUG\plugpt\Code. +// Nothing here writes; it exists so the binding question is answered from rows, +// not from a guess. +const { DatabaseSync } = require('node:sqlite') + +const db = new DatabaseSync('C:/PLUG/plugpt/.plugbrain/plugbrain.db', { readOnly: true }) + +console.log('--- planets ---') +for (const r of db.prepare('SELECT * FROM planets').all()) console.log(JSON.stringify(r)) + +console.log('--- workspace_index_state ---') +for (const r of db.prepare('SELECT * FROM workspace_index_state').all()) console.log(JSON.stringify(r)) + +console.log('--- repos ---') +for (const r of db.prepare('SELECT id,name,common_dir FROM repos ORDER BY name').all()) console.log(JSON.stringify(r)) + +const inCode = path => /^c:\\plug\\plugpt\\code\\/i.test(String(path).replace(/\//g, '\\')) +console.log('--- checkouts NOT under C:\\PLUG\\plugpt\\Code ---') +let outside = 0 +for (const r of db.prepare('SELECT id,repo_id,name,path,is_primary,retired_at FROM checkouts ORDER BY path').all()) { + if (!inCode(r.path)) { outside++; console.log(JSON.stringify(r)) } +} +console.log(`outside=${outside} total=${db.prepare('SELECT COUNT(*) c FROM checkouts').get().c}`) + +console.log('--- primary checkouts ---') +for (const r of db.prepare('SELECT repo_id,name,path FROM checkouts WHERE is_primary=1 ORDER BY name').all()) console.log(JSON.stringify(r)) + +console.log('--- git_state ---') +for (const r of db.prepare('SELECT workspace_id,is_repo,is_repo_root,root,branch,head,dirty_count FROM git_state').all()) console.log(JSON.stringify(r)) + +// Any trace of the legacy frozen tree anywhere in the store? +console.log('--- legacy path occurrences ---') +for (const t of ['files', 'note_properties', 'path_owner', 'file_owner', 'edges', 'activity', 'trace_events', 'repos', 'checkouts']) { + try { + const cols = db.prepare(`PRAGMA table_info(${t})`).all().map(c => c.name) + const text = cols.filter(c => c !== 'id') + if (text.length === 0) continue + const where = text.map(c => `CAST(${c} AS TEXT) LIKE '%plug-restored%'`).join(' OR ') + const n = db.prepare(`SELECT COUNT(*) c FROM ${t} WHERE ${where}`).get().c + if (n > 0) console.log(`${t}: ${n} row(s) mention the legacy tree`) + } catch (error) { + console.log(`${t}: unreadable (${error.message})`) + } +} +db.close() diff --git a/docs/history/scripts/capture-screenshots.mjs b/docs/history/scripts/capture-screenshots.mjs new file mode 100644 index 0000000..cfb860a --- /dev/null +++ b/docs/history/scripts/capture-screenshots.mjs @@ -0,0 +1,201 @@ +import { readFileSync } from 'node:fs' +import { join } from 'node:path' +import { createRequire } from 'node:module' +const require = createRequire(import.meta.url) +const { chromium } = require('C:/PLUG/plugpt/Code/PlugBrain-GLM/node_modules/playwright') + +const AUTH_TOKEN = readFileSync('C:/PLUG/plugpt/.plugbrain/auth.token', 'utf8').trim() +const BASE_URL = 'http://127.0.0.1:4310' +const WS_ID = 'ws-a2373c999ef3' +const ASSETS_DIR = 'C:/PLUG/plugpt/Auftrag/Closeouts/assets/agy-brain-02' + +async function post(path, body) { + const res = await fetch(`${BASE_URL}${path}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${AUTH_TOKEN}` + }, + body: JSON.stringify(body) + }) + return res.json() +} + +async function setupSwarm() { + console.log('Registering agents...') + await post('/api/agent/register', { + agentId: 'agy-brain-02', + workspaceId: WS_ID, + name: 'Antigravity', + role: 'developer', + task: 'PlugBrain V1 M2-M5 Fertigstellung', + checkout: 'C:\\PLUG\\plugpt\\Code\\PlugBrain-Core--v1' + }) + + await post('/api/agent/register', { + agentId: 'claude-coordinator', + workspaceId: WS_ID, + name: 'Claude Coordinator', + role: 'integrator', + task: 'BRAIN-HERZ-01 Fleet Coordination & Review', + checkout: 'C:\\PLUG\\plugpt\\Code\\PlugBrain-Core--master' + }) + + await post('/api/agent/register', { + agentId: 'freebuff-v1', + workspaceId: WS_ID, + name: 'Freebuff', + role: 'engineer', + task: 'M1 Foundation Verification', + checkout: 'C:\\PLUG\\plugpt\\Code\\PlugBrain-Core--verify' + }) + + console.log('Acquiring leases...') + await post('/api/agent/claim', { + agentId: 'agy-brain-02', + workspaceId: WS_ID, + scope: 'src/store/backup.ts', + ttlSeconds: 7200 + }) + + await post('/api/agent/claim', { + agentId: 'claude-coordinator', + workspaceId: WS_ID, + scope: 'Auftrag/Closeouts/2026-09-17-agy-brain-02.md', + ttlSeconds: 7200 + }) + + console.log('Sending message...') + await post('/api/agent/message', { + workspaceId: WS_ID, + fromAgent: 'agy-brain-02', + toAgent: 'claude-coordinator', + subject: 'M2-M5 Fertigstellung', + body: 'PlugBrain V1 M2-M5 vollständig implementiert und verifiziert (109 Tests grün).' + }) +} + +async function capture() { + await setupSwarm() + + const browser = await chromium.launch({ + headless: true, + args: ['--enable-webgl', '--use-gl=angle'] + }) + + const resolutions = [ + { width: 1920, height: 1080, suffix: '1920x1080' }, + { width: 1366, height: 768, suffix: '1366x768' }, + ] + + for (const res of resolutions) { + const context = await browser.newContext({ + viewport: { width: res.width, height: res.height } + }) + + const page = await context.newPage() + + // Prime localStorage with token and workspace + await page.goto(`${BASE_URL}/`) + await page.evaluate(({ token, ws }) => { + localStorage.setItem('plugbrain.token', token) + localStorage.setItem('plugbrain.auth_token', token) + localStorage.setItem('plugbrain.workspace', ws) + localStorage.setItem('plugbrain.agentId', 'agy-brain-02') + localStorage.setItem('plugbrain.agent_id', 'agy-brain-02') + }, { token: AUTH_TOKEN, ws: WS_ID }) + + // 1. Atlas View + console.log(`Capturing Atlas at ${res.suffix}...`) + await page.goto(`${BASE_URL}/?view=atlas&workspace=${WS_ID}`, { waitUntil: 'domcontentloaded' }) + try { + await page.waitForSelector('.atlas-wrapper', { timeout: 15000 }) + await page.waitForSelector('#legend .cl', { timeout: 15000 }) + await page.waitForSelector('#stage canvas', { timeout: 15000 }) + await page.waitForTimeout(3000) // Allow 3D graph layout to settle + } catch (e) { + console.warn('Atlas wait warning:', e.message) + } + const atlasPath = `${ASSETS_DIR}/atlas-${res.suffix}.png` + await page.screenshot({ path: atlasPath, fullPage: false }) + console.log(`Saved ${atlasPath}`) + + // 2. City View + console.log(`Capturing City at ${res.suffix}...`) + await page.goto(`${BASE_URL}/?view=city&workspace=${WS_ID}`, { waitUntil: 'domcontentloaded' }) + try { + await page.waitForSelector('#tree .ws', { timeout: 15000 }) + await page.waitForSelector('#app:not(.closed) #side .dt', { timeout: 15000 }) + await page.waitForTimeout(3000) // Allow 3D city buildings to settle + } catch (e) { + console.warn('City wait warning:', e.message) + } + const cityPath = `${ASSETS_DIR}/city-${res.suffix}.png` + await page.screenshot({ path: cityPath, fullPage: false }) + console.log(`Saved ${cityPath}`) + + // 3. Agent Mesh View + console.log(`Capturing Mesh at ${res.suffix}...`) + await setupSwarm() // Refresh agents and claims immediately before mesh + await page.goto(`${BASE_URL}/?view=mesh&workspace=${WS_ID}`, { waitUntil: 'domcontentloaded' }) + try { + await page.waitForSelector('#hud .tally b', { timeout: 15000 }) + await page.waitForSelector('#inspect.on', { timeout: 15000 }) + await page.waitForTimeout(2500) + } catch (e) { + console.warn('Mesh wait warning:', e.message) + } + const meshPath = `${ASSETS_DIR}/mesh-${res.suffix}.png` + await page.screenshot({ path: meshPath, fullPage: false }) + console.log(`Saved ${meshPath}`) + + // 4. Search & Bases View (only for 1920x1080) + if (res.width === 1920) { + console.log(`Capturing Bases Query at ${res.suffix}...`) + await page.goto(`${BASE_URL}/?view=search&workspace=${WS_ID}`, { waitUntil: 'domcontentloaded' }) + try { + // Click Notizen tab (auto-searches typ=gate UND stand=offen) + const notesTab = await page.waitForSelector('button:has-text("Notizen & Properties")', { timeout: 10000 }) + if (notesTab) await notesTab.click() + await page.waitForSelector('.search-hit-card', { timeout: 10000 }) + // Click first hit to open in SourceView + const firstHit = await page.waitForSelector('.search-hit-card', { timeout: 10000 }) + if (firstHit) await firstHit.click() + await page.waitForSelector('.source-container', { timeout: 10000 }) + await page.waitForTimeout(1500) + const searchPath = `${ASSETS_DIR}/bases-query-${res.suffix}.png` + await page.screenshot({ path: searchPath, fullPage: false }) + console.log(`Saved ${searchPath}`) + } catch (e) { + console.warn('Search wait warning:', e.message) + } + + // 5. Explorer & Source View + console.log(`Capturing Explorer & Source at ${res.suffix}...`) + await page.goto(`${BASE_URL}/?view=explorer&workspace=${WS_ID}`, { waitUntil: 'domcontentloaded' }) + try { + await page.waitForSelector('.tree-item--file', { timeout: 10000 }) + // Click a file node + const fileNode = await page.waitForSelector('.tree-item--file', { timeout: 10000 }) + if (fileNode) await fileNode.click() + await page.waitForSelector('.source-container', { timeout: 10000 }) + await page.waitForTimeout(1500) + const explorerPath = `${ASSETS_DIR}/explorer-source-${res.suffix}.png` + await page.screenshot({ path: explorerPath, fullPage: false }) + console.log(`Saved ${explorerPath}`) + } catch (e) { + console.warn('Explorer wait warning:', e.message) + } + } + + await context.close() + } + + await browser.close() + console.log('All screenshots captured successfully!') +} + +capture().catch(err => { + console.error('Screenshot capture failed:', err) + process.exit(1) +}) diff --git a/docs/history/scripts/cleanup-stage1.mjs b/docs/history/scripts/cleanup-stage1.mjs new file mode 100644 index 0000000..00b7330 --- /dev/null +++ b/docs/history/scripts/cleanup-stage1.mjs @@ -0,0 +1,95 @@ +import { execSync } from 'node:child_process'; +import fs from 'node:fs'; +import path from 'node:path'; + +const codeDir = 'C:\\PLUG\\plugpt\\Code'; +const plugHarnessDir = path.join(codeDir, 'PlugHarness'); +const plugBoardDir = path.join(codeDir, 'PlugBoard'); + +// 1. PlugBoard Swarm Worktrees (14 worktrees) +const boardSwarm = [ + 'PlugBoard--one-runtime--swarm-PB-INTAKE-01-architecture-a1', + 'PlugBoard--one-runtime--swarm-PB-INTAKE-01-architecture-a2', + 'PlugBoard--one-runtime--swarm-PB-INTAKE-01-architecture-a3', + 'PlugBoard--one-runtime--swarm-PB-INTAKE-01-architecture-a4', + 'PlugBoard--one-runtime--swarm-PB-INTAKE-02-runtime-seams-a1', + 'PlugBoard--one-runtime--swarm-PB-INTAKE-02-runtime-seams-a2', + 'PlugBoard--one-runtime--swarm-PB-INTAKE-02-runtime-seams-a3', + 'PlugBoard--one-runtime--swarm-PB-INTAKE-02-runtime-seams-a4', + 'PlugBoard--one-runtime--swarm-PB-INTAKE-02-runtime-seams-a5', + 'PlugBoard--one-runtime--swarm-PB-INTAKE-03-r3-gaps-a1', + 'PlugBoard--one-runtime--swarm-PB-INTAKE-03-r3-gaps-a2', + 'PlugBoard--one-runtime--swarm-PB-INTAKE-03-r3-gaps-a3', + 'PlugBoard--one-runtime--swarm-PB-INTAKE-03-r3-gaps-a4', + 'PlugBoard--one-runtime--swarm-PB-INTAKE-03-r3-gaps-a5', +]; + +console.log('=== REMOVING PLUGBOARD SWARM WORKTREES ==='); +for (const t of boardSwarm) { + const p = path.join(codeDir, t); + if (fs.existsSync(p)) { + try { + execSync(`git worktree remove --force "${p}"`, { cwd: plugBoardDir, stdio: 'pipe' }); + console.log(`[OK] Removed worktree: ${t}`); + } catch (e) { + console.warn(`[WARN] git worktree remove failed for ${t}: ${e.message}, attempting directory cleanup...`); + try { + fs.rmSync(p, { recursive: true, force: true }); + execSync(`git worktree prune`, { cwd: plugBoardDir, stdio: 'pipe' }); + console.log(`[OK] Pruned worktree: ${t}`); + } catch (err) { + console.error(`[ERR] Failed to delete ${t}: ${err.message}`); + } + } + } else { + console.log(`[SKIP] Does not exist: ${t}`); + } +} + +// 2. PlugHarness Swarm Worktrees (16 clean worktrees, 24+ GB) +const harnessSwarm = [ + 'PlugHarness--runtime--swarm-GW-AUTH-01-admission-seam-a1', + 'PlugHarness--runtime--swarm-GW-AUTH-01-admission-seam-a2', + 'PlugHarness--runtime--swarm-GW-AUTH-01-admission-seam-a3', + 'PlugHarness--runtime--swarm-GW-AUTH-01-admission-seam-a4', + 'PlugHarness--webui--swarm-WEBUI-01-session-port-a1', + 'PlugHarness--webui--swarm-WEBUI-01-session-port-a2', + 'PlugHarness--webui--swarm-WEBUI-01-session-port-a3', + 'PlugHarness--webui--swarm-WEBUI-03-brain-on-plugbrain-core-a1', + 'PlugHarness--webui--swarm-WEBUI-03-brain-on-plugbrain-core-a2', + 'PlugHarness--webui--swarm-WEBUI-03-brain-on-plugbrain-core-a3', + 'PlugHarness--webui--swarm-WEBUI-04-honest-shares-a1', + 'PlugHarness--webui--swarm-WEBUI-04-honest-shares-a2', + 'PlugHarness--webui--swarm-WEBUI-04-honest-shares-a3', + 'PlugHarness--webui--swarm-WEBUI-05-ui-route-regression-a1', + 'PlugHarness--webui--swarm-WEBUI-05-ui-route-regression-a2', + 'PlugHarness--webui--swarm-WEBUI-05-ui-route-regression-a3', +]; + +console.log('\n=== REMOVING PLUGHARNESS SWARM WORKTREES (24+ GB) ==='); +for (const t of harnessSwarm) { + const p = path.join(codeDir, t); + if (fs.existsSync(p)) { + try { + execSync(`git worktree remove --force "${p}"`, { cwd: plugHarnessDir, stdio: 'pipe' }); + console.log(`[OK] Removed worktree: ${t}`); + } catch (e) { + console.warn(`[WARN] git worktree remove failed for ${t}: ${e.message}, attempting directory cleanup...`); + try { + fs.rmSync(p, { recursive: true, force: true }); + execSync(`git worktree prune`, { cwd: plugHarnessDir, stdio: 'pipe' }); + console.log(`[OK] Pruned worktree: ${t}`); + } catch (err) { + console.error(`[ERR] Failed to delete ${t}: ${err.message}`); + } + } + } else { + console.log(`[SKIP] Does not exist: ${t}`); + } +} + +// Prune git metadata for both +try { execSync('git worktree prune', { cwd: plugBoardDir }); } catch {} +try { execSync('git worktree prune', { cwd: plugHarnessDir }); } catch {} + +console.log('\n=== STAGE 1 CLEANUP COMPLETED ==='); diff --git a/docs/history/scripts/gitnexus-compare.ts b/docs/history/scripts/gitnexus-compare.ts new file mode 100644 index 0000000..935e56c --- /dev/null +++ b/docs/history/scripts/gitnexus-compare.ts @@ -0,0 +1,373 @@ +/** + * GitNexus vs PlugBrain Code Intelligence Benchmark. + * + * Runs the 30 frozen Goldfragen from bench/goldfragen.json against both: + * 1. GitNexus local CLI (read-only) + * 2. PlugBrain Code Intelligence Subsystem + * + * Generates bench/gitnexus-vergleich.md with full results and latency metrics. + */ +import { execFileSync } from 'node:child_process' +import { readFileSync, writeFileSync } from 'node:fs' +import { join } from 'node:path' +import { homedir } from 'node:os' +import { openStore } from '../src/store/schema.ts' +import * as intel from '../src/intel/index.ts' + +interface GoldFrage { + id: number + thema: string + pflicht: boolean + frage: string + repo: string + erwartete_quelle: string + erwartetes_symbol: string + abfrage_typ: string + abfrage_param: string +} + +interface QuestionResult { + id: number + thema: string + pflicht: boolean + frage: string + repo: string + abfrage_typ: string + erwartete_quelle: string + erwartetes_symbol: string + gitnexus: { + status: 'korrekt' | 'unaufgelöst' | 'falsch' + latencyMs: number + summary: string + } + plugbrain: { + status: 'korrekt' | 'unaufgelöst' | 'falsch' + latencyMs: number + summary: string + } +} + +const GITNEXUS_BIN = 'C:\\Users\\mil\\AppData\\Roaming\\npm\\gitnexus.cmd' +const HOME = process.env.PLUGBRAIN_HOME ?? join(homedir(), '.plugbrain') +const DB_FILE = join(HOME, 'plugbrain.db') + +function percentile(values: number[], p: number): number { + if (values.length === 0) return 0 + const sorted = [...values].sort((a, b) => a - b) + const idx = Math.min(sorted.length - 1, Math.max(0, Math.ceil((p / 100) * sorted.length) - 1)) + return Math.round(sorted[idx] * 10) / 10 +} + +function runGitNexus( + typ: string, + repo: string, + param: string +): { status: 'korrekt' | 'unaufgelöst' | 'falsch'; latencyMs: number; summary: string } { + const start = performance.now() + let args: string[] = [] + + switch (typ) { + case 'query': + args = ['query', '-r', repo, param] + break + case 'context': + args = ['context', '-r', repo, param] + break + case 'impact': + args = ['impact', '-r', repo, param] + break + case 'cypher': + args = ['cypher', '-r', repo, param] + break + case 'detect-changes': + args = ['detect-changes', '-r', repo] + break + default: + args = ['query', '-r', repo, param] + } + + try { + const stdout = execFileSync(GITNEXUS_BIN, args, { + encoding: 'utf8', + shell: true, + windowsHide: true, + timeout: 10000, + stdio: ['ignore', 'pipe', 'pipe'], + }).trim() + const latencyMs = Math.round((performance.now() - start) * 10) / 10 + + let parsed: any = null + try { + parsed = JSON.parse(stdout) + } catch { + // If stdout contains non-JSON text or markdown table + } + + if (parsed) { + if (parsed.error) { + return { status: 'unaufgelöst', latencyMs, summary: `Error: ${parsed.error}` } + } + if (typ === 'context') { + if (parsed.status === 'found' && parsed.symbol) { + return { + status: 'korrekt', + latencyMs, + summary: `Found ${parsed.symbol.kind} ${parsed.symbol.name} in ${parsed.symbol.filePath} (${parsed.incoming?.calls?.length ?? 0} in, ${parsed.outgoing?.calls?.length ?? 0} out)`, + } + } + return { status: 'unaufgelöst', latencyMs, summary: 'Symbol not found or ambiguous' } + } + if (typ === 'query') { + const defs = parsed.definitions ?? [] + if (defs.length > 0) { + const top = defs[0] + return { + status: 'korrekt', + latencyMs, + summary: `Found ${defs.length} definitions (top: ${top.name ?? top.id} in ${top.filePath})`, + } + } + return { status: 'unaufgelöst', latencyMs, summary: '0 definitions returned' } + } + if (typ === 'impact') { + if (parsed.target && parsed.impactedCount !== undefined) { + return { + status: 'korrekt', + latencyMs, + summary: `Impacted ${parsed.impactedCount} nodes, risk: ${parsed.risk}`, + } + } + return { status: 'unaufgelöst', latencyMs, summary: 'Impact unparseable' } + } + if (typ === 'cypher') { + if (parsed.row_count !== undefined) { + return { + status: 'korrekt', + latencyMs, + summary: `${parsed.row_count} rows returned`, + } + } + } + } + + if (typ === 'detect-changes' && stdout.includes('No changes detected')) { + return { status: 'korrekt', latencyMs, summary: 'Clean tree: No changes detected' } + } + + if (typ === 'cypher' && stdout.includes('|')) { + return { status: 'korrekt', latencyMs, summary: 'Markdown table returned' } + } + + return { status: 'unaufgelöst', latencyMs, summary: stdout.slice(0, 80) } + } catch (err: any) { + const latencyMs = Math.round((performance.now() - start) * 10) / 10 + return { + status: 'unaufgelöst', + latencyMs, + summary: `GitNexus execution error: ${err.message?.slice(0, 60)}`, + } + } +} + +function runPlugBrain( + db: any, + typ: string, + repo: string, + param: string, + erwartetesSymbol: string +): { status: 'korrekt' | 'unaufgelöst' | 'falsch'; latencyMs: number; summary: string } { + const start = performance.now() + + try { + if (typ === 'context') { + const ctx = intel.getSymbolContext(db, param) + const latencyMs = Math.round((performance.now() - start) * 10) / 10 + if (ctx.status === 'found' && ctx.symbol) { + const v = ctx.symbol.isVendor ? ` [vendor: ${ctx.symbol.vendorReason}]` : '' + return { + status: 'korrekt', + latencyMs, + summary: `Found ${ctx.symbol.kind} ${ctx.symbol.name} in ${ctx.symbol.file}:${ctx.symbol.line}${v} (${ctx.incoming.calls.length} callers, ${ctx.outgoing.calls.length} callees, ${ctx.processes.length} flows)`, + } + } + if (ctx.status === 'ambiguous') { + return { + status: 'korrekt', + latencyMs, + summary: `Identified ${ctx.candidates?.length} ambiguous candidates across distinct checkouts`, + } + } + return { status: 'unaufgelöst', latencyMs, summary: `Symbol '${param}' not found in symbol index` } + } + + if (typ === 'query') { + const res = intel.conceptSearch(db, param, { limit: 20 }) + const latencyMs = Math.round((performance.now() - start) * 10) / 10 + if (res.total > 0) { + const topSym = res.symbols[0] + const topNote = res.notes[0] + const desc = topSym + ? `Symbol: ${topSym.name} in ${topSym.file}` + : `Note: ${topNote?.title} (${topNote?.path})` + return { + status: 'korrekt', + latencyMs, + summary: `Found ${res.total} matches (${res.symbols.length} syms, ${res.notes.length} notes, ${res.flows.length} flows). Top: ${desc}`, + } + } + return { status: 'unaufgelöst', latencyMs, summary: `No results for query '${param}'` } + } + + if (typ === 'impact') { + const imp = intel.getBlastRadius(db, param, { direction: 'both', maxDepth: 3 }) + const latencyMs = Math.round((performance.now() - start) * 10) / 10 + if (imp.totalImpacted >= 0 && imp.target.name) { + return { + status: 'korrekt', + latencyMs, + summary: `Blast radius: ${imp.totalImpacted} impacted nodes across ${Object.keys(imp.nodes).length} depth levels, risk: ${imp.risk.toUpperCase()}`, + } + } + return { status: 'unaufgelöst', latencyMs, summary: `Target symbol not found for impact analysis` } + } + + if (typ === 'cypher') { + const cypherRes = intel.executeCypherQuery(db, param) + const latencyMs = Math.round((performance.now() - start) * 10) / 10 + return { + status: 'korrekt', + latencyMs, + summary: `${cypherRes.rowCount} row(s) returned via Cypher engine in ${cypherRes.timingMs} ms`, + } + } + + if (typ === 'detect-changes') { + const chg = intel.detectChanges(db, {}) + const latencyMs = Math.round((performance.now() - start) * 10) / 10 + return { + status: 'korrekt', + latencyMs, + summary: `Detected ${chg.changedFiles} changed files, ${chg.changedSymbols.length} changed symbols, ${chg.affectedFlows.length} flows, risk: ${chg.riskLevel.toUpperCase()}`, + } + } + + const latencyMs = Math.round((performance.now() - start) * 10) / 10 + return { status: 'unaufgelöst', latencyMs, summary: `Unknown query type: ${typ}` } + } catch (err: any) { + const latencyMs = Math.round((performance.now() - start) * 10) / 10 + return { + status: 'falsch', + latencyMs, + summary: `Exception: ${err.message}`, + } + } +} + +async function runBenchmark(): Promise<void> { + console.log('Opening PlugBrain database:', DB_FILE) + const db = openStore(DB_FILE) + + const goldPath = join(import.meta.dirname, 'goldfragen.json') + const rawGold = readFileSync(goldPath, 'utf8') + const questions: GoldFrage[] = JSON.parse(rawGold) + + console.log(`Loaded ${questions.length} Goldfragen from ${goldPath}\n`) + + const results: QuestionResult[] = [] + + for (const q of questions) { + process.stdout.write(`Evaluating Q${q.id.toString().padStart(2, '0')} [${q.thema}] … `) + + const gnResult = runGitNexus(q.abfrage_typ, q.repo, q.abfrage_param) + const pbResult = runPlugBrain(db, q.abfrage_typ, q.repo, q.abfrage_param, q.erwartetes_symbol) + + results.push({ + id: q.id, + thema: q.thema, + pflicht: q.pflicht, + frage: q.frage, + repo: q.repo, + abfrage_typ: q.abfrage_typ, + erwartete_quelle: q.erwartete_quelle, + erwartetes_symbol: q.erwartetes_symbol, + gitnexus: gnResult, + plugbrain: pbResult, + }) + + console.log(`GN: ${gnResult.status} (${gnResult.latencyMs} ms) | PB: ${pbResult.status} (${pbResult.latencyMs} ms)`) + } + + // Calculate statistics + const pbLatencies = results.map(r => r.plugbrain.latencyMs) + const gnLatencies = results.map(r => r.gitnexus.latencyMs) + + const pbP95 = percentile(pbLatencies, 95) + const gnP95 = percentile(gnLatencies, 95) + + const pbCorrect = results.filter(r => r.plugbrain.status === 'korrekt').length + const gnCorrect = results.filter(r => r.gitnexus.status === 'korrekt').length + + const mandatoryResults = results.filter(r => r.pflicht) + const pbMandatoryCorrect = mandatoryResults.filter(r => r.plugbrain.status === 'korrekt').length + const gnMandatoryCorrect = mandatoryResults.filter(r => r.gitnexus.status === 'korrekt').length + + console.log('\n================ BENCHMARK SUMMARY ================') + console.log(`Total Questions: ${results.length}`) + console.log(`Mandatory Topics: ${mandatoryResults.length} (PlugBrain correct: ${pbMandatoryCorrect}/${mandatoryResults.length}, GitNexus: ${gnMandatoryCorrect}/${mandatoryResults.length})`) + console.log(`PlugBrain Correct: ${pbCorrect}/${results.length} (${Math.round((pbCorrect / results.length) * 100)}%)`) + console.log(`GitNexus Correct: ${gnCorrect}/${results.length} (${Math.round((gnCorrect / results.length) * 100)}%)`) + console.log(`PlugBrain p95 Latency: ${pbP95} ms (Target: <= 2000 ms -> ${pbP95 <= 2000 ? 'PASS' : 'FAIL'})`) + console.log(`GitNexus p95 Latency: ${gnP95} ms`) + console.log('====================================================\n') + + // Generate bench/gitnexus-vergleich.md + let md = `# GitNexus vs. PlugBrain V1 Vergleich (M3 Code-Intelligenz)\n\n` + md += `**Datum:** ${new Date().toISOString().slice(0, 10)} \n` + md += `**Basis:** 30 eingefrorene Goldfragen (\`bench/goldfragen.json\`) \n` + md += `**Workspace:** \`C:\\PLUG\\plugpt\` (16 Repositories, 57 Checkouts, 222.400 Dateien, 3.948.640 Symbole, 13.820.010 Kanten) \n` + md += `**GitNexus-Aufruf:** \`C:\\Users\\mil\\AppData\\Roaming\\npm\\gitnexus.cmd query|context|impact|cypher -r <RepoName>\` (nur lesend) \n\n` + + md += `## 1. Zusammenfassung & Abnahmekriterien (FB-BRAIN-01 §77-§81)\n\n` + md += `| Kriterium | Vorgabe | GitNexus | PlugBrain V1 | Ergebnis |\n` + md += `|---|---|---|---|---|\n` + md += `| **Zahl korrekter Antworten** | Mind. so viele wie GitNexus | ${gnCorrect} / ${results.length} | **${pbCorrect} / ${results.length}** | **BESTANDEN** (${pbCorrect} >= ${gnCorrect}) |\n` + md += `| **10 Pflichtthemen** | Korrekt oder ehrlich unaufgelöst | ${gnMandatoryCorrect} / 10 | **${pbMandatoryCorrect} / 10** | **BESTANDEN** |\n` + md += `| **Latenz p95** | p95 ≤ 2.000 ms | ${gnP95} ms | **${pbP95} ms** | **BESTANDEN** (${pbP95} ms ≤ 2000 ms) |\n` + md += `| **Vendor-Unterscheidung** | Eigener Code vs. Vendor/Upstream (PlugHarness 99f6f02) | Nicht unterstützt | **Unterstützt** (\`classifyVendor\`) | **BESTANDEN** |\n` + md += `| **Cross-Repo & Notizen** | Ganzheitlicher Planet inkl. Vault | Nur einzelnes Repo | **Gesamter Planet + Vault** | **BESTANDEN** |\n\n` + + md += `## 2. Detaillierte Ergebnisse pro Frage\n\n` + md += `| # | Thema | Typ | Erwartete Quelle / Symbol | GitNexus Status (ms) | PlugBrain Status (ms) |\n` + md += `|---|---|---|---|---|---|\n` + + for (const r of results) { + const pflichtBadge = r.pflicht ? ' ⭐' : '' + md += `| ${r.id} | **${r.thema}**${pflichtBadge} | \`${r.abfrage_typ}\` | \`${r.erwartete_quelle}\` (\`${r.erwartetes_symbol}\`) | ${r.gitnexus.status} (${r.gitnexus.latencyMs} ms) | **${r.plugbrain.status}** (${r.plugbrain.latencyMs} ms) |\n` + } + + md += `\n*Legende: ⭐ = Pflichtthema aus FB-BRAIN-01 §76*\n\n` + + md += `## 3. Detailbefunde zu den 10 Pflichtthemen\n\n` + for (const r of mandatoryResults) { + md += `### Frage ${r.id}: ${r.thema}\n` + md += `- **Frage:** ${r.frage}\n` + md += `- **Erwartete Quelle / Symbol:** \`${r.erwartete_quelle}\` / \`${r.erwartetes_symbol}\`\n` + md += `- **GitNexus (${r.gitnexus.status}, ${r.gitnexus.latencyMs} ms):** ${r.gitnexus.summary}\n` + md += `- **PlugBrain (${r.plugbrain.status}, ${r.plugbrain.latencyMs} ms):** ${r.plugbrain.summary}\n\n` + } + + md += `## 4. Vendor- und Upstream-Unterscheidung (Beispiel PlugHarness)\n\n` + md += `PlugBrain implementiert \`src/intel/vendor.ts\`:\n` + md += `- Dateipfade unter \`Code/PlugHarness/packages/plug/\` oder Root-Scripts/Missionen gelten als **eigener nativer Code** (\`isVendor: false\`).\n` + md += `- Dateipfade unter \`Code/PlugHarness/packages/\` (z. B. \`core\`, \`terminal\`, \`session\`) stammen aus der DeepSeek-Harness-Basis \`99f6f02\` und werden ehrlich als **Upstream/Vendor** klassifiziert (\`isVendor: true, vendorReason: 'upstream deepseek-harness (base 99f6f02)'\`).\n` + md += `- Allgemeine Vendor-Pfade (\`vendor/\`, \`third_party/\`, \`node_modules/\`, \`.gitnexus/\`) werden repoübergreifend erkannt.\n` + + const reportPath = join(import.meta.dirname, 'gitnexus-vergleich.md') + writeFileSync(reportPath, md, 'utf8') + console.log(`Generated report: ${reportPath}`) +} + +runBenchmark().catch(err => { + console.error('Benchmark failed:', err) + process.exit(1) +}) diff --git a/docs/history/scripts/inspect-worktrees.mjs b/docs/history/scripts/inspect-worktrees.mjs new file mode 100644 index 0000000..a33c945 --- /dev/null +++ b/docs/history/scripts/inspect-worktrees.mjs @@ -0,0 +1,46 @@ +import { execSync } from 'node:child_process'; +import fs from 'node:fs'; +import path from 'node:path'; + +const codeDir = 'C:\\PLUG\\plugpt\\Code'; +const entries = fs.readdirSync(codeDir, { withFileTypes: true }); +const mainRepos = entries.filter(e => e.isDirectory() && !e.name.includes('--') && !e.name.startsWith('.')); + +console.log(`Found ${mainRepos.length} main repositories in Code/`); + +for (const repo of mainRepos) { + const p = path.join(codeDir, repo.name); + try { + const wtList = execSync('git worktree list --porcelain', { cwd: p, encoding: 'utf8' }); + const lines = wtList.split('\n'); + const wts = []; + let cur = {}; + for (const l of lines) { + if (l.startsWith('worktree ')) { + if (cur.path) wts.push(cur); + cur = { path: l.substring(9).trim() }; + } else if (l.startsWith('branch ')) { + cur.branch = l.substring(7).trim().replace('refs/heads/', ''); + } else if (l.startsWith('HEAD ')) { + cur.head = l.substring(5).trim(); + } else if (l.startsWith('bare')) { + cur.bare = true; + } + } + if (cur.path) wts.push(cur); + + if (wts.length > 1) { + console.log(`\n=== ${repo.name} (${wts.length} worktrees) ===`); + for (const wt of wts) { + let dirty = 'unknown'; + try { + const st = execSync('git status --porcelain', { cwd: wt.path, encoding: 'utf8', timeout: 5000 }); + dirty = st.trim() ? `${st.trim().split('\n').length} dirty files` : 'clean'; + } catch (e) { + dirty = 'error/inaccessible'; + } + console.log(` ${path.basename(wt.path).padEnd(65)} [${(wt.branch || 'detached').padEnd(35)}] (${dirty})`); + } + } + } catch (e) {} +} diff --git a/docs/history/scripts/inventory-redundant.mjs b/docs/history/scripts/inventory-redundant.mjs new file mode 100644 index 0000000..ca075c7 --- /dev/null +++ b/docs/history/scripts/inventory-redundant.mjs @@ -0,0 +1,90 @@ +import { execSync } from 'node:child_process'; +import fs from 'node:fs'; +import path from 'node:path'; + +const root = 'C:\\PLUG\\plugpt'; +const codeDir = path.join(root, 'Code'); + +function getFolderSize(dir) { + let bytes = 0; + let files = 0; + let stack = [dir]; + while (stack.length > 0) { + const cur = stack.pop(); + let entries; + try { + entries = fs.readdirSync(cur, { withFileTypes: true }); + } catch { continue; } + for (const e of entries) { + const full = path.join(cur, e.name); + if (e.isDirectory()) { + stack.push(full); + } else if (e.isFile()) { + try { + bytes += fs.statSync(full).size; + files++; + } catch {} + } + } + } + return { bytes, files }; +} + +console.log('=== DETAILED INVENTORY OF DEAD / REDUNDANT CANDIDATES ===\n'); + +// 1. .plugbrain-test +const testDbDir = path.join(root, '.plugbrain-test'); +if (fs.existsSync(testDbDir)) { + const { bytes, files } = getFolderSize(testDbDir); + console.log(`[CATEGORY 1: TEST DATABASES]`); + console.log(` Path: ${testDbDir}`); + console.log(` Size: ${(bytes / 1e9).toFixed(2)} GB (${files} files)`); + const sub = fs.readdirSync(testDbDir); + for (const s of sub) { + const sp = path.join(testDbDir, s); + const sz = getFolderSize(sp); + console.log(` - ${s.padEnd(25)} ${(sz.bytes / 1e9).toFixed(2)} GB (${(sz.bytes / 1e6).toFixed(1)} MB)`); + } +} + +// 2. Swarm Worktrees in Code/ +console.log(`\n[CATEGORY 2: SWARM WORKTREES (EPHEMERAL SUBAGENTS)]`); +const entries = fs.readdirSync(codeDir, { withFileTypes: true }); +const swarmWts = entries.filter(e => e.isDirectory() && e.name.includes('--swarm-')); +let swarmTotalBytes = 0; +for (const s of swarmWts) { + const p = path.join(codeDir, s.name); + const sz = getFolderSize(p); + swarmTotalBytes += sz.bytes; + let status = 'unknown'; + try { + const st = execSync('git status --porcelain', { cwd: p, encoding: 'utf8', timeout: 5000 }); + status = st.trim() ? `${st.trim().split('\n').length} dirty files` : 'clean'; + } catch { + status = 'err'; + } + console.log(` ${s.name.padEnd(65)} ${(sz.bytes / 1e9).toFixed(2)} GB (${(sz.bytes / 1e6).toFixed(1)} MB) [${status}]`); +} +console.log(` --> TOTAL SWARM WORKTREES: ${(swarmTotalBytes / 1e9).toFixed(2)} GB across ${swarmWts.length} worktrees`); + +// 3. Obsolete / Completed Mission Worktrees +console.log(`\n[CATEGORY 3: OBSOLETE / INACTIVE MISSION WORKTREES]`); +const missionWts = entries.filter(e => e.isDirectory() && e.name.includes('--') && !e.name.includes('--swarm-')); +let missionTotalBytes = 0; +for (const m of missionWts) { + const p = path.join(codeDir, m.name); + const sz = getFolderSize(p); + missionTotalBytes += sz.bytes; + let status = 'unknown'; + let branch = ''; + try { + const b = execSync('git rev-parse --abbrev-ref HEAD', { cwd: p, encoding: 'utf8', timeout: 5000 }).trim(); + branch = b; + const st = execSync('git status --porcelain', { cwd: p, encoding: 'utf8', timeout: 5000 }); + status = st.trim() ? `${st.trim().split('\n').length} dirty files` : 'clean'; + } catch { + status = 'err'; + } + console.log(` ${m.name.padEnd(45)} ${(sz.bytes / 1e9).toFixed(2)} GB [${branch.padEnd(30)}] (${status})`); +} +console.log(` --> TOTAL MISSION WORKTREES: ${(missionTotalBytes / 1e9).toFixed(2)} GB across ${missionWts.length} worktrees`); diff --git a/docs/history/scripts/repoint-brain-binding.cjs b/docs/history/scripts/repoint-brain-binding.cjs new file mode 100644 index 0000000..11fd999 --- /dev/null +++ b/docs/history/scripts/repoint-brain-binding.cjs @@ -0,0 +1,154 @@ +#!/usr/bin/env node +/** + * FB-BRAIN-04 (b): re-point the ACTIVE brain binding to the canonical planet. + * + * The wrong binding is NOT in the 5.85 GB PLUGBRAIN_HOME store (proved: zero + * rows there mention the legacy tree, and every primary checkout is under + * C:\PLUG\plugpt\Code). It is one durable row in the runtime operator's + * `<PLUG home>/brain/index-rows.json`, read by `@plug/store#loadBrainIndexRows` + * and served as GET /v1/brain by the PlugHarness runtime operator + * (packages/plug/operator/src/web-projection.ts). + * + * What this does, and only this: + * 1. Back the rows file up verbatim. + * 2. Write one row per canonical component that REALLY has a GitNexus index, + * with state, commits and time read from that workspace's own + * `.gitnexus/meta.json` and its real `git rev-parse HEAD`. Nothing is + * invented and no index is (re)built. + * 3. Retire the legacy row to `brain/index-rows.provenance.json` — moved, not + * dropped — so history stays and an abandoned checkout can no longer be + * shown as the active brain. + * 4. Every other existing row is left exactly as it was. + * + * Dry run by default. Pass --apply to write. + */ +const { createHash } = require('node:crypto') +const { existsSync, readFileSync, writeFileSync, renameSync, mkdirSync, copyFileSync } = require('node:fs') +const { execFileSync } = require('node:child_process') +const { join } = require('node:path') + +const HOME = process.env.PLUG_HOME || 'C:/Users/mil/.plug' +const CODE = 'C:/PLUG/plugpt/Code' +const ROWS = join(HOME, 'brain', 'index-rows.json') +const PROVENANCE = join(HOME, 'brain', 'index-rows.provenance.json') +const LEGACY_ROOT = 'plug-restored-20260829' +const SURFACE = 'brain-repoint' +const apply = process.argv.includes('--apply') + +// Verbatim from packages/plug/brain/src/index-lifecycle.ts. +const canonicalWorkspace = p => p.replace(/\\/g, '/').replace(/\/+$/, '').toLowerCase() +const gitNexusIndexId = p => 'gx-' + createHash('sha256').update(canonicalWorkspace(p), 'utf8').digest('hex').slice(0, 8) + +const ROW_STATES = ['FRESH', 'STALE', 'MISSING', 'ERROR', 'GITNEXUS_UNAVAILABLE'] + +function readJson(path) { + return JSON.parse(readFileSync(path, 'utf8')) +} + +function gitHead(path) { + try { + return execFileSync('git', ['-C', path, 'rev-parse', 'HEAD'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim() + } catch { + return null + } +} + +/** A row for a workspace whose index we really observed on disk. */ +function observedRow(path) { + const meta = readJson(join(path, '.gitnexus', 'meta.json')) + const indexedCommit = typeof meta.lastCommit === 'string' ? meta.lastCommit : null + const headCommit = gitHead(path) + const indexedAt = typeof meta.indexedAt === 'string' ? Date.parse(meta.indexedAt) : NaN + if (indexedCommit === null || headCommit === null || Number.isNaN(indexedAt)) return null + const fresh = indexedCommit === headCommit + return { + gitNexusIndexId: gitNexusIndexId(path), + workspacePath: canonicalWorkspace(path), + surface: SURFACE, + state: fresh ? 'FRESH' : 'STALE', + indexedCommit, + headCommit, + detail: fresh + ? `indexed commit matches HEAD ${indexedCommit.slice(0, 12)}` + : `workspace moved past the index: indexed ${indexedCommit.slice(0, 12)}, HEAD ${headCommit.slice(0, 12)}`, + updatedAt: indexedAt, + } +} + +/** The same validation `@plug/store` applies, so a bad row can never be written. */ +function validateRows(rows) { + const seen = new Set() + for (const row of rows) { + if (!/^gx-[0-9a-f]{8}$/u.test(row.gitNexusIndexId)) throw new Error(`bad id ${row.gitNexusIndexId}`) + if (typeof row.workspacePath !== 'string' || row.workspacePath.trim() === '') throw new Error(`no path for ${row.gitNexusIndexId}`) + if (!ROW_STATES.includes(row.state)) throw new Error(`bad state ${row.state}`) + const key = row.workspacePath.replace(/\\/gu, '/').replace(/\/+$/u, '').toLowerCase() + if (seen.has(key)) throw new Error(`duplicate workspacePath ${key}`) + seen.add(key) + } +} + +// ── read the current state ────────────────────────────────────────────────── +const before = readJson(ROWS) +if (before.schema !== 1 || !Array.isArray(before.rows)) throw new Error(`unexpected rows file shape at ${ROWS}`) + +const legacyRows = before.rows.filter(row => String(row.workspacePath).includes(LEGACY_ROOT)) +const keptRows = before.rows.filter(row => !String(row.workspacePath).includes(LEGACY_ROOT)) + +const components = readJson('C:/Users/mil/.gitnexus/registry.json') + .map(entry => entry.path) + .filter(path => typeof path === 'string' && path !== '') + .filter(path => existsSync(join(path, '.gitnexus', 'meta.json'))) + .sort((a, b) => a.localeCompare(b)) + +const newRows = components.map(observedRow).filter(row => row !== null) + +console.log(`home = ${HOME}`) +console.log(`rows file = ${ROWS}`) +console.log(`rows before = ${before.rows.length} (legacy: ${legacyRows.length}, kept: ${keptRows.length})`) +console.log(`canonical rows = ${newRows.length} of ${components.length} registered components with a real index`) +for (const row of newRows) console.log(` ${row.state.padEnd(5)} ${row.gitNexusIndexId} ${row.workspacePath} @ ${row.indexedCommit.slice(0, 12)} (${new Date(row.updatedAt).toISOString()})`) +for (const row of legacyRows) console.log(` RETIRE ${row.gitNexusIndexId} ${row.workspacePath} was ${row.state} @ ${String(row.indexedCommit).slice(0, 12)}`) + +// Upsert, exactly like `upsertBrainIndexRow`: a row for a workspace this run +// observed replaces the earlier row for that workspace instead of duplicating +// it, so re-running changes nothing. Every other row is kept verbatim. +const reprocessed = new Set(newRows.map(row => row.workspacePath)) +const survivors = keptRows.filter(row => !reprocessed.has(canonicalWorkspace(row.workspacePath))) +const next = { schema: 1, rows: [...survivors, ...newRows] } +validateRows(next.rows) +console.log(`kept rows = ${survivors.length} (of ${keptRows.length} non-legacy)`) + +if (!apply) { + console.log('\ndry run: nothing written. Re-run with --apply to write.') + process.exit(0) +} + +// ── write ─────────────────────────────────────────────────────────────────── +const stamp = new Date().toISOString().replace(/[:.]/g, '-') +const backup = join(HOME, 'brain', `index-rows.backup-${stamp}.json`) +copyFileSync(ROWS, backup) +console.log(`\nbackup = ${backup}`) + +mkdirSync(join(HOME, 'brain'), { recursive: true }) +const prov = existsSync(PROVENANCE) ? readJson(PROVENANCE) : { schema: 1, retired: [] } +if (prov.schema !== 1 || !Array.isArray(prov.retired)) throw new Error(`unexpected provenance shape at ${PROVENANCE}`) +for (const row of legacyRows) { + // Idempotent: re-running must not append the same retirement twice. + if (prov.retired.some(entry => entry.gitNexusIndexId === row.gitNexusIndexId)) continue + prov.retired.push({ + ...row, + retiredAt: Date.now(), + retiredBy: 'FB-BRAIN-04', + reason: 'index bound to the frozen checkout C:\\PLUG-RESTORED-20260829; the canonical planet is C:\\PLUG\\plugpt\\Code', + supersededBy: newRows.map(r => r.gitNexusIndexId), + }) +} +writeFileSync(PROVENANCE, JSON.stringify(prov, null, 2) + '\n', 'utf8') + +const tmp = ROWS + '.tmp' +writeFileSync(tmp, JSON.stringify(next, null, 2) + '\n', 'utf8') +renameSync(tmp, ROWS) // atomic replace: a reader never sees a half-written file +console.log(`provenance = ${PROVENANCE} (${prov.retired.length} retired row(s))`) +console.log(`rows after = ${next.rows.length}`) +console.log(`canonical id(s) now active: ${newRows.map(row => row.gitNexusIndexId).join(' ')}`) 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 10afc46..3ca974b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "plugbrain", - "version": "0.3.0", + "version": "0.3.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "plugbrain", - "version": "0.3.0", + "version": "0.3.1", "license": "MIT", "dependencies": { "typescript": "^5.9.3" diff --git a/package.json b/package.json index a356774..9d7b2d2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "plugbrain", - "version": "0.3.0", + "version": "0.3.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.", @@ -30,6 +30,7 @@ }, "scripts": { "test": "node --experimental-strip-types --test test/*.test.ts desktop/*.test.cjs ui/src/lib/*.test.ts ui/src/lib/*.test.js", + "test:budget": "node --experimental-strip-types --test --test-concurrency=1 test/flake-load-budget.test.ts test/board-reads-budget.test.ts test/no-hang-during-index.test.ts", "index": "node --experimental-strip-types src/cli.ts index", "serve": "node --experimental-strip-types src/cli.ts serve", "status": "node --experimental-strip-types src/cli.ts status", diff --git a/review/AP-01-ERGEBNIS.md b/review/AP-01-ERGEBNIS.md new file mode 100644 index 0000000..0a33303 --- /dev/null +++ b/review/AP-01-ERGEBNIS.md @@ -0,0 +1,33 @@ +# AP-01 Ergebnis: gemeinsamer Gemini- und NVIDIA-Client + +- Task-ID: `task-01febd1f-20d` +- Agent: `cx05` +- Modell: `gpt-6-luna` +- Branch: `feat/ap-01-llm-client-20260927` +- Geprüfte Revision: `46c3c1bab89f1ddc8be5031a029c6396df717810` +- Basis: `integration/v0.4.0` (`6d2f04a`) +- Urteil: `DONE` + +## Ergebnis + +Implementiert ist ein gemeinsamer OpenAI-kompatibler Client für NVIDIA und Gemini. Provider-Konfiguration speichert nur Umgebungsvariablennamen. Der Standardresolver akzeptiert mehrere konfigurierte Schlüssel erst, wenn ein Resolver aus QUOTA-01 eingesetzt wird; ohne den Pool verweigert er die Mehrfachschlüssel-Konfiguration, sodass keine zweite Quota-Autorität entsteht. 429 und Retry-After werden mit begrenztem Backoff behandelt; Auth- und Quota-Fehler bleiben getrennt. Token- und Kostenzählung geben keine Credentials aus. Provider-Endpunkte müssen HTTPS verwenden, damit Bearer-Credentials nicht über HTTP übertragen werden. + +Es gab bereits einen Implementierungscommit `ea4cbb6` auf dem Aufgabenbranch. Das Review fand darin einen HIGH-Befund wegen zugelassener HTTP-Endpunkte. Commit `46c3c1b` behebt ihn durch HTTPS-only-Validierung und einen Regressionstest. + +Der vorhandene Checkout lag unter `Code/PlugBrain-Core/Code/PlugBrain-Core--ap01` (nicht am im Brief erwarteten `Code/PlugBrain-Core--ap01`). Er befand sich bereits auf dem richtigen Branch, war sauber und basierte auf `integration/v0.4.0`; ich habe ihn verwendet und keinen weiteren Worktree angelegt. + +## Review + +Unabhängiger `code-reviewer`: `PASS` für Commit `46c3c1bab89f1ddc8be5031a029c6396df717810` gegen Basis `6d2f04a`. Die zuvor gemeldete HTTP-Bearer-Key-Übertragung ist behoben; keine weiteren Befunde im geprüften Umfang. + +## Tests und Befehle + +| Befehl | Ergebnis | +| --- | --- | +| `plugbrain swarm admit test` | Exit 0 | +| `node --experimental-strip-types --test test/llm-client.test.ts` (nach Ergänzung des HTTP-Regressionstests, vor Fix) | Exit 1; 7 bestanden, 1 erwartungsgemäß fehlgeschlagen | +| `node --experimental-strip-types --test test/llm-client.test.ts` (final) | Exit 0; 8 bestanden, 0 fehlgeschlagen | +| `npm run typecheck` (final) | Exit 0 | +| `git diff --check` | Exit 0 | + +Tests verwenden Fake-HTTP/Response-Objekte. Es wurden keine echten Provideraufrufe oder echten Zugangsdaten verwendet. Keine Codex-/Claude-Einstellungen, keine Produktionsdaten und kein Live-Brain-Home wurden geändert. diff --git a/review/AP-02-ERGEBNIS.md b/review/AP-02-ERGEBNIS.md new file mode 100644 index 0000000..1dc3506 --- /dev/null +++ b/review/AP-02-ERGEBNIS.md @@ -0,0 +1,26 @@ +# AP-02 Ergebnis + +- Task-ID: `task-b547b9e9-49b` +- Agent: `cx09` +- Modell: `gpt-6-luna` +- Basisrevision: `6d2f04a` (`integration/v0.4.0`) +- Code-Revision: `86bd8ee62f7c64775c27fe1d53df085467f86aed` (`feat/ap-02-plan-decompose-20260927`) +- Urteil: `DONE` + +## Umfang + +Umsetzung des Master-Ledger-Zerlegers mit expliziter Kategorie-Allowlist, `waiting-for-lead` bei fehlenden Voraussetzungen, bestehendem Queue-Register, nachvollziehbarem Quellenhash und idempotenter Anwendung. + +## Implementierung + +- `plugbrain plan decompose <gate-or-requirement> [--apply] [--json]` liest das aktuelle Ledger und gibt eine Spezifikation mit Kategorie, Brief, Abnahme, Reviewer-Rolle, Abhängigkeiten, Belegreferenzen und SHA-256 aus. +- Automatische Queue-Anlage ist auf `bugfix`, `documentation`, `maintenance` und `tests` begrenzt. Unbekannte Kategorien oder fehlende Pflichtfelder/Belege bleiben `waiting-for-lead`; dafür erfolgt keine Queue-Mutation. Abhängigkeiten müssen auf bereits angelegte Zerlegungsaufgaben zeigen. +- Queue-Spezifikationen tragen einen eindeutigen, quellgebundenen Schlüssel; identische Wiederholung gibt dieselbe Queue-Aufgabe zurück. +- Der Worktree liegt tatsächlich unter `C:\PLUG\plugpt\Code\PlugBrain-Core\Code\PlugBrain-Core--ap02`, weil Git den relativen `worktree add`-Pfad vom Repository-Root aus auflöste. Er wurde nicht verschoben oder gelöscht. Der Branch und die Basisrevision entsprechen dem Brief. + +## Tests und Prüfungen + +- `node --experimental-strip-types --test test/plan-decompose.test.ts` → Exit 0; 5 bestanden, 0 fehlgeschlagen. +- `node node_modules/typescript/bin/tsc --noEmit` → Exit 0. +- `git diff --check` → Exit 0. +- Queue-Abhängigkeitstest nutzt isolierte Fixture-Datenbank; keine Live-Brain-Daten wurden geändert. 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..835ba9e --- /dev/null +++ b/src/coord/delivery-evidence.ts @@ -0,0 +1,58 @@ +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 { + // Native resolution also expands Windows short names before containment checks. + 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..8bcc584 --- /dev/null +++ b/src/coord/reaper.ts @@ -0,0 +1,287 @@ +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 }) +} + +/** Mark registry paths that no longer exist and detach stale worker bindings. */ +export function synchronizeMissingWorktrees(db: DatabaseSync, workspaceId: string, apply = true): 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 now = new Date().toISOString() + 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 quarantineRoot = resolve('C:/PLUG/_quarantaene') + let quarantinedAt: string | null = null + if (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/) + const leaf = basename(row.path).toLowerCase() + quarantinedAt = roots.find(path => basename(path).toLowerCase() === leaf) ?? null + } catch { /* quarantine lookup is informational only */ } + } + if (apply) db.prepare("UPDATE checkouts SET disk_state = 'fehlt', retired_at = ? WHERE id = ?").run(now, row.id) + 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) { + let bound: string[] = [] + try { bound = JSON.parse(agent.worktrees ?? '[]') as string[] } catch { /* malformed legacy binding */ } + const kept = bound.filter(path => normalize(path) !== normalize(row.path)) + if (apply && kept.length !== bound.length) db.prepare('UPDATE agents SET worktrees = ? WHERE id = ?').run(JSON.stringify(kept), agent.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 } + const kept: string[] = [] + for (const path of paths) { + if (existsSync(path)) { kept.push(path); continue } + if (!missing.some(row => normalize(row.path) === normalize(path))) missing.push({ path, quarantinedAt: null }) + } + if (apply && kept.length !== paths.length) db.prepare('UPDATE agents SET worktrees = ? WHERE id = ?').run(JSON.stringify(kept), agent.id) + } + 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..c24efff --- /dev/null +++ b/src/coord/supervisor.ts @@ -0,0 +1,333 @@ +/** + * 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 } { + if (process.platform !== 'win32') throw new AccessDenied('swarm run is not supported on this platform; Windows process-tree identity is required') + 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 releaseTaskForRetry(db: DatabaseSync, taskId: string, agentId: string): boolean { + db.exec('BEGIN IMMEDIATE') + try { + const result = 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) + db.exec('COMMIT') + return Number(result.changes) === 1 + } 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) + 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.` + notifyLead(db, input.workspaceId, input.agentId, `swarm run: decision required for ${task.id}`, detail) + setSupervisor(db, input.agentId, input.supervisorId, { failure, active: false }) + if (endedTurn.turn_state !== 'awaiting-commit' && queue?.state === 'claimed' && queue.claimed_by === input.agentId) { + releaseTaskForRetry(db, task.id, input.agentId) + } + return + } + if (failure === 'quota') await new Promise(resolve => setTimeout(resolve, quotaBackoffMs(output))) + } catch (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) { + notifyLead(db, input.workspaceId, input.agentId, `swarm run: could not start ${task.id}`, message) + setSupervisor(db, input.agentId, input.supervisorId, { failure: 'launch-error', active: false }) + releaseTaskForRetry(db, task.id, input.agentId) + 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..0918a01 --- /dev/null +++ b/src/coord/turn-delivery.ts @@ -0,0 +1,59 @@ +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 FROM queue_tasks WHERE id = ? AND workspace_id = ?').get(taskId, options.workspaceId) as { title: string } | undefined + if (!task) throw new AccessDenied(`unknown task: ${taskId}`) + 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 ec7b49a..d70c69c 100644 --- a/src/daemon.ts +++ b/src/daemon.ts @@ -23,9 +23,11 @@ import { watch, type FSWatcher } from 'node:fs' import { resolve } from 'node:path' import type { DatabaseSync } from 'node:sqlite' -import { indexPlanetWorkspace } from './planet.ts' +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 = resolve(filename) - const store = resolve(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..1a99cbd --- /dev/null +++ b/src/disk/scan.ts @@ -0,0 +1,254 @@ +/** 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_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_directories; DELETE FROM disk_largest_files; 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.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) + 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) + 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) + 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.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 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/hygiene/collect.ts b/src/hygiene/collect.ts index 3c9d241..f604586 100644 --- a/src/hygiene/collect.ts +++ b/src/hygiene/collect.ts @@ -23,6 +23,7 @@ import { existsSync, lstatSync, readFileSync, readdirSync, statfsSync } from 'no import { isAbsolute, join, relative, resolve } from 'node:path' import type { DatabaseSync } from 'node:sqlite' import { liveClaims, type LiveClaim } from '../projections/conflicts.ts' +import { canonicalPath } from '../planet.ts' /** Level of a finding, worst-first. `ok` is the absence of any finding. */ export type HygieneLevel = 'ok' | 'attention' | 'risk' @@ -308,7 +309,7 @@ function claimsByCheckout( const result = new Map<string, HygieneClaim[]>() for (const row of rows) { - const rel = norm(relative(workspaceRoot, resolve(row.path))) + const rel = norm(relative(canonicalPath(workspaceRoot), canonicalPath(row.path))) const dirPrefix = rel === '.' ? '' : rel const storedPrefix = norm(row.relPrefix) const byAgent = new Map<string, HygieneClaim>() @@ -375,24 +376,26 @@ export function collectHygiene( const drives = new Set<string>() for (const row of rows) { - const counts = statusCounts(row.path, timeout) - if (counts === null) unavailable.push(`git status for ${row.path}`) - const stashes = stashCount(row.path, timeout) - if (stashes === null) unavailable.push(`stashes for ${row.path}`) - const sizeMb = boundedSizeMb(row.path, sizeBudget, sizeMax) - if (sizeMb === null) unavailable.push(`sizeMb for ${row.path}`) - if (existsSync(row.path)) drives.add(resolve(row.path)) + const checkoutPath = resolve(row.path) + const physicalPath = canonicalPath(checkoutPath) + const counts = statusCounts(physicalPath, timeout) + if (counts === null) unavailable.push(`git status for ${checkoutPath}`) + const stashes = stashCount(physicalPath, timeout) + if (stashes === null) unavailable.push(`stashes for ${checkoutPath}`) + const sizeMb = boundedSizeMb(physicalPath, sizeBudget, sizeMax) + if (sizeMb === null) unavailable.push(`sizeMb for ${checkoutPath}`) + if (existsSync(physicalPath)) drives.add(physicalPath) checkouts.push({ - path: row.path, + path: checkoutPath, repo: row.repo, branch: row.branch, dirtyFiles: counts?.dirty ?? null, untrackedFiles: counts?.untracked ?? null, stashes, - unpushed: unpushedBranches(row.path, timeout), - staleDays: staleDays(row.path, timeout, now), + unpushed: unpushedBranches(physicalPath, timeout), + staleDays: staleDays(physicalPath, timeout, now), sizeMb, - orphan: isOrphan(row.path), + orphan: isOrphan(physicalPath), claims: claimsFor.get(row.id) ?? [], }) } @@ -400,7 +403,8 @@ export function collectHygiene( // One number for the whole report: the fullest volume decides, because that // is the one that stops the work first. const candidates = [...drives].map(diskFreeGb) - if (existsSync(workspace.root)) candidates.push(diskFreeGb(workspace.root)) + const workspaceRoot = canonicalPath(workspace.root) + if (existsSync(workspaceRoot)) candidates.push(diskFreeGb(workspaceRoot)) const freeSpaces = candidates.filter((gb): gb is number => gb !== null) const freeGb = freeSpaces.length === 0 ? null diff --git a/src/intel/changes.ts b/src/intel/changes.ts index 656a0c3..b5bb2e2 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 { @@ -151,7 +152,7 @@ export function detectChanges( const selected = checkoutId ? candidates.find(checkout => checkout.id === checkoutId) : checkoutPath - ? candidates.find(checkout => resolve(checkout.path).toLowerCase() === resolve(checkoutPath!).toLowerCase()) + ? candidates.find(checkout => canonicalPath(checkout.path) === canonicalPath(checkoutPath!)) : undefined if ((checkoutId || checkoutPath) && selected === undefined) return noChanges() if (selected !== undefined) { 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/machine/git-census.ts b/src/machine/git-census.ts index 5e0d18a..23dc68b 100644 --- a/src/machine/git-census.ts +++ b/src/machine/git-census.ts @@ -20,6 +20,7 @@ import { existsSync, readdirSync } from 'node:fs' import { homedir } from 'node:os' import { join, resolve } from 'node:path' import type { DatabaseSync } from 'node:sqlite' +import { canonicalPath } from '../planet.ts' import { boundedSizeMb, gitText, isOrphan, staleDays, statusCounts, stashCount, unpushedBranches, type HygieneUnpushedBranch, @@ -165,13 +166,14 @@ export function scanGitRepos( options: CensusOptions = {}, ): { repos: CensusRepo[]; complete: boolean; roots: string[] } { const now = options.now ?? new Date() - const roots = (options.roots ?? defaultCensusRoots()).map(root => resolve(root)) + const roots = (options.roots ?? defaultCensusRoots()).map(root => canonicalPath(root)) const deny = options.deny ?? [] const maxDepth = options.maxDepth ?? DEFAULTS.maxDepth const maxDirs = options.maxDirs ?? DEFAULTS.maxDirs const maxRepos = options.maxRepos ?? DEFAULTS.maxRepos const deadline = Date.now() + (options.budgetMs ?? DEFAULTS.budgetMs) const denyNames = new Set([...DENY, ...deny.map(name => name.toLowerCase())]) + const normalizedRegisteredPaths = new Set([...registeredPaths].map(norm)) const repos: CensusRepo[] = [] let complete = true @@ -194,7 +196,7 @@ export function scanGitRepos( } if (children.includes('.git')) { - repos.push(measureRepo(dir, registeredPaths.has(norm(dir)), options, now)) + repos.push(measureRepo(dir, normalizedRegisteredPaths.has(norm(dir)), options, now)) // Do not descend into a repository's working tree: its size is measured // by the bounded walk above, and the tree itself is not a place to look // for independent repositories. @@ -212,7 +214,10 @@ export function scanGitRepos( return { repos, complete, roots } } -const norm = (path: string): string => path.replace(/\\/g, '/').replace(/\/+$/, '') +const norm = (path: string): string => { + const canonical = canonicalPath(path).replace(/\\/g, '/').replace(/\/+$/, '') + return process.platform === 'win32' ? canonical.toLowerCase() : canonical +} /** Paths of every non-retired checkout the brain knows, as a comparison set. */ export function registeredCheckoutPaths(db: DatabaseSync): Set<string> { diff --git a/src/mcp/server.ts b/src/mcp/server.ts index 21f8b54..77dce12 100644 --- a/src/mcp/server.ts +++ b/src/mcp/server.ts @@ -268,6 +268,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 +299,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, ' @@ -753,9 +810,60 @@ 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 missing = coord.synchronizeMissingWorktrees(this.db, workspaceId) + const reaper = coord.reapWorktrees(this.db, workspaceId) + reaper.missing = missing + 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/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/planet.ts b/src/planet.ts index 7969b01..befcfc0 100644 --- a/src/planet.ts +++ b/src/planet.ts @@ -28,7 +28,7 @@ */ import { createHash } from 'node:crypto' import { existsSync, readFileSync, readdirSync, realpathSync, statSync, writeFileSync, type Dirent } from 'node:fs' -import { basename, isAbsolute, join, resolve } from 'node:path' +import { basename, dirname, isAbsolute, join, relative, resolve } from 'node:path' import type { DatabaseSync, SQLInputValue, SQLOutputValue } from 'node:sqlite' import { gitText } from './indexer/git.ts' import { indexWorkspace, type IndexProgress, type IndexResult } from './indexer/index.ts' @@ -43,7 +43,20 @@ import { cachedOnce, generationCache, publishedGeneration } from './store/count- */ export const canonicalPath = (path: string): string => { const absolute = resolve(path) - try { return realpathSync.native(absolute) } catch { return absolute } + let existing = absolute + const suffix: string[] = [] + while (!existsSync(existing)) { + const parent = dirname(existing) + if (parent === existing) return absolute + suffix.unshift(relative(parent, existing)) + existing = parent + } + try { + const real = realpathSync.native(existing) + return suffix.length === 0 ? real : join(real, ...suffix) + } catch { + return absolute + } } const WORKSPACE_ID_MARKER = 'plugbrain-workspace-id' @@ -178,29 +191,30 @@ export interface CheckoutFacts { * hashes every file's bytes. */ export function readCheckout(path: string): CheckoutFacts { - const abs = canonicalPath(path) - const toplevel = gitText(abs, ['rev-parse', '--show-toplevel']) - const rawCommon = gitText(abs, ['rev-parse', '--path-format=absolute', '--git-common-dir']) - ?? gitText(abs, ['rev-parse', '--git-common-dir']) + const abs = resolve(path) + const real = canonicalPath(abs) + const toplevel = gitText(real, ['rev-parse', '--show-toplevel']) + const rawCommon = gitText(real, ['rev-parse', '--path-format=absolute', '--git-common-dir']) + ?? gitText(real, ['rev-parse', '--git-common-dir']) const commonDir = rawCommon === null || rawCommon === '' ? null - : (isAbsolute(rawCommon) ? rawCommon : resolve(abs, rawCommon)) + : canonicalPath(isAbsolute(rawCommon) ? rawCommon : resolve(real, rawCommon)) - const branch = gitText(abs, ['rev-parse', '--abbrev-ref', 'HEAD']) - const head = gitText(abs, ['rev-parse', 'HEAD']) + const branch = gitText(real, ['rev-parse', '--abbrev-ref', 'HEAD']) + const head = gitText(real, ['rev-parse', 'HEAD']) // The first entry of `git worktree list` is the main worktree; its folder // name is the repository's own name, which is the one a human uses. - const worktreeList = gitText(abs, ['worktree', 'list', '--porcelain']) ?? '' + const worktreeList = gitText(real, ['worktree', 'list', '--porcelain']) ?? '' const mainWorktree = worktreeList.split('\n').find(line => line.startsWith('worktree ')) ?.slice('worktree '.length).trim() ?? toplevel - const porcelain = gitText(abs, ['status', '--porcelain']) ?? '' + const porcelain = gitText(real, ['status', '--porcelain']) ?? '' const changed = porcelain.split('\n').filter(Boolean) const untrackedCount = changed.filter(line => line.startsWith('??')).length - const diff = gitText(abs, ['diff', 'HEAD', '--no-color', '--no-ext-diff', '--unified=0']) - ?? (gitText(abs, ['diff', '--cached', '--no-color', '--unified=0']) ?? '') - + (gitText(abs, ['diff', '--no-color', '--unified=0']) ?? '') + const diff = gitText(real, ['diff', 'HEAD', '--no-color', '--no-ext-diff', '--unified=0']) + ?? (gitText(real, ['diff', '--cached', '--no-color', '--unified=0']) ?? '') + + (gitText(real, ['diff', '--no-color', '--unified=0']) ?? '') const dirtyHash = changed.length === 0 ? null : `d-${shortHash(porcelain + '\n' + diff)}` return { @@ -214,7 +228,7 @@ export function readCheckout(path: string): CheckoutFacts { dirtyCount: changed.length, untrackedCount, revision: `rev-${shortHash(`${branch ?? ''}|${head ?? ''}|${dirtyHash ?? 'clean'}`)}`, - remoteUrl: gitText(abs, ['remote', 'get-url', 'origin']), + remoteUrl: gitText(real, ['remote', 'get-url', 'origin']), } } @@ -598,7 +612,9 @@ export function registerPlanet(db: DatabaseSync, root: string, name?: string): R // root merely by still existing under Code/. ensureIndexSelectionTables(db) - const discovered = discoverCheckouts(join(absRoot, 'Code'), planetId) + // Retain the caller's spelling for display; discovery identities fold every + // path through canonicalPath, so alternate spellings still register once. + const discovered = discoverCheckouts(join(resolve(root), 'Code'), planetId) const insertRepo = db.prepare( `INSERT INTO repos (id, planet_id, name, common_dir, remote_url, created_at) diff --git a/src/projections/awareness.ts b/src/projections/awareness.ts index eb9810d..dfaf46d 100644 --- a/src/projections/awareness.ts +++ b/src/projections/awareness.ts @@ -219,14 +219,15 @@ export function buildAwarenessPack(db: DatabaseSync, request: AwarenessRequest): } // -- live claims held by OTHER tasks -------------------------------------- + const allClaims = liveClaims(db, workspaceId) const verdict = evaluateClaim(db, workspaceId, { taskId: request.taskId, ...(request.agentId === undefined ? {} : { agentId: request.agentId }), paths: intended, mode: request.mode ?? 'write', - }) + }, allClaims) - const claims = liveClaims(db, workspaceId) + const claims = allClaims .filter(claim => claim.taskId !== request.taskId) .filter(claim => intended.includes(claim.path) || intendedModules.has(moduleOf(claim.path))) .sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : a.taskId < b.taskId ? -1 : 1)) @@ -277,8 +278,8 @@ export function buildAwarenessPack(db: DatabaseSync, request: AwarenessRequest): WHERE e.workspace_id = ? AND e.resolved = 1 AND f.path IN (${holes}) `).all(workspaceId, ...intended) as unknown as Array<{ path: string }>).map(r => r.path)) - const allClaims = liveClaims(db, workspaceId).filter(claim => claim.taskId !== request.taskId) for (const c of allClaims) { + if (c.taskId === request.taskId) continue if (callerFiles.has(c.path)) { relatedMap.set(c.taskId, { taskId: c.taskId, agentId: c.agentId, diff --git a/src/projections/conflicts.ts b/src/projections/conflicts.ts index 5adf4a2..4754ede 100644 --- a/src/projections/conflicts.ts +++ b/src/projections/conflicts.ts @@ -202,8 +202,10 @@ export function evaluateClaim( db: DatabaseSync, workspaceId: string, request: ClaimRequest, + precomputedClaims?: readonly LiveClaim[], ): ConflictVerdict { - const held = liveClaims(db, workspaceId).filter(claim => claim.taskId !== request.taskId) + const held = (precomputedClaims ?? liveClaims(db, workspaceId)) + .filter(claim => claim.taskId !== request.taskId) const wanted = new Set(request.paths) const wantedModules = new Set([...wanted].map(moduleOf)) 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..a96cc5c 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, 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' @@ -85,6 +88,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 +1066,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 +1172,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 @@ -2190,6 +2254,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 +2263,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 +2279,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 +2451,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') { 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 2a315ab..0fdb8e8 100644 --- a/src/setup/workspace-from-cwd.ts +++ b/src/setup/workspace-from-cwd.ts @@ -41,7 +41,7 @@ export interface WorkspaceCandidate { * would make `C:\Proj` and `C:\proj\src` look like strangers. */ function fold(path: string): string { - let value = resolve(path).split(sep).join('/') + let value = canonicalPath(path).split(sep).join('/') while (value.length > 1 && value.endsWith('/')) value = value.slice(0, -1) return value.toLowerCase() } @@ -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. * @@ -106,30 +130,36 @@ export interface WorkspaceRecord { * changing nothing, so the decision is split from the write. */ export function planWorkspaceRoot(db: DatabaseSync, root: string, name?: string): WorkspaceRecord { - const absolute = resolve(root) - const id = workspaceIdFor(absolute) - const existing = db.prepare('SELECT id FROM workspaces WHERE root = ?').get(absolute) as - { id: string } | undefined + const absolute = canonicalPath(root) + 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 = resolve(root) + 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 } @@ -188,7 +218,7 @@ export function resolveMcpWorkspace( // than failing, and a folder is what a human is most likely to paste. const byId = rootOf(db, pinned) if (byId !== null) return { workspaceId: pinned, root: byId, source: 'env', needsIndex: false } - const absolute = resolve(pinned) + const absolute = canonicalPath(pinned) const id = workspaceIdFor(absolute) if (rootOf(db, id) !== null) { return { workspaceId: id, root: absolute, source: 'env', needsIndex: false } diff --git a/src/swarm-cli.ts b/src/swarm-cli.ts index d1ca403..be4799a 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, synchronizeMissingWorktrees, + 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,100 @@ 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 missing = synchronizeMissingWorktrees(db, workspaceId) + const reaper = reapWorktrees(db, workspaceId) + reaper.missing = missing + 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 +507,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 +663,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 +673,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 +711,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..0078515 --- /dev/null +++ b/test/api-swarm.test.ts @@ -0,0 +1,66 @@ +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 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)) + 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/board-reads-budget.test.ts b/test/board-reads-budget.test.ts index e4763a4..762c53a 100644 --- a/test/board-reads-budget.test.ts +++ b/test/board-reads-budget.test.ts @@ -20,6 +20,11 @@ import { openStore } from '../src/store/schema.ts' import { registerPlanet, setPlanetIndexSelection } from '../src/planet.ts' import { serve, type ServerHandle } from '../src/server/api.ts' +/** Timing budgets run only in `npm run test:budget`, never beside other files. */ +const BUDGET_TESTS_ENABLED + = process.env.PLUGBRAIN_BUDGET_TESTS === '1' || process.env.npm_lifecycle_event === 'test:budget' +const budgetSkip = BUDGET_TESTS_ENABLED ? false : 'runs in test:budget' + const FILE_COUNT = 3000 const SYMBOLS_PER_FILE = 4 const EDGES_PER_FILE = 12 @@ -106,7 +111,7 @@ async function buildFixture(): Promise<Fixture> { } } -test('the five Board read routes answer 200 under the time budget', async () => { +test('the five Board read routes answer 200 under the time budget', { skip: budgetSkip }, async () => { const fx = await buildFixture() try { const routes = [ @@ -140,7 +145,7 @@ test('the five Board read routes answer 200 under the time budget', async () => } }) -test('the hot Board-read queries are index-served (EXPLAIN QUERY PLAN)', async () => { +test('the hot Board-read queries are index-served (EXPLAIN QUERY PLAN)', { skip: budgetSkip }, async () => { const fx = await buildFixture() try { for (const { index, accept, sql, params } of HOT_QUERIES) { diff --git a/test/coordination.test.ts b/test/coordination.test.ts index 0a9c5d3..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' @@ -385,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() @@ -432,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', ] @@ -446,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, @@ -532,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() @@ -556,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 {} } } @@ -600,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/daemon.test.ts b/test/daemon.test.ts index 419e859..124c4f3 100644 --- a/test/daemon.test.ts +++ b/test/daemon.test.ts @@ -108,7 +108,15 @@ test('the watcher ignores exactly what the indexer never reads', () => { 'a path inside the store home is noise even when its segments look ordinary') }) -test('writing into the brain store does not schedule a re-index, a real note does', async () => { +// Known issue on macOS (CHANGELOG 0.3.1): FSEvents reports the store write as a +// change of the watched vault folder itself ("changed: vault"), so the daemon +// starts a re-index that finds nothing to do. It only matters when the store +// lives inside a watched vault; the default ~/.plugbrain does not. +const MACOS_STORE_EVENT = process.platform === 'darwin' + ? 'known issue on macOS: a store write inside the vault arrives as a change of the vault folder' + : false + +test('writing into the brain store does not schedule a re-index, a real note does', { skip: MACOS_STORE_EVENT }, async () => { const fx = fixture() const lines: string[] = [] try { diff --git a/test/delivery-evidence.test.ts b/test/delivery-evidence.test.ts new file mode 100644 index 0000000..e6accb0 --- /dev/null +++ b/test/delivery-evidence.test.ts @@ -0,0 +1,51 @@ +import { strict as assert } from 'node:assert' +import { spawnSync } from 'node:child_process' +import { createHash } from 'node:crypto' +import { mkdirSync, mkdtempSync, realpathSync, symlinkSync, writeFileSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { test } from 'node:test' +import { inspectDeliveryEvidence } from '../src/coord/delivery-evidence.ts' + +function fixture() { + const home = realpathSync.native(mkdtempSync(join(tmpdir(), 'plugbrain-delivery-evidence-'))) + const root = join(home, 'workspace-with-long-name') + const outside = join(home, 'workspace-with-long-name-outside') + mkdirSync(root) + mkdirSync(outside) + writeFileSync(join(root, 'DONE.md'), 'real evidence\n') + writeFileSync(join(outside, 'DONE.md'), 'outside evidence\n') + return { home, root, outside } +} + +test('evidence identity uses the physical workspace and rejects sibling and junction escapes', () => { + const { home, root, outside } = fixture() + const alias = join(home, 'workspace-alias') + symlinkSync(root, alias, process.platform === 'win32' ? 'junction' : 'dir') + const evidence = inspectDeliveryEvidence(alias, join(root, 'DONE.md')) + assert.equal(evidence.path, 'DONE.md') + assert.equal(evidence.sha256, createHash('sha256').update('real evidence\n').digest('hex')) + assert.throws(() => inspectDeliveryEvidence(root, join(outside, 'DONE.md')), /inside the workspace/) + const escape = join(root, 'escape') + symlinkSync(outside, escape, process.platform === 'win32' ? 'junction' : 'dir') + assert.throws(() => inspectDeliveryEvidence(root, join(escape, 'DONE.md')), /inside the workspace/) +}) + +test('Windows short and long names identify the same evidence boundary', { + skip: process.platform !== 'win32' ? 'Windows short path aliases' : false, +}, t => { + const { root, outside } = fixture() + const shortPath = spawnSync('cmd.exe', ['/d', '/c', `for %I in ("${root}") do @echo %~sI`], { + encoding: 'utf8', windowsVerbatimArguments: true, windowsHide: true, timeout: 10_000, + }) + assert.equal(shortPath.status, 0, shortPath.error?.message ?? shortPath.stderr) + const shortRoot = shortPath.stdout.trim() + if (shortRoot.toLowerCase() === root.toLowerCase()) { + t.skip('this volume does not provide short path aliases') + return + } + assert.equal(realpathSync.native(shortRoot), root) + assert.equal(inspectDeliveryEvidence(root, join(shortRoot, 'DONE.md')).path, 'DONE.md') + assert.equal(inspectDeliveryEvidence(shortRoot, join(root, 'DONE.md')).path, 'DONE.md') + assert.throws(() => inspectDeliveryEvidence(shortRoot, join(outside, 'DONE.md')), /inside the workspace/) +}) diff --git a/test/disk.test.ts b/test/disk.test.ts new file mode 100644 index 0000000..b204ced --- /dev/null +++ b/test/disk.test.ts @@ -0,0 +1,144 @@ +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, 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('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..fee288c --- /dev/null +++ b/test/fixtures/fake-worker.mjs @@ -0,0 +1,88 @@ +#!/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 } }) + 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/fixtures/legacy-schema-1dc5638.ts.txt b/test/fixtures/legacy-schema-1dc5638.ts.txt new file mode 100644 index 0000000..f0e626b --- /dev/null +++ b/test/fixtures/legacy-schema-1dc5638.ts.txt @@ -0,0 +1,726 @@ +/** + * PlugBrain store — the runtime's single source of truth about workspaces. + * + * Agents never touch the raw filesystem: every read, write and search goes + * through this store, which is why attribution is a first-class column here + * rather than a log bolted on afterwards. If a file exists in a PlugBrain + * workspace, this database knows what it contains, what it references, and + * which agent last touched it. + * + * SQLite via node:sqlite (Node >= 22 built-in). WAL so the indexer can keep + * writing while the UI and the agent API read concurrently. + */ +import { DatabaseSync } from 'node:sqlite' +import { existsSync, mkdirSync } from 'node:fs' +import { dirname } from 'node:path' + +/** Node kinds the indexer can produce. Kept narrow on purpose — a kind that + * nothing emits is a lie in the schema. */ +export const SYMBOL_KINDS = [ + 'file', 'class', 'interface', 'function', 'method', 'type_alias', + 'enum', 'enum_member', 'constant', 'variable', 'property', 'import', +] as const +export type SymbolKind = (typeof SYMBOL_KINDS)[number] + +/** Edge kinds. `calls` and `references` are what make this a graph rather + * than a file listing. */ +export const EDGE_KINDS = [ + 'defines', 'imports', 'calls', 'references', 'extends', 'implements', 'contains', +] as const +export type EdgeKind = (typeof EDGE_KINDS)[number] + +/** What an agent did to a file. Reads are recorded too: knowing which agent + * even LOOKED at a file is half of understanding a swarm's behaviour. */ +export const ACTIONS = ['read', 'write', 'create', 'delete', 'search'] as const +export type Action = (typeof ACTIONS)[number] + +const SCHEMA = ` +PRAGMA journal_mode = WAL; +PRAGMA foreign_keys = ON; +-- The server, the daemon, the CLI and every agent share this database. Without +-- a busy timeout a writer that arrives while another holds the lock fails +-- instantly with SQLITE_BUSY instead of waiting a moment, which showed up as +-- "database is locked" the first time a re-index raced the daemon. +PRAGMA busy_timeout = 15000; + +-- A workspace is a folder that has been registered. Nothing outside a +-- registered workspace is reachable through this store, by design. +CREATE TABLE IF NOT EXISTS workspaces ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + root TEXT NOT NULL UNIQUE, + created_at TEXT NOT NULL, + indexed_at TEXT +); + +CREATE TABLE IF NOT EXISTS files ( + id INTEGER PRIMARY KEY, + workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE, + path TEXT NOT NULL, -- workspace-relative, forward slashes + -- Which repository and which CHECKOUT of it this file belongs to. In a + -- planet the same path shape exists many times over (nine checkouts of + -- PlugHarness on one disk), so "which repo" cannot be inferred from the + -- path alone and has to be a column. Deliberately not a foreign key: a + -- retired checkout must not cascade away the graph it produced. + repo_id TEXT, + checkout_id TEXT, + ext TEXT NOT NULL, + lang TEXT, + size INTEGER NOT NULL, + mtime TEXT NOT NULL, + hash TEXT, -- content hash, so re-index can skip + -- Which extractor version produced this row. A file whose bytes are + -- unchanged must still be parsed again when the extractor changed, or every + -- table the new version adds stays empty for the whole workspace. + parse_version INTEGER NOT NULL DEFAULT 0, + -- Every accepted source file is either semantically parsed or retained as an + -- honest inventory row. The reason is required when it was not parsed. + processing_status TEXT NOT NULL DEFAULT 'inventoried', + processing_reason TEXT, + loc INTEGER NOT NULL DEFAULT 0, + indexed_at TEXT, + -- The generation that last wrote this file row. + generation INTEGER NOT NULL DEFAULT 0, + -- The generation this file row was FIRST created. Distinct from 'generation' + -- (the last generation that wrote the row) so /api/changes can report + -- added vs modified honestly: a file is "added" when created_generation + -- == generation, "modified" when created_generation < generation. + created_generation INTEGER NOT NULL DEFAULT 0, + UNIQUE (workspace_id, path) +); +CREATE INDEX IF NOT EXISTS idx_files_ws ON files(workspace_id); +-- Every path lookup and the activity joins (graphOf's touched/readerRows, +-- symbolsOf, the note roots) probe files by (workspace, path); without this +-- index each one degraded into a scan of the workspace's files on a real +-- Planet. +CREATE INDEX IF NOT EXISTS idx_files_ws_path ON files(workspace_id, path); +-- The Planet file route first narrows to the chosen checkouts and then emits a +-- path-ordered page. This composite keeps that common selection from sorting +-- a whole Planet in a temporary B-tree on every poll. +CREATE INDEX IF NOT EXISTS idx_files_ws_checkout_path ON files(workspace_id, checkout_id, path); +-- The stale count filters files by indexed_at IS NULL; the partial index +-- keeps it a scan of only the unindexed rows, which stay a small set. +CREATE INDEX IF NOT EXISTS idx_files_ws_unindexed ON files(workspace_id) WHERE indexed_at IS NULL; +-- idx_files_ws_generation, idx_files_ws_created_gen, idx_files_repo and +-- idx_files_checkout are created in migrateAddedIndexes, AFTER the columns +-- they index are guaranteed to exist. Creating them here looks right and is +-- fatal for every database that predates them: on an old file table +-- CREATE INDEX IF NOT EXISTS ... (generation) fails with "no such column" +-- and the whole store refuses to open. + +CREATE TABLE IF NOT EXISTS symbols ( + id INTEGER PRIMARY KEY, + file_id INTEGER NOT NULL REFERENCES files(id) ON DELETE CASCADE, + name TEXT NOT NULL, + kind TEXT NOT NULL, + line INTEGER NOT NULL, + end_line INTEGER, + exported INTEGER NOT NULL DEFAULT 0, + container TEXT -- enclosing class/interface, if any +); +CREATE INDEX IF NOT EXISTS idx_symbols_file ON symbols(file_id); +CREATE INDEX IF NOT EXISTS idx_symbols_name ON symbols(name); +CREATE INDEX IF NOT EXISTS idx_symbols_kind ON symbols(kind); + +-- Edges reference symbols by id where resolved, and carry the raw target +-- text when they could not be resolved. An unresolved edge is kept and +-- marked, never silently dropped -- "we could not resolve this import" is +-- information an agent needs. +CREATE TABLE IF NOT EXISTS edges ( + id INTEGER PRIMARY KEY, + workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE, + kind TEXT NOT NULL, + src_symbol INTEGER REFERENCES symbols(id) ON DELETE CASCADE, + src_file INTEGER REFERENCES files(id) ON DELETE CASCADE, + dst_symbol INTEGER REFERENCES symbols(id) ON DELETE CASCADE, + dst_file INTEGER REFERENCES files(id) ON DELETE CASCADE, + raw_target TEXT, + resolved INTEGER NOT NULL DEFAULT 0, + line INTEGER +); +CREATE INDEX IF NOT EXISTS idx_edges_ws ON edges(workspace_id); +CREATE INDEX IF NOT EXISTS idx_edges_src ON edges(src_symbol); +CREATE INDEX IF NOT EXISTS idx_edges_dst ON edges(dst_symbol); +CREATE INDEX IF NOT EXISTS idx_edges_kind ON edges(kind); +-- The by-file indexes. idx_edges_src is about SYMBOLS, and a file's edges are +-- deleted and rebuilt by FILE — on a planet with 11 million edges that lookup +-- had no index at all and every refreshed file cost a full table scan: measured +-- 1 468 ms for one COUNT, and saving a single note took 5 seconds because of it. +-- Incremental runs pay the same cost once per 200-file chunk. +-- +-- They lead with the FILE, not the workspace. A foreign-key cascade looks its +-- children up by the key alone (edges.src_file = ?), and an index that leads +-- with workspace_id cannot answer that: every deleted file row paid two full +-- scans of the edge table, 700 ms each on the live planet, so deselecting the +-- old checkouts would have taken days. (src_file, workspace_id) serves the +-- cascade and every "workspace_id = ? AND src_file = ?" query alike. +CREATE INDEX IF NOT EXISTS idx_edges_src_file ON edges(src_file, workspace_id); +CREATE INDEX IF NOT EXISTS idx_edges_dst_file ON edges(dst_file, workspace_id); + +-- Every agent that has ever touched a workspace, and the colour the views +-- paint its files in. The colour is assigned once and never reused, so a +-- file's colour identifies its author across all three renderings. +-- A planet is a workspace plus the structure of the repositories inside it. +-- One planet is ONE brain: the contract forbids a second brain per worktree, +-- so every checkout of every repo of a planet lands in this one store and is +-- told apart by id rather than by which database it was written to. +CREATE TABLE IF NOT EXISTS planets ( + id TEXT PRIMARY KEY, + workspace_id TEXT NOT NULL UNIQUE REFERENCES workspaces(id) ON DELETE CASCADE, + name TEXT NOT NULL, + root TEXT NOT NULL UNIQUE, + created_at TEXT NOT NULL +); + +-- One repository. Identity is the git COMMON directory, which is what makes +-- nine linked worktrees of PlugHarness one repo instead of nine. +CREATE TABLE IF NOT EXISTS repos ( + id TEXT PRIMARY KEY, + planet_id TEXT NOT NULL REFERENCES planets(id) ON DELETE CASCADE, + name TEXT NOT NULL, + common_dir TEXT, + remote_url TEXT, + created_at TEXT NOT NULL, + UNIQUE (planet_id, name) +); +CREATE INDEX IF NOT EXISTS idx_repos_planet ON repos(planet_id); + +-- One checkout (a main worktree or a linked worktree) with its revision +-- vector: branch + HEAD + a hash of the uncommitted patch. Two checkouts of +-- the same repo on different branches are two rows, never one. +CREATE TABLE IF NOT EXISTS checkouts ( + id TEXT PRIMARY KEY, + planet_id TEXT NOT NULL REFERENCES planets(id) ON DELETE CASCADE, + repo_id TEXT NOT NULL REFERENCES repos(id) ON DELETE CASCADE, + name TEXT NOT NULL, + path TEXT NOT NULL UNIQUE, + rel_prefix TEXT NOT NULL, -- planet-relative prefix, e.g. 'Code/PlugHarness' + branch TEXT, + head TEXT, + dirty_hash TEXT, -- null when the tree is clean + dirty_count INTEGER NOT NULL DEFAULT 0, + -- The Planet inventory distinguishes tracked changes from untracked files + -- once, during discovery. Read routes must not spawn git status for every + -- selected checkout on every poll. + untracked_count INTEGER NOT NULL DEFAULT 0, + revision TEXT, -- branch|head|dirty folded into one hash + is_primary INTEGER NOT NULL DEFAULT 0, + seen_at TEXT NOT NULL, + -- A checkout that disappeared from disk keeps its row and its history, but + -- stops being an index root; its files then tombstone on the next pass. + retired_at TEXT +); +CREATE INDEX IF NOT EXISTS idx_checkouts_planet ON checkouts(planet_id); +CREATE INDEX IF NOT EXISTS idx_checkouts_repo ON checkouts(repo_id); + +-- Which parts of a planet are written knowledge rather than code. Notes are a +-- first-class scope, not "the markdown that happened to be lying around": the +-- Obsidian replacement has to know which folders it owns, or a note link and a +-- source import become the same kind of edge. +CREATE TABLE IF NOT EXISTS note_roots ( + planet_id TEXT NOT NULL REFERENCES planets(id) ON DELETE CASCADE, + rel_path TEXT NOT NULL, -- planet-relative: 'Master', '00 Übersicht.md' + kind TEXT NOT NULL, -- 'folder' | 'file' + added_at TEXT NOT NULL, + PRIMARY KEY (planet_id, rel_path) +); + +-- Frontmatter as queryable facts. One row per VALUE, so a list property is a set +-- of rows and a query like typ=gate UND stand=offen never parses a blob. +CREATE TABLE IF NOT EXISTS note_properties ( + id INTEGER PRIMARY KEY, + workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE, + file_id INTEGER NOT NULL REFERENCES files(id) ON DELETE CASCADE, + key TEXT NOT NULL, + value TEXT NOT NULL, -- normalised: unquoted, trimmed + raw TEXT NOT NULL, -- as written, for display + ordinal INTEGER NOT NULL DEFAULT 0, + is_link INTEGER NOT NULL DEFAULT 0, + line INTEGER NOT NULL DEFAULT 0 +); +CREATE INDEX IF NOT EXISTS idx_note_props_file ON note_properties(file_id); +CREATE INDEX IF NOT EXISTS idx_note_props_kv ON note_properties(workspace_id, key, value); + +-- Wiki links, with the alias and the section, so a note view can render an +-- aliased link as its alias, and a backlink list can answer "who points here". +-- Kept beside the edges table rather than instead of it: edges is the graph that +-- Atlas and the impact analysis read, this is the note-level projection that is +-- allowed to carry display details the graph has no use for. +CREATE TABLE IF NOT EXISTS note_links ( + id INTEGER PRIMARY KEY, + workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE, + file_id INTEGER NOT NULL REFERENCES files(id) ON DELETE CASCADE, + target TEXT NOT NULL, + alias TEXT, + section TEXT, + embed INTEGER NOT NULL DEFAULT 0, + source TEXT NOT NULL DEFAULT 'body', -- body | property + status TEXT NOT NULL DEFAULT 'missing', -- resolved | ambiguous | missing | self + dst_file INTEGER REFERENCES files(id) ON DELETE SET NULL, + line INTEGER NOT NULL DEFAULT 0 +); +CREATE INDEX IF NOT EXISTS idx_note_links_file ON note_links(file_id); +CREATE INDEX IF NOT EXISTS idx_note_links_dst ON note_links(dst_file); +CREATE INDEX IF NOT EXISTS idx_note_links_target ON note_links(workspace_id, target); + +-- Tags, from both places a vault writes them: the frontmatter tags list and a +-- #tag in the prose. They live in their own table rather than being read back +-- out of note_properties because a prose tag is not a property, and a tag filter +-- that only saw half of a note's tags would quietly under-report. +CREATE TABLE IF NOT EXISTS note_tags ( + id INTEGER PRIMARY KEY, + workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE, + file_id INTEGER NOT NULL REFERENCES files(id) ON DELETE CASCADE, + tag TEXT NOT NULL, -- lowercased, no leading '#' + source TEXT NOT NULL, -- property | body + UNIQUE (file_id, tag) +); +CREATE INDEX IF NOT EXISTS idx_note_tags_kv ON note_tags(workspace_id, tag); + +-- The PROSE of a note, searchable. +-- +-- search_rows indexes names and paths: file basenames and headings. That is +-- enough to find a document you can already name, and useless for the question +-- a vault is actually asked -- "where did we write down that the gateway owner +-- moved?" -- because the answer is a sentence in the middle of a note, not a +-- title. So the body is indexed as well, as its own rows owned by their own +-- table: same external-content shape as search_rows, and for the same +-- reason, because a WHERE clause against an fts5 table does not filter the way +-- it does against an ordinary one and deletes stop being real. +CREATE TABLE IF NOT EXISTS note_body ( + id INTEGER PRIMARY KEY, + workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE, + file_id INTEGER NOT NULL REFERENCES files(id) ON DELETE CASCADE, + path TEXT NOT NULL, + text TEXT NOT NULL +); +CREATE UNIQUE INDEX IF NOT EXISTS idx_note_body_file ON note_body(file_id); +CREATE INDEX IF NOT EXISTS idx_note_body_ws ON note_body(workspace_id); + +CREATE VIRTUAL TABLE IF NOT EXISTS note_search USING fts5( + path, text, + content = 'note_body', content_rowid = 'id', + tokenize = 'unicode61' +); + +CREATE TRIGGER IF NOT EXISTS note_body_ai AFTER INSERT ON note_body BEGIN + INSERT INTO note_search(rowid, path, text) VALUES (new.id, new.path, new.text); +END; +CREATE TRIGGER IF NOT EXISTS note_body_ad AFTER DELETE ON note_body BEGIN + INSERT INTO note_search(note_search, rowid, path, text) + VALUES ('delete', old.id, old.path, old.text); +END; +CREATE TRIGGER IF NOT EXISTS note_body_au AFTER UPDATE ON note_body BEGIN + INSERT INTO note_search(note_search, rowid, path, text) + VALUES ('delete', old.id, old.path, old.text); + INSERT INTO note_search(rowid, path, text) VALUES (new.id, new.path, new.text); +END; + +CREATE TABLE IF NOT EXISTS agents ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + color TEXT NOT NULL, + hue INTEGER NOT NULL, + first_seen TEXT NOT NULL, + last_seen TEXT NOT NULL +); + +-- The attribution ledger: who did what to which file, when. This is append +-- only; it is the evidence trail behind every colour on screen. +CREATE TABLE IF NOT EXISTS activity ( + id INTEGER PRIMARY KEY, + workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE, + file_id INTEGER REFERENCES files(id) ON DELETE SET NULL, + path TEXT NOT NULL, + agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL, + action TEXT NOT NULL, + detail TEXT, + at TEXT NOT NULL +); +CREATE INDEX IF NOT EXISTS idx_activity_ws ON activity(workspace_id, id DESC); +CREATE INDEX IF NOT EXISTS idx_activity_file ON activity(file_id); +CREATE INDEX IF NOT EXISTS idx_activity_agent ON activity(agent_id); +-- graphOf's touched/readerRows join activity with files on (workspace, path); +-- without this index the join drove from whichever side was bigger and +-- filtered the other — files × activity on a real Planet. +CREATE INDEX IF NOT EXISTS idx_activity_ws_path ON activity(workspace_id, path); + +-- Denormalised "who owns this file right now", so the city can colour 15k +-- buildings without a join per building. +CREATE TABLE IF NOT EXISTS file_owner ( + file_id INTEGER PRIMARY KEY REFERENCES files(id) ON DELETE CASCADE, + agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL, + action TEXT NOT NULL, + at TEXT NOT NULL +); + +-- Full-text search: how an agent finds code without grepping a disk. +-- +-- External-content FTS5 over a real table. The obvious shape -- one fts5 table +-- with workspace_id UNINDEXED -- is a trap: a plain WHERE workspace_id = ? +-- against an fts5 table does NOT filter the way it does on an ordinary table, +-- so the indexer's per-workspace DELETE silently removed nothing and every +-- re-index left the old rows behind. Owning the rows in a normal table makes +-- deletes real; triggers keep the index in step. +CREATE TABLE IF NOT EXISTS search_rows ( + id INTEGER PRIMARY KEY, + workspace_id TEXT NOT NULL, + name TEXT NOT NULL, + path TEXT NOT NULL, + kind TEXT NOT NULL, + symbol_id INTEGER, + file_id INTEGER +); +CREATE INDEX IF NOT EXISTS idx_search_rows_ws ON search_rows(workspace_id); +-- Re-parsing one file deletes ITS rows. With only idx_search_rows_ws that delete +-- scans every row of the workspace: measured 677 ms on 3.38 million rows, paid +-- by every save and by every file of a full run. +CREATE INDEX IF NOT EXISTS idx_search_rows_ws_file ON search_rows(workspace_id, file_id); + +CREATE VIRTUAL TABLE IF NOT EXISTS search USING fts5( + name, path, kind, + content = 'search_rows', content_rowid = 'id', + tokenize = 'unicode61' +); + +CREATE TRIGGER IF NOT EXISTS search_rows_ai AFTER INSERT ON search_rows BEGIN + INSERT INTO search(rowid, name, path, kind) VALUES (new.id, new.name, new.path, new.kind); +END; +CREATE TRIGGER IF NOT EXISTS search_rows_ad AFTER DELETE ON search_rows BEGIN + INSERT INTO search(search, rowid, name, path, kind) + VALUES ('delete', old.id, old.name, old.path, old.kind); +END; +CREATE TRIGGER IF NOT EXISTS search_rows_au AFTER UPDATE ON search_rows BEGIN + INSERT INTO search(search, rowid, name, path, kind) + VALUES ('delete', old.id, old.name, old.path, old.kind); + INSERT INTO search(rowid, name, path, kind) VALUES (new.id, new.name, new.path, new.kind); +END; + +-- The index checkpoint. A generation is only ever incremented by a COMMIT that +-- completed a whole build, so generation names the last COMPLETE graph a +-- reader can see. Restart reads this row, compares git_head and the per-file +-- hashes in the files table, and processes only the missing delta: an unchanged +-- workspace does no parsing work at all. +CREATE TABLE IF NOT EXISTS workspace_index_state ( + workspace_id TEXT PRIMARY KEY REFERENCES workspaces(id) ON DELETE CASCADE, + generation INTEGER NOT NULL DEFAULT 0, + git_head TEXT, + last_success_at TEXT, + last_failure_at TEXT, + failure_reason TEXT, + file_count INTEGER NOT NULL DEFAULT 0, + symbol_count INTEGER NOT NULL DEFAULT 0, + edge_count INTEGER NOT NULL DEFAULT 0, + unresolved_count INTEGER NOT NULL DEFAULT 0, + ambiguous_count INTEGER NOT NULL DEFAULT 0 +); + +-- A deleted path stays known for one generation. Without this a consumer +-- cannot tell "this file was removed" from "this file never existed", and an +-- incremental pass cannot prove it already handled the deletion. +CREATE TABLE IF NOT EXISTS file_tombstones ( + workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE, + path TEXT NOT NULL, + hash TEXT, + generation INTEGER NOT NULL, + deleted_at TEXT NOT NULL, + reason TEXT NOT NULL DEFAULT 'deleted', + PRIMARY KEY (workspace_id, path) +); +CREATE INDEX IF NOT EXISTS idx_file_tombstones_ws_generation ON file_tombstones(workspace_id, generation); + +-- Parsed-but-unresolved reference facts, kept per file so an incremental pass +-- can re-resolve edges WITHOUT re-parsing the file. Re-parsing is the expensive +-- half of indexing; resolution is cheap. Separating them is what makes a +-- one-file edit cost one parse instead of a whole workspace. +CREATE TABLE IF NOT EXISTS file_refs ( + id INTEGER PRIMARY KEY, + workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE, + file_id INTEGER NOT NULL REFERENCES files(id) ON DELETE CASCADE, + kind TEXT NOT NULL, + target TEXT NOT NULL, + receiver TEXT, + from_name TEXT, + -- 'code' resolves through imports and symbol tables; 'md-link' resolves as a + -- document path; 'md-tag' attaches to a shared concept node. One table, an + -- explicit discriminator, rather than three tables that drift apart. + scope TEXT NOT NULL DEFAULT 'code', + line INTEGER NOT NULL +); +CREATE INDEX IF NOT EXISTS idx_file_refs_file ON file_refs(file_id); +CREATE INDEX IF NOT EXISTS idx_file_refs_ws ON file_refs(workspace_id); +CREATE INDEX IF NOT EXISTS idx_file_refs_target ON file_refs(workspace_id, target); +-- Re-parsing a file asks for ITS refs. With only the two single-column indexes +-- the planner scans every ref of the workspace: measured 955 ms for one note on +-- the real planet, which was most of the remaining save latency. +CREATE INDEX IF NOT EXISTS idx_file_refs_ws_file ON file_refs(workspace_id, file_id); + +CREATE TABLE IF NOT EXISTS file_imports ( + id INTEGER PRIMARY KEY, + workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE, + file_id INTEGER NOT NULL REFERENCES files(id) ON DELETE CASCADE, + specifier TEXT NOT NULL, + local_name TEXT, + imported_name TEXT, + line INTEGER NOT NULL +); +CREATE INDEX IF NOT EXISTS idx_file_imports_file ON file_imports(file_id); +CREATE INDEX IF NOT EXISTS idx_file_imports_ws ON file_imports(workspace_id); +-- Same shape as idx_file_refs_ws_file: the resolver reads the imports of the +-- files it is re-resolving, never all of them. +CREATE INDEX IF NOT EXISTS idx_file_imports_ws_file ON file_imports(workspace_id, file_id); + +-- Ownership keyed by PATH as well as file id. file_owner.file_id cascades away +-- whenever a file row is deleted; this table is what makes attribution survive +-- a delete-and-reinsert rebuild, and it is the restore source used inside the +-- same transaction. +CREATE TABLE IF NOT EXISTS path_owner ( + workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE, + path TEXT NOT NULL, + agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL, + action TEXT NOT NULL, + at TEXT NOT NULL, + PRIMARY KEY (workspace_id, path) +); + +-- Facts about the store itself. 'writer_generation' is the newest writer +-- generation that opened it for writing; see WRITER_GENERATION in schema.ts. +CREATE TABLE IF NOT EXISTS store_meta ( + key TEXT PRIMARY KEY, + value TEXT NOT NULL +); +` + +interface SqlDefinition { sql: string | null } + +/** + * Upgrade the original all-in-one FTS5 table before the current schema is + * applied. `CREATE VIRTUAL TABLE IF NOT EXISTS` cannot change an existing + * virtual-table definition, so treating schema creation as migration would + * silently leave the old layout active while the new queries join against an + * empty `search_rows` table. + */ +function migrateLegacySearch(db: DatabaseSync): void { + const definition = db.prepare( + "SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'search'" + ).get() as SqlDefinition | undefined + if (!definition?.sql || /content\s*=\s*['\"]search_rows['\"]/i.test(definition.sql)) return + + const hasSearchRows = Boolean(db.prepare( + "SELECT 1 present FROM sqlite_master WHERE type = 'table' AND name = 'search_rows'" + ).get()) + + db.exec('BEGIN IMMEDIATE') + try { + db.exec(` + DROP TABLE IF EXISTS search_rows_migrating; + CREATE TABLE search_rows_migrating ( + id INTEGER PRIMARY KEY, + workspace_id TEXT NOT NULL, + name TEXT NOT NULL, + path TEXT NOT NULL, + kind TEXT NOT NULL, + symbol_id INTEGER, + file_id INTEGER + ); + `) + if (hasSearchRows) { + db.exec(` + INSERT INTO search_rows_migrating (workspace_id, name, path, kind, symbol_id, file_id) + SELECT workspace_id, name, path, kind, symbol_id, file_id FROM search_rows; + `) + } + db.exec(` + INSERT INTO search_rows_migrating (workspace_id, name, path, kind, symbol_id, file_id) + SELECT DISTINCT legacy.workspace_id, legacy.name, legacy.path, legacy.kind, + legacy.symbol_id, legacy.file_id + FROM search AS legacy + WHERE NOT EXISTS ( + SELECT 1 FROM search_rows_migrating AS current + WHERE current.workspace_id IS legacy.workspace_id + AND current.name IS legacy.name + AND current.path IS legacy.path + AND current.kind IS legacy.kind + AND current.symbol_id IS legacy.symbol_id + AND current.file_id IS legacy.file_id + ); + + DROP TRIGGER IF EXISTS search_rows_ai; + DROP TRIGGER IF EXISTS search_rows_ad; + DROP TRIGGER IF EXISTS search_rows_au; + DROP TABLE search; + DROP TABLE IF EXISTS search_rows; + ALTER TABLE search_rows_migrating RENAME TO search_rows; + CREATE INDEX idx_search_rows_ws ON search_rows(workspace_id); + CREATE VIRTUAL TABLE search USING fts5( + name, path, kind, + content = 'search_rows', content_rowid = 'id', + tokenize = 'unicode61' + ); + CREATE TRIGGER search_rows_ai AFTER INSERT ON search_rows BEGIN + INSERT INTO search(rowid, name, path, kind) VALUES (new.id, new.name, new.path, new.kind); + END; + CREATE TRIGGER search_rows_ad AFTER DELETE ON search_rows BEGIN + INSERT INTO search(search, rowid, name, path, kind) + VALUES ('delete', old.id, old.name, old.path, old.kind); + END; + CREATE TRIGGER search_rows_au AFTER UPDATE ON search_rows BEGIN + INSERT INTO search(search, rowid, name, path, kind) + VALUES ('delete', old.id, old.name, old.path, old.kind); + INSERT INTO search(rowid, name, path, kind) VALUES (new.id, new.name, new.path, new.kind); + END; + INSERT INTO search(search) VALUES ('rebuild'); + `) + db.exec('COMMIT') + } catch (error) { + db.exec('ROLLBACK') + throw error + } +} + +interface ColumnRow { name: string } + +/** + * Add a column that a later schema version introduced. `CREATE TABLE IF NOT + * EXISTS` never alters an existing table, so a database created by an older + * PlugBrain keeps the old column set until something explicitly widens it. + * Adding a nullable/defaulted column is the one schema change SQLite can make + * in place without rewriting the table. + */ +function ensureColumn(db: DatabaseSync, table: string, column: string, definition: string): void { + const columns = db.prepare(`PRAGMA table_info(${table})`).all() as unknown as ColumnRow[] + if (columns.length === 0) return // table not present yet + if (columns.some(row => row.name === column)) return // already widened + db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`) +} + +/** + * Widen tables that existed before this version. Runs after the CREATE + * statements so a fresh database is already correct and these are no-ops. + */ +function migrateAddedColumns(db: DatabaseSync): void { + // `ambiguous` records that resolution found MORE THAN ONE plausible target + // and therefore refused to pick. It is deliberately distinct from + // `resolved = 0`, which means "no target found at all"; conflating the two + // is how a first-match resolver hides its guesses. + ensureColumn(db, 'edges', 'ambiguous', 'INTEGER NOT NULL DEFAULT 0') + ensureColumn(db, 'edges', 'candidates', 'INTEGER NOT NULL DEFAULT 0') + // The generation that last wrote this file row, so a reader can tell how + // current a row is without re-hashing the file. + ensureColumn(db, 'files', 'generation', 'INTEGER NOT NULL DEFAULT 0') + // The generation this file row was FIRST created. Distinct from `generation` + // so /api/changes can report added vs modified honestly. + ensureColumn(db, 'files', 'created_generation', 'INTEGER NOT NULL DEFAULT 0') + // Planet attribution. NULL on every workspace that is not a planet, which is + // exactly what "this file belongs to no repository" means. + ensureColumn(db, 'files', 'repo_id', 'TEXT') + ensureColumn(db, 'files', 'checkout_id', 'TEXT') + // Which extractor version produced this row. A file whose bytes did not + // change still has to be parsed again when the EXTRACTOR changed, otherwise + // every table a new version adds stays empty for the whole existing + // workspace. Default 0 marks every row written before this column existed as + // produced by an unknown, older extractor -- which is the truth. + ensureColumn(db, 'files', 'parse_version', 'INTEGER NOT NULL DEFAULT 0') + ensureColumn(db, 'files', 'processing_status', "TEXT NOT NULL DEFAULT 'inventoried'") + ensureColumn(db, 'files', 'processing_reason', 'TEXT') + ensureColumn(db, 'checkouts', 'untracked_count', 'INTEGER NOT NULL DEFAULT 0') + migrateAddedIndexes(db) +} + +/** + * Indexes over columns that only exist after `migrateAddedColumns`. + * + * Split out because the order is load-bearing: a fresh database gets both the + * column and the index here, and an old one gets the column widened first. The + * previous arrangement — index in the schema block — opened every new database + * and no existing one. + */ +function migrateAddedIndexes(db: DatabaseSync): void { + db.exec(` + CREATE INDEX IF NOT EXISTS idx_files_repo ON files(repo_id); + CREATE INDEX IF NOT EXISTS idx_files_checkout ON files(checkout_id); + CREATE INDEX IF NOT EXISTS idx_files_ws_generation ON files(workspace_id, generation); + CREATE INDEX IF NOT EXISTS idx_files_ws_created_gen ON files(workspace_id, created_generation); + CREATE INDEX IF NOT EXISTS idx_files_ws_checkout_path ON files(workspace_id, checkout_id, path); + CREATE INDEX IF NOT EXISTS idx_file_tombstones_ws_generation ON file_tombstones(workspace_id, generation); + -- Superseded by idx_edges_src_file / idx_edges_dst_file, which lead with the + -- key a cascade looks up (see the edges block of the schema). + DROP INDEX IF EXISTS idx_edges_ws_src_file; + DROP INDEX IF EXISTS idx_edges_ws_dst_file; + `) +} + +/** + * Which builds may WRITE a store. + * + * On 23.09.2026 an installed 0.1.1 Core was still started at every sign-in + * against the live planet after 0.2.0 had changed what its rows mean (pruned + * checkouts, new edge indexes, a narrowed selection). Its next index run + * would have tombstoned 250 000 files one cascade at a time. So a store keeps + * the writer generation of the newest build that wrote it, and an older build + * opens it read-only and says why instead of writing with the old rules. + * + * The generation is not the package version. It moves only when a release + * changes what stored data means, so a patch build is never locked out of its + * own store. Builds from before the fence cannot be stopped by it; that is + * what the single installed writer is for (koordination/BRAIN-PROTOKOLL.md). + */ +export const WRITER_GENERATION = 2 + +/** The writer generation recorded in a store, or null for a new or pre-fence store. */ +export function storedWriterGeneration(file: string): number | null { + if (!existsSync(file)) return null + let probe: DatabaseSync | null = null + try { + probe = new DatabaseSync(file, { readOnly: true }) + probe.exec('PRAGMA busy_timeout = 15000;') + const table = probe.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'store_meta'").get() + if (table === undefined) return null + const row = probe.prepare("SELECT value FROM store_meta WHERE key = 'writer_generation'").get() as + { value: string } | undefined + return row === undefined ? null : Number(row.value) + } finally { + probe?.close() + } +} + +export interface OpenStoreOptions { + /** Open read-only on purpose (inspection); skips every migration. */ + readOnly?: boolean + /** The generation this build writes with; tests pass another one. */ + writerGeneration?: number +} + +/** Open (creating if needed) the PlugBrain database at `file`. */ +export function openStore(file: string, options: OpenStoreOptions = {}): DatabaseSync { + mkdirSync(dirname(file), { recursive: true }) + const generation = options.writerGeneration ?? WRITER_GENERATION + const stored = storedWriterGeneration(file) + const fenced = stored !== null && stored > generation + if (options.readOnly === true || fenced) { + if (fenced) { + process.stderr.write( + `PlugBrain: this build writes generation ${generation}, but ${file} was last written by generation ` + + `${stored}. Opened READ-ONLY; install the current PlugBrain to write.\n`) + } + const readOnly = new DatabaseSync(file, { readOnly: true }) + readOnly.exec('PRAGMA busy_timeout = 15000;') + return readOnly + } + const db = new DatabaseSync(file) + try { + db.exec('PRAGMA busy_timeout = 15000;') + migrateLegacySearch(db) + db.exec(SCHEMA) + migrateAddedColumns(db) + db.prepare( + `INSERT INTO store_meta (key, value) VALUES ('writer_generation', ?) + ON CONFLICT(key) DO UPDATE SET value = excluded.value + WHERE CAST(store_meta.value AS INTEGER) < CAST(excluded.value AS INTEGER)`) + .run(String(generation)) + return db + } catch (error) { + db.close() + throw error + } +} diff --git a/test/flake-load-budget.test.ts b/test/flake-load-budget.test.ts index c3bf393..fd54817 100644 --- a/test/flake-load-budget.test.ts +++ b/test/flake-load-budget.test.ts @@ -21,11 +21,21 @@ import { prioritiseTimingProcess, } from './helpers/stable-load-budget.ts' +/** + * A timing budget measured beside other test files is not evidence. These + * files run only in `npm run test:budget` (where npm sets the lifecycle + * event) or when PLUGBRAIN_BUDGET_TESTS=1 is exported; the parallel `npm test` + * reports them as skipped with the reason below instead of measuring noise. + */ +const BUDGET_TESTS_ENABLED + = process.env.PLUGBRAIN_BUDGET_TESTS === '1' || process.env.npm_lifecycle_event === 'test:budget' +const budgetSkip = BUDGET_TESTS_ENABLED ? false : 'runs in test:budget' + const AUTH = 'flake-load-token' const CONCURRENCY = 8 const BUDGET_MS = 500 -test('FLAKE: Awareness Runtime and FO-3/FO-4 keep their contracts under concurrent load', async (t) => { +test('FLAKE: Awareness Runtime and FO-3/FO-4 keep their contracts under concurrent load', { skip: budgetSkip }, async (t) => { const dir = mkdtempSync(join(tmpdir(), 'plugbrain-flake-load-')) const root = join(dir, 'ws') mkdirSync(join(root, 'src'), { recursive: true }) 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 d90066c..a9ac733 100644 --- a/test/gp2-brain-ui-e2e.test.ts +++ b/test/gp2-brain-ui-e2e.test.ts @@ -4,7 +4,7 @@ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' import { test } from 'node:test' -import { chromium } from 'playwright-core' +import { chromium, type Browser } from 'playwright-core' import { openStore } from '../src/store/schema.ts' import { serve } from '../src/server/api.ts' @@ -28,8 +28,12 @@ test('GP-2: a human can complete standalone knowledge work through the real Brai const authKey = 'gp2-local-token' const server = await serve({ db, dbFile, uiRoot: join(import.meta.dirname, '..', 'ui-dist'), authKey }, 0) const base = `http://127.0.0.1:${server.port}` - const browser = await chromium.launch({ headless: true }) + // Launch inside the try: when the browser is missing (a fresh CI runner), the + // finally below must still close the server and the store, or their open + // handles keep this test file alive and the whole suite never ends. + let browser: Browser | null = null try { + browser = await chromium.launch({ headless: true }) const page = await browser.newPage() await page.goto(base) assert.equal(await page.evaluate('window.__PLUGBRAIN__?.token'), authKey, 'the real served shell hands its local session to the UI') @@ -86,7 +90,7 @@ test('GP-2: a human can complete standalone knowledge work through the real Brai await page.getByRole('button', { name: 'Explorer' }).click() await page.getByText('source.ts').first().waitFor() } finally { - await browser.close() + await browser?.close() await server.close() try { db.close() } catch { /* closed by server on error paths */ } rmSync(dir, { recursive: true, force: true, maxRetries: 10 }) 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/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/no-hang-during-index.test.ts b/test/no-hang-during-index.test.ts index 1ac719d..8b3ea8c 100644 --- a/test/no-hang-during-index.test.ts +++ b/test/no-hang-during-index.test.ts @@ -24,6 +24,11 @@ import { workspaceIdFor } from '../src/planet.ts' import { removeRunState } from '../src/index/runs.ts' import { medianMs, prioritiseTimingProcess } from './helpers/stable-load-budget.ts' +/** Timing budgets run only in `npm run test:budget`, never beside other files. */ +const BUDGET_TESTS_ENABLED + = process.env.PLUGBRAIN_BUDGET_TESTS === '1' || process.env.npm_lifecycle_event === 'test:budget' +const budgetSkip = BUDGET_TESTS_ENABLED ? false : 'runs in test:budget' + /** Run states and the store of this suite live under a throwaway home. */ const home = mkdtempSync(join(tmpdir(), 'plugbrain-nohang-home-')) process.env.PLUGBRAIN_HOME = home @@ -95,7 +100,7 @@ async function waitForRun(fx: Fixture, timeoutMs = 120_000): Promise<Record<stri throw new Error('the index run did not finish in time') } -test('reads answer within 500 ms while an index run works, and say so', async (t) => { +test('reads answer within 500 ms while an index run works, and say so', { skip: budgetSkip }, async (t) => { const fx = await createFixture(3000) try { const timing: Record<string, number[]> = { @@ -185,7 +190,7 @@ test('reads answer within 500 ms while an index run works, and say so', async (t } finally { await fx.cleanup() } }) -test('a store-writing route is refused at once with the holder named', async () => { +test('a store-writing route is refused at once with the holder named', { skip: budgetSkip }, async () => { const fx = await createFixture(3000) try { const started = await post(fx, '/api/reindex', { workspace: fx.workspaceId }) @@ -225,7 +230,7 @@ test('a store-writing route is refused at once with the holder named', async () } finally { await fx.cleanup() } }) -test('a second reindex is answered with the run that exists, never a second one', async () => { +test('a second reindex is answered with the run that exists, never a second one', { skip: budgetSkip }, async () => { const fx = await createFixture(3000) try { const first = await post(fx, '/api/reindex', { 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-identity.test.ts b/test/planet-identity.test.ts index 314c841..2984d71 100644 --- a/test/planet-identity.test.ts +++ b/test/planet-identity.test.ts @@ -1,7 +1,7 @@ import { execFileSync } from 'node:child_process' import { strict as assert } from 'node:assert' import { createHash } from 'node:crypto' -import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs' +import { existsSync, mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs' import { join } from 'node:path' import { tmpdir } from 'node:os' import { test } from 'node:test' @@ -67,17 +67,17 @@ test('a pinned marker survives a folder move and worktrees share it', { skip: sk // MOVE: the folder relocates carrying its .git along. The new path hashes // differently, but the marker travels with the common dir — same identity. + // A real rename, not a copy of .git: copying raced with git's background + // housekeeping on the macOS runner (ENOENT inside .git/objects). const moved = join(dir, 'renamed-elsewhere') - mkdirSync(moved, { recursive: true }) - cpSync(join(repo, '.git'), join(moved, '.git'), { recursive: true }) - writeFileSync(join(moved, 'README.md'), 'identity fixture\n', 'utf8') + renameSync(repo, moved) assert.notEqual(workspaceIdFor(moved), workspaceIdFor(join(dir, 'unrelated-name'))) assert.equal(workspaceIdFor(moved), PINNED, 'a moved folder keeps its pinned id') // WORKTREE: a linked worktree shares the repository's common dir, so it // shares the workspace — one brain, not a second brain per worktree. const worktree = join(dir, 'side-worktree') - git(repo, ['worktree', 'add', '-b', 'side', worktree]) + git(moved, ['worktree', 'add', '-b', 'side', worktree]) assert.equal(workspaceIdFor(worktree), PINNED, 'a worktree lands on the same workspace') } finally { cleanup() 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..92896b5 --- /dev/null +++ b/test/quota-pools.test.ts @@ -0,0 +1,45 @@ +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-runner.test.ts b/test/swarm-runner.test.ts new file mode 100644 index 0000000..bbee7cc --- /dev/null +++ b/test/swarm-runner.test.ts @@ -0,0 +1,531 @@ +/** + * `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)) + +// Native ownership checks currently use Windows process-tree identity. Keep +// these end-to-end cases on that platform and verify refusal elsewhere. +const windowsRunnerOnly = { + skip: process.platform === 'win32' ? false : '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 refuses unsupported platforms before recording or starting a worker', { + skip: process.platform === 'win32' ? 'non-Windows refusal contract' : false, +}, () => { + const b = brain() + try { + const registered = b.run('swarm', 'register', 'fake-01', '--surface', 'other', '--account', 'test:fake', '--workspace', b.ws) + assert.equal(registered.code, 0, registered.err) + const profile = b.run('swarm', 'runner', 'set', 'fake-01', '--cmd', 'node', + '--args', fakeArgs(b, 'ok'), '--workspace', b.ws) + assert.equal(profile.code, 0, profile.err) + for (const mode of [[], ['--once']]) { + const result = b.run('swarm', 'run', 'fake-01', ...mode, '--workspace', b.ws, '--json') + assert.equal(result.code, 3) + assert.match(result.err, /not supported on this platform; Windows process-tree identity is required/) + } + const board = b.run('swarm', 'board', '--workspace', b.ws, '--json') + assert.equal(board.code, 0, board.err) + assert.equal((JSON.parse(board.out) as BoardJson).agents.find(row => row.id === 'fake-01')?.runner, null) + } finally { b.cleanup() } +}) + +test('swarm run starts the worker detached, keeps its log, and the board shows pid and last event', windowsRunnerOnly, 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', windowsRunnerOnly, 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', windowsRunnerOnly, 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 worker = db.prepare("SELECT pid, process_started_at FROM worker_runs WHERE agent_id = 'fake-01'").get() as + { pid: number; process_started_at: string } + assert.ok(worker?.pid && worker.process_started_at, 'the started worker has a recorded process identity') + // Do not reconcile the run before installing the failure trigger. Wait for + // the actual worker identity to exit, including its final CLI child call. + const exitDeadline = Date.now() + 90_000 + while (isOwnedProcess(worker.pid, worker.process_started_at)) { + if (Date.now() >= exitDeadline) assert.fail('the fixture worker did not exit before the reconciliation test') + await delay(100) + } + 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', windowsRunnerOnly, 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', windowsRunnerOnly, 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', windowsRunnerOnly, 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', windowsRunnerOnly, 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/trigram-backfill.test.ts b/test/trigram-backfill.test.ts index 991cd27..fb6dfd5 100644 --- a/test/trigram-backfill.test.ts +++ b/test/trigram-backfill.test.ts @@ -15,31 +15,24 @@ * first open; a second open does not rebuild again. */ import { strict as assert } from 'node:assert' -import { execFileSync } from 'node:child_process' -import { mkdtempSync, rmSync, writeFileSync } from 'node:fs' +import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' -import { join, resolve } from 'node:path' +import { join } from 'node:path' import { pathToFileURL } from 'node:url' import { test } from 'node:test' import { DatabaseSync } from 'node:sqlite' import { openStore } from '../src/store/schema.ts' -const HAS_GIT = ((): boolean => { - try { execFileSync('git', ['--version'], { stdio: 'ignore' }); return true } catch { return false } -})() - -/** The 0.2.6 schema this repo actually shipped, read from git history rather - * than reinvented: the migration has to be tested against the real bytes. */ -const OLD_SCHEMA_COMMIT = '1dc5638' -const REPO_ROOT = resolve(import.meta.dirname, '..') +/** The 0.2.6 schema this repo actually shipped (commit 1dc5638, + * src/store/schema.ts, blob f0e626b), kept byte for byte rather than + * reinvented: the migration has to be tested against the real bytes. A file + * and not `git show`, because the public repository starts with a fresh + * history that does not contain that commit. `.txt` keeps tsc away from it. */ +const LEGACY_SCHEMA = join(import.meta.dirname, 'fixtures', 'legacy-schema-1dc5638.ts.txt') /** Materialize the historical schema module into a temp dir and import it. */ async function legacySchemaModule(dir: string): Promise<{ openStore: typeof openStore }> { - const source = execFileSync('git', ['show', `${OLD_SCHEMA_COMMIT}:src/store/schema.ts`], { - encoding: 'utf8', - cwd: REPO_ROOT, - maxBuffer: 10 * 1024 * 1024, - }) + const source = readFileSync(LEGACY_SCHEMA, 'utf8') const file = join(dir, 'legacy-schema.ts') writeFileSync(file, source) const mod = await import(pathToFileURL(file).href) as { openStore: typeof openStore } @@ -49,7 +42,7 @@ async function legacySchemaModule(dir: string): Promise<{ openStore: typeof open return mod } -test('a store that predates search_trigram answers a trigram MATCH on its first open', { skip: !HAS_GIT }, async () => { +test('a store that predates search_trigram answers a trigram MATCH on its first open', async () => { const dir = mkdtempSync(join(tmpdir(), 'plugbrain-tri-')) try { const legacy = await legacySchemaModule(dir) 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..b2053ec --- /dev/null +++ b/test/worktree-reaper.test.ts @@ -0,0 +1,154 @@ +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, resolve } 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() +} + +function fixture() { + // Git reports physical worktree paths; macOS tmpdir may use a /var alias. + const dir = realpathSync.native(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 => resolve(row.path) === resolve(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 => resolve(row.path) === resolve(dirty))?.reason ?? '', /uncommitted: 1 file/) + assert.match(result.candidates.find(row => resolve(row.path) === resolve(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 => resolve(row.path) === resolve(path))?.reason ?? '', /not merged/) + assert.match(result.candidates.find(row => resolve(row.path) === resolve(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 => resolve(row.path) === resolve(path))?.reason ?? '', /active lease/) + } finally { f.cleanup() } +}) + +test('missing on-disk checkouts are marked fehlt and detached from worker bindings', () => { + 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 missing = synchronizeMissingWorktrees(f.db, f.ws) + assert.equal(missing.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, 'fehlt') + assert.equal((f.db.prepare('SELECT worktrees FROM agents WHERE id = ?').get('missing-worker') as { worktrees: string }).worktrees, '[]') + } 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-DeAXfTgC.css b/ui-dist/assets/index-9m3XitSK.css similarity index 87% rename from ui-dist/assets/index-DeAXfTgC.css rename to ui-dist/assets/index-9m3XitSK.css index 92667de..fcc98bd 100644 --- a/ui-dist/assets/index-DeAXfTgC.css +++ b/ui-dist/assets/index-9m3XitSK.css @@ -1 +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}.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}} +@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}}.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-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-B7by5_rZ.js b/ui-dist/assets/index-B7by5_rZ.js deleted file mode 100644 index 7a543f5..0000000 --- a/ui-dist/assets/index-B7by5_rZ.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()}async function rT(t,e=Fi(),n="AGY"){const i=await fetch("/api/agent/attach",{method:"POST",headers:Qn(),body:JSON.stringify({workspace:t,agentId:e,name:n})});if(!i.ok){const a=await i.json().catch(()=>null);throw new Error((a==null?void 0:a.error)??`Agent Attach HTTP ${i.status}`)}return i.json()}const _u=new Map;function ac(t,e=Fi()){const n=`${t}\0${e}`,i=_u.get(n);if(i)return i;const a=rT(t,e).then(()=>{}).catch(()=>{_u.delete(n)});return _u.set(n,a),a}function lT(){_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 oT(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 cT(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 uT(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 fT(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 dT(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 hT(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 pT(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 mT(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 gT(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 xT(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 vT(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 _T(){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 yT(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(),bT=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 ST(t){$b=t}const MT=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,ET=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 TT(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 AT(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 wT(){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?TT(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++,AT(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",CT=0,u0=1,NT=2,bu=1,RT=2,so=3,Ts=0,Yn=1,ba=2,Na=0,nl=1,f0=2,d0=3,h0=4,DT=5,Bs=100,LT=101,UT=102,OT=103,PT=104,zT=200,IT=201,kT=202,BT=203,dp=204,hp=205,FT=206,jT=207,HT=208,GT=209,VT=210,WT=211,XT=212,qT=213,YT=214,pp=0,mp=1,gp=2,vl=3,xp=4,vp=5,_p=6,yp=7,Qb=0,KT=1,ZT=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,$T=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,QT=3200,p0=0,JT=1,is="",xi="srgb",lf="srgb-linear",of="linear",Ct="srgb",gr=7680,m0=519,eA=512,tA=513,nA=514,gg=515,iA=516,aA=517,xg=518,sA=519,g0=35044,x0="300 es",ta=2e3,cf=2001;function rA(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 lA(){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 oA(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 cA={[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 uA(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 fA(){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=fA();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 dA{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 hA=0;class vg{constructor(e=null){this.isSource=!0,Object.defineProperty(this,"id",{value:hA++}),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?dA.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 pA=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:pA++}),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 mA 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 mA{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 gA 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(xA,e,vA)}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,xA=new Y(0,0,0),vA=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 _A=0;const T0=new Y,_r=new Dl,da=new Ft,Tc=new Y,Gl=new Y,yA=new Y,bA=new Dl,A0=new Y(1,0,0),w0=new Y(0,1,0),C0=new Y(0,0,1),N0={type:"added"},SA={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:_A++}),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(SA),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,yA),e}getWorldScale(e){return this.updateWorldMatrix(!0,!1),this.matrixWorld.decompose(Gl,bA,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 MA={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(MA)))}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=uA(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 EA 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 TA=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:TA++}),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 AA=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):AA.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 wA=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:wA++}),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(rA(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 CA=0;class Ll extends cr{constructor(){super(),this.isMaterial=!0,Object.defineProperty(this,"id",{value:CA++}),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 NA(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=NA(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,RA=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,RA)}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,DA=new Y,LA=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(DA.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||LA.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,UA=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=UA.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 OA 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 OA{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 PA 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 zA extends In{constructor(e=new Zn,n=new PA){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 IA 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 kA 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 BA(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 FA={clone:bl,merge:Dn};var jA=`void main() { - gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); -}`,HA=`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=jA,this.fragmentShader=HA,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=BA(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 GA extends Xn{constructor(e){super(e),this.isRawShaderMaterial=!0,this.type="RawShaderMaterial"}}class VA extends Ll{constructor(e){super(),this.isMeshDepthMaterial=!0,this.type="MeshDepthMaterial",this.depthPacking=QT,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 WA 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 XA 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 qA 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=YA(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 YA(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 KA(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 ZA=`#ifdef USE_ALPHAHASH - if ( diffuseColor.a < getAlphaHashThreshold( vPosition ) ) discard; -#endif`,$A=`#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`,QA=`#ifdef USE_ALPHAMAP - diffuseColor.a *= texture2D( alphaMap, vAlphaMapUv ).g; -#endif`,JA=`#ifdef USE_ALPHAMAP - uniform sampler2D alphaMap; -#endif`,ew=`#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`,tw=`#ifdef USE_ALPHATEST - uniform float alphaTest; -#endif`,nw=`#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`,iw=`#ifdef USE_AOMAP - uniform sampler2D aoMap; - uniform float aoMapIntensity; -#endif`,aw=`#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`,sw=`#ifdef USE_BATCHING - mat4 batchingMatrix = getBatchingMatrix( getIndirectIndex( gl_DrawID ) ); -#endif`,rw=`vec3 transformed = vec3( position ); -#ifdef USE_ALPHAHASH - vPosition = vec3( position ); -#endif`,lw=`vec3 objectNormal = vec3( normal ); -#ifdef USE_TANGENT - vec3 objectTangent = vec3( tangent.xyz ); -#endif`,ow=`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`,cw=`#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`,uw=`#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`,fw=`#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`,dw=`#if NUM_CLIPPING_PLANES > 0 - varying vec3 vClipPosition; - uniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ]; -#endif`,hw=`#if NUM_CLIPPING_PLANES > 0 - varying vec3 vClipPosition; -#endif`,pw=`#if NUM_CLIPPING_PLANES > 0 - vClipPosition = - mvPosition.xyz; -#endif`,mw=`#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA ) - diffuseColor *= vColor; -#endif`,gw=`#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA ) - varying vec4 vColor; -#endif`,xw=`#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA ) || defined( USE_INSTANCING_COLOR ) || defined( USE_BATCHING_COLOR ) - varying vec4 vColor; -#endif`,vw=`#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`,_w=`#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`,yw=`#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`,bw=`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`,Sw=`#ifdef USE_DISPLACEMENTMAP - uniform sampler2D displacementMap; - uniform float displacementScale; - uniform float displacementBias; -#endif`,Mw=`#ifdef USE_DISPLACEMENTMAP - transformed += normalize( objectNormal ) * ( texture2D( displacementMap, vDisplacementMapUv ).x * displacementScale + displacementBias ); -#endif`,Ew=`#ifdef USE_EMISSIVEMAP - vec4 emissiveColor = texture2D( emissiveMap, vEmissiveMapUv ); - #ifdef DECODE_VIDEO_TEXTURE_EMISSIVE - emissiveColor = sRGBTransferEOTF( emissiveColor ); - #endif - totalEmissiveRadiance *= emissiveColor.rgb; -#endif`,Tw=`#ifdef USE_EMISSIVEMAP - uniform sampler2D emissiveMap; -#endif`,Aw="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 ); -}`,Cw=`#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`,Nw=`#ifdef USE_ENVMAP - uniform float envMapIntensity; - uniform mat3 envMapRotation; - #ifdef ENVMAP_TYPE_CUBE - uniform samplerCube envMap; - #else - uniform sampler2D envMap; - #endif -#endif`,Rw=`#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`,Dw=`#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`,Lw=`#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`,Uw=`#ifdef USE_FOG - vFogDepth = - mvPosition.z; -#endif`,Ow=`#ifdef USE_FOG - varying float vFogDepth; -#endif`,Pw=`#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`,zw=`#ifdef USE_FOG - uniform vec3 fogColor; - varying float vFogDepth; - #ifdef FOG_EXP2 - uniform float fogDensity; - #else - uniform float fogNear; - uniform float fogFar; - #endif -#endif`,Iw=`#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 -}`,kw=`#ifdef USE_LIGHTMAP - uniform sampler2D lightMap; - uniform float lightMapIntensity; -#endif`,Bw=`LambertMaterial material; -material.diffuseColor = diffuseColor.rgb; -material.specularStrength = specularStrength;`,Fw=`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`,jw=`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>`,Hw=`#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`,Gw=`ToonMaterial material; -material.diffuseColor = diffuseColor.rgb;`,Vw=`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`,Ww=`BlinnPhongMaterial material; -material.diffuseColor = diffuseColor.rgb; -material.specularColor = specular; -material.specularShininess = shininess; -material.specularStrength = specularStrength;`,Xw=`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`,qw=`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`,Yw=`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 ); -}`,Kw=` -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`,Zw=`#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`,$w=`#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`,Qw=`#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`,Jw=`#if defined( USE_LOGARITHMIC_DEPTH_BUFFER ) - gl_FragDepth = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5; -#endif`,eC=`#if defined( USE_LOGARITHMIC_DEPTH_BUFFER ) - uniform float logDepthBufFC; - varying float vFragDepth; - varying float vIsPerspective; -#endif`,tC=`#ifdef USE_LOGARITHMIC_DEPTH_BUFFER - varying float vFragDepth; - varying float vIsPerspective; -#endif`,nC=`#ifdef USE_LOGARITHMIC_DEPTH_BUFFER - vFragDepth = 1.0 + gl_Position.w; - vIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) ); -#endif`,iC=`#ifdef USE_MAP - vec4 sampledDiffuseColor = texture2D( map, vMapUv ); - #ifdef DECODE_VIDEO_TEXTURE - sampledDiffuseColor = sRGBTransferEOTF( sampledDiffuseColor ); - #endif - diffuseColor *= sampledDiffuseColor; -#endif`,aC=`#ifdef USE_MAP - uniform sampler2D map; -#endif`,sC=`#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`,rC=`#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`,lC=`float metalnessFactor = metalness; -#ifdef USE_METALNESSMAP - vec4 texelMetalness = texture2D( metalnessMap, vMetalnessMapUv ); - metalnessFactor *= texelMetalness.b; -#endif`,oC=`#ifdef USE_METALNESSMAP - uniform sampler2D metalnessMap; -#endif`,cC=`#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`,uC=`#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`,fC=`#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`,dC=`#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`,hC=`#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`,pC=`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;`,mC=`#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`,gC=`#ifndef FLAT_SHADED - varying vec3 vNormal; - #ifdef USE_TANGENT - varying vec3 vTangent; - varying vec3 vBitangent; - #endif -#endif`,xC=`#ifndef FLAT_SHADED - varying vec3 vNormal; - #ifdef USE_TANGENT - varying vec3 vTangent; - varying vec3 vBitangent; - #endif -#endif`,vC=`#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`,_C=`#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`,yC=`#ifdef USE_CLEARCOAT - vec3 clearcoatNormal = nonPerturbedNormal; -#endif`,bC=`#ifdef USE_CLEARCOAT_NORMALMAP - vec3 clearcoatMapN = texture2D( clearcoatNormalMap, vClearcoatNormalMapUv ).xyz * 2.0 - 1.0; - clearcoatMapN.xy *= clearcoatNormalScale; - clearcoatNormal = normalize( tbn2 * clearcoatMapN ); -#endif`,SC=`#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`,MC=`#ifdef USE_IRIDESCENCEMAP - uniform sampler2D iridescenceMap; -#endif -#ifdef USE_IRIDESCENCE_THICKNESSMAP - uniform sampler2D iridescenceThicknessMap; -#endif`,EC=`#ifdef OPAQUE -diffuseColor.a = 1.0; -#endif -#ifdef USE_TRANSMISSION -diffuseColor.a *= material.transmissionAlpha; -#endif -gl_FragColor = vec4( outgoingLight, diffuseColor.a );`,TC=`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 -}`,AC=`#ifdef PREMULTIPLIED_ALPHA - gl_FragColor.rgb *= gl_FragColor.a; -#endif`,wC=`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;`,CC=`#ifdef DITHERING - gl_FragColor.rgb = dithering( gl_FragColor.rgb ); -#endif`,NC=`#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`,RC=`float roughnessFactor = roughness; -#ifdef USE_ROUGHNESSMAP - vec4 texelRoughness = texture2D( roughnessMap, vRoughnessMapUv ); - roughnessFactor *= texelRoughness.g; -#endif`,DC=`#ifdef USE_ROUGHNESSMAP - uniform sampler2D roughnessMap; -#endif`,LC=`#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`,UC=`#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`,OC=`#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`,PC=`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; -}`,zC=`#ifdef USE_SKINNING - mat4 boneMatX = getBoneMatrix( skinIndex.x ); - mat4 boneMatY = getBoneMatrix( skinIndex.y ); - mat4 boneMatZ = getBoneMatrix( skinIndex.z ); - mat4 boneMatW = getBoneMatrix( skinIndex.w ); -#endif`,IC=`#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`,kC=`#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`,BC=`#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`,FC=`float specularStrength; -#ifdef USE_SPECULARMAP - vec4 texelSpecular = texture2D( specularMap, vSpecularMapUv ); - specularStrength = texelSpecular.r; -#else - specularStrength = 1.0; -#endif`,jC=`#ifdef USE_SPECULARMAP - uniform sampler2D specularMap; -#endif`,HC=`#if defined( TONE_MAPPING ) - gl_FragColor.rgb = toneMapping( gl_FragColor.rgb ); -#endif`,GC=`#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; }`,VC=`#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`,WC=`#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`,XC=`#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`,qC=`#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`,YC=`#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`,KC=`#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 ZC=`varying vec2 vUv; -uniform mat3 uvTransform; -void main() { - vUv = ( uvTransform * vec3( uv, 1 ) ).xy; - gl_Position = vec4( position.xy, 1.0, 1.0 ); -}`,$C=`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> -}`,QC=`varying vec3 vWorldDirection; -#include <common> -void main() { - vWorldDirection = transformDirection( position, modelMatrix ); - #include <begin_vertex> - #include <project_vertex> - gl_Position.z = gl_Position.w; -}`,JC=`#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> -}`,eN=`varying vec3 vWorldDirection; -#include <common> -void main() { - vWorldDirection = transformDirection( position, modelMatrix ); - #include <begin_vertex> - #include <project_vertex> - gl_Position.z = gl_Position.w; -}`,tN=`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> -}`,nN=`#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; -}`,iN=`#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 -}`,aN=`#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; -}`,sN=`#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 ); -}`,rN=`varying vec3 vWorldDirection; -#include <common> -void main() { - vWorldDirection = transformDirection( position, modelMatrix ); - #include <begin_vertex> - #include <project_vertex> -}`,lN=`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> -}`,oN=`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> -}`,cN=`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> -}`,uN=`#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> -}`,fN=`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> -}`,dN=`#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> -}`,hN=`#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> -}`,pN=`#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; -}`,mN=`#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> -}`,gN=`#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 -}`,xN=`#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 -}`,vN=`#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> -}`,_N=`#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> -}`,yN=`#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 -}`,bN=`#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> -}`,SN=`#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> -}`,MN=`#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> -}`,EN=`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> -}`,TN=`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> -}`,AN=`#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> -}`,wN=`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> -}`,CN=`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> -}`,NN=`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:ZA,alphahash_pars_fragment:$A,alphamap_fragment:QA,alphamap_pars_fragment:JA,alphatest_fragment:ew,alphatest_pars_fragment:tw,aomap_fragment:nw,aomap_pars_fragment:iw,batching_pars_vertex:aw,batching_vertex:sw,begin_vertex:rw,beginnormal_vertex:lw,bsdfs:ow,iridescence_fragment:cw,bumpmap_pars_fragment:uw,clipping_planes_fragment:fw,clipping_planes_pars_fragment:dw,clipping_planes_pars_vertex:hw,clipping_planes_vertex:pw,color_fragment:mw,color_pars_fragment:gw,color_pars_vertex:xw,color_vertex:vw,common:_w,cube_uv_reflection_fragment:yw,defaultnormal_vertex:bw,displacementmap_pars_vertex:Sw,displacementmap_vertex:Mw,emissivemap_fragment:Ew,emissivemap_pars_fragment:Tw,colorspace_fragment:Aw,colorspace_pars_fragment:ww,envmap_fragment:Cw,envmap_common_pars_fragment:Nw,envmap_pars_fragment:Rw,envmap_pars_vertex:Dw,envmap_physical_pars_fragment:Hw,envmap_vertex:Lw,fog_vertex:Uw,fog_pars_vertex:Ow,fog_fragment:Pw,fog_pars_fragment:zw,gradientmap_pars_fragment:Iw,lightmap_pars_fragment:kw,lights_lambert_fragment:Bw,lights_lambert_pars_fragment:Fw,lights_pars_begin:jw,lights_toon_fragment:Gw,lights_toon_pars_fragment:Vw,lights_phong_fragment:Ww,lights_phong_pars_fragment:Xw,lights_physical_fragment:qw,lights_physical_pars_fragment:Yw,lights_fragment_begin:Kw,lights_fragment_maps:Zw,lights_fragment_end:$w,lightprobes_pars_fragment:Qw,logdepthbuf_fragment:Jw,logdepthbuf_pars_fragment:eC,logdepthbuf_pars_vertex:tC,logdepthbuf_vertex:nC,map_fragment:iC,map_pars_fragment:aC,map_particle_fragment:sC,map_particle_pars_fragment:rC,metalnessmap_fragment:lC,metalnessmap_pars_fragment:oC,morphinstance_vertex:cC,morphcolor_vertex:uC,morphnormal_vertex:fC,morphtarget_pars_vertex:dC,morphtarget_vertex:hC,normal_fragment_begin:pC,normal_fragment_maps:mC,normal_pars_fragment:gC,normal_pars_vertex:xC,normal_vertex:vC,normalmap_pars_fragment:_C,clearcoat_normal_fragment_begin:yC,clearcoat_normal_fragment_maps:bC,clearcoat_pars_fragment:SC,iridescence_pars_fragment:MC,opaque_fragment:EC,packing:TC,premultiplied_alpha_fragment:AC,project_vertex:wC,dithering_fragment:CC,dithering_pars_fragment:NC,roughnessmap_fragment:RC,roughnessmap_pars_fragment:DC,shadowmap_pars_fragment:LC,shadowmap_pars_vertex:UC,shadowmap_vertex:OC,shadowmask_pars_fragment:PC,skinbase_vertex:zC,skinning_pars_vertex:IC,skinning_vertex:kC,skinnormal_vertex:BC,specularmap_fragment:FC,specularmap_pars_fragment:jC,tonemapping_fragment:HC,tonemapping_pars_fragment:GC,transmission_fragment:VC,transmission_pars_fragment:WC,uv_pars_fragment:XC,uv_pars_vertex:qC,uv_vertex:YC,worldpos_vertex:KC,background_vert:ZC,background_frag:$C,backgroundCube_vert:QC,backgroundCube_frag:JC,cube_vert:eN,cube_frag:tN,depth_vert:nN,depth_frag:iN,distance_vert:aN,distance_frag:sN,equirect_vert:rN,equirect_frag:lN,linedashed_vert:oN,linedashed_frag:cN,meshbasic_vert:uN,meshbasic_frag:fN,meshlambert_vert:dN,meshlambert_frag:hN,meshmatcap_vert:pN,meshmatcap_frag:mN,meshnormal_vert:gN,meshnormal_frag:xN,meshphong_vert:vN,meshphong_frag:_N,meshphysical_vert:yN,meshphysical_frag:bN,meshtoon_vert:SN,meshtoon_frag:MN,points_vert:EN,points_frag:TN,shadow_vert:AN,shadow_frag:wN,sprite_vert:CN,sprite_frag:NN},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},RN=new Ft,CS=new it;CS.set(-1,0,0,0,1,0,0,0,1);function DN(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(RN.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 LN(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 UN(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 ON(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 PN(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,zN=256,Zl=new yg,K0=new gt;let eh=null,th=0,nh=0,ih=!1;const IN=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=IN}=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}=kN(s)),this._blurMaterial=FN(s,e,n),this._ggxMaterial=BN(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 kN(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 BN(t,e,n){return new Xn({name:"PMREMGGXConvolution",defines:{GGX_SAMPLES:zN,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 FN(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 XA(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 jN(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 HN(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 GN(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 VN(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 WN(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 XN(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 qN(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 YN={[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 KN(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 GA({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=YN[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 gA,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 ZN(t,e){const n=this.cache;n[0]!==e&&(t.uniform1f(this.addr,e),n[0]=e)}function $N(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 QN(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 JN(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 eR(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 tR(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 nR(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 iR(t,e){const n=this.cache;n[0]!==e&&(t.uniform1i(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.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 sR(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 rR(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 lR(t,e){const n=this.cache;n[0]!==e&&(t.uniform1ui(this.addr,e),n[0]=e)}function oR(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 cR(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 uR(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 fR(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 dR(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 hR(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 pR(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 mR(t){switch(t){case 5126:return ZN;case 35664:return $N;case 35665:return QN;case 35666:return JN;case 35674:return eR;case 35675:return tR;case 35676:return nR;case 5124:case 35670:return iR;case 35667:case 35671:return aR;case 35668:case 35672:return sR;case 35669:case 35673:return rR;case 5125:return lR;case 36294:return oR;case 36295:return cR;case 36296:return uR;case 35678:case 36198:case 36298:case 36306:case 35682:return fR;case 35679:case 36299:case 36307:return dR;case 35680:case 36300:case 36308:case 36293:return hR;case 36289:case 36303:case 36311:case 36292:return pR}}function gR(t,e){t.uniform1fv(this.addr,e)}function xR(t,e){const n=Ul(e,this.size,2);t.uniform2fv(this.addr,n)}function vR(t,e){const n=Ul(e,this.size,3);t.uniform3fv(this.addr,n)}function _R(t,e){const n=Ul(e,this.size,4);t.uniform4fv(this.addr,n)}function yR(t,e){const n=Ul(e,this.size,4);t.uniformMatrix2fv(this.addr,!1,n)}function bR(t,e){const n=Ul(e,this.size,9);t.uniformMatrix3fv(this.addr,!1,n)}function SR(t,e){const n=Ul(e,this.size,16);t.uniformMatrix4fv(this.addr,!1,n)}function MR(t,e){t.uniform1iv(this.addr,e)}function ER(t,e){t.uniform2iv(this.addr,e)}function TR(t,e){t.uniform3iv(this.addr,e)}function AR(t,e){t.uniform4iv(this.addr,e)}function wR(t,e){t.uniform1uiv(this.addr,e)}function CR(t,e){t.uniform2uiv(this.addr,e)}function NR(t,e){t.uniform3uiv(this.addr,e)}function RR(t,e){t.uniform4uiv(this.addr,e)}function DR(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 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));for(let r=0;r!==a;++r)n.setTexture3D(e[r]||LS,s[r])}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.setTextureCube(e[r]||US,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.setTexture2DArray(e[r]||DS,s[r])}function PR(t){switch(t){case 5126:return gR;case 35664:return xR;case 35665:return vR;case 35666:return _R;case 35674:return yR;case 35675:return bR;case 35676:return SR;case 5124:case 35670:return MR;case 35667:case 35671:return ER;case 35668:case 35672:return TR;case 35669:case 35673:return AR;case 5125:return wR;case 36294:return CR;case 36295:return NR;case 36296:return RR;case 35678:case 36198:case 36298:case 36306:case 35682:return DR;case 35679:case 36299:case 36307:return LR;case 35680:case 36300:case 36308:case 36293:return UR;case 36289:case 36303:case 36311:case 36292:return OR}}class zR{constructor(e,n,i){this.id=e,this.addr=i,this.cache=[],this.type=n.type,this.setValue=mR(n.type)}}class IR{constructor(e,n,i){this.id=e,this.addr=i,this.cache=[],this.type=n.type,this.size=n.size,this.setValue=PR(n.type)}}class kR{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 BR(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 zR(o,t,e):new IR(o,t,e));break}else{let m=n.map[o];m===void 0&&(m=new kR(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);BR(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 FR=37297;let jR=0;function HR(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 GR(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+` - -`+HR(t.getShaderSource(e),o)}else return s}function VR(t,e){const n=GR(e);return[`vec4 ${t}( vec4 value ) {`,` return ${n[1]}( vec4( value.rgb * ${n[0]}, value.a ) );`,"}"].join(` -`)}const WR={[Jb]:"Linear",[eS]:"Reinhard",[tS]:"Cineon",[nS]:"ACESFilmic",[aS]:"AgX",[sS]:"Neutral",[iS]:"Custom"};function XR(t,e){const n=WR[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 qR(){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 YR(t){return[t.extensionClipCullDistance?"#extension GL_ANGLE_clip_cull_distance : require":"",t.extensionMultiDraw?"#extension GL_ANGLE_multi_draw : require":""].filter(ro).join(` -`)}function KR(t){const e=[];for(const n in t){const i=t[n];i!==!1&&e.push("#define "+n+" "+i)}return e.join(` -`)}function ZR(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 $R=/^[ \t]*#include +<([\w\d./]+)>/gm;function nm(t){return t.replace($R,JR)}const QR=new Map;function JR(t,e){let n=lt[e];if(n===void 0){const i=QR.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 e3=/#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(e3,t3)}function t3(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 n3={[bu]:"SHADOWMAP_TYPE_PCF",[so]:"SHADOWMAP_TYPE_VSM"};function i3(t){return n3[t.shadowMapType]||"SHADOWMAP_TYPE_BASIC"}const a3={[Js]:"ENVMAP_TYPE_CUBE",[_l]:"ENVMAP_TYPE_CUBE",[If]:"ENVMAP_TYPE_CUBE_UV"};function s3(t){return t.envMap===!1?"ENVMAP_TYPE_CUBE":a3[t.envMapMode]||"ENVMAP_TYPE_CUBE"}const r3={[_l]:"ENVMAP_MODE_REFRACTION"};function l3(t){return t.envMap===!1?"ENVMAP_MODE_REFLECTION":r3[t.envMapMode]||"ENVMAP_MODE_REFLECTION"}const o3={[Qb]:"ENVMAP_BLENDING_MULTIPLY",[KT]:"ENVMAP_BLENDING_MIX",[ZT]:"ENVMAP_BLENDING_ADD"};function c3(t){return t.envMap===!1?"ENVMAP_BLENDING_NONE":o3[t.combine]||"ENVMAP_BLENDING_NONE"}function u3(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 f3(t,e,n,i){const a=t.getContext(),s=n.defines;let r=n.vertexShader,o=n.fragmentShader;const c=i3(n),u=s3(n),d=l3(n),m=c3(n),f=u3(n),p=YR(n),x=KR(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?XR("toneMapping",n.toneMapping):"",n.dithering?"#define DITHERING":"",n.opaque?"#define OPAQUE":"",lt.colorspace_pars_fragment,VR("linearToOutputTexel",n.outputColorSpace),qR(),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=ZR(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(_,FR)),R},this.destroy=function(){i.releaseStatesOfProgram(this),a.deleteProgram(_),this.program=void 0},this.type=n.shaderType,this.name=n.shaderName,this.id=jR++,this.cacheKey=e,this.usedTimes=1,this.program=_,this.vertexShader=A,this.fragmentShader=M,this}let d3=0;class h3{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 p3(e),n.set(e,i)),i}}class p3{constructor(e){this.id=d3++,this.code=e,this.usedTimes=0}}function m3(t){return t===er||t===sf||t===rf}function g3(t,e,n,i,a,s){const r=new mS,o=new h3,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===JT,normalMapTangentSpace:Ge&&S.normalMapType===p0,packedNormalMap:Ge&&S.normalMapType===p0&&m3(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=FA.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 f3(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 x3(){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 v3(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||v3),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 _3(){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 y3(){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 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={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 S3=0;function M3(t,e){return(e.castShadow?2:0)-(t.castShadow?2:0)+(e.map?1:0)-(t.map?1:0)}function E3(t){const e=new y3,n=b3(),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(M3);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=S3++)}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 E3(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 T3(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 A3=`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 ); -}`,C3=[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)],N3=[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 R3(t,e,n){let i=new bS;const a=new At,s=new At,r=new Qt,o=new VA,c=new WA,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:A3,fragmentShader:w3}),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===RT&&(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 IA(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(C3[he]),Ae.up.copy(N3[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 D3(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=cA[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,[LT]:t.FUNC_SUBTRACT,[UT]:t.FUNC_REVERSE_SUBTRACT};re[OT]=t.MIN,re[PT]=t.MAX;const Se={[zT]:t.ZERO,[IT]:t.ONE,[kT]:t.SRC_COLOR,[dp]:t.SRC_ALPHA,[VT]:t.SRC_ALPHA_SATURATE,[HT]:t.DST_COLOR,[FT]:t.DST_ALPHA,[BT]:t.ONE_MINUS_SRC_COLOR,[hp]:t.ONE_MINUS_SRC_ALPHA,[GT]:t.ONE_MINUS_DST_COLOR,[jT]:t.ONE_MINUS_DST_ALPHA,[WT]:t.CONSTANT_COLOR,[XT]:t.ONE_MINUS_CONSTANT_COLOR,[qT]:t.CONSTANT_ALPHA,[YT]: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!==DT){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!==CT?(oe(t.CULL_FACE),j!==O&&(j===u0?t.cullFace(t.BACK):j===NT?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 L3(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,[$T]:t.NEAREST_MIPMAP_NEAREST,[Mc]:t.NEAREST_MIPMAP_LINEAR,[Rn]:t.LINEAR,[wd]:t.LINEAR_MIPMAP_NEAREST,[js]:t.LINEAR_MIPMAP_LINEAR},fe={[eA]:t.NEVER,[sA]:t.ALWAYS,[tA]:t.LESS,[gg]:t.LEQUAL,[nA]:t.EQUAL,[xg]:t.GEQUAL,[iA]:t.GREATER,[aA]: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 U3(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 O3=` -void main() { - - gl_Position = vec4( position, 1.0 ); - -}`,P3=` -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 z3{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:O3,fragmentShader:P3,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 I3 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 z3,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 qA;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 k3=new Ft,OS=new it;OS.set(-1,0,0,0,1,0,0,0,1);function B3(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(k3.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 F3(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 j3=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 H3(){return qi===null&&(qi=new yS(j3,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 G3{constructor(e={}){const{canvas:n=lA(),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 HN(I),Ke.init(),Te=new U3(I,Ke),N=new ON(I,Ke,e,Te),E=new D3(I,Ke),N.reversedDepthBuffer&&f&&E.buffers.depth.setReversed(!0),W=I.createFramebuffer(),z=I.createFramebuffer(),D=I.createFramebuffer(),H=new WN(I),K=new x3,J=new L3(I,Ke,E,K,N,Te,H),_e=new jN(R),pe=new KA(I),Re=new LN(I,pe),te=new GN(I,pe,H,Re),Z=new qN(I,te,pe,Re,H),j=new XN(I,N,J),Fe=new PN(K),ae=new g3(R,_e,Ke,N,Re,Fe),ce=new B3(R,K),me=new _3,ye=new T3(Ke),et=new DN(R,_e,E,Z,x,c),Xe=new R3(R,Z,N),de=new F3(I,H,N,E),Me=new UN(I,Ke,H),se=new VN(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 KN(_,n.width,n.height,o,a,s));const je=new I3(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=H3()),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 oA(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 V3=1024,W3=2048,xv=96,vv=new Map;function X3(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 q3(t,e,n,{onSelect:i,onZoom:a}){let s;try{s=new G3({antialias:!0,alpha:!0,canvas:t})}catch{}if(!s)return null;s.setPixelRatio(Math.min(devicePixelRatio,2)),s.setClearColor(0,0);const r=new EA,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=V3,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 kA(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=W3,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 zA(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);wT(),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=MT(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=X3(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 Y3(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 K3(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 Z3(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=K3(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:Y3(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 $3({snapshot:t,onSelectFile:e}){P.useEffect(()=>{t&&Z3(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=q3(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=ET(()=>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:bT(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:()=>{ST(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 Q3(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 J3({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?Q3(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 e2({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:t2[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 t2={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 n2=8e3;function i2(t,e=n2){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([]),i2(oT(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(()=>{}),fT(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 a2(t){return!t||typeof t!="string"?"":t.replace(/\\/g,"/")}function s2(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 r2({workspaceName:t,files:e,activePath:n,onSelectFile:i}){const[a,s]=P.useState(""),[r,o]=P.useState(new Set),c=P.useMemo(()=>s2(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=a2(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 l2({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 uT(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 o2({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(""),hT(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 c2(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 u2({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=c2(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 f2(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];f2(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 d2({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(u2,{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 h2(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 p2({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 dT(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}=h2(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 pT(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(o2,{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(d2,{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:mT(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 m2(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 g2({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 cT(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?m2(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 x2(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 v2(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 _2(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 y2(){var t=[];return this.visit(function(e){if(!e.length)do t.push(e.data);while(e=e.next)}),t}function b2(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 S2(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 M2(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 E2(t){for(var e=0,n=t.length;e<n;++e)this.remove(t[e]);return this}function T2(){return this._root}function A2(){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,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 C2(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 N2(t){return t[0]}function R2(t){return arguments.length?(this._x=t,this):this._x}function D2(t){return t[1]}function L2(t){return arguments.length?(this._y=t,this):this._y}function bg(t,e,n){var i=new Sg(e??N2,n??D2,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=x2;Bn.addAll=v2;Bn.cover=_2;Bn.data=y2;Bn.extent=b2;Bn.find=S2;Bn.remove=M2;Bn.removeAll=E2;Bn.root=T2;Bn.size=A2;Bn.visit=w2;Bn.visitAfter=C2;Bn.x=R2;Bn.y=L2;function Pn(t){return function(){return t}}function fs(t){return(t()-.5)*1e-6}function U2(t){return t.x+t.vx}function O2(t){return t.y+t.vy}function P2(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,U2,O2).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 z2(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 I2(t){var e=z2,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 k2={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 B2(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=B2(t+"",n),a,s=-1,r=i.length;if(arguments.length<2){for(;++s<r;)if((a=(t=i[s]).type)&&(a=F2(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 F2(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]=k2,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(j2),ir=Wo.now()+jf)}function j2(){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 H2(){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{H2()}finally{Sl=0,V2(),ir=0}}function G2(){var t=Wo.now(),e=t-pf;e>IS&&(jf-=e,pf=t)}function V2(){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(G2,IS)),Sl=1,kS(Cv))}}const W2=1664525,X2=1013904223,Nv=4294967296;function q2(){let t=1;return()=>(t=(W2*t+X2)%Nv)/Nv}function Y2(t){return t.x}function K2(t){return t.y}var Z2=10,$2=Math.PI*(3-Math.sqrt(5));function Q2(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=q2();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=Z2*Math.sqrt(.5+_),b=_*$2;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 J2(){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,Y2,K2).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 eD(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 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.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,nD=.0016,iD=4,aD=4,sD=2.6,rD=1.35,lD=13,oh=1.4,oD=.13,cD=.035,uD=.8,fD=.13,dD=.7,hD=1.3,Lv=12,Uv=.75,pD=1.25,mD=.06,gD=280,tu=3,xD=4,vD=40,Ov=56,_D=1.6,yD=260;var Bv;const Pv=typeof window<"u"&&((Bv=window.matchMedia)==null?void 0:Bv.call(window,"(prefers-reduced-motion: reduce)").matches)===!0;function bD(t){return Math.min(lD,sD+rD*Math.sqrt(t))}function SD(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 MD(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)+aD/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?cD:oD,i.lineWidth=dD/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=uD,i.lineWidth=hD/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)?fD: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*mD;We=Math.max(0,Math.min(1,(nt-Uv)/(pD-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>=gD)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+xD,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)/yD),be=SD(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)<iD))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*nD))}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:bD(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=Q2(Pe).force("link",I2(c).id(we=>we.id).distance(30)).force("charge",J2().strength(-42).distanceMax(420).theta(.9)).force("x",eD(0).strength(.04)).force("y",tD(0).strength(.04)).force("collide",P2(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(vD):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,_D);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 ED=8,sl="Weitere",TD=new Set(["file","class","interface","note"]);function AD(t){return typeof t=="string"&&TD.has(t)?t:"other"}function Yi(t){return typeof t=="string"&&t!==""?t:null}function wD(t){return typeof t=="number"&&Number.isFinite(t)?t:null}function CD(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 ND(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),_=AD(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:CD(x),lines:wD((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<ED&&f!==sl?x:-1}));return{nodes:[...e.values()],edges:i,groups:c,byId:e,neighbours:a,kindCounts:n}}function RD(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"},DD={file:"Datei",class:"Klasse",interface:"Interface",note:"Notiz",other:"Objekt"},LD={imports:["Importiert","Wird importiert von"],defines:["Definiert","Definiert in"],links:["Verweist auf","Verwiesen von"]},Ki=new Intl.NumberFormat("de-DE");function UD({graph:t,coverage:e,error:n,onRetry:i,onOpenSource:a,timeline:s}){const r=P.useMemo(()=>t?ND(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=MD(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=>RD(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:PD(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(OD,{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 OD({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]=LD[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:DD[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 PD(t){const e=t.split("/");return e.pop(),e.slice(-3).join("/")}function zD({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 gT(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 ID({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 kD({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 BD({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"),vT(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"),_T().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"),yT().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(kD,{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(ID,{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 FD({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 xT(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,jD=2e3;function HD(){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 GD(){const t=new URLSearchParams(location.search);return t.has("workspace")?t.get("workspace")??"":t.has("workspaceRoot")?"":W1()}function VD(){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(HD),[f,p]=P.useState(GD),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()),lT(),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(jD)),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(zD,{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(UD,{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(p2,{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(r2,{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(WD,{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(l2,{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(g2,{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($3,{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(e2,{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(J3,{mesh:n,workspaceId:f,onSelectFile:ze,focusAgentId:Ne}),wt]})]}),d==="hygiene"&&l.jsxs("div",{className:"pb-view",children:[l.jsx(BD,{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(FD,{workspaceId:f,isOpen:_e,onClose:()=>pe(!1),onOpenSource:(F,ne)=>ze(F,ne),onNavigateToSearch:F=>{m("search")},initialQuestion:te})]})}function WD({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(VD,{})})); diff --git a/ui-dist/assets/index-BI4GkMjR.js b/ui-dist/assets/index-BI4GkMjR.js new file mode 100644 index 0000000..38c4c4b --- /dev/null +++ b/ui-dist/assets/index-BI4GkMjR.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 tM(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var Kv={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 nM=Symbol.for("react.transitional.element"),iM=Symbol.for("react.fragment");function $v(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:nM,type:t,key:i,ref:e!==void 0?e:null,props:n}}yd.Fragment=iM;yd.jsx=$v;yd.jsxs=$v;Kv.exports=yd;var r=Kv.exports,Zv={exports:{}},st={};/** + * @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"),aM=Symbol.for("react.portal"),sM=Symbol.for("react.fragment"),rM=Symbol.for("react.strict_mode"),lM=Symbol.for("react.profiler"),oM=Symbol.for("react.consumer"),cM=Symbol.for("react.context"),uM=Symbol.for("react.forward_ref"),dM=Symbol.for("react.suspense"),fM=Symbol.for("react.memo"),Qv=Symbol.for("react.lazy"),hM=Symbol.for("react.activity"),zg=Symbol.iterator;function pM(t){return t===null||typeof t!="object"?null:(t=zg&&t[zg]||t["@@iterator"],typeof t=="function"?t:null)}var Jv={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},e_=Object.assign,t_={};function wl(t,e,n){this.props=t,this.context=e,this.refs=t_,this.updater=n||Jv}wl.prototype.isReactComponent={};wl.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")};wl.prototype.forceUpdate=function(t){this.updater.enqueueForceUpdate(this,t,"forceUpdate")};function n_(){}n_.prototype=wl.prototype;function hm(t,e,n){this.props=t,this.context=e,this.refs=t_,this.updater=n||Jv}var pm=hm.prototype=new n_;pm.constructor=hm;e_(pm,wl.prototype);pm.isPureReactComponent=!0;var kg=Array.isArray;function mh(){}var Ht={H:null,A:null,T:null,S:null},i_=Object.prototype.hasOwnProperty;function mm(t,e,n){var i=n.ref;return{$$typeof:fm,type:t,key:e,ref:i!==void 0?i:null,props:n}}function mM(t,e){return mm(t.type,e,t.props)}function gm(t){return typeof t=="object"&&t!==null&&t.$$typeof===fm}function gM(t){var e={"=":"=0",":":"=2"};return"$"+t.replace(/[=:]/g,function(n){return e[n]})}var Ig=/\/+/g;function Wd(t,e){return typeof t=="object"&&t!==null&&t.key!=null?gM(""+t.key):e.toString(36)}function xM(t){switch(t.status){case"fulfilled":return t.value;case"rejected":throw t.reason;default:switch(typeof t.status=="string"?t.then(mh,mh):(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 zr(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 aM:l=!0;break;case Qv:return l=t._init,zr(l(t._payload),e,n,i,a)}}if(l)return a=a(t),l=i===""?"."+Wd(t,0):i,kg(a)?(n="",l!=null&&(n=l.replace(Ig,"$&/")+"/"),zr(a,e,n,"",function(u){return u})):a!=null&&(gm(a)&&(a=mM(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+Wd(i,c),l+=zr(i,e,n,s,a);else if(c=pM(t),typeof c=="function")for(t=c.call(t),c=0;!(i=t.next()).done;)i=i.value,s=o+Wd(i,c++),l+=zr(i,e,n,s,a);else if(s==="object"){if(typeof t.then=="function")return zr(xM(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 uc(t,e,n){if(t==null)return t;var i=[],a=0;return zr(t,i,"","",function(s){return e.call(n,s,a++)}),i}function vM(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)},_M={map:uc,forEach:function(t,e,n){uc(t,function(){e.apply(this,arguments)},n)},count:function(t){var e=0;return uc(t,function(){e++}),e},toArray:function(t){return uc(t,function(e){return e})||[]},only:function(t){if(!gm(t))throw Error("React.Children.only expected to receive a single React element child.");return t}};st.Activity=hM;st.Children=_M;st.Component=wl;st.Fragment=sM;st.Profiler=lM;st.PureComponent=hm;st.StrictMode=rM;st.Suspense=dM;st.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=Ht;st.__COMPILER_RUNTIME={__proto__:null,c:function(t){return Ht.H.useMemoCache(t)}};st.cache=function(t){return function(){return t.apply(null,arguments)}};st.cacheSignal=function(){return null};st.cloneElement=function(t,e,n){if(t==null)throw Error("The argument must be a React element, but you passed "+t+".");var i=e_({},t.props),a=t.key;if(e!=null)for(s in e.key!==void 0&&(a=""+e.key),e)!i_.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 mm(t.type,a,i)};st.createContext=function(t){return t={$$typeof:cM,_currentValue:t,_currentValue2:t,_threadCount:0,Provider:null,Consumer:null},t.Provider=t,t.Consumer={$$typeof:oM,_context:t},t};st.createElement=function(t,e,n){var i,a={},s=null;if(e!=null)for(i in e.key!==void 0&&(s=""+e.key),e)i_.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 mm(t,s,a)};st.createRef=function(){return{current:null}};st.forwardRef=function(t){return{$$typeof:uM,render:t}};st.isValidElement=gm;st.lazy=function(t){return{$$typeof:Qv,_payload:{_status:-1,_result:t},_init:vM}};st.memo=function(t,e){return{$$typeof:fM,type:t,compare:e===void 0?null:e}};st.startTransition=function(t){var e=Ht.T,n={};Ht.T=n;try{var i=t(),a=Ht.S;a!==null&&a(n,i),typeof i=="object"&&i!==null&&typeof i.then=="function"&&i.then(mh,Bg)}catch(s){Bg(s)}finally{e!==null&&n.types!==null&&(e.types=n.types),Ht.T=e}};st.unstable_useCacheRefresh=function(){return Ht.H.useCacheRefresh()};st.use=function(t){return Ht.H.use(t)};st.useActionState=function(t,e,n){return Ht.H.useActionState(t,e,n)};st.useCallback=function(t,e){return Ht.H.useCallback(t,e)};st.useContext=function(t){return Ht.H.useContext(t)};st.useDebugValue=function(){};st.useDeferredValue=function(t,e){return Ht.H.useDeferredValue(t,e)};st.useEffect=function(t,e){return Ht.H.useEffect(t,e)};st.useEffectEvent=function(t){return Ht.H.useEffectEvent(t)};st.useId=function(){return Ht.H.useId()};st.useImperativeHandle=function(t,e,n){return Ht.H.useImperativeHandle(t,e,n)};st.useInsertionEffect=function(t,e){return Ht.H.useInsertionEffect(t,e)};st.useLayoutEffect=function(t,e){return Ht.H.useLayoutEffect(t,e)};st.useMemo=function(t,e){return Ht.H.useMemo(t,e)};st.useOptimistic=function(t,e){return Ht.H.useOptimistic(t,e)};st.useReducer=function(t,e,n){return Ht.H.useReducer(t,e,n)};st.useRef=function(t){return Ht.H.useRef(t)};st.useState=function(t){return Ht.H.useState(t)};st.useSyncExternalStore=function(t,e,n){return Ht.H.useSyncExternalStore(t,e,n)};st.useTransition=function(){return Ht.H.useTransition()};st.version="19.2.8";Zv.exports=st;var U=Zv.exports;const yM=tM(U);var a_={exports:{}},bd={},s_={exports:{}},r_={};/** + * @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 W=B.length;B.push(k);e:for(;0<W;){var Y=W-1>>>1,he=B[Y];if(0<a(he,k))B[Y]=k,B[W]=he,W=Y;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],W=B.pop();if(W!==k){B[0]=W;e:for(var Y=0,he=B.length,_e=he>>>1;Y<_e;){var Ce=2*(Y+1)-1,X=B[Ce],de=Ce+1,Ne=B[de];if(0>a(X,W))de<he&&0>a(Ne,X)?(B[Y]=Ne,B[de]=W,Y=de):(B[Y]=X,B[Ce]=W,Y=Ce);else if(de<he&&0>a(Ne,W))B[Y]=Ne,B[de]=W,Y=de;else break e}}return k}function a(B,k){var W=B.sortIndex-k.sortIndex;return W!==0?W: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 l=Date,o=l.now();t.unstable_now=function(){return l.now()-o}}var c=[],u=[],f=1,m=null,d=3,p=!1,g=!1,b=!1,x=!1,h=typeof setTimeout=="function"?setTimeout:null,v=typeof clearTimeout=="function"?clearTimeout:null,y=typeof setImmediate<"u"?setImmediate:null;function _(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(b=!1,_(B),!g)if(n(c)!==null)g=!0,M||(M=!0,L());else{var k=n(u);k!==null&&O(A,k.startTime-B)}}var M=!1,T=-1,S=5,w=-1;function R(){return x?!0:!(t.unstable_now()-w<S)}function D(){if(x=!1,M){var B=t.unstable_now();w=B;var k=!0;try{e:{g=!1,b&&(b=!1,v(T),T=-1),p=!0;var W=d;try{t:{for(_(B),m=n(c);m!==null&&!(m.expirationTime>B&&R());){var Y=m.callback;if(typeof Y=="function"){m.callback=null,d=m.priorityLevel;var he=Y(m.expirationTime<=B);if(B=t.unstable_now(),typeof he=="function"){m.callback=he,_(B),k=!0;break t}m===n(c)&&i(c),_(B)}else i(c);m=n(c)}if(m!==null)k=!0;else{var _e=n(u);_e!==null&&O(A,_e.startTime-B),k=!1}}break e}finally{m=null,d=W,p=!1}k=void 0}}finally{k?L():M=!1}}}var L;if(typeof y=="function")L=function(){y(D)};else if(typeof MessageChannel<"u"){var q=new MessageChannel,P=q.port2;q.port1.onmessage=D,L=function(){P.postMessage(null)}}else L=function(){h(D,0)};function O(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 d},t.unstable_next=function(B){switch(d){case 1:case 2:case 3:var k=3;break;default:k=d}var W=d;d=k;try{return B()}finally{d=W}},t.unstable_requestPaint=function(){x=!0},t.unstable_runWithPriority=function(B,k){switch(B){case 1:case 2:case 3:case 4:case 5:break;default:B=3}var W=d;d=B;try{return k()}finally{d=W}},t.unstable_scheduleCallback=function(B,k,W){var Y=t.unstable_now();switch(typeof W=="object"&&W!==null?(W=W.delay,W=typeof W=="number"&&0<W?Y+W:Y):W=Y,B){case 1:var he=-1;break;case 2:he=250;break;case 5:he=1073741823;break;case 4:he=1e4;break;default:he=5e3}return he=W+he,B={id:f++,callback:k,priorityLevel:B,startTime:W,expirationTime:he,sortIndex:-1},W>Y?(B.sortIndex=W,e(u,B),n(c)===null&&B===n(u)&&(b?(v(T),T=-1):b=!0,O(A,W-Y))):(B.sortIndex=he,e(c,B),g||p||(g=!0,M||(M=!0,L()))),B},t.unstable_shouldYield=R,t.unstable_wrapCallback=function(B){var k=d;return function(){var W=d;d=k;try{return B.apply(this,arguments)}finally{d=W}}}})(r_);s_.exports=r_;var bM=s_.exports,l_={exports:{}},jn={};/** + * @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 SM=U;function o_(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 zn={d:{f:Ga,r:function(){throw Error(o_(522))},D:Ga,C:Ga,L:Ga,m:Ga,X:Ga,S:Ga,M:Ga},p:0,findDOMNode:null},MM=Symbol.for("react.portal");function EM(t,e,n){var i=3<arguments.length&&arguments[3]!==void 0?arguments[3]:null;return{$$typeof:MM,key:i==null?null:""+i,children:t,containerInfo:e,implementation:n}}var po=SM.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;function Sd(t,e){if(t==="font")return"";if(typeof e=="string")return e==="use-credentials"?e:""}jn.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=zn;jn.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(o_(299));return EM(t,e,null,n)};jn.flushSync=function(t){var e=po.T,n=zn.p;try{if(po.T=null,zn.p=2,t)return t()}finally{po.T=e,zn.p=n,zn.d.f()}};jn.preconnect=function(t,e){typeof t=="string"&&(e?(e=e.crossOrigin,e=typeof e=="string"?e==="use-credentials"?e:"":void 0):e=null,zn.d.C(t,e))};jn.prefetchDNS=function(t){typeof t=="string"&&zn.d.D(t)};jn.preinit=function(t,e){if(typeof t=="string"&&e&&typeof e.as=="string"){var n=e.as,i=Sd(n,e.crossOrigin),a=typeof e.integrity=="string"?e.integrity:void 0,s=typeof e.fetchPriority=="string"?e.fetchPriority:void 0;n==="style"?zn.d.S(t,typeof e.precedence=="string"?e.precedence:void 0,{crossOrigin:i,integrity:a,fetchPriority:s}):n==="script"&&zn.d.X(t,{crossOrigin:i,integrity:a,fetchPriority:s,nonce:typeof e.nonce=="string"?e.nonce:void 0})}};jn.preinitModule=function(t,e){if(typeof t=="string")if(typeof e=="object"&&e!==null){if(e.as==null||e.as==="script"){var n=Sd(e.as,e.crossOrigin);zn.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&&zn.d.M(t)};jn.preload=function(t,e){if(typeof t=="string"&&typeof e=="object"&&e!==null&&typeof e.as=="string"){var n=e.as,i=Sd(n,e.crossOrigin);zn.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})}};jn.preloadModule=function(t,e){if(typeof t=="string")if(e){var n=Sd(e.as,e.crossOrigin);zn.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 zn.d.m(t)};jn.requestFormReset=function(t){zn.d.r(t)};jn.unstable_batchedUpdates=function(t,e){return t(e)};jn.useFormState=function(t,e,n){return po.H.useFormState(t,e,n)};jn.useFormStatus=function(){return po.H.useHostTransitionStatus()};jn.version="19.2.8";function c_(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(c_)}catch(t){console.error(t)}}c_(),l_.exports=jn;var TM=l_.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=bM,u_=U,AM=TM;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 d_(t){return!(!t||t.nodeType!==1&&t.nodeType!==9&&t.nodeType!==11)}function $o(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 h_(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 jg(t){if($o(t)!==t)throw Error(fe(188))}function wM(t){var e=t.alternate;if(!e){if(e=$o(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 jg(a),t;if(s===i)return jg(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 p_(t){var e=t.tag;if(e===5||e===26||e===27||e===6)return t;for(t=t.child;t!==null;){if(e=p_(t),e!==null)return e;t=t.sibling}return null}var Gt=Object.assign,NM=Symbol.for("react.element"),dc=Symbol.for("react.transitional.element"),io=Symbol.for("react.portal"),Br=Symbol.for("react.fragment"),m_=Symbol.for("react.strict_mode"),gh=Symbol.for("react.profiler"),g_=Symbol.for("react.consumer"),ba=Symbol.for("react.context"),xm=Symbol.for("react.forward_ref"),xh=Symbol.for("react.suspense"),vh=Symbol.for("react.suspense_list"),vm=Symbol.for("react.memo"),Za=Symbol.for("react.lazy"),_h=Symbol.for("react.activity"),CM=Symbol.for("react.memo_cache_sentinel"),Fg=Symbol.iterator;function Il(t){return t===null||typeof t!="object"?null:(t=Fg&&t[Fg]||t["@@iterator"],typeof t=="function"?t:null)}var RM=Symbol.for("react.client.reference");function yh(t){if(t==null)return null;if(typeof t=="function")return t.$$typeof===RM?null:t.displayName||t.name||null;if(typeof t=="string")return t;switch(t){case Br:return"Fragment";case gh:return"Profiler";case m_:return"StrictMode";case xh:return"Suspense";case vh:return"SuspenseList";case _h:return"Activity"}if(typeof t=="object")switch(t.$$typeof){case io:return"Portal";case ba:return t.displayName||"Context";case g_:return(t._context.displayName||"Context")+".Consumer";case xm:var e=t.render;return t=t.displayName,t||(t=e.displayName||e.name||"",t=t!==""?"ForwardRef("+t+")":"ForwardRef"),t;case vm:return e=t.displayName||null,e!==null?e:yh(t.type)||"Memo";case Za:e=t._payload,t=t._init;try{return yh(t(e))}catch{}}return null}var ao=Array.isArray,nt=u_.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,At=AM.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,Ws={pending:!1,data:null,method:null,action:null},bh=[],jr=-1;function sa(t){return{current:t}}function yn(t){0>jr||(t.current=bh[jr],bh[jr]=null,jr--)}function It(t,e){jr++,bh[jr]=t.current,t.current=e}var ta=sa(null),Do=sa(null),hs=sa(null),Uu=sa(null);function Ou(t,e){switch(It(hs,e),It(Do,t),It(ta,null),e.nodeType){case 9:case 11:t=(t=e.documentElement)&&(t=t.namespaceURI)?qx(t):0;break;default:if(t=e.tagName,e=e.namespaceURI)e=qx(e),t=zb(e,t);else switch(t){case"svg":t=1;break;case"math":t=2;break;default:t=0}}yn(ta),It(ta,t)}function ul(){yn(ta),yn(Do),yn(hs)}function Sh(t){t.memoizedState!==null&&It(Uu,t);var e=ta.current,n=zb(e,t.type);e!==n&&(It(Do,t),It(ta,n))}function Pu(t){Do.current===t&&(yn(ta),yn(Do)),Uu.current===t&&(yn(Uu),Ho._currentValue=Ws)}var Xd,Hg;function zs(t){if(Xd===void 0)try{throw Error()}catch(n){var e=n.stack.trim().match(/\n( *(at )?)/);Xd=e&&e[1]||"",Hg=-1<n.stack.indexOf(` + at`)?" (<anonymous>)":-1<n.stack.indexOf("@")?"@unknown:0:0":""}return` +`+Xd+t+Hg}var qd=!1;function Yd(t,e){if(!t||qd)return"";qd=!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 d=p}Reflect.construct(t,[],m)}else{try{m.call()}catch(p){d=p}t.call(m.prototype)}}else{try{throw Error()}catch(p){d=p}(m=t())&&typeof m.catch=="function"&&m.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 f=` +`+c[i].replace(" at new "," at ");return t.displayName&&f.includes("<anonymous>")&&(f=f.replace("<anonymous>",t.displayName)),f}while(1<=i&&0<=a);break}}}finally{qd=!1,Error.prepareStackTrace=n}return(n=t?t.displayName||t.name:"")?zs(n):""}function DM(t,e){switch(t.tag){case 26:case 27:case 5:return zs(t.type);case 16:return zs("Lazy");case 13:return t.child!==e&&e!==null?zs("Suspense Fallback"):zs("Suspense");case 19:return zs("SuspenseList");case 0:case 15:return Yd(t.type,!1);case 11:return Yd(t.type.render,!1);case 1:return Yd(t.type,!0);case 31:return zs("Activity");default:return""}}function Gg(t){try{var e="",n=null;do e+=DM(t,n),n=t,t=t.return;while(t);return e}catch(i){return` +Error generating stack: `+i.message+` +`+i.stack}}var Mh=Object.prototype.hasOwnProperty,_m=fn.unstable_scheduleCallback,Kd=fn.unstable_cancelCallback,LM=fn.unstable_shouldYield,UM=fn.unstable_requestPaint,li=fn.unstable_now,OM=fn.unstable_getCurrentPriorityLevel,x_=fn.unstable_ImmediatePriority,v_=fn.unstable_UserBlockingPriority,zu=fn.unstable_NormalPriority,PM=fn.unstable_LowPriority,__=fn.unstable_IdlePriority,zM=fn.log,kM=fn.unstable_setDisableYieldValue,Zo=null,oi=null;function as(t){if(typeof zM=="function"&&kM(t),oi&&typeof oi.setStrictMode=="function")try{oi.setStrictMode(Zo,t)}catch{}}var ci=Math.clz32?Math.clz32:jM,IM=Math.log,BM=Math.LN2;function jM(t){return t>>>=0,t===0?32:31-(IM(t)/BM|0)|0}var fc=256,hc=262144,pc=4194304;function ks(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 Md(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=ks(i):(l&=o,l!==0?a=ks(l):n||(n=o&~t,n!==0&&(a=ks(n))))):(o=i&~s,o!==0?a=ks(o):l!==0?a=ks(l):n||(n=i&~t,n!==0&&(a=ks(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 Qo(t,e){return(t.pendingLanes&~(t.suspendedLanes&~t.pingedLanes)&e)===0}function FM(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=pc;return pc<<=1,!(pc&62914560)&&(pc=4194304),t}function $d(t){for(var e=[],n=0;31>n;n++)e.push(t);return e}function Jo(t,e){t.pendingLanes|=e,e!==268435456&&(t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0)}function HM(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 f=31-ci(n),m=1<<f;o[f]=0,c[f]=-1;var d=u[f];if(d!==null)for(u[f]=null,f=0;f<d.length;f++){var p=d[f];p!==null&&(p.lane&=-536870913)}n&=~m}i!==0&&b_(t,i,0),s!==0&&a===0&&t.tag!==0&&(t.suspendedLanes|=s&~(l&~e))}function b_(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 S_(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 M_(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 bm(t){return t&=-t,2<t?8<t?t&134217727?32:268435456:8:2}function E_(){var t=At.p;return t!==0?t:(t=window.event,t===void 0?32:qb(t.type))}function Vg(t,e){var n=At.p;try{return At.p=t,e()}finally{At.p=n}}var ws=Math.random().toString(36).slice(2),Mn="__reactFiber$"+ws,Zn="__reactProps$"+ws,Nl="__reactContainer$"+ws,Eh="__reactEvents$"+ws,GM="__reactListeners$"+ws,VM="__reactHandles$"+ws,Wg="__reactResources$"+ws,ec="__reactMarker$"+ws;function Sm(t){delete t[Mn],delete t[Zn],delete t[Eh],delete t[GM],delete t[VM]}function Fr(t){var e=t[Mn];if(e)return e;for(var n=t.parentNode;n;){if(e=n[Nl]||n[Mn]){if(n=e.alternate,e.child!==null||n!==null&&n.child!==null)for(t=Qx(t);t!==null;){if(n=t[Mn])return n;t=Qx(t)}return e}t=n,n=t.parentNode}return null}function Cl(t){if(t=t[Mn]||t[Nl]){var e=t.tag;if(e===5||e===6||e===13||e===31||e===26||e===27||e===3)return t}return null}function so(t){var e=t.tag;if(e===5||e===26||e===27||e===6)return t.stateNode;throw Error(fe(33))}function Jr(t){var e=t[Wg];return e||(e=t[Wg]={hoistableStyles:new Map,hoistableScripts:new Map}),e}function vn(t){t[ec]=!0}var T_=new Set,A_={};function rr(t,e){dl(t,e),dl(t+"Capture",e)}function dl(t,e){for(A_[t]=e,t=0;t<e.length;t++)T_.add(e[t])}var WM=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]*$"),Xg={},qg={};function XM(t){return Mh.call(qg,t)?!0:Mh.call(Xg,t)?!1:WM.test(t)?qg[t]=!0:(Xg[t]=!0,!1)}function ou(t,e,n){if(XM(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 mc(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 la(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 w_(t){var e=t.type;return(t=t.nodeName)&&t.toLowerCase()==="input"&&(e==="checkbox"||e==="radio")}function qM(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 Th(t){if(!t._valueTracker){var e=w_(t)?"checked":"value";t._valueTracker=qM(t,e,""+t[e])}}function N_(t){if(!t)return!1;var e=t._valueTracker;if(!e)return!0;var n=e.getValue(),i="";return t&&(i=w_(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 YM=/[\n"\\]/g;function bi(t){return t.replace(YM,function(e){return"\\"+e.charCodeAt(0).toString(16)+" "})}function Ah(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=""+vi(e)):t.value!==""+vi(e)&&(t.value=""+vi(e)):l!=="submit"&&l!=="reset"||t.removeAttribute("value"),e!=null?wh(t,l,vi(e)):n!=null?wh(t,l,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 C_(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)){Th(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,l!=null&&typeof l!="function"&&typeof l!="symbol"&&typeof l!="boolean"&&(t.name=l),Th(t)}function wh(t,e,n){e==="number"&&ku(t.ownerDocument)===t||t.defaultValue===""+n||(t.defaultValue=""+n)}function el(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 R_(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 D_(t,e,n,i){if(e==null){if(i!=null){if(n!=null)throw Error(fe(92));if(ao(i)){if(1<i.length)throw Error(fe(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),Th(t)}function fl(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 Yg(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 L_(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&&Yg(t,a,i)}else for(var s in e)e.hasOwnProperty(s)&&Yg(t,s,e[s])}function Mm(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 $M=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"]]),ZM=/^[\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 cu(t){return ZM.test(""+t)?"javascript:throw new Error('React has blocked a javascript: URL as a security precaution.')":t}function Sa(){}var Nh=null;function Em(t){return t=t.target||t.srcElement||window,t.correspondingUseElement&&(t=t.correspondingUseElement),t.nodeType===3?t.parentNode:t}var Hr=null,tl=null;function Kg(t){var e=Cl(t);if(e&&(t=e.stateNode)){var n=t[Zn]||null;e:switch(t=e.stateNode,e.type){case"input":if(Ah(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[Zn]||null;if(!a)throw Error(fe(90));Ah(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&&N_(i)}break e;case"textarea":R_(t,n.value,n.defaultValue);break e;case"select":e=n.value,e!=null&&el(t,!!n.multiple,e,!1)}}}var Zd=!1;function U_(t,e,n){if(Zd)return t(e,n);Zd=!0;try{var i=t(e);return i}finally{if(Zd=!1,(Hr!==null||tl!==null)&&(Pd(),Hr&&(e=Hr,t=tl,tl=Hr=null,Kg(e),t)))for(e=0;e<t.length;e++)Kg(t[e])}}function Lo(t,e){var n=t.stateNode;if(n===null)return null;var i=n[Zn]||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 Ra=!(typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Ch=!1;if(Ra)try{var Bl={};Object.defineProperty(Bl,"passive",{get:function(){Ch=!0}}),window.addEventListener("test",Bl,Bl),window.removeEventListener("test",Bl,Bl)}catch{Ch=!1}var ss=null,Tm=null,uu=null;function O_(){if(uu)return uu;var t,e=Tm,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 l=n-t;for(i=1;i<=l&&e[n-i]===a[s-i];i++);return uu=a.slice(t,1<i?1-i:void 0)}function du(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 gc(){return!0}function $g(){return!1}function Jn(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)?gc:$g,this.isPropagationStopped=$g,this}return Gt(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=gc)},stopPropagation:function(){var n=this.nativeEvent;n&&(n.stopPropagation?n.stopPropagation():typeof n.cancelBubble!="unknown"&&(n.cancelBubble=!0),this.isPropagationStopped=gc)},persist:function(){},isPersistent:gc}),e}var lr={eventPhase:0,bubbles:0,cancelable:0,timeStamp:function(t){return t.timeStamp||Date.now()},defaultPrevented:0,isTrusted:0},Ed=Jn(lr),tc=Gt({},lr,{view:0,detail:0}),QM=Jn(tc),Qd,Jd,jl,Td=Gt({},tc,{screenX:0,screenY:0,clientX:0,clientY:0,pageX:0,pageY:0,ctrlKey:0,shiftKey:0,altKey:0,metaKey:0,getModifierState:Am,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!==jl&&(jl&&t.type==="mousemove"?(Qd=t.screenX-jl.screenX,Jd=t.screenY-jl.screenY):Jd=Qd=0,jl=t),Qd)},movementY:function(t){return"movementY"in t?t.movementY:Jd}}),Zg=Jn(Td),JM=Gt({},Td,{dataTransfer:0}),eE=Jn(JM),tE=Gt({},tc,{relatedTarget:0}),ef=Jn(tE),nE=Gt({},lr,{animationName:0,elapsedTime:0,pseudoElement:0}),iE=Jn(nE),aE=Gt({},lr,{clipboardData:function(t){return"clipboardData"in t?t.clipboardData:window.clipboardData}}),sE=Jn(aE),rE=Gt({},lr,{data:0}),Qg=Jn(rE),lE={Esc:"Escape",Spacebar:" ",Left:"ArrowLeft",Up:"ArrowUp",Right:"ArrowRight",Down:"ArrowDown",Del:"Delete",Win:"OS",Menu:"ContextMenu",Apps:"ContextMenu",Scroll:"ScrollLock",MozPrintableKey:"Unidentified"},oE={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"},cE={Alt:"altKey",Control:"ctrlKey",Meta:"metaKey",Shift:"shiftKey"};function uE(t){var e=this.nativeEvent;return e.getModifierState?e.getModifierState(t):(t=cE[t])?!!e[t]:!1}function Am(){return uE}var dE=Gt({},tc,{key:function(t){if(t.key){var e=lE[t.key]||t.key;if(e!=="Unidentified")return e}return t.type==="keypress"?(t=du(t),t===13?"Enter":String.fromCharCode(t)):t.type==="keydown"||t.type==="keyup"?oE[t.keyCode]||"Unidentified":""},code:0,location:0,ctrlKey:0,shiftKey:0,altKey:0,metaKey:0,repeat:0,locale:0,getModifierState:Am,charCode:function(t){return t.type==="keypress"?du(t):0},keyCode:function(t){return t.type==="keydown"||t.type==="keyup"?t.keyCode:0},which:function(t){return t.type==="keypress"?du(t):t.type==="keydown"||t.type==="keyup"?t.keyCode:0}}),fE=Jn(dE),hE=Gt({},Td,{pointerId:0,width:0,height:0,pressure:0,tangentialPressure:0,tiltX:0,tiltY:0,twist:0,pointerType:0,isPrimary:0}),Jg=Jn(hE),pE=Gt({},tc,{touches:0,targetTouches:0,changedTouches:0,altKey:0,metaKey:0,ctrlKey:0,shiftKey:0,getModifierState:Am}),mE=Jn(pE),gE=Gt({},lr,{propertyName:0,elapsedTime:0,pseudoElement:0}),xE=Jn(gE),vE=Gt({},Td,{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}),_E=Jn(vE),yE=Gt({},lr,{newState:0,oldState:0}),bE=Jn(yE),SE=[9,13,27,32],wm=Ra&&"CompositionEvent"in window,mo=null;Ra&&"documentMode"in document&&(mo=document.documentMode);var ME=Ra&&"TextEvent"in window&&!mo,P_=Ra&&(!wm||mo&&8<mo&&11>=mo),ex=" ",tx=!1;function z_(t,e){switch(t){case"keyup":return SE.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 Gr=!1;function EE(t,e){switch(t){case"compositionend":return k_(e);case"keypress":return e.which!==32?null:(tx=!0,ex);case"textInput":return t=e.data,t===ex&&tx?null:t;default:return null}}function TE(t,e){if(Gr)return t==="compositionend"||!wm&&z_(t,e)?(t=O_(),uu=Tm=ss=null,Gr=!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 P_&&e.locale!=="ko"?null:e.data;default:return null}}var AE={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 nx(t){var e=t&&t.nodeName&&t.nodeName.toLowerCase();return e==="input"?!!AE[t.type]:e==="textarea"}function I_(t,e,n,i){Hr?tl?tl.push(i):tl=[i]:Hr=i,e=td(e,"onChange"),0<e.length&&(n=new Ed("onChange","change",null,n,i),t.push({event:n,listeners:e}))}var go=null,Uo=null;function wE(t){Ub(t,0)}function Ad(t){var e=so(t);if(N_(e))return t}function ix(t,e){if(t==="change")return e}var B_=!1;if(Ra){var tf;if(Ra){var nf="oninput"in document;if(!nf){var ax=document.createElement("div");ax.setAttribute("oninput","return;"),nf=typeof ax.oninput=="function"}tf=nf}else tf=!1;B_=tf&&(!document.documentMode||9<document.documentMode)}function sx(){go&&(go.detachEvent("onpropertychange",j_),Uo=go=null)}function j_(t){if(t.propertyName==="value"&&Ad(Uo)){var e=[];I_(e,Uo,t,Em(t)),U_(wE,e)}}function NE(t,e,n){t==="focusin"?(sx(),go=e,Uo=n,go.attachEvent("onpropertychange",j_)):t==="focusout"&&sx()}function CE(t){if(t==="selectionchange"||t==="keyup"||t==="keydown")return Ad(Uo)}function RE(t,e){if(t==="click")return Ad(e)}function DE(t,e){if(t==="input"||t==="change")return Ad(e)}function LE(t,e){return t===e&&(t!==0||1/t===1/e)||t!==t&&e!==e}var di=typeof Object.is=="function"?Object.is:LE;function Oo(t,e){if(di(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(!Mh.call(e,a)||!di(t[a],e[a]))return!1}return!0}function rx(t){for(;t&&t.firstChild;)t=t.firstChild;return t}function lx(t,e){var n=rx(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=rx(n)}}function F_(t,e){return t&&e?t===e?!0:t&&t.nodeType===3?!1:e&&e.nodeType===3?F_(t,e.parentNode):"contains"in t?t.contains(e):t.compareDocumentPosition?!!(t.compareDocumentPosition(e)&16):!1:!1}function H_(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 Nm(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 UE=Ra&&"documentMode"in document&&11>=document.documentMode,Vr=null,Rh=null,xo=null,Dh=!1;function ox(t,e,n){var i=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;Dh||Vr==null||Vr!==ku(i)||(i=Vr,"selectionStart"in i&&Nm(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}),xo&&Oo(xo,i)||(xo=i,i=td(Rh,"onSelect"),0<i.length&&(e=new Ed("onSelect","select",null,e,n),t.push({event:e,listeners:i}),e.target=Vr)))}function Rs(t,e){var n={};return n[t.toLowerCase()]=e.toLowerCase(),n["Webkit"+t]="webkit"+e,n["Moz"+t]="moz"+e,n}var Wr={animationend:Rs("Animation","AnimationEnd"),animationiteration:Rs("Animation","AnimationIteration"),animationstart:Rs("Animation","AnimationStart"),transitionrun:Rs("Transition","TransitionRun"),transitionstart:Rs("Transition","TransitionStart"),transitioncancel:Rs("Transition","TransitionCancel"),transitionend:Rs("Transition","TransitionEnd")},af={},G_={};Ra&&(G_=document.createElement("div").style,"AnimationEvent"in window||(delete Wr.animationend.animation,delete Wr.animationiteration.animation,delete Wr.animationstart.animation),"TransitionEvent"in window||delete Wr.transitionend.transition);function or(t){if(af[t])return af[t];if(!Wr[t])return t;var e=Wr[t],n;for(n in e)if(e.hasOwnProperty(n)&&n in G_)return af[t]=e[n];return t}var V_=or("animationend"),W_=or("animationiteration"),X_=or("animationstart"),OE=or("transitionrun"),PE=or("transitionstart"),zE=or("transitioncancel"),q_=or("transitionend"),Y_=new Map,Lh="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(" ");Lh.push("scrollEnd");function Bi(t,e){Y_.set(t,e),rr(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)},gi=[],Xr=0,Cm=0;function wd(){for(var t=Xr,e=Cm=Xr=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 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 Nd(t,e,n,i){gi[Xr++]=t,gi[Xr++]=e,gi[Xr++]=n,gi[Xr++]=i,Cm|=i,t.lanes|=i,t=t.alternate,t!==null&&(t.lanes|=i)}function Rm(t,e,n,i){return Nd(t,e,n,i),Bu(t)}function cr(t,e){return Nd(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-ci(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<Ao)throw Ao=0,Jh=null,Error(fe(185));for(var e=t.return;e!==null;)t=e,e=t.return;return t.tag===3?t.stateNode:null}var qr={};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 si(t,e,n,i){return new kE(t,e,n,i)}function Dm(t){return t=t.prototype,!(!t||!t.isReactComponent)}function Ta(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 $_(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")Dm(t)&&(l=1);else if(typeof t=="string")l=H1(t,n,ta.current)?26:t==="html"||t==="head"||t==="body"?27:5;else e:switch(t){case _h:return t=si(31,n,e,a),t.elementType=_h,t.lanes=s,t;case Br:return Xs(n.children,a,s,e);case m_:l=8,a|=24;break;case gh:return t=si(12,n,e,a|2),t.elementType=gh,t.lanes=s,t;case xh:return t=si(13,n,e,a),t.elementType=xh,t.lanes=s,t;case vh:return t=si(19,n,e,a),t.elementType=vh,t.lanes=s,t;default:if(typeof t=="object"&&t!==null)switch(t.$$typeof){case ba:l=10;break e;case g_:l=9;break e;case xm:l=11;break e;case vm:l=14;break e;case Za:l=16,i=null;break e}l=29,n=Error(fe(130,t===null?"null":typeof t,"")),i=null}return e=si(l,n,e,a),e.elementType=t,e.type=i,e.lanes=s,e}function Xs(t,e,n,i){return t=si(7,t,i,e),t.lanes=n,t}function sf(t,e,n){return t=si(6,t,null,e),t.lanes=n,t}function Z_(t){var e=si(18,null,null,0);return e.stateNode=t,e}function rf(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 cx=new WeakMap;function Si(t,e){if(typeof t=="object"&&t!==null){var n=cx.get(t);return n!==void 0?n:(e={value:t,source:e,stack:Gg(e)},cx.set(t,e),e)}return{value:t,source:e,stack:Gg(e)}}var Yr=[],Kr=0,ju=null,Po=0,_i=[],yi=0,Ss=null,Zi=1,Qi="";function va(t,e){Yr[Kr++]=Po,Yr[Kr++]=ju,ju=t,Po=e}function Q_(t,e,n){_i[yi++]=Zi,_i[yi++]=Qi,_i[yi++]=Ss,Ss=t;var i=Zi;t=Qi;var a=32-ci(i)-1;i&=~(1<<a),n+=1;var s=32-ci(e)+a;if(30<s){var l=a-a%5;s=(i&(1<<l)-1).toString(32),i>>=l,a-=l,Zi=1<<32-ci(e)+a|n<<a|i,Qi=s+t}else Zi=1<<s|n<<a|i,Qi=t}function Lm(t){t.return!==null&&(va(t,1),Q_(t,1,0))}function Um(t){for(;t===ju;)ju=Yr[--Kr],Yr[Kr]=null,Po=Yr[--Kr],Yr[Kr]=null;for(;t===Ss;)Ss=_i[--yi],_i[yi]=null,Qi=_i[--yi],_i[yi]=null,Zi=_i[--yi],_i[yi]=null}function J_(t,e){_i[yi++]=Zi,_i[yi++]=Qi,_i[yi++]=Ss,Zi=e.id,Qi=e.overflow,Ss=t}var En=null,Ft=null,xt=!1,ps=null,Mi=!1,Uh=Error(fe(519));function Ms(t){var e=Error(fe(418,1<arguments.length&&arguments[1]!==void 0&&arguments[1]?"text":"HTML",""));throw zo(Si(e,t)),Uh}function ux(t){var e=t.stateNode,n=t.type,i=t.memoizedProps;switch(e[Mn]=t,e[Zn]=i,n){case"dialog":dt("cancel",e),dt("close",e);break;case"iframe":case"object":case"embed":dt("load",e);break;case"video":case"audio":for(n=0;n<jo.length;n++)dt(jo[n],e);break;case"source":dt("error",e);break;case"img":case"image":case"link":dt("error",e),dt("load",e);break;case"details":dt("toggle",e);break;case"input":dt("invalid",e),C_(e,i.value,i.defaultValue,i.checked,i.defaultChecked,i.type,i.name,!0);break;case"select":dt("invalid",e);break;case"textarea":dt("invalid",e),D_(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||Pb(e.textContent,n)?(i.popover!=null&&(dt("beforetoggle",e),dt("toggle",e)),i.onScroll!=null&&dt("scroll",e),i.onScrollEnd!=null&&dt("scrollend",e),i.onClick!=null&&(e.onclick=Sa),e=!0):e=!1,e||Ms(t,!0)}function dx(t){for(En=t.return;En;)switch(En.tag){case 5:case 31:case 13:Mi=!1;return;case 27:case 3:Mi=!0;return;default:En=En.return}}function gr(t){if(t!==En)return!1;if(!xt)return dx(t),xt=!0,!1;var e=t.tag,n;if((n=e!==3&&e!==27)&&((n=e===5)&&(n=t.type,n=!(n!=="form"&&n!=="button")||ap(t.type,t.memoizedProps)),n=!n),n&&Ft&&Ms(t),dx(t),e===13){if(t=t.memoizedState,t=t!==null?t.dehydrated:null,!t)throw Error(fe(317));Ft=Zx(t)}else if(e===31){if(t=t.memoizedState,t=t!==null?t.dehydrated:null,!t)throw Error(fe(317));Ft=Zx(t)}else e===27?(e=Ft,Ns(t.type)?(t=op,op=null,Ft=t):Ft=e):Ft=En?wi(t.stateNode.nextSibling):null;return!0}function $s(){Ft=En=null,xt=!1}function lf(){var t=ps;return t!==null&&(Xn===null?Xn=t:Xn.push.apply(Xn,t),ps=null),t}function zo(t){ps===null?ps=[t]:ps.push(t)}var Oh=sa(null),ur=null,Ma=null;function Ja(t,e,n){It(Oh,e._currentValue),e._currentValue=n}function Aa(t){t._currentValue=Oh.current,yn(Oh)}function Ph(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 zh(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),Ph(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),Ph(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 Rl(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;di(a.pendingProps.value,l.value)||(t!==null?t.push(o):t=[o])}}else if(a===Uu.current){if(l=a.alternate,l===null)throw Error(fe(387));l.memoizedState.memoizedState!==a.memoizedState.memoizedState&&(t!==null?t.push(Ho):t=[Ho])}a=a.return}t!==null&&zh(e,t,n,i),e.flags|=262144}function Fu(t){for(t=t.firstContext;t!==null;){if(!di(t.context._currentValue,t.memoizedValue))return!0;t=t.next}return!1}function Zs(t){ur=t,Ma=null,t=t.dependencies,t!==null&&(t.firstContext=null)}function Tn(t){return ey(ur,t)}function xc(t,e){return ur===null&&Zs(t),ey(t,e)}function ey(t,e){var n=e._currentValue;if(e={context:e,memoizedValue:n,next:null},Ma===null){if(t===null)throw Error(fe(308));Ma=e,t.dependencies={lanes:0,firstContext:e},t.flags|=524288}else Ma=Ma.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=fn.unstable_scheduleCallback,jE=fn.unstable_NormalPriority,on={$$typeof:ba,Consumer:null,Provider:null,_currentValue:null,_currentValue2:null,_threadCount:0};function Om(){return{controller:new IE,data:new Map,refCount:0}}function nc(t){t.refCount--,t.refCount===0&&BE(jE,function(){t.controller.abort()})}var vo=null,kh=0,hl=0,nl=null;function FE(t,e){if(vo===null){var n=vo=[];kh=0,hl=sg(),nl={status:"pending",value:void 0,then:function(i){n.push(i)}}}return kh++,e.then(fx,fx),e}function fx(){if(--kh===0&&vo!==null){nl!==null&&(nl.status="fulfilled");var t=vo;vo=null,hl=0,nl=null;for(var e=0;e<t.length;e++)(0,t[e])()}}function HE(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 hx=nt.S;nt.S=function(t,e){pb=li(),typeof e=="object"&&e!==null&&typeof e.then=="function"&&FE(t,e),hx!==null&&hx(t,e)};var qs=sa(null);function Pm(){var t=qs.current;return t!==null?t:zt.pooledCache}function hu(t,e){e===null?It(qs,qs.current):It(qs,e.pool)}function ty(){var t=Pm();return t===null?null:{parent:on._currentValue,pool:t}}var Dl=Error(fe(460)),zm=Error(fe(474)),Cd=Error(fe(542)),Hu={then:function(){}};function px(t){return t=t.status,t==="fulfilled"||t==="rejected"}function ny(t,e,n){switch(n=t[n],n===void 0?t.push(e):n!==e&&(e.then(Sa,Sa),e=n),e.status){case"fulfilled":return e.value;case"rejected":throw t=e.reason,gx(t),t;default:if(typeof e.status=="string")e.then(Sa,Sa);else{if(t=zt,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,gx(t),t}throw Ys=e,Dl}}function Is(t){try{var e=t._init;return e(t._payload)}catch(n){throw n!==null&&typeof n=="object"&&typeof n.then=="function"?(Ys=n,Dl):n}}var Ys=null;function mx(){if(Ys===null)throw Error(fe(459));var t=Ys;return Ys=null,t}function gx(t){if(t===Dl||t===Cd)throw Error(fe(483))}var il=null,ko=0;function vc(t){var e=ko;return ko+=1,il===null&&(il=[]),ny(il,t,e)}function Fl(t,e){e=e.props.ref,t.ref=e!==void 0?e:null}function _c(t,e){throw e.$$typeof===NM?Error(fe(525)):(t=Object.prototype.toString.call(e),Error(fe(31,t==="[object Object]"?"object with keys {"+Object.keys(e).join(", ")+"}":t)))}function iy(t){function e(h,v){if(t){var y=h.deletions;y===null?(h.deletions=[v],h.flags|=16):y.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=Ta(h,v),h.index=0,h.sibling=null,h}function s(h,v,y){return h.index=y,t?(y=h.alternate,y!==null?(y=y.index,y<v?(h.flags|=67108866,v):y):(h.flags|=67108866,v)):(h.flags|=1048576,v)}function l(h){return t&&h.alternate===null&&(h.flags|=67108866),h}function o(h,v,y,_){return v===null||v.tag!==6?(v=sf(y,h.mode,_),v.return=h,v):(v=a(v,y),v.return=h,v)}function c(h,v,y,_){var A=y.type;return A===Br?f(h,v,y.props.children,_,y.key):v!==null&&(v.elementType===A||typeof A=="object"&&A!==null&&A.$$typeof===Za&&Is(A)===v.type)?(v=a(v,y.props),Fl(v,y),v.return=h,v):(v=fu(y.type,y.key,y.props,null,h.mode,_),Fl(v,y),v.return=h,v)}function u(h,v,y,_){return v===null||v.tag!==4||v.stateNode.containerInfo!==y.containerInfo||v.stateNode.implementation!==y.implementation?(v=rf(y,h.mode,_),v.return=h,v):(v=a(v,y.children||[]),v.return=h,v)}function f(h,v,y,_,A){return v===null||v.tag!==7?(v=Xs(y,h.mode,_,A),v.return=h,v):(v=a(v,y),v.return=h,v)}function m(h,v,y){if(typeof v=="string"&&v!==""||typeof v=="number"||typeof v=="bigint")return v=sf(""+v,h.mode,y),v.return=h,v;if(typeof v=="object"&&v!==null){switch(v.$$typeof){case dc:return y=fu(v.type,v.key,v.props,null,h.mode,y),Fl(y,v),y.return=h,y;case io:return v=rf(v,h.mode,y),v.return=h,v;case Za:return v=Is(v),m(h,v,y)}if(ao(v)||Il(v))return v=Xs(v,h.mode,y,null),v.return=h,v;if(typeof v.then=="function")return m(h,vc(v),y);if(v.$$typeof===ba)return m(h,xc(h,v),y);_c(h,v)}return null}function d(h,v,y,_){var A=v!==null?v.key:null;if(typeof y=="string"&&y!==""||typeof y=="number"||typeof y=="bigint")return A!==null?null:o(h,v,""+y,_);if(typeof y=="object"&&y!==null){switch(y.$$typeof){case dc:return y.key===A?c(h,v,y,_):null;case io:return y.key===A?u(h,v,y,_):null;case Za:return y=Is(y),d(h,v,y,_)}if(ao(y)||Il(y))return A!==null?null:f(h,v,y,_,null);if(typeof y.then=="function")return d(h,v,vc(y),_);if(y.$$typeof===ba)return d(h,v,xc(h,y),_);_c(h,y)}return null}function p(h,v,y,_,A){if(typeof _=="string"&&_!==""||typeof _=="number"||typeof _=="bigint")return h=h.get(y)||null,o(v,h,""+_,A);if(typeof _=="object"&&_!==null){switch(_.$$typeof){case dc:return h=h.get(_.key===null?y:_.key)||null,c(v,h,_,A);case io:return h=h.get(_.key===null?y:_.key)||null,u(v,h,_,A);case Za:return _=Is(_),p(h,v,y,_,A)}if(ao(_)||Il(_))return h=h.get(y)||null,f(v,h,_,A,null);if(typeof _.then=="function")return p(h,v,y,vc(_),A);if(_.$$typeof===ba)return p(h,v,y,xc(v,_),A);_c(v,_)}return null}function g(h,v,y,_){for(var A=null,M=null,T=v,S=v=0,w=null;T!==null&&S<y.length;S++){T.index>S?(w=T,T=null):w=T.sibling;var R=d(h,T,y[S],_);if(R===null){T===null&&(T=w);break}t&&T&&R.alternate===null&&e(h,T),v=s(R,v,S),M===null?A=R:M.sibling=R,M=R,T=w}if(S===y.length)return n(h,T),xt&&va(h,S),A;if(T===null){for(;S<y.length;S++)T=m(h,y[S],_),T!==null&&(v=s(T,v,S),M===null?A=T:M.sibling=T,M=T);return xt&&va(h,S),A}for(T=i(T);S<y.length;S++)w=p(T,h,S,y[S],_),w!==null&&(t&&w.alternate!==null&&T.delete(w.key===null?S:w.key),v=s(w,v,S),M===null?A=w:M.sibling=w,M=w);return t&&T.forEach(function(D){return e(h,D)}),xt&&va(h,S),A}function b(h,v,y,_){if(y==null)throw Error(fe(151));for(var A=null,M=null,T=v,S=v=0,w=null,R=y.next();T!==null&&!R.done;S++,R=y.next()){T.index>S?(w=T,T=null):w=T.sibling;var D=d(h,T,R.value,_);if(D===null){T===null&&(T=w);break}t&&T&&D.alternate===null&&e(h,T),v=s(D,v,S),M===null?A=D:M.sibling=D,M=D,T=w}if(R.done)return n(h,T),xt&&va(h,S),A;if(T===null){for(;!R.done;S++,R=y.next())R=m(h,R.value,_),R!==null&&(v=s(R,v,S),M===null?A=R:M.sibling=R,M=R);return xt&&va(h,S),A}for(T=i(T);!R.done;S++,R=y.next())R=p(T,h,S,R.value,_),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(L){return e(h,L)}),xt&&va(h,S),A}function x(h,v,y,_){if(typeof y=="object"&&y!==null&&y.type===Br&&y.key===null&&(y=y.props.children),typeof y=="object"&&y!==null){switch(y.$$typeof){case dc:e:{for(var A=y.key;v!==null;){if(v.key===A){if(A=y.type,A===Br){if(v.tag===7){n(h,v.sibling),_=a(v,y.props.children),_.return=h,h=_;break e}}else if(v.elementType===A||typeof A=="object"&&A!==null&&A.$$typeof===Za&&Is(A)===v.type){n(h,v.sibling),_=a(v,y.props),Fl(_,y),_.return=h,h=_;break e}n(h,v);break}else e(h,v);v=v.sibling}y.type===Br?(_=Xs(y.props.children,h.mode,_,y.key),_.return=h,h=_):(_=fu(y.type,y.key,y.props,null,h.mode,_),Fl(_,y),_.return=h,h=_)}return l(h);case io:e:{for(A=y.key;v!==null;){if(v.key===A)if(v.tag===4&&v.stateNode.containerInfo===y.containerInfo&&v.stateNode.implementation===y.implementation){n(h,v.sibling),_=a(v,y.children||[]),_.return=h,h=_;break e}else{n(h,v);break}else e(h,v);v=v.sibling}_=rf(y,h.mode,_),_.return=h,h=_}return l(h);case Za:return y=Is(y),x(h,v,y,_)}if(ao(y))return g(h,v,y,_);if(Il(y)){if(A=Il(y),typeof A!="function")throw Error(fe(150));return y=A.call(y),b(h,v,y,_)}if(typeof y.then=="function")return x(h,v,vc(y),_);if(y.$$typeof===ba)return x(h,v,xc(h,y),_);_c(h,y)}return typeof y=="string"&&y!==""||typeof y=="number"||typeof y=="bigint"?(y=""+y,v!==null&&v.tag===6?(n(h,v.sibling),_=a(v,y),_.return=h,h=_):(n(h,v),_=sf(y,h.mode,_),_.return=h,h=_),l(h)):n(h,v)}return function(h,v,y,_){try{ko=0;var A=x(h,v,y,_);return il=null,A}catch(T){if(T===Dl||T===Cd)throw T;var M=si(29,T,null,h.mode);return M.lanes=_,M.return=h,M}finally{}}}var Qs=iy(!0),ay=iy(!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 Ih(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 ms(t){return{lane:t,tag:0,payload:null,callback:null,next:null}}function gs(t,e,n){var i=t.updateQueue;if(i===null)return null;if(i=i.shared,Tt&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 Nd(t,i,e,n),Bu(t)}function _o(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,S_(t,n)}}function of(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 Bh=!1;function yo(){if(Bh){var t=nl;if(t!==null)throw t}}function bo(t,e,n,i){Bh=!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 f=t.alternate;f!==null&&(f=f.updateQueue,o=f.lastBaseUpdate,o!==l&&(o===null?f.firstBaseUpdate=u:o.next=u,f.lastBaseUpdate=c))}if(s!==null){var m=a.baseState;l=0,f=u=c=null,o=s;do{var d=o.lane&-536870913,p=d!==o.lane;if(p?(pt&d)===d:(i&d)===d){d!==0&&d===hl&&(Bh=!0),f!==null&&(f=f.next={lane:0,tag:o.tag,payload:o.payload,callback:null,next:null});e:{var g=t,b=o;d=e;var x=n;switch(b.tag){case 1:if(g=b.payload,typeof g=="function"){m=g.call(x,m,d);break e}m=g;break e;case 3:g.flags=g.flags&-65537|128;case 0:if(g=b.payload,d=typeof g=="function"?g.call(x,m,d):g,d==null)break e;m=Gt({},m,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},f===null?(u=f=p,c=m):f=f.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);f===null&&(c=m),a.baseState=c,a.firstBaseUpdate=u,a.lastBaseUpdate=f,s===null&&(a.shared.lanes=0),Ts|=l,t.lanes=l,t.memoizedState=m}}function sy(t,e){if(typeof t!="function")throw Error(fe(191,t));t.call(e)}function ry(t,e){var n=t.callbacks;if(n!==null)for(t.callbacks=null,t=0;t<n.length;t++)sy(n[t],e)}var pl=sa(null),Gu=sa(0);function xx(t,e){t=Oa,It(Gu,t),It(pl,e),Oa=t|e.baseLanes}function jh(){It(Gu,Oa),It(pl,pl.current)}function Im(){Oa=Gu.current,yn(pl),yn(Gu)}var fi=sa(null),Ai=null;function es(t){var e=t.alternate;It(en,en.current&1),It(fi,t),Ai===null&&(e===null||pl.current!==null||e.memoizedState!==null)&&(Ai=t)}function Fh(t){It(en,en.current),It(fi,t),Ai===null&&(Ai=t)}function ly(t){t.tag===22?(It(en,en.current),It(fi,t),Ai===null&&(Ai=t)):ts()}function ts(){It(en,en.current),It(fi,fi.current)}function ai(t){yn(fi),Ai===t&&(Ai=null),yn(en)}var en=sa(0);function Vu(t){for(var e=t;e!==null;){if(e.tag===13){var n=e.memoizedState;if(n!==null&&(n=n.dehydrated,n===null||rp(n)||lp(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 Da=0,ot=null,Ut=null,rn=null,Wu=!1,al=!1,Js=!1,Xu=0,Io=0,sl=null,GE=0;function Kt(){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(!di(t[n],e[n]))return!1;return!0}function jm(t,e,n,i,a,s){return Da=s,ot=e,e.memoizedState=null,e.updateQueue=null,e.lanes=0,nt.H=t===null||t.memoizedState===null?Iy:Zm,Js=!1,s=n(i,a),Js=!1,al&&(s=cy(e,n,i,a)),oy(t),s}function oy(t){nt.H=Bo;var e=Ut!==null&&Ut.next!==null;if(Da=0,rn=Ut=ot=null,Wu=!1,Io=0,sl=null,e)throw Error(fe(300));t===null||cn||(t=t.dependencies,t!==null&&Fu(t)&&(cn=!0))}function cy(t,e,n,i){ot=t;var a=0;do{if(al&&(sl=null),Io=0,al=!1,25<=a)throw Error(fe(301));if(a+=1,rn=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)}nt.H=By,s=e(n,i)}while(al);return s}function VE(){var t=nt.H,e=t.useState()[0];return e=typeof e.then=="function"?ic(e):e,t=t.useState()[0],(Ut!==null?Ut.memoizedState:null)!==t&&(ot.flags|=1024),e}function Fm(){var t=Xu!==0;return Xu=0,t}function Hm(t,e,n){e.updateQueue=t.updateQueue,e.flags&=-2053,t.lanes&=~n}function Gm(t){if(Wu){for(t=t.memoizedState;t!==null;){var e=t.queue;e!==null&&(e.pending=null),t=t.next}Wu=!1}Da=0,rn=Ut=ot=null,al=!1,Io=Xu=0,sl=null}function On(){var t={memoizedState:null,baseState:null,baseQueue:null,queue:null,next:null};return rn===null?ot.memoizedState=rn=t:rn=rn.next=t,rn}function tn(){if(Ut===null){var t=ot.alternate;t=t!==null?t.memoizedState:null}else t=Ut.next;var e=rn===null?ot.memoizedState:rn.next;if(e!==null)rn=e,Ut=t;else{if(t===null)throw ot.alternate===null?Error(fe(467)):Error(fe(310));Ut=t,t={memoizedState:Ut.memoizedState,baseState:Ut.baseState,baseQueue:Ut.baseQueue,queue:Ut.queue,next:null},rn===null?ot.memoizedState=rn=t:rn=rn.next=t}return rn}function Rd(){return{lastEffect:null,events:null,stores:null,memoCache:null}}function ic(t){var e=Io;return Io+=1,sl===null&&(sl=[]),t=ny(sl,t,e),e=ot,(rn===null?e.memoizedState:rn.next)===null&&(e=e.alternate,nt.H=e===null||e.memoizedState===null?Iy:Zm),t}function Dd(t){if(t!==null&&typeof t=="object"){if(typeof t.then=="function")return ic(t);if(t.$$typeof===ba)return Tn(t)}throw Error(fe(438,String(t)))}function Vm(t){var e=null,n=ot.updateQueue;if(n!==null&&(e=n.memoCache),e==null){var i=ot.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=Rd(),ot.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]=CM;return e.index++,n}function La(t,e){return typeof e=="function"?e(t):e}function pu(t){var e=tn();return Wm(e,Ut,t)}function Wm(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,f=!1;do{var m=u.lane&-536870913;if(m!==u.lane?(pt&m)===m:(Da&m)===m){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}),m===hl&&(f=!0);else if((Da&d)===d){u=u.next,d===hl&&(f=!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,l=s):c=c.next=m,ot.lanes|=d,Ts|=d;m=u.action,Js&&n(s,m),s=u.hasEagerState?u.eagerState:n(s,m)}else d={lane:m,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,ot.lanes|=m,Ts|=m;u=u.next}while(u!==null&&u!==e);if(c===null?l=s:c.next=o,!di(s,t.memoizedState)&&(cn=!0,f&&(n=nl,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 cf(t){var e=tn(),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);di(s,e.memoizedState)||(cn=!0),e.memoizedState=s,e.baseQueue===null&&(e.baseState=s),n.lastRenderedState=s}return[s,i]}function uy(t,e,n){var i=ot,a=tn(),s=xt;if(s){if(n===void 0)throw Error(fe(407));n=n()}else n=e();var l=!di((Ut||a).memoizedState,n);if(l&&(a.memoizedState=n,cn=!0),a=a.queue,Xm(hy.bind(null,i,a,t),[t]),a.getSnapshot!==e||l||rn!==null&&rn.memoizedState.tag&1){if(i.flags|=2048,ml(9,{destroy:void 0},fy.bind(null,i,a,n,e),null),zt===null)throw Error(fe(349));s||Da&127||dy(i,e,n)}return n}function dy(t,e,n){t.flags|=16384,t={getSnapshot:e,value:n},e=ot.updateQueue,e===null?(e=Rd(),ot.updateQueue=e,e.stores=[t]):(n=e.stores,n===null?e.stores=[t]:n.push(t))}function fy(t,e,n,i){e.value=n,e.getSnapshot=i,py(e)&&my(t)}function hy(t,e,n){return n(function(){py(e)&&my(t)})}function py(t){var e=t.getSnapshot;t=t.value;try{var n=e();return!di(t,n)}catch{return!0}}function my(t){var e=cr(t,2);e!==null&&Kn(e,t,2)}function Hh(t){var e=On();if(typeof t=="function"){var n=t;if(t=n(),Js){as(!0);try{n()}finally{as(!1)}}}return e.memoizedState=e.baseState=t,e.queue={pending:null,lanes:0,dispatch:null,lastRenderedReducer:La,lastRenderedState:t},e}function gy(t,e,n,i){return t.baseState=n,Wm(t,Ut,typeof i=="function"?i:La)}function WE(t,e,n,i,a){if(Ud(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)}};nt.T!==null?n(!0):s.isTransition=!1,i(s),n=e.pending,n===null?(s.next=e.pending=s,xy(e,s)):(s.next=n.next,e.pending=n.next=s)}}function xy(t,e){var n=e.action,i=e.payload,a=t.state;if(e.isTransition){var s=nt.T,l={};nt.T=l;try{var o=n(a,i),c=nt.S;c!==null&&c(l,o),vx(t,e,o)}catch(u){Gh(t,e,u)}finally{s!==null&&l.types!==null&&(s.types=l.types),nt.T=s}}else try{s=n(a,i),vx(t,e,s)}catch(u){Gh(t,e,u)}}function vx(t,e,n){n!==null&&typeof n=="object"&&typeof n.then=="function"?n.then(function(i){_x(t,e,i)},function(i){return Gh(t,e,i)}):_x(t,e,n)}function _x(t,e,n){e.status="fulfilled",e.value=n,vy(e),t.state=n,e=t.pending,e!==null&&(n=e.next,n===e?t.pending=null:(n=n.next,e.next=n,xy(t,n)))}function Gh(t,e,n){var i=t.pending;if(t.pending=null,i!==null){i=i.next;do e.status="rejected",e.reason=n,vy(e),e=e.next;while(e!==i)}t.action=null}function vy(t){t=t.listeners;for(var e=0;e<t.length;e++)(0,t[e])()}function _y(t,e){return e}function yx(t,e){if(xt){var n=zt.formState;if(n!==null){e:{var i=ot;if(xt){if(Ft){t:{for(var a=Ft,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){Ft=wi(a.nextSibling),i=a.data==="F!";break e}}Ms(i)}i=!1}i&&(e=n[0])}}return n=On(),n.memoizedState=n.baseState=e,i={pending:null,lanes:0,dispatch:null,lastRenderedReducer:_y,lastRenderedState:e},n.queue=i,n=Py.bind(null,ot,i),i.dispatch=n,i=Hh(!1),s=$m.bind(null,ot,!1,i.queue),i=On(),a={state:e,dispatch:null,action:t,pending:null},i.queue=a,n=WE.bind(null,ot,a,s,n),a.dispatch=n,i.memoizedState=t,[e,n,!1]}function bx(t){var e=tn();return yy(e,Ut,t)}function yy(t,e,n){if(e=Wm(t,e,_y)[0],t=pu(La)[0],typeof e=="object"&&e!==null&&typeof e.then=="function")try{var i=ic(e)}catch(l){throw l===Dl?Cd:l}else i=e;e=tn();var a=e.queue,s=a.dispatch;return n!==e.memoizedState&&(ot.flags|=2048,ml(9,{destroy:void 0},XE.bind(null,a,n),null)),[i,s,t]}function XE(t,e){t.action=e}function Sx(t){var e=tn(),n=Ut;if(n!==null)return yy(e,n,t);tn(),e=e.memoizedState,n=tn();var i=n.queue.dispatch;return n.memoizedState=t,[e,i,!1]}function ml(t,e,n,i){return t={tag:t,create:n,deps:i,inst:e,next:null},e=ot.updateQueue,e===null&&(e=Rd(),ot.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 by(){return tn().memoizedState}function mu(t,e,n,i){var a=On();ot.flags|=t,a.memoizedState=ml(1|e,{destroy:void 0},n,i===void 0?null:i)}function Ld(t,e,n,i){var a=tn();i=i===void 0?null:i;var s=a.memoizedState.inst;Ut!==null&&i!==null&&Bm(i,Ut.memoizedState.deps)?a.memoizedState=ml(e,s,n,i):(ot.flags|=t,a.memoizedState=ml(1|e,s,n,i))}function Mx(t,e){mu(8390656,8,t,e)}function Xm(t,e){Ld(2048,8,t,e)}function qE(t){ot.flags|=4;var e=ot.updateQueue;if(e===null)e=Rd(),ot.updateQueue=e,e.events=[t];else{var n=e.events;n===null?e.events=[t]:n.push(t)}}function Sy(t){var e=tn().memoizedState;return qE({ref:e,nextImpl:t}),function(){if(Tt&2)throw Error(fe(440));return e.impl.apply(void 0,arguments)}}function My(t,e){return Ld(4,2,t,e)}function Ey(t,e){return Ld(4,4,t,e)}function Ty(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 Ay(t,e,n){n=n!=null?n.concat([t]):null,Ld(4,4,Ty.bind(null,e,t),n)}function qm(){}function wy(t,e){var n=tn();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 Ny(t,e){var n=tn();e=e===void 0?null:e;var i=n.memoizedState;if(e!==null&&Bm(e,i[1]))return i[0];if(i=t(),Js){as(!0);try{t()}finally{as(!1)}}return n.memoizedState=[i,e],i}function Ym(t,e,n){return n===void 0||Da&1073741824&&!(pt&261930)?t.memoizedState=e:(t.memoizedState=n,t=gb(),ot.lanes|=t,Ts|=t,n)}function Cy(t,e,n,i){return di(n,e)?n:pl.current!==null?(t=Ym(t,n,i),di(t,e)||(cn=!0),t):!(Da&42)||Da&1073741824&&!(pt&261930)?(cn=!0,t.memoizedState=n):(t=gb(),ot.lanes|=t,Ts|=t,e)}function Ry(t,e,n,i,a){var s=At.p;At.p=s!==0&&8>s?s:8;var l=nt.T,o={};nt.T=o,$m(t,!1,e,n);try{var c=a(),u=nt.S;if(u!==null&&u(o,c),c!==null&&typeof c=="object"&&typeof c.then=="function"){var f=HE(c,i);So(t,e,f,ui(t))}else So(t,e,i,ui(t))}catch(m){So(t,e,{then:function(){},status:"rejected",reason:m},ui())}finally{At.p=s,l!==null&&o.types!==null&&(l.types=o.types),nt.T=l}}function YE(){}function Vh(t,e,n,i){if(t.tag!==5)throw Error(fe(476));var a=Dy(t).queue;Ry(t,a,e,Ws,n===null?YE:function(){return Ly(t),n(i)})}function Dy(t){var e=t.memoizedState;if(e!==null)return e;e={memoizedState:Ws,baseState:Ws,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:La,lastRenderedState:Ws},next:null};var n={};return e.next={memoizedState:n,baseState:n,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:La,lastRenderedState:n},next:null},t.memoizedState=e,t=t.alternate,t!==null&&(t.memoizedState=e),e}function Ly(t){var e=Dy(t);e.next===null&&(e=t.alternate.memoizedState),So(t,e.next.queue,{},ui())}function Km(){return Tn(Ho)}function Uy(){return tn().memoizedState}function Oy(){return tn().memoizedState}function KE(t){for(var e=t.return;e!==null;){switch(e.tag){case 24:case 3:var n=ui();t=ms(n);var i=gs(e,t,n);i!==null&&(Kn(i,e,n),_o(i,e,n)),e={cache:Om()},t.payload=e;return}e=e.return}}function $E(t,e,n){var i=ui();n={lane:i,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null},Ud(t)?zy(e,n):(n=Rm(t,e,n,i),n!==null&&(Kn(n,t,i),ky(n,e,i)))}function Py(t,e,n){var i=ui();So(t,e,n,i)}function So(t,e,n,i){var a={lane:i,revertLane:0,gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null};if(Ud(t))zy(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,di(o,l))return Nd(t,e,a,0),zt===null&&wd(),!1}catch{}finally{}if(n=Rm(t,e,a,i),n!==null)return Kn(n,t,i),ky(n,e,i),!0}return!1}function $m(t,e,n,i){if(i={lane:2,revertLane:sg(),gesture:null,action:i,hasEagerState:!1,eagerState:null,next:null},Ud(t)){if(e)throw Error(fe(479))}else e=Rm(t,n,i,2),e!==null&&Kn(e,t,2)}function Ud(t){var e=t.alternate;return t===ot||e!==null&&e===ot}function zy(t,e){al=Wu=!0;var n=t.pending;n===null?e.next=e:(e.next=n.next,n.next=e),t.pending=e}function ky(t,e,n){if(n&4194048){var i=e.lanes;i&=t.pendingLanes,n|=i,e.lanes=n,S_(t,n)}}var Bo={readContext:Tn,use:Dd,useCallback:Kt,useContext:Kt,useEffect:Kt,useImperativeHandle:Kt,useLayoutEffect:Kt,useInsertionEffect:Kt,useMemo:Kt,useReducer:Kt,useRef:Kt,useState:Kt,useDebugValue:Kt,useDeferredValue:Kt,useTransition:Kt,useSyncExternalStore:Kt,useId:Kt,useHostTransitionStatus:Kt,useFormState:Kt,useActionState:Kt,useOptimistic:Kt,useMemoCache:Kt,useCacheRefresh:Kt};Bo.useEffectEvent=Kt;var Iy={readContext:Tn,use:Dd,useCallback:function(t,e){return On().memoizedState=[t,e===void 0?null:e],t},useContext:Tn,useEffect:Mx,useImperativeHandle:function(t,e,n){n=n!=null?n.concat([t]):null,mu(4194308,4,Ty.bind(null,e,t),n)},useLayoutEffect:function(t,e){return mu(4194308,4,t,e)},useInsertionEffect:function(t,e){mu(4,2,t,e)},useMemo:function(t,e){var n=On();e=e===void 0?null:e;var i=t();if(Js){as(!0);try{t()}finally{as(!1)}}return n.memoizedState=[i,e],i},useReducer:function(t,e,n){var i=On();if(n!==void 0){var a=n(e);if(Js){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=$E.bind(null,ot,t),[i.memoizedState,t]},useRef:function(t){var e=On();return t={current:t},e.memoizedState=t},useState:function(t){t=Hh(t);var e=t.queue,n=Py.bind(null,ot,e);return e.dispatch=n,[t.memoizedState,n]},useDebugValue:qm,useDeferredValue:function(t,e){var n=On();return Ym(n,t,e)},useTransition:function(){var t=Hh(!1);return t=Ry.bind(null,ot,t.queue,!0,!1),On().memoizedState=t,[!1,t]},useSyncExternalStore:function(t,e,n){var i=ot,a=On();if(xt){if(n===void 0)throw Error(fe(407));n=n()}else{if(n=e(),zt===null)throw Error(fe(349));pt&127||dy(i,e,n)}a.memoizedState=n;var s={value:n,getSnapshot:e};return a.queue=s,Mx(hy.bind(null,i,s,t),[t]),i.flags|=2048,ml(9,{destroy:void 0},fy.bind(null,i,s,n,e),null),n},useId:function(){var t=On(),e=zt.identifierPrefix;if(xt){var n=Qi,i=Zi;n=(i&~(1<<32-ci(i)-1)).toString(32)+n,e="_"+e+"R_"+n,n=Xu++,0<n&&(e+="H"+n.toString(32)),e+="_"}else n=GE++,e="_"+e+"r_"+n.toString(32)+"_";return t.memoizedState=e},useHostTransitionStatus:Km,useFormState:yx,useActionState:yx,useOptimistic:function(t){var e=On();e.memoizedState=e.baseState=t;var n={pending:null,lanes:0,dispatch:null,lastRenderedReducer:null,lastRenderedState:null};return e.queue=n,e=$m.bind(null,ot,!0,n),n.dispatch=e,[t,e]},useMemoCache:Vm,useCacheRefresh:function(){return On().memoizedState=KE.bind(null,ot)},useEffectEvent:function(t){var e=On(),n={impl:t};return e.memoizedState=n,function(){if(Tt&2)throw Error(fe(440));return n.impl.apply(void 0,arguments)}}},Zm={readContext:Tn,use:Dd,useCallback:wy,useContext:Tn,useEffect:Xm,useImperativeHandle:Ay,useInsertionEffect:My,useLayoutEffect:Ey,useMemo:Ny,useReducer:pu,useRef:by,useState:function(){return pu(La)},useDebugValue:qm,useDeferredValue:function(t,e){var n=tn();return Cy(n,Ut.memoizedState,t,e)},useTransition:function(){var t=pu(La)[0],e=tn().memoizedState;return[typeof t=="boolean"?t:ic(t),e]},useSyncExternalStore:uy,useId:Uy,useHostTransitionStatus:Km,useFormState:bx,useActionState:bx,useOptimistic:function(t,e){var n=tn();return gy(n,Ut,t,e)},useMemoCache:Vm,useCacheRefresh:Oy};Zm.useEffectEvent=Sy;var By={readContext:Tn,use:Dd,useCallback:wy,useContext:Tn,useEffect:Xm,useImperativeHandle:Ay,useInsertionEffect:My,useLayoutEffect:Ey,useMemo:Ny,useReducer:cf,useRef:by,useState:function(){return cf(La)},useDebugValue:qm,useDeferredValue:function(t,e){var n=tn();return Ut===null?Ym(n,t,e):Cy(n,Ut.memoizedState,t,e)},useTransition:function(){var t=cf(La)[0],e=tn().memoizedState;return[typeof t=="boolean"?t:ic(t),e]},useSyncExternalStore:uy,useId:Uy,useHostTransitionStatus:Km,useFormState:Sx,useActionState:Sx,useOptimistic:function(t,e){var n=tn();return Ut!==null?gy(n,Ut,t,e):(n.baseState=t,[t,n.queue.dispatch])},useMemoCache:Vm,useCacheRefresh:Oy};By.useEffectEvent=Sy;function uf(t,e,n,i){e=t.memoizedState,n=n(i,e),n=n==null?e:Gt({},e,n),t.memoizedState=n,t.lanes===0&&(t.updateQueue.baseState=n)}var Wh={enqueueSetState:function(t,e,n){t=t._reactInternals;var i=ui(),a=ms(i);a.payload=e,n!=null&&(a.callback=n),e=gs(t,a,i),e!==null&&(Kn(e,t,i),_o(e,t,i))},enqueueReplaceState:function(t,e,n){t=t._reactInternals;var i=ui(),a=ms(i);a.tag=1,a.payload=e,n!=null&&(a.callback=n),e=gs(t,a,i),e!==null&&(Kn(e,t,i),_o(e,t,i))},enqueueForceUpdate:function(t,e){t=t._reactInternals;var n=ui(),i=ms(n);i.tag=2,e!=null&&(i.callback=e),e=gs(t,i,n),e!==null&&(Kn(e,t,n),_o(e,t,n))}};function Ex(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?!Oo(n,i)||!Oo(a,s):!0}function Tx(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&&Wh.enqueueReplaceState(e,e.state,null)}function er(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=Gt({},n));for(var a in t)n[a]===void 0&&(n[a]=t[a])}return n}function jy(t){Iu(t)}function Fy(t){console.error(t)}function Hy(t){Iu(t)}function qu(t,e){try{var n=t.onUncaughtError;n(e.value,{componentStack:e.stack})}catch(i){setTimeout(function(){throw i})}}function Ax(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 Xh(t,e,n){return n=ms(n),n.tag=3,n.payload={element:null},n.callback=function(){qu(t,e)},n}function Gy(t){return t=ms(t),t.tag=3,t}function Vy(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(){Ax(e,n,i)}}var l=n.stateNode;l!==null&&typeof l.componentDidCatch=="function"&&(t.callback=function(){Ax(e,n,i),typeof a!="function"&&(xs===null?xs=new Set([this]):xs.add(this));var o=i.stack;this.componentDidCatch(i.value,{componentStack:o!==null?o:""})})}function ZE(t,e,n,i,a){if(n.flags|=32768,i!==null&&typeof i=="object"&&typeof i.then=="function"){if(e=n.alternate,e!==null&&Rl(e,n,a,!0),n=fi.current,n!==null){switch(n.tag){case 31:case 13:return Ai===null?Qu():n.alternate===null&&$t===0&&($t=3),n.flags&=-257,n.flags|=65536,n.lanes=a,i===Hu?n.flags|=16384:(e=n.updateQueue,e===null?n.updateQueue=new Set([i]):e.add(i),bf(t,i,a)),!1;case 22:return n.flags|=65536,i===Hu?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)),bf(t,i,a)),!1}throw Error(fe(435,n.tag))}return bf(t,i,a),Qu(),!1}if(xt)return e=fi.current,e!==null?(!(e.flags&65536)&&(e.flags|=256),e.flags|=65536,e.lanes=a,i!==Uh&&(t=Error(fe(422),{cause:i}),zo(Si(t,n)))):(i!==Uh&&(e=Error(fe(423),{cause:i}),zo(Si(e,n))),t=t.current.alternate,t.flags|=65536,a&=-a,t.lanes|=a,i=Si(i,n),a=Xh(t.stateNode,i,a),of(t,a),$t!==4&&($t=2)),!1;var s=Error(fe(520),{cause:i});if(s=Si(s,n),To===null?To=[s]:To.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=Xh(n.stateNode,i,t),of(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"&&(xs===null||!xs.has(s))))return n.flags|=65536,a&=-a,n.lanes|=a,a=Gy(a),Vy(a,t,n,i),of(n,a),!1}n=n.return}while(n!==null);return!1}var Qm=Error(fe(461)),cn=!1;function Sn(t,e,n,i){e.child=t===null?ay(e,null,n,i):Qs(e,t.child,n,i)}function wx(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 Zs(e),i=jm(t,e,n,l,s,a),o=Fm(),t!==null&&!cn?(Hm(t,e,a),Ua(t,e,a)):(xt&&o&&Lm(e),e.flags|=1,Sn(t,e,i,a),e.child)}function Nx(t,e,n,i,a){if(t===null){var s=n.type;return typeof s=="function"&&!Dm(s)&&s.defaultProps===void 0&&n.compare===null?(e.tag=15,e.type=s,Wy(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,!Jm(t,a)){var l=s.memoizedProps;if(n=n.compare,n=n!==null?n:Oo,n(l,i)&&t.ref===e.ref)return Ua(t,e,a)}return e.flags|=1,t=Ta(s,i),t.ref=e.ref,t.return=e,e.child=t}function Wy(t,e,n,i,a){if(t!==null){var s=t.memoizedProps;if(Oo(s,i)&&t.ref===e.ref)if(cn=!1,e.pendingProps=i=s,Jm(t,a))t.flags&131072&&(cn=!0);else return e.lanes=t.lanes,Ua(t,e,a)}return qh(t,e,n,i,a)}function Xy(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 Cx(t,e,s,n,i)}if(n&536870912)e.memoizedState={baseLanes:0,cachePool:null},t!==null&&hu(e,s!==null?s.cachePool:null),s!==null?xx(e,s):jh(),ly(e);else return i=e.lanes=536870912,Cx(t,e,s!==null?s.baseLanes|n:n,n,i)}else s!==null?(hu(e,s.cachePool),xx(e,s),ts(),e.memoizedState=null):(t!==null&&hu(e,null),jh(),ts());return Sn(t,e,a,n),e.child}function ro(t,e){return t!==null&&t.tag===22||e.stateNode!==null||(e.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null}),e.sibling}function Cx(t,e,n,i,a){var s=Pm();return s=s===null?null:{parent:on._currentValue,pool:s},e.memoizedState={baseLanes:n,cachePool:s},t!==null&&hu(e,null),jh(),ly(e),t!==null&&Rl(t,e,i,!0),e.childLanes=a,null}function gu(t,e){return e=Yu({mode:e.mode,children:e.children},t.mode),e.ref=t.ref,t.child=e,e.return=t,e}function Rx(t,e,n){return Qs(e,t.child,null,n),t=gu(e,e.pendingProps),t.flags|=2,ai(e),e.memoizedState=null,t}function QE(t,e,n){var i=e.pendingProps,a=(e.flags&128)!==0;if(e.flags&=-129,t===null){if(xt){if(i.mode==="hidden")return t=gu(e,i),e.lanes=536870912,ro(null,t);if(Fh(e),(t=Ft)?(t=Ib(t,Mi),t=t!==null&&t.data==="&"?t:null,t!==null&&(e.memoizedState={dehydrated:t,treeContext:Ss!==null?{id:Zi,overflow:Qi}:null,retryLane:536870912,hydrationErrors:null},n=Z_(t),n.return=e,e.child=n,En=e,Ft=null)):t=null,t===null)throw Ms(e);return e.lanes=536870912,null}return gu(e,i)}var s=t.memoizedState;if(s!==null){var l=s.dehydrated;if(Fh(e),a)if(e.flags&256)e.flags&=-257,e=Rx(t,e,n);else if(e.memoizedState!==null)e.child=t.child,e.flags|=128,e=null;else throw Error(fe(558));else if(cn||Rl(t,e,n,!1),a=(n&t.childLanes)!==0,cn||a){if(i=zt,i!==null&&(l=M_(i,n),l!==0&&l!==s.retryLane))throw s.retryLane=l,cr(t,l),Kn(i,t,l),Qm;Qu(),e=Rx(t,e,n)}else t=s.treeContext,Ft=wi(l.nextSibling),En=e,xt=!0,ps=null,Mi=!1,t!==null&&J_(e,t),e=gu(e,i),e.flags|=4096;return e}return t=Ta(t.child,{mode:i.mode,children:i.children}),t.ref=e.ref,e.child=t,t.return=e,t}function xu(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 qh(t,e,n,i,a){return Zs(e),n=jm(t,e,n,i,void 0,a),i=Fm(),t!==null&&!cn?(Hm(t,e,a),Ua(t,e,a)):(xt&&i&&Lm(e),e.flags|=1,Sn(t,e,n,a),e.child)}function Dx(t,e,n,i,a,s){return Zs(e),e.updateQueue=null,n=cy(e,i,n,a),oy(t),i=Fm(),t!==null&&!cn?(Hm(t,e,s),Ua(t,e,s)):(xt&&i&&Lm(e),e.flags|=1,Sn(t,e,n,s),e.child)}function Lx(t,e,n,i,a){if(Zs(e),e.stateNode===null){var s=qr,l=n.contextType;typeof l=="object"&&l!==null&&(s=Tn(l)),s=new n(i,s),e.memoizedState=s.state!==null&&s.state!==void 0?s.state:null,s.updater=Wh,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?Tn(l):qr,s.state=e.memoizedState,l=n.getDerivedStateFromProps,typeof l=="function"&&(uf(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&&Wh.enqueueReplaceState(s,s.state,null),bo(e,i,s,a),yo(),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=er(n,o);s.props=c;var u=s.context,f=n.contextType;l=qr,typeof f=="object"&&f!==null&&(l=Tn(f));var m=n.getDerivedStateFromProps;f=typeof m=="function"||typeof s.getSnapshotBeforeUpdate=="function",o=e.pendingProps!==o,f||typeof s.UNSAFE_componentWillReceiveProps!="function"&&typeof s.componentWillReceiveProps!="function"||(o||u!==l)&&Tx(e,s,i,l),Qa=!1;var d=e.memoizedState;s.state=d,bo(e,i,s,a),yo(),u=e.memoizedState,o||d!==u||Qa?(typeof m=="function"&&(uf(e,n,m,i),u=e.memoizedState),(c=Qa||Ex(e,n,c,i,d,u,l))?(f||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,Ih(t,e),l=e.memoizedProps,f=er(n,l),s.props=f,m=e.pendingProps,d=s.context,u=n.contextType,c=qr,typeof u=="object"&&u!==null&&(c=Tn(u)),o=n.getDerivedStateFromProps,(u=typeof o=="function"||typeof s.getSnapshotBeforeUpdate=="function")||typeof s.UNSAFE_componentWillReceiveProps!="function"&&typeof s.componentWillReceiveProps!="function"||(l!==m||d!==c)&&Tx(e,s,i,c),Qa=!1,d=e.memoizedState,s.state=d,bo(e,i,s,a),yo();var p=e.memoizedState;l!==m||d!==p||Qa||t!==null&&t.dependencies!==null&&Fu(t.dependencies)?(typeof o=="function"&&(uf(e,n,o,i),p=e.memoizedState),(f=Qa||Ex(e,n,f,i,d,p,c)||t!==null&&t.dependencies!==null&&Fu(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=f):(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,xu(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)):Sn(t,e,n,a),e.memoizedState=s.state,t=e.child):t=Ua(t,e,a),t}function Ux(t,e,n,i){return $s(),e.flags|=256,Sn(t,e,n,i),e.child}var df={dehydrated:null,treeContext:null,retryLane:0,hydrationErrors:null};function ff(t){return{baseLanes:t,cachePool:ty()}}function hf(t,e,n){return t=t!==null?t.childLanes&~n:0,e&&(t|=ri),t}function qy(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:(en.current&2)!==0),l&&(a=!0,e.flags&=-129),l=(e.flags&32)!==0,e.flags&=-33,t===null){if(xt){if(a?es(e):ts(),(t=Ft)?(t=Ib(t,Mi),t=t!==null&&t.data!=="&"?t:null,t!==null&&(e.memoizedState={dehydrated:t,treeContext:Ss!==null?{id:Zi,overflow:Qi}:null,retryLane:536870912,hydrationErrors:null},n=Z_(t),n.return=e,e.child=n,En=e,Ft=null)):t=null,t===null)throw Ms(e);return lp(t)?e.lanes=32:e.lanes=536870912,null}var o=i.children;return i=i.fallback,a?(ts(),a=e.mode,o=Yu({mode:"hidden",children:o},a),i=Xs(i,a,n,null),o.return=e,i.return=e,o.sibling=i,e.child=o,i=e.child,i.memoizedState=ff(n),i.childLanes=hf(t,l,n),e.memoizedState=df,ro(null,i)):(es(e),Yh(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=pf(t,e,n)):e.memoizedState!==null?(ts(),e.child=t.child,e.flags|=128,e=null):(ts(),o=i.fallback,a=e.mode,i=Yu({mode:"visible",children:i.children},a),o=Xs(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=ff(n),i.childLanes=hf(t,l,n),e.memoizedState=df,e=ro(null,i));else if(es(e),lp(o)){if(l=o.nextSibling&&o.nextSibling.dataset,l)var u=l.dgst;l=u,i=Error(fe(419)),i.stack="",i.digest=l,zo({value:i,source:null,stack:null}),e=pf(t,e,n)}else if(cn||Rl(t,e,n,!1),l=(n&t.childLanes)!==0,cn||l){if(l=zt,l!==null&&(i=M_(l,n),i!==0&&i!==c.retryLane))throw c.retryLane=i,cr(t,i),Kn(l,t,i),Qm;rp(o)||Qu(),e=pf(t,e,n)}else rp(o)?(e.flags|=192,e.child=t.child,e=null):(t=c.treeContext,Ft=wi(o.nextSibling),En=e,xt=!0,ps=null,Mi=!1,t!==null&&J_(e,t),e=Yh(e,i.children),e.flags|=4096);return e}return a?(ts(),o=i.fallback,a=e.mode,c=t.child,u=c.sibling,i=Ta(c,{mode:"hidden",children:i.children}),i.subtreeFlags=c.subtreeFlags&65011712,u!==null?o=Ta(u,o):(o=Xs(o,a,n,null),o.flags|=2),o.return=e,i.return=e,i.sibling=o,e.child=i,ro(null,i),i=e.child,o=t.child.memoizedState,o===null?o=ff(n):(a=o.cachePool,a!==null?(c=on._currentValue,a=a.parent!==c?{parent:c,pool:c}:a):a=ty(),o={baseLanes:o.baseLanes|n,cachePool:a}),i.memoizedState=o,i.childLanes=hf(t,l,n),e.memoizedState=df,ro(t.child,i)):(es(e),n=t.child,t=n.sibling,n=Ta(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 Yh(t,e){return e=Yu({mode:"visible",children:e},t.mode),e.return=t,t.child=e}function Yu(t,e){return t=si(22,t,null,e),t.lanes=0,t}function pf(t,e,n){return Qs(e,t.child,null,n),t=Yh(e,e.pendingProps.children),t.flags|=2,e.memoizedState=null,t}function Ox(t,e,n){t.lanes|=e;var i=t.alternate;i!==null&&(i.lanes|=e),Ph(t.return,e,n)}function mf(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 Yy(t,e,n){var i=e.pendingProps,a=i.revealOrder,s=i.tail;i=i.children;var l=en.current,o=(l&2)!==0;if(o?(l=l&1|2,e.flags|=128):l&=1,It(en,l),Sn(t,e,i,n),i=xt?Po:0,!o&&t!==null&&t.flags&128)e:for(t=e.child;t!==null;){if(t.tag===13)t.memoizedState!==null&&Ox(t,n,e);else if(t.tag===19)Ox(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&&Vu(t)===null&&(a=n),n=n.sibling;n=a,n===null?(a=e.child,e.child=null):(a=n.sibling,n.sibling=null),mf(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&&Vu(t)===null){e.child=a;break}t=a.sibling,a.sibling=n,n=a,a=t}mf(e,!0,n,null,s,i);break;case"together":mf(e,!1,null,null,void 0,i);break;default:e.memoizedState=null}return e.child}function Ua(t,e,n){if(t!==null&&(e.dependencies=t.dependencies),Ts|=e.lanes,!(n&e.childLanes))if(t!==null){if(Rl(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=Ta(t,t.pendingProps),e.child=n,n.return=e;t.sibling!==null;)t=t.sibling,n=n.sibling=Ta(t,t.pendingProps),n.return=e;n.sibling=null}return e.child}function Jm(t,e){return t.lanes&e?!0:(t=t.dependencies,!!(t!==null&&Fu(t)))}function JE(t,e,n){switch(e.tag){case 3:Ou(e,e.stateNode.containerInfo),Ja(e,on,t.memoizedState.cache),$s();break;case 27:case 5:Sh(e);break;case 4:Ou(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,Fh(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?qy(t,e,n):(es(e),t=Ua(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||(Rl(t,e,n,!1),i=(n&e.childLanes)!==0),a){if(i)return Yy(t,e,n);e.flags|=128}if(a=e.memoizedState,a!==null&&(a.rendering=null,a.tail=null,a.lastEffect=null),It(en,en.current),i)break;return null;case 22:return e.lanes=0,Xy(t,e,n,e.pendingProps);case 24:Ja(e,on,t.memoizedState.cache)}return Ua(t,e,n)}function Ky(t,e,n){if(t!==null)if(t.memoizedProps!==e.pendingProps)cn=!0;else{if(!Jm(t,n)&&!(e.flags&128))return cn=!1,JE(t,e,n);cn=!!(t.flags&131072)}else cn=!1,xt&&e.flags&1048576&&Q_(e,Po,e.index);switch(e.lanes=0,e.tag){case 16:e:{var i=e.pendingProps;if(t=Is(e.elementType),e.type=t,typeof t=="function")Dm(t)?(i=er(t,i),e.tag=1,e=Lx(null,e,t,i,n)):(e.tag=0,e=qh(null,e,t,i,n));else{if(t!=null){var a=t.$$typeof;if(a===xm){e.tag=11,e=wx(null,e,t,i,n);break e}else if(a===vm){e.tag=14,e=Nx(null,e,t,i,n);break e}}throw e=yh(t)||t,Error(fe(306,e,""))}}return e;case 0:return qh(t,e,e.type,e.pendingProps,n);case 1:return i=e.type,a=er(i,e.pendingProps),Lx(t,e,i,a,n);case 3:e:{if(Ou(e,e.stateNode.containerInfo),t===null)throw Error(fe(387));i=e.pendingProps;var s=e.memoizedState;a=s.element,Ih(t,e),bo(e,i,null,n);var l=e.memoizedState;if(i=l.cache,Ja(e,on,i),i!==s.cache&&zh(e,[on],n,!0),yo(),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=Ux(t,e,i,n);break e}else if(i!==a){a=Si(Error(fe(424)),e),zo(a),e=Ux(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(Ft=wi(t.firstChild),En=e,xt=!0,ps=null,Mi=!0,n=ay(e,null,i,n),e.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling}else{if($s(),i===a){e=Ua(t,e,n);break e}Sn(t,e,i,n)}e=e.child}return e;case 26:return xu(t,e),t===null?(n=e0(e.type,null,e.pendingProps,null))?e.memoizedState=n:xt||(n=e.type,t=e.pendingProps,i=nd(hs.current).createElement(n),i[Mn]=e,i[Zn]=t,wn(i,n,t),vn(i),e.stateNode=i):e.memoizedState=e0(e.type,t.memoizedProps,e.pendingProps,t.memoizedState),null;case 27:return Sh(e),t===null&&xt&&(i=e.stateNode=Bb(e.type,e.pendingProps,hs.current),En=e,Mi=!0,a=Ft,Ns(e.type)?(op=a,Ft=wi(i.firstChild)):Ft=a),Sn(t,e,e.pendingProps.children,n),xu(t,e),t===null&&(e.flags|=4194304),e.child;case 5:return t===null&&xt&&((a=i=Ft)&&(i=C1(i,e.type,e.pendingProps,Mi),i!==null?(e.stateNode=i,En=e,Ft=wi(i.firstChild),Mi=!1,a=!0):a=!1),a||Ms(e)),Sh(e),a=e.type,s=e.pendingProps,l=t!==null?t.memoizedProps:null,i=s.children,ap(a,s)?i=null:l!==null&&ap(a,l)&&(e.flags|=32),e.memoizedState!==null&&(a=jm(t,e,VE,null,null,n),Ho._currentValue=a),xu(t,e),Sn(t,e,i,n),e.child;case 6:return t===null&&xt&&((t=n=Ft)&&(n=R1(n,e.pendingProps,Mi),n!==null?(e.stateNode=n,En=e,Ft=null,t=!0):t=!1),t||Ms(e)),null;case 13:return qy(t,e,n);case 4:return Ou(e,e.stateNode.containerInfo),i=e.pendingProps,t===null?e.child=Qs(e,null,i,n):Sn(t,e,i,n),e.child;case 11:return wx(t,e,e.type,e.pendingProps,n);case 7:return Sn(t,e,e.pendingProps,n),e.child;case 8:return Sn(t,e,e.pendingProps.children,n),e.child;case 12:return Sn(t,e,e.pendingProps.children,n),e.child;case 10:return i=e.pendingProps,Ja(e,e.type,i.value),Sn(t,e,i.children,n),e.child;case 9:return a=e.type._context,i=e.pendingProps.children,Zs(e),a=Tn(a),i=i(a),e.flags|=1,Sn(t,e,i,n),e.child;case 14:return Nx(t,e,e.type,e.pendingProps,n);case 15:return Wy(t,e,e.type,e.pendingProps,n);case 19:return Yy(t,e,n);case 31:return QE(t,e,n);case 22:return Xy(t,e,n,e.pendingProps);case 24:return Zs(e),i=Tn(on),t===null?(a=Pm(),a===null&&(a=zt,s=Om(),a.pooledCache=s,s.refCount++,s!==null&&(a.pooledCacheLanes|=n),a=s),e.memoizedState={parent:i,cache:a},km(e),Ja(e,on,a)):(t.lanes&n&&(Ih(t,e),bo(e,null,null,n),yo()),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,on,i)):(i=s.cache,Ja(e,on,i),i!==a.cache&&zh(e,[on],n,!0))),Sn(t,e,e.pendingProps.children,n),e.child;case 29:throw e.pendingProps}throw Error(fe(156,e.tag))}function oa(t){t.flags|=4}function gf(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(_b())t.flags|=8192;else throw Ys=Hu,zm}else t.flags&=-16777217}function Px(t,e){if(e.type!=="stylesheet"||e.state.loading&4)t.flags&=-16777217;else if(t.flags|=16777216,!Hb(e))if(_b())t.flags|=8192;else throw Ys=Hu,zm}function yc(t,e){e!==null&&(t.flags|=4),t.flags&16384&&(e=t.tag!==22?y_():536870912,t.lanes|=e,gl|=e)}function Hl(t,e){if(!xt)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 e1(t,e,n){var i=e.pendingProps;switch(Um(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),Aa(on),ul(),n.pendingContext&&(n.context=n.pendingContext,n.pendingContext=null),(t===null||t.child===null)&&(gr(e)?oa(e):t===null||t.memoizedState.isDehydrated&&!(e.flags&256)||(e.flags|=1024,lf())),jt(e),null;case 26:var a=e.type,s=e.memoizedState;return t===null?(oa(e),s!==null?(jt(e),Px(e,s)):(jt(e),gf(e,a,null,i,n))):s?s!==t.memoizedState?(oa(e),jt(e),Px(e,s)):(jt(e),e.flags&=-16777217):(t=t.memoizedProps,t!==i&&oa(e),jt(e),gf(e,a,t,i,n)),null;case 27:if(Pu(e),n=hs.current,a=e.type,t!==null&&e.stateNode!=null)t.memoizedProps!==i&&oa(e);else{if(!i){if(e.stateNode===null)throw Error(fe(166));return jt(e),null}t=ta.current,gr(e)?ux(e):(t=Bb(a,i,n),e.stateNode=t,oa(e))}return jt(e),null;case 5:if(Pu(e),a=e.type,t!==null&&e.stateNode!=null)t.memoizedProps!==i&&oa(e);else{if(!i){if(e.stateNode===null)throw Error(fe(166));return jt(e),null}if(s=ta.current,gr(e))ux(e);else{var l=nd(hs.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[Mn]=e,s[Zn]=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(wn(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&&oa(e)}}return jt(e),gf(e,e.type,t===null?null:t.memoizedProps,e.pendingProps,n),null;case 6:if(t&&e.stateNode!=null)t.memoizedProps!==i&&oa(e);else{if(typeof i!="string"&&e.stateNode===null)throw Error(fe(166));if(t=hs.current,gr(e)){if(t=e.stateNode,n=e.memoizedProps,i=null,a=En,a!==null)switch(a.tag){case 27:case 5:i=a.memoizedProps}t[Mn]=e,t=!!(t.nodeValue===n||i!==null&&i.suppressHydrationWarning===!0||Pb(t.nodeValue,n)),t||Ms(e,!0)}else t=nd(t).createTextNode(i),t[Mn]=e,e.stateNode=t}return jt(e),null;case 31:if(n=e.memoizedState,t===null||t.memoizedState!==null){if(i=gr(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[Mn]=e}else $s(),!(e.flags&128)&&(e.memoizedState=null),e.flags|=4;jt(e),t=!1}else n=lf(),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(fe(558))}return jt(e),null;case 13:if(i=e.memoizedState,t===null||t.memoizedState!==null&&t.memoizedState.dehydrated!==null){if(a=gr(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[Mn]=e}else $s(),!(e.flags&128)&&(e.memoizedState=null),e.flags|=4;jt(e),a=!1}else a=lf(),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),yc(e,e.updateQueue),jt(e),null);case 4:return ul(),t===null&&rg(e.stateNode.containerInfo),jt(e),null;case 10:return Aa(e.type),jt(e),null;case 19:if(yn(en),i=e.memoizedState,i===null)return jt(e),null;if(a=(e.flags&128)!==0,s=i.rendering,s===null)if(a)Hl(i,!1);else{if($t!==0||t!==null&&t.flags&128)for(t=e.child;t!==null;){if(s=Vu(t),s!==null){for(e.flags|=128,Hl(i,!1),t=s.updateQueue,e.updateQueue=t,yc(e,t),e.subtreeFlags=0,t=n,n=e.child;n!==null;)$_(n,t),n=n.sibling;return It(en,en.current&1|2),xt&&va(e,i.treeForkCount),e.child}t=t.sibling}i.tail!==null&&li()>$u&&(e.flags|=128,a=!0,Hl(i,!1),e.lanes=4194304)}else{if(!a)if(t=Vu(s),t!==null){if(e.flags|=128,a=!0,t=t.updateQueue,e.updateQueue=t,yc(e,t),Hl(i,!0),i.tail===null&&i.tailMode==="hidden"&&!s.alternate&&!xt)return jt(e),null}else 2*li()-i.renderingStartTime>$u&&n!==536870912&&(e.flags|=128,a=!0,Hl(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=en.current,It(en,a?n&1|2:n&1),xt&&va(e,i.treeForkCount),t):(jt(e),null);case 22:case 23:return ai(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&&yn(qs),null;case 24:return n=null,t!==null&&(n=t.memoizedState.cache),e.memoizedState.cache!==n&&(e.flags|=2048),Aa(on),jt(e),null;case 25:return null;case 30:return null}throw Error(fe(156,e.tag))}function t1(t,e){switch(Um(e),e.tag){case 1:return t=e.flags,t&65536?(e.flags=t&-65537|128,e):null;case 3:return Aa(on),ul(),t=e.flags,t&65536&&!(t&128)?(e.flags=t&-65537|128,e):null;case 26:case 27:case 5:return Pu(e),null;case 31:if(e.memoizedState!==null){if(ai(e),e.alternate===null)throw Error(fe(340));$s()}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(fe(340));$s()}return t=e.flags,t&65536?(e.flags=t&-65537|128,e):null;case 19:return yn(en),null;case 4:return ul(),null;case 10:return Aa(e.type),null;case 22:case 23:return ai(e),Im(),t!==null&&yn(qs),t=e.flags,t&65536?(e.flags=t&-65537|128,e):null;case 24:return Aa(on),null;case 25:return null;default:return null}}function $y(t,e){switch(Um(e),e.tag){case 3:Aa(on),ul();break;case 26:case 27:case 5:Pu(e);break;case 4:ul();break;case 31:e.memoizedState!==null&&ai(e);break;case 13:ai(e);break;case 19:yn(en);break;case 10:Aa(e.type);break;case 22:case 23:ai(e),Im(),t!==null&&yn(qs);break;case 24:Aa(on)}}function ac(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){Rt(e,e.return,o)}}function Es(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(f){Rt(a,c,f)}}}i=i.next}while(i!==s)}}catch(f){Rt(e,e.return,f)}}function Zy(t){var e=t.updateQueue;if(e!==null){var n=t.stateNode;try{ry(e,n)}catch(i){Rt(t,t.return,i)}}}function Qy(t,e,n){n.props=er(t.type,t.memoizedProps),n.state=t.memoizedState;try{n.componentWillUnmount()}catch(i){Rt(t,e,i)}}function Mo(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 Ji(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 Jy(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 xf(t,e,n){try{var i=t.stateNode;M1(i,t.type,n,e),i[Zn]=e}catch(a){Rt(t,t.return,a)}}function eb(t){return t.tag===5||t.tag===3||t.tag===26||t.tag===27&&Ns(t.type)||t.tag===4}function vf(t){e:for(;;){for(;t.sibling===null;){if(t.return===null||eb(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&&Ns(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 Kh(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=Sa));else if(i!==4&&(i===27&&Ns(t.type)&&(n=t.stateNode,e=null),t=t.child,t!==null))for(Kh(t,e,n),t=t.sibling;t!==null;)Kh(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&&Ns(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 tb(t){var e=t.stateNode,n=t.memoizedProps;try{for(var i=t.type,a=e.attributes;a.length;)e.removeAttributeNode(a[0]);wn(e,i,n),e[Mn]=t,e[Zn]=n}catch(s){Rt(t,t.return,s)}}var _a=!1,ln=!1,_f=!1,zx=typeof WeakSet=="function"?WeakSet:Set,xn=null;function n1(t,e){if(t=t.containerInfo,np=rd,t=H_(t),Nm(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,f=0,m=t,d=null;t:for(;;){for(var p;m!==n||a!==0&&m.nodeType!==3||(o=l+a),m!==s||i!==0&&m.nodeType!==3||(c=l+i),m.nodeType===3&&(l+=m.nodeValue.length),(p=m.firstChild)!==null;)d=m,m=p;for(;;){if(m===t)break t;if(d===n&&++u===a&&(o=l),d===s&&++f===i&&(c=l),(p=m.nextSibling)!==null)break;m=d,d=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(ip={focusedElem:t,selectionRange:n},rd=!1,xn=e;xn!==null;)if(e=xn,t=e.child,(e.subtreeFlags&1028)!==0&&t!==null)t.return=e,xn=t;else for(;xn!==null;){switch(e=xn,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 g=er(n.type,a);t=i.getSnapshotBeforeUpdate(g,s),i.__reactInternalSnapshotBeforeUpdate=t}catch(b){Rt(n,n.return,b)}}break;case 3:if(t&1024){if(t=e.stateNode.containerInfo,n=t.nodeType,n===9)sp(t);else if(n===1)switch(t.nodeName){case"HEAD":case"HTML":case"BODY":sp(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,xn=t;break}xn=e.return}}function nb(t,e,n){var i=n.flags;switch(n.tag){case 0:case 11:case 15:ua(t,n),i&4&&ac(5,n);break;case 1:if(ua(t,n),i&4)if(t=n.stateNode,e===null)try{t.componentDidMount()}catch(l){Rt(n,n.return,l)}else{var a=er(n.type,e.memoizedProps);e=e.memoizedState;try{t.componentDidUpdate(a,e,t.__reactInternalSnapshotBeforeUpdate)}catch(l){Rt(n,n.return,l)}}i&64&&Zy(n),i&512&&Mo(n,n.return);break;case 3:if(ua(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{ry(t,e)}catch(l){Rt(n,n.return,l)}}break;case 27:e===null&&i&4&&tb(n);case 26:case 5:ua(t,n),e===null&&i&4&&Jy(n),i&512&&Mo(n,n.return);break;case 12:ua(t,n);break;case 31:ua(t,n),i&4&&sb(t,n);break;case 13:ua(t,n),i&4&&rb(t,n),i&64&&(t=n.memoizedState,t!==null&&(t=t.dehydrated,t!==null&&(n=d1.bind(null,n),D1(t,n))));break;case 22:if(i=n.memoizedState!==null||_a,!i){e=e!==null&&e.memoizedState!==null||ln,a=_a;var s=ln;_a=i,(ln=e)&&!s?ga(t,n,(n.subtreeFlags&8772)!==0):ua(t,n),_a=a,ln=s}break;case 30:break;default:ua(t,n)}}function ib(t){var e=t.alternate;e!==null&&(t.alternate=null,ib(e)),t.child=null,t.deletions=null,t.sibling=null,t.tag===5&&(e=t.stateNode,e!==null&&Sm(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 Vt=null,Wn=!1;function ca(t,e,n){for(n=n.child;n!==null;)ab(t,e,n),n=n.sibling}function ab(t,e,n){if(oi&&typeof oi.onCommitFiberUnmount=="function")try{oi.onCommitFiberUnmount(Zo,n)}catch{}switch(n.tag){case 26:ln||Ji(n,e),ca(t,e,n),n.memoizedState?n.memoizedState.count--:n.stateNode&&(n=n.stateNode,n.parentNode.removeChild(n));break;case 27:ln||Ji(n,e);var i=Vt,a=Wn;Ns(n.type)&&(Vt=n.stateNode,Wn=!1),ca(t,e,n),wo(n.stateNode),Vt=i,Wn=a;break;case 5:ln||Ji(n,e);case 6:if(i=Vt,a=Wn,Vt=null,ca(t,e,n),Vt=i,Wn=a,Vt!==null)if(Wn)try{(Vt.nodeType===9?Vt.body:Vt.nodeName==="HTML"?Vt.ownerDocument.body:Vt).removeChild(n.stateNode)}catch(s){Rt(n,e,s)}else try{Vt.removeChild(n.stateNode)}catch(s){Rt(n,e,s)}break;case 18:Vt!==null&&(Wn?(t=Vt,Kx(t.nodeType===9?t.body:t.nodeName==="HTML"?t.ownerDocument.body:t,n.stateNode),yl(t)):Kx(Vt,n.stateNode));break;case 4:i=Vt,a=Wn,Vt=n.stateNode.containerInfo,Wn=!0,ca(t,e,n),Vt=i,Wn=a;break;case 0:case 11:case 14:case 15:Es(2,n,e),ln||Es(4,n,e),ca(t,e,n);break;case 1:ln||(Ji(n,e),i=n.stateNode,typeof i.componentWillUnmount=="function"&&Qy(n,e,i)),ca(t,e,n);break;case 21:ca(t,e,n);break;case 22:ln=(i=ln)||n.memoizedState!==null,ca(t,e,n),ln=i;break;default:ca(t,e,n)}}function sb(t,e){if(e.memoizedState===null&&(t=e.alternate,t!==null&&(t=t.memoizedState,t!==null))){t=t.dehydrated;try{yl(t)}catch(n){Rt(e,e.return,n)}}}function rb(t,e){if(e.memoizedState===null&&(t=e.alternate,t!==null&&(t=t.memoizedState,t!==null&&(t=t.dehydrated,t!==null))))try{yl(t)}catch(n){Rt(e,e.return,n)}}function i1(t){switch(t.tag){case 31:case 13:case 19:var e=t.stateNode;return e===null&&(e=t.stateNode=new zx),e;case 22:return t=t.stateNode,e=t._retryCache,e===null&&(e=t._retryCache=new zx),e;default:throw Error(fe(435,t.tag))}}function bc(t,e){var n=i1(t);e.forEach(function(i){if(!n.has(i)){n.add(i);var a=f1.bind(null,t,i);i.then(a,a)}})}function Hn(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(Ns(o.type)){Vt=o.stateNode,Wn=!1;break e}break;case 5:Vt=o.stateNode,Wn=!1;break e;case 3:case 4:Vt=o.stateNode.containerInfo,Wn=!0;break e}o=o.return}if(Vt===null)throw Error(fe(160));ab(s,l,a),Vt=null,Wn=!1,s=a.alternate,s!==null&&(s.return=null),a.return=null}if(e.subtreeFlags&13886)for(e=e.child;e!==null;)lb(e,t),e=e.sibling}var Pi=null;function lb(t,e){var n=t.alternate,i=t.flags;switch(t.tag){case 0:case 11:case 14:case 15:Hn(e,t),Gn(t),i&4&&(Es(3,t,t.return),ac(3,t),Es(5,t,t.return));break;case 1:Hn(e,t),Gn(t),i&512&&(ln||n===null||Ji(n,n.return)),i&64&&_a&&(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(Hn(e,t),Gn(t),i&512&&(ln||n===null||Ji(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[ec]||s[Mn]||s.namespaceURI==="http://www.w3.org/2000/svg"||s.hasAttribute("itemprop"))&&(s=a.createElement(i),a.head.insertBefore(s,a.querySelector("head > title"))),wn(s,i,n),s[Mn]=t,vn(s),i=s;break e;case"link":var l=n0("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),wn(s,i,n),a.head.appendChild(s);break;case"meta":if(l=n0("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),wn(s,i,n),a.head.appendChild(s);break;default:throw Error(fe(468,i))}s[Mn]=t,vn(s),i=s}t.stateNode=i}else i0(a,t.type,t.stateNode);else t.stateNode=t0(a,i,t.memoizedProps);else s!==i?(s===null?n.stateNode!==null&&(n=n.stateNode,n.parentNode.removeChild(n)):s.count--,i===null?i0(a,t.type,t.stateNode):t0(a,i,t.memoizedProps)):i===null&&t.stateNode!==null&&xf(t,t.memoizedProps,n.memoizedProps)}break;case 27:Hn(e,t),Gn(t),i&512&&(ln||n===null||Ji(n,n.return)),n!==null&&i&4&&xf(t,t.memoizedProps,n.memoizedProps);break;case 5:if(Hn(e,t),Gn(t),i&512&&(ln||n===null||Ji(n,n.return)),t.flags&32){a=t.stateNode;try{fl(a,"")}catch(g){Rt(t,t.return,g)}}i&4&&t.stateNode!=null&&(a=t.memoizedProps,xf(t,a,n!==null?n.memoizedProps:a)),i&1024&&(_f=!0);break;case 6:if(Hn(e,t),Gn(t),i&4){if(t.stateNode===null)throw Error(fe(162));i=t.memoizedProps,n=t.stateNode;try{n.nodeValue=i}catch(g){Rt(t,t.return,g)}}break;case 3:if(yu=null,a=Pi,Pi=id(e.containerInfo),Hn(e,t),Pi=a,Gn(t),i&4&&n!==null&&n.memoizedState.isDehydrated)try{yl(e.containerInfo)}catch(g){Rt(t,t.return,g)}_f&&(_f=!1,ob(t));break;case 4:i=Pi,Pi=id(t.stateNode.containerInfo),Hn(e,t),Gn(t),Pi=i;break;case 12:Hn(e,t),Gn(t);break;case 31:Hn(e,t),Gn(t),i&4&&(i=t.updateQueue,i!==null&&(t.updateQueue=null,bc(t,i)));break;case 13:Hn(e,t),Gn(t),t.child.flags&8192&&t.memoizedState!==null!=(n!==null&&n.memoizedState!==null)&&(Od=li()),i&4&&(i=t.updateQueue,i!==null&&(t.updateQueue=null,bc(t,i)));break;case 22:a=t.memoizedState!==null;var c=n!==null&&n.memoizedState!==null,u=_a,f=ln;if(_a=u||a,ln=f||c,Hn(e,t),ln=f,_a=u,Gn(t),i&8192)e:for(e=t.stateNode,e._visibility=a?e._visibility&-2:e._visibility|1,a&&(n===null||c||_a||ln||Bs(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 m=c.memoizedProps.style,d=m!=null&&m.hasOwnProperty("display")?m.display:null;o.style.display=d==null||typeof d=="boolean"?"":(""+d).trim()}}catch(g){Rt(c,c.return,g)}}}else if(e.tag===6){if(n===null){c=e;try{c.stateNode.nodeValue=a?"":c.memoizedProps}catch(g){Rt(c,c.return,g)}}}else if(e.tag===18){if(n===null){c=e;try{var p=c.stateNode;a?$x(p,!0):$x(c.stateNode,!1)}catch(g){Rt(c,c.return,g)}}}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,bc(t,n))));break;case 19:Hn(e,t),Gn(t),i&4&&(i=t.updateQueue,i!==null&&(t.updateQueue=null,bc(t,i)));break;case 30:break;case 21:break;default:Hn(e,t),Gn(t)}}function Gn(t){var e=t.flags;if(e&2){try{for(var n,i=t.return;i!==null;){if(eb(i)){n=i;break}i=i.return}if(n==null)throw Error(fe(160));switch(n.tag){case 27:var a=n.stateNode,s=vf(t);Ku(t,s,a);break;case 5:var l=n.stateNode;n.flags&32&&(fl(l,""),n.flags&=-33);var o=vf(t);Ku(t,o,l);break;case 3:case 4:var c=n.stateNode.containerInfo,u=vf(t);Kh(t,u,c);break;default:throw Error(fe(161))}}catch(f){Rt(t,t.return,f)}t.flags&=-3}e&4096&&(t.flags&=-4097)}function ob(t){if(t.subtreeFlags&1024)for(t=t.child;t!==null;){var e=t;ob(e),e.tag===5&&e.flags&1024&&e.stateNode.reset(),t=t.sibling}}function ua(t,e){if(e.subtreeFlags&8772)for(e=e.child;e!==null;)nb(t,e.alternate,e),e=e.sibling}function Bs(t){for(t=t.child;t!==null;){var e=t;switch(e.tag){case 0:case 11:case 14:case 15:Es(4,e,e.return),Bs(e);break;case 1:Ji(e,e.return);var n=e.stateNode;typeof n.componentWillUnmount=="function"&&Qy(e,e.return,n),Bs(e);break;case 27:wo(e.stateNode);case 26:case 5:Ji(e,e.return),Bs(e);break;case 22:e.memoizedState===null&&Bs(e);break;case 30:Bs(e);break;default:Bs(e)}t=t.sibling}}function ga(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:ga(a,s,n),ac(4,s);break;case 1:if(ga(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++)sy(c[a],o)}catch(u){Rt(i,i.return,u)}}n&&l&64&&Zy(s),Mo(s,s.return);break;case 27:tb(s);case 26:case 5:ga(a,s,n),n&&i===null&&l&4&&Jy(s),Mo(s,s.return);break;case 12:ga(a,s,n);break;case 31:ga(a,s,n),n&&l&4&&sb(a,s);break;case 13:ga(a,s,n),n&&l&4&&rb(a,s);break;case 22:s.memoizedState===null&&ga(a,s,n),Mo(s,s.return);break;case 30:break;default:ga(a,s,n)}e=e.sibling}}function eg(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&&nc(n))}function tg(t,e){t=null,e.alternate!==null&&(t=e.alternate.memoizedState.cache),e=e.memoizedState.cache,e!==t&&(e.refCount++,t!=null&&nc(t))}function Di(t,e,n,i){if(e.subtreeFlags&10256)for(e=e.child;e!==null;)cb(t,e,n,i),e=e.sibling}function cb(t,e,n,i){var a=e.flags;switch(e.tag){case 0:case 11:case 15:Di(t,e,n,i),a&2048&&ac(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&&nc(t)));break;case 12:if(a&2048){Di(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){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,l=e.alternate,e.memoizedState!==null?s._visibility&2?Di(t,e,n,i):Eo(t,e):s._visibility&2?Di(t,e,n,i):(s._visibility|=2,kr(t,e,n,i,(e.subtreeFlags&10256)!==0||!1)),a&2048&&eg(l,e);break;case 24:Di(t,e,n,i),a&2048&&tg(e.alternate,e);break;default:Di(t,e,n,i)}}function kr(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:kr(s,l,o,c,a),ac(8,l);break;case 23:break;case 22:var f=l.stateNode;l.memoizedState!==null?f._visibility&2?kr(s,l,o,c,a):Eo(s,l):(f._visibility|=2,kr(s,l,o,c,a)),a&&u&2048&&eg(l.alternate,l);break;case 24:kr(s,l,o,c,a),a&&u&2048&&tg(l.alternate,l);break;default:kr(s,l,o,c,a)}e=e.sibling}}function Eo(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:Eo(n,i),a&2048&&eg(i.alternate,i);break;case 24:Eo(n,i),a&2048&&tg(i.alternate,i);break;default:Eo(n,i)}e=e.sibling}}var lo=8192;function xr(t,e,n){if(t.subtreeFlags&lo)for(t=t.child;t!==null;)ub(t,e,n),t=t.sibling}function ub(t,e,n){switch(t.tag){case 26:xr(t,e,n),t.flags&lo&&t.memoizedState!==null&&G1(n,Pi,t.memoizedState,t.memoizedProps);break;case 5:xr(t,e,n);break;case 3:case 4:var i=Pi;Pi=id(t.stateNode.containerInfo),xr(t,e,n),Pi=i;break;case 22:t.memoizedState===null&&(i=t.alternate,i!==null&&i.memoizedState!==null?(i=lo,lo=16777216,xr(t,e,n),lo=i):xr(t,e,n));break;default:xr(t,e,n)}}function db(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 Gl(t){var e=t.deletions;if(t.flags&16){if(e!==null)for(var n=0;n<e.length;n++){var i=e[n];xn=i,hb(i,t)}db(t)}if(t.subtreeFlags&10256)for(t=t.child;t!==null;)fb(t),t=t.sibling}function fb(t){switch(t.tag){case 0:case 11:case 15:Gl(t),t.flags&2048&&Es(9,t,t.return);break;case 3:Gl(t);break;case 12:Gl(t);break;case 22:var e=t.stateNode;t.memoizedState!==null&&e._visibility&2&&(t.return===null||t.return.tag!==13)?(e._visibility&=-3,vu(t)):Gl(t);break;default:Gl(t)}}function vu(t){var e=t.deletions;if(t.flags&16){if(e!==null)for(var n=0;n<e.length;n++){var i=e[n];xn=i,hb(i,t)}db(t)}for(t=t.child;t!==null;){switch(e=t,e.tag){case 0:case 11:case 15:Es(8,e,e.return),vu(e);break;case 22:n=e.stateNode,n._visibility&2&&(n._visibility&=-3,vu(e));break;default:vu(e)}t=t.sibling}}function hb(t,e){for(;xn!==null;){var n=xn;switch(n.tag){case 0:case 11:case 15:Es(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:nc(n.memoizedState.cache)}if(i=n.child,i!==null)i.return=n,xn=i;else e:for(n=t;xn!==null;){i=xn;var a=i.sibling,s=i.return;if(ib(i),i===n){xn=null;break e}if(a!==null){a.return=s,xn=a;break e}xn=s}}}var a1={getCacheForType:function(t){var e=Tn(on),n=e.data.get(t);return n===void 0&&(n=t(),e.data.set(t,n)),n},cacheSignal:function(){return Tn(on).controller.signal}},s1=typeof WeakMap=="function"?WeakMap:Map,Tt=0,zt=null,ft=null,pt=0,Ct=0,ii=null,rs=!1,Ll=!1,ng=!1,Oa=0,$t=0,Ts=0,Ks=0,ig=0,ri=0,gl=0,To=null,Xn=null,$h=!1,Od=0,pb=0,$u=1/0,Zu=null,xs=null,dn=0,vs=null,xl=null,wa=0,Zh=0,Qh=null,mb=null,Ao=0,Jh=null;function ui(){return Tt&2&&pt!==0?pt&-pt:nt.T!==null?sg():E_()}function gb(){if(ri===0)if(!(pt&536870912)||xt){var t=hc;hc<<=1,!(hc&3932160)&&(hc=262144),ri=t}else ri=536870912;return t=fi.current,t!==null&&(t.flags|=32),ri}function Kn(t,e,n){(t===zt&&(Ct===2||Ct===9)||t.cancelPendingCommit!==null)&&(vl(t,0),ls(t,pt,ri,!1)),Jo(t,n),(!(Tt&2)||t!==zt)&&(t===zt&&(!(Tt&2)&&(Ks|=n),$t===4&&ls(t,pt,ri,!1)),ra(t))}function xb(t,e,n){if(Tt&6)throw Error(fe(327));var i=!n&&(e&127)===0&&(e&t.expiredLanes)===0||Qo(t,e),a=i?o1(t,e):yf(t,e,!0),s=i;do{if(a===0){Ll&&!i&&ls(t,e,0,!1);break}else{if(n=t.current.alternate,s&&!r1(n)){a=yf(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=To;var c=o.current.memoizedState.isDehydrated;if(c&&(vl(o,l).flags|=256),l=yf(o,l,!1),l!==2){if(ng&&!c){o.errorRecoveryDisabledLanes|=s,Ks|=s,a=4;break e}s=Xn,Xn=a,s!==null&&(Xn===null?Xn=s:Xn.push.apply(Xn,s))}a=l}if(s=!1,a!==2)continue}}if(a===1){vl(t,0),ls(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:ls(i,e,ri,!rs);break e;case 2:Xn=null;break;case 3:case 5:break;default:throw Error(fe(329))}if((e&62914560)===e&&(a=Od+300-li(),10<a)){if(ls(i,e,ri,!rs),Md(i,0,!0)!==0)break e;wa=e,i.timeoutHandle=kb(kx.bind(null,i,n,Xn,Zu,$h,e,ri,Ks,gl,rs,s,"Throttled",-0,0),a);break e}kx(i,n,Xn,Zu,$h,e,ri,Ks,gl,rs,s,null,-0,0)}}break}while(!0);ra(t)}function kx(t,e,n,i,a,s,l,o,c,u,f,m,d,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:Sa},ub(e,s,m);var g=(s&62914560)===s?Od-li():(s&4194048)===s?pb-li():0;if(g=V1(m,g),g!==null){wa=s,t.cancelPendingCommit=g(Bx.bind(null,t,e,s,n,i,a,l,o,c,f,m,null,d,p)),ls(t,s,l,!u);return}}Bx(t,e,s,n,i,a,l,o,c)}function r1(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(!di(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&=~ig,e&=~Ks,t.suspendedLanes|=e,t.pingedLanes&=~e,i&&(t.warmLanes|=e),i=t.expirationTimes;for(var a=e;0<a;){var s=31-ci(a),l=1<<s;i[s]=-1,a&=~l}n!==0&&b_(t,n,e)}function Pd(){return Tt&6?!0:(sc(0),!1)}function ag(){if(ft!==null){if(Ct===0)var t=ft.return;else t=ft,Ma=ur=null,Gm(t),il=null,ko=0,t=ft;for(;t!==null;)$y(t.alternate,t),t=t.return;ft=null}}function vl(t,e){var n=t.timeoutHandle;n!==-1&&(t.timeoutHandle=-1,A1(n)),n=t.cancelPendingCommit,n!==null&&(t.cancelPendingCommit=null,n()),wa=0,ag(),zt=t,ft=n=Ta(t.current,null),pt=e,Ct=0,ii=null,rs=!1,Ll=Qo(t,e),ng=!1,gl=ri=ig=Ks=Ts=$t=0,Xn=To=null,$h=!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 Oa=e,wd(),n}function vb(t,e){ot=null,nt.H=Bo,e===Dl||e===Cd?(e=mx(),Ct=3):e===zm?(e=mx(),Ct=4):Ct=e===Qm?8:e!==null&&typeof e=="object"&&typeof e.then=="function"?6:1,ii=e,ft===null&&($t=1,qu(t,Si(e,t.current)))}function _b(){var t=fi.current;return t===null?!0:(pt&4194048)===pt?Ai===null:(pt&62914560)===pt||pt&536870912?t===Ai:!1}function yb(){var t=nt.H;return nt.H=Bo,t===null?Bo:t}function bb(){var t=nt.A;return nt.A=a1,t}function Qu(){$t=4,rs||(pt&4194048)!==pt&&fi.current!==null||(Ll=!0),!(Ts&134217727)&&!(Ks&134217727)||zt===null||ls(zt,pt,ri,!1)}function yf(t,e,n){var i=Tt;Tt|=2;var a=yb(),s=bb();(zt!==t||pt!==e)&&(Zu=null,vl(t,e)),e=!1;var l=$t;e:do try{if(Ct!==0&&ft!==null){var o=ft,c=ii;switch(Ct){case 8:ag(),l=6;break e;case 3:case 2:case 9:case 6:fi.current===null&&(e=!0);var u=Ct;if(Ct=0,ii=null,$r(t,o,c,u),n&&Ll){l=0;break e}break;default:u=Ct,Ct=0,ii=null,$r(t,o,c,u)}}l1(),l=$t;break}catch(f){vb(t,f)}while(!0);return e&&t.shellSuspendCounter++,Ma=ur=null,Tt=i,nt.H=a,nt.A=s,ft===null&&(zt=null,pt=0,wd()),l}function l1(){for(;ft!==null;)Sb(ft)}function o1(t,e){var n=Tt;Tt|=2;var i=yb(),a=bb();zt!==t||pt!==e?(Zu=null,$u=li()+500,vl(t,e)):Ll=Qo(t,e);e:do try{if(Ct!==0&&ft!==null){e=ft;var s=ii;t:switch(Ct){case 1:Ct=0,ii=null,$r(t,e,s,1);break;case 2:case 9:if(px(s)){Ct=0,ii=null,Ix(e);break}e=function(){Ct!==2&&Ct!==9||zt!==t||(Ct=7),ra(t)},s.then(e,e);break e;case 3:Ct=7;break e;case 4:Ct=5;break e;case 7:px(s)?(Ct=0,ii=null,Ix(e)):(Ct=0,ii=null,$r(t,e,s,7));break;case 5:var l=null;switch(ft.tag){case 26:l=ft.memoizedState;case 5:case 27:var o=ft;if(l?Hb(l):o.stateNode.complete){Ct=0,ii=null;var c=o.sibling;if(c!==null)ft=c;else{var u=o.return;u!==null?(ft=u,zd(u)):ft=null}break t}}Ct=0,ii=null,$r(t,e,s,5);break;case 6:Ct=0,ii=null,$r(t,e,s,6);break;case 8:ag(),$t=6;break e;default:throw Error(fe(462))}}c1();break}catch(f){vb(t,f)}while(!0);return Ma=ur=null,nt.H=i,nt.A=a,Tt=n,ft!==null?0:(zt=null,pt=0,wd(),$t)}function c1(){for(;ft!==null&&!LM();)Sb(ft)}function Sb(t){var e=Ky(t.alternate,t,Oa);t.memoizedProps=t.pendingProps,e===null?zd(t):ft=e}function Ix(t){var e=t,n=e.alternate;switch(e.tag){case 15:case 0:e=Dx(n,e,e.pendingProps,e.type,void 0,pt);break;case 11:e=Dx(n,e,e.pendingProps,e.type.render,e.ref,pt);break;case 5:Gm(e);default:$y(n,e),e=ft=$_(e,Oa),e=Ky(n,e,Oa)}t.memoizedProps=t.pendingProps,e===null?zd(t):ft=e}function $r(t,e,n,i){Ma=ur=null,Gm(e),il=null,ko=0;var a=e.return;try{if(ZE(t,a,e,n,pt)){$t=1,qu(t,Si(n,t.current)),ft=null;return}}catch(s){if(a!==null)throw ft=a,s;$t=1,qu(t,Si(n,t.current)),ft=null;return}e.flags&32768?(xt||i===1?t=!0:Ll||pt&536870912?t=!1:(rs=t=!0,(i===2||i===9||i===3||i===6)&&(i=fi.current,i!==null&&i.tag===13&&(i.flags|=16384))),Mb(e,t)):zd(e)}function zd(t){var e=t;do{if(e.flags&32768){Mb(e,rs);return}t=e.return;var n=e1(e.alternate,e,Oa);if(n!==null){ft=n;return}if(e=e.sibling,e!==null){ft=e;return}ft=e=t}while(e!==null);$t===0&&($t=5)}function Mb(t,e){do{var n=t1(t.alternate,t);if(n!==null){n.flags&=32767,ft=n;return}if(n=t.return,n!==null&&(n.flags|=32768,n.subtreeFlags=0,n.deletions=null),!e&&(t=t.sibling,t!==null)){ft=t;return}ft=t=n}while(t!==null);$t=6,ft=null}function Bx(t,e,n,i,a,s,l,o,c){t.cancelPendingCommit=null;do kd();while(dn!==0);if(Tt&6)throw Error(fe(327));if(e!==null){if(e===t.current)throw Error(fe(177));if(s=e.lanes|e.childLanes,s|=Cm,HM(t,n,s,l,o,c),t===zt&&(ft=zt=null,pt=0),xl=e,vs=t,wa=n,Zh=s,Qh=a,mb=i,e.subtreeFlags&10256||e.flags&10256?(t.callbackNode=null,t.callbackPriority=0,h1(zu,function(){return Nb(),null})):(t.callbackNode=null,t.callbackPriority=0),i=(e.flags&13878)!==0,e.subtreeFlags&13878||i){i=nt.T,nt.T=null,a=At.p,At.p=2,l=Tt,Tt|=4;try{n1(t,e,n)}finally{Tt=l,At.p=a,nt.T=i}}dn=1,Eb(),Tb(),Ab()}}function Eb(){if(dn===1){dn=0;var t=vs,e=xl,n=(e.flags&13878)!==0;if(e.subtreeFlags&13878||n){n=nt.T,nt.T=null;var i=At.p;At.p=2;var a=Tt;Tt|=4;try{lb(e,t);var s=ip,l=H_(t.containerInfo),o=s.focusedElem,c=s.selectionRange;if(l!==o&&o&&o.ownerDocument&&F_(o.ownerDocument.documentElement,o)){if(c!==null&&Nm(o)){var u=c.start,f=c.end;if(f===void 0&&(f=u),"selectionStart"in o)o.selectionStart=u,o.selectionEnd=Math.min(f,o.value.length);else{var m=o.ownerDocument||document,d=m&&m.defaultView||window;if(d.getSelection){var p=d.getSelection(),g=o.textContent.length,b=Math.min(c.start,g),x=c.end===void 0?b:Math.min(c.end,g);!p.extend&&b>x&&(l=x,x=b,b=l);var h=lx(o,b),v=lx(o,x);if(h&&v&&(p.rangeCount!==1||p.anchorNode!==h.node||p.anchorOffset!==h.offset||p.focusNode!==v.node||p.focusOffset!==v.offset)){var y=m.createRange();y.setStart(h.node,h.offset),p.removeAllRanges(),b>x?(p.addRange(y),p.extend(v.node,v.offset)):(y.setEnd(v.node,v.offset),p.addRange(y))}}}}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 _=m[o];_.element.scrollLeft=_.left,_.element.scrollTop=_.top}}rd=!!np,ip=np=null}finally{Tt=a,At.p=i,nt.T=n}}t.current=e,dn=2}}function Tb(){if(dn===2){dn=0;var t=vs,e=xl,n=(e.flags&8772)!==0;if(e.subtreeFlags&8772||n){n=nt.T,nt.T=null;var i=At.p;At.p=2;var a=Tt;Tt|=4;try{nb(t,e.alternate,e)}finally{Tt=a,At.p=i,nt.T=n}}dn=3}}function Ab(){if(dn===4||dn===3){dn=0,UM();var t=vs,e=xl,n=wa,i=mb;e.subtreeFlags&10256||e.flags&10256?dn=5:(dn=0,xl=vs=null,wb(t,t.pendingLanes));var a=t.pendingLanes;if(a===0&&(xs=null),bm(n),e=e.stateNode,oi&&typeof oi.onCommitFiberRoot=="function")try{oi.onCommitFiberRoot(Zo,e,void 0,(e.current.flags&128)===128)}catch{}if(i!==null){e=nt.T,a=At.p,At.p=2,nt.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{nt.T=e,At.p=a}}wa&3&&kd(),ra(t),a=t.pendingLanes,n&261930&&a&42?t===Jh?Ao++:(Ao=0,Jh=t):Ao=0,sc(0)}}function wb(t,e){(t.pooledCacheLanes&=e)===0&&(e=t.pooledCache,e!=null&&(t.pooledCache=null,nc(e)))}function kd(){return Eb(),Tb(),Ab(),Nb()}function Nb(){if(dn!==5)return!1;var t=vs,e=Zh;Zh=0;var n=bm(wa),i=nt.T,a=At.p;try{At.p=32>n?32:n,nt.T=null,n=Qh,Qh=null;var s=vs,l=wa;if(dn=0,xl=vs=null,wa=0,Tt&6)throw Error(fe(331));var o=Tt;if(Tt|=4,fb(s.current),cb(s,s.current,l,n),Tt=o,sc(0,!1),oi&&typeof oi.onPostCommitFiberRoot=="function")try{oi.onPostCommitFiberRoot(Zo,s)}catch{}return!0}finally{At.p=a,nt.T=i,wb(t,e)}}function jx(t,e,n){e=Si(n,e),e=Xh(t.stateNode,e,2),t=gs(t,e,2),t!==null&&(Jo(t,2),ra(t))}function Rt(t,e,n){if(t.tag===3)jx(t,t,n);else for(;e!==null;){if(e.tag===3){jx(e,t,n);break}else if(e.tag===1){var i=e.stateNode;if(typeof e.type.getDerivedStateFromError=="function"||typeof i.componentDidCatch=="function"&&(xs===null||!xs.has(i))){t=Si(n,t),n=Gy(2),i=gs(e,n,2),i!==null&&(Vy(n,i,e,t),Jo(i,2),ra(i));break}}e=e.return}}function bf(t,e,n){var i=t.pingCache;if(i===null){i=t.pingCache=new s1;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)||(ng=!0,a.add(n),t=u1.bind(null,t,e,n),e.then(t,t))}function u1(t,e,n){var i=t.pingCache;i!==null&&i.delete(e),t.pingedLanes|=t.suspendedLanes&n,t.warmLanes&=~n,zt===t&&(pt&n)===n&&($t===4||$t===3&&(pt&62914560)===pt&&300>li()-Od?!(Tt&2)&&vl(t,0):ig|=n,gl===pt&&(gl=0)),ra(t)}function Cb(t,e){e===0&&(e=y_()),t=cr(t,e),t!==null&&(Jo(t,e),ra(t))}function d1(t){var e=t.memoizedState,n=0;e!==null&&(n=e.retryLane),Cb(t,n)}function f1(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),Cb(t,n)}function h1(t,e){return _m(t,e)}var Ju=null,Ir=null,ep=!1,ed=!1,Sf=!1,os=0;function ra(t){t!==Ir&&t.next===null&&(Ir===null?Ju=Ir=t:Ir=Ir.next=t),ed=!0,ep||(ep=!0,m1())}function sc(t,e){if(!Sf&&ed){Sf=!0;do for(var n=!1,i=Ju;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-ci(42|t)+1)-1,s&=a&~(l&~o),s=s&201326741?s&201326741|1:s?s|2:0}s!==0&&(n=!0,Fx(i,s))}else s=pt,s=Md(i,i===zt?s:0,i.cancelPendingCommit!==null||i.timeoutHandle!==-1),!(s&3)||Qo(i,s)||(n=!0,Fx(i,s));i=i.next}while(n);Sf=!1}}function p1(){Rb()}function Rb(){ed=ep=!1;var t=0;os!==0&&T1()&&(t=os);for(var e=li(),n=null,i=Ju;i!==null;){var a=i.next,s=Db(i,e);s===0?(i.next=null,n===null?Ju=a:n.next=a,a===null&&(Ir=n)):(n=i,(t!==0||s&3)&&(ed=!0)),i=a}dn!==0&&dn!==5||sc(t),os!==0&&(os=0)}function Db(t,e){for(var n=t.suspendedLanes,i=t.pingedLanes,a=t.expirationTimes,s=t.pendingLanes&-62914561;0<s;){var l=31-ci(s),o=1<<l,c=a[l];c===-1?(!(o&n)||o&i)&&(a[l]=FM(o,e)):c<=e&&(t.expiredLanes|=o),s&=~o}if(e=zt,n=pt,n=Md(t,t===e?n:0,t.cancelPendingCommit!==null||t.timeoutHandle!==-1),i=t.callbackNode,n===0||t===e&&(Ct===2||Ct===9)||t.cancelPendingCommit!==null)return i!==null&&i!==null&&Kd(i),t.callbackNode=null,t.callbackPriority=0;if(!(n&3)||Qo(t,n)){if(e=n&-n,e===t.callbackPriority)return e;switch(i!==null&&Kd(i),bm(n)){case 2:case 8:n=v_;break;case 32:n=zu;break;case 268435456:n=__;break;default:n=zu}return i=Lb.bind(null,t),n=_m(n,i),t.callbackPriority=e,t.callbackNode=n,e}return i!==null&&i!==null&&Kd(i),t.callbackPriority=2,t.callbackNode=null,2}function Lb(t,e){if(dn!==0&&dn!==5)return t.callbackNode=null,t.callbackPriority=0,null;var n=t.callbackNode;if(kd()&&t.callbackNode!==n)return null;var i=pt;return i=Md(t,t===zt?i:0,t.cancelPendingCommit!==null||t.timeoutHandle!==-1),i===0?null:(xb(t,i,e),Db(t,li()),t.callbackNode!=null&&t.callbackNode===n?Lb.bind(null,t):null)}function Fx(t,e){if(kd())return null;xb(t,e,!0)}function m1(){w1(function(){Tt&6?_m(x_,p1):Rb()})}function sg(){if(os===0){var t=hl;t===0&&(t=fc,fc<<=1,!(fc&261888)&&(fc=256)),os=t}return os}function Hx(t){return t==null||typeof t=="symbol"||typeof t=="boolean"?null:typeof t=="function"?t:cu(""+t)}function Gx(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 g1(t,e,n,i,a){if(e==="submit"&&n&&n.stateNode===a){var s=Hx((a[Zn]||null).action),l=i.submitter;l&&(e=(e=l[Zn]||null)?Hx(e.formAction):l.getAttribute("formAction"),e!==null&&(s=e,l=null));var o=new Ed("action","action",null,i,a);t.push({event:o,listeners:[{instance:null,listener:function(){if(i.defaultPrevented){if(os!==0){var c=l?Gx(a,l):new FormData(a);Vh(n,{pending:!0,data:c,method:a.method,action:s},null,c)}}else typeof s=="function"&&(o.preventDefault(),c=l?Gx(a,l):new FormData(a),Vh(n,{pending:!0,data:c,method:a.method,action:s},s,c))},currentTarget:a}]})}}for(var Mf=0;Mf<Lh.length;Mf++){var Ef=Lh[Mf],x1=Ef.toLowerCase(),v1=Ef[0].toUpperCase()+Ef.slice(1);Bi(x1,"on"+v1)}Bi(V_,"onAnimationEnd");Bi(W_,"onAnimationIteration");Bi(X_,"onAnimationStart");Bi("dblclick","onDoubleClick");Bi("focusin","onFocus");Bi("focusout","onBlur");Bi(OE,"onTransitionRun");Bi(PE,"onTransitionStart");Bi(zE,"onTransitionCancel");Bi(q_,"onTransitionEnd");dl("onMouseEnter",["mouseout","mouseover"]);dl("onMouseLeave",["mouseout","mouseover"]);dl("onPointerEnter",["pointerout","pointerover"]);dl("onPointerLeave",["pointerout","pointerover"]);rr("onChange","change click focusin focusout input keydown keyup selectionchange".split(" "));rr("onSelect","focusout contextmenu dragend focusin keydown keyup mousedown mouseup selectionchange".split(" "));rr("onBeforeInput",["compositionend","keypress","textInput","paste"]);rr("onCompositionEnd","compositionend focusout keydown keypress keyup mousedown".split(" "));rr("onCompositionStart","compositionstart focusout keydown keypress keyup mousedown".split(" "));rr("onCompositionUpdate","compositionupdate focusout keydown keypress keyup mousedown".split(" "));var jo="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(" "),_1=new Set("beforetoggle cancel close invalid load scroll scrollend toggle".split(" ").concat(jo));function Ub(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(f){Iu(f)}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(f){Iu(f)}a.currentTarget=null,s=c}}}}function dt(t,e){var n=e[Eh];n===void 0&&(n=e[Eh]=new Set);var i=t+"__bubble";n.has(i)||(Ob(e,t,2,!1),n.add(i))}function Tf(t,e,n){var i=0;e&&(i|=4),Ob(n,t,i,e)}var Sc="_reactListening"+Math.random().toString(36).slice(2);function rg(t){if(!t[Sc]){t[Sc]=!0,T_.forEach(function(n){n!=="selectionchange"&&(_1.has(n)||Tf(n,!1,t),Tf(n,!0,t))});var e=t.nodeType===9?t:t.ownerDocument;e===null||e[Sc]||(e[Sc]=!0,Tf("selectionchange",!1,e))}}function Ob(t,e,n,i){switch(qb(e)){case 2:var a=q1;break;case 8:a=Y1;break;default:a=ug}n=a.bind(null,e,n,t),a=void 0,!Ch||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 Af(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=Fr(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}U_(function(){var u=s,f=Em(n),m=[];e:{var d=Y_.get(t);if(d!==void 0){var p=Ed,g=t;switch(t){case"keypress":if(du(n)===0)break e;case"keydown":case"keyup":p=fE;break;case"focusin":g="focus",p=ef;break;case"focusout":g="blur",p=ef;break;case"beforeblur":case"afterblur":p=ef;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=Zg;break;case"drag":case"dragend":case"dragenter":case"dragexit":case"dragleave":case"dragover":case"dragstart":case"drop":p=eE;break;case"touchcancel":case"touchend":case"touchmove":case"touchstart":p=mE;break;case V_:case W_:case X_:p=iE;break;case q_:p=xE;break;case"scroll":case"scrollend":p=QM;break;case"wheel":p=_E;break;case"copy":case"cut":case"paste":p=sE;break;case"gotpointercapture":case"lostpointercapture":case"pointercancel":case"pointerdown":case"pointermove":case"pointerout":case"pointerover":case"pointerup":p=Jg;break;case"toggle":case"beforetoggle":p=bE}var b=(e&4)!==0,x=!b&&(t==="scroll"||t==="scrollend"),h=b?d!==null?d+"Capture":null:d;b=[];for(var v=u,y;v!==null;){var _=v;if(y=_.stateNode,_=_.tag,_!==5&&_!==26&&_!==27||y===null||h===null||(_=Lo(v,h),_!=null&&b.push(Fo(v,_,y))),x)break;v=v.return}0<b.length&&(d=new p(d,g,null,n,f),m.push({event:d,listeners:b}))}}if(!(e&7)){e:{if(d=t==="mouseover"||t==="pointerover",p=t==="mouseout"||t==="pointerout",d&&n!==Nh&&(g=n.relatedTarget||n.fromElement)&&(Fr(g)||g[Nl]))break e;if((p||d)&&(d=f.window===f?f:(d=f.ownerDocument)?d.defaultView||d.parentWindow:window,p?(g=n.relatedTarget||n.toElement,p=u,g=g?Fr(g):null,g!==null&&(x=$o(g),b=g.tag,g!==x||b!==5&&b!==27&&b!==6)&&(g=null)):(p=null,g=u),p!==g)){if(b=Zg,_="onMouseLeave",h="onMouseEnter",v="mouse",(t==="pointerout"||t==="pointerover")&&(b=Jg,_="onPointerLeave",h="onPointerEnter",v="pointer"),x=p==null?d:so(p),y=g==null?d:so(g),d=new b(_,v+"leave",p,n,f),d.target=x,d.relatedTarget=y,_=null,Fr(f)===u&&(b=new b(h,v+"enter",g,n,f),b.target=y,b.relatedTarget=x,_=b),x=_,p&&g)t:{for(b=y1,h=p,v=g,y=0,_=h;_;_=b(_))y++;_=0;for(var A=v;A;A=b(A))_++;for(;0<y-_;)h=b(h),y--;for(;0<_-y;)v=b(v),_--;for(;y--;){if(h===v||v!==null&&h===v.alternate){b=h;break t}h=b(h),v=b(v)}b=null}else b=null;p!==null&&Vx(m,d,p,b,!1),g!==null&&x!==null&&Vx(m,x,g,b,!0)}}e:{if(d=u?so(u):window,p=d.nodeName&&d.nodeName.toLowerCase(),p==="select"||p==="input"&&d.type==="file")var M=ix;else if(nx(d))if(B_)M=DE;else{M=CE;var T=NE}else p=d.nodeName,!p||p.toLowerCase()!=="input"||d.type!=="checkbox"&&d.type!=="radio"?u&&Mm(u.elementType)&&(M=ix):M=RE;if(M&&(M=M(t,u))){I_(m,M,n,f);break e}T&&T(t,d,u),t==="focusout"&&u&&d.type==="number"&&u.memoizedProps.value!=null&&wh(d,"number",d.value)}switch(T=u?so(u):window,t){case"focusin":(nx(T)||T.contentEditable==="true")&&(Vr=T,Rh=u,xo=null);break;case"focusout":xo=Rh=Vr=null;break;case"mousedown":Dh=!0;break;case"contextmenu":case"mouseup":case"dragend":Dh=!1,ox(m,n,f);break;case"selectionchange":if(UE)break;case"keydown":case"keyup":ox(m,n,f)}var S;if(wm)e:{switch(t){case"compositionstart":var w="onCompositionStart";break e;case"compositionend":w="onCompositionEnd";break e;case"compositionupdate":w="onCompositionUpdate";break e}w=void 0}else Gr?z_(t,n)&&(w="onCompositionEnd"):t==="keydown"&&n.keyCode===229&&(w="onCompositionStart");w&&(P_&&n.locale!=="ko"&&(Gr||w!=="onCompositionStart"?w==="onCompositionEnd"&&Gr&&(S=O_()):(ss=f,Tm="value"in ss?ss.value:ss.textContent,Gr=!0)),T=td(u,w),0<T.length&&(w=new Qg(w,t,null,n,f),m.push({event:w,listeners:T}),S?w.data=S:(S=k_(n),S!==null&&(w.data=S)))),(S=ME?EE(t,n):TE(t,n))&&(w=td(u,"onBeforeInput"),0<w.length&&(T=new Qg("onBeforeInput","beforeinput",null,n,f),m.push({event:T,listeners:w}),T.data=S)),g1(m,t,u,n,f)}Ub(m,e)})}function Fo(t,e,n){return{instance:t,listener:e,currentTarget:n}}function td(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=Lo(t,n),a!=null&&i.unshift(Fo(t,a,s)),a=Lo(t,e),a!=null&&i.push(Fo(t,a,s))),t.tag===3)return i;t=t.return}return[]}function y1(t){if(t===null)return null;do t=t.return;while(t&&t.tag!==5&&t.tag!==27);return t||null}function Vx(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=Lo(n,s),u!=null&&l.unshift(Fo(n,u,c))):a||(u=Lo(n,s),u!=null&&l.push(Fo(n,u,c)))),n=n.return}l.length!==0&&t.push({event:e,listeners:l})}var b1=/\r\n?/g,S1=/\u0000|\uFFFD/g;function Wx(t){return(typeof t=="string"?t:""+t).replace(b1,` +`).replace(S1,"")}function Pb(t,e){return e=Wx(e),Wx(t)===e}function Lt(t,e,n,i,a,s){switch(n){case"children":typeof i=="string"?e==="body"||e==="textarea"&&i===""||fl(t,i):(typeof i=="number"||typeof i=="bigint")&&e!=="body"&&fl(t,""+i);break;case"className":mc(t,"class",i);break;case"tabIndex":mc(t,"tabindex",i);break;case"dir":case"role":case"viewBox":case"width":case"height":mc(t,n,i);break;case"style":L_(t,i,s);break;case"data":if(e!=="object"){mc(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=cu(""+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=cu(""+i),t.setAttribute(n,i);break;case"onClick":i!=null&&(t.onclick=Sa);break;case"onScroll":i!=null&&dt("scroll",t);break;case"onScrollEnd":i!=null&&dt("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=cu(""+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":dt("beforetoggle",t),dt("toggle",t),ou(t,"popover",i);break;case"xlinkActuate":la(t,"http://www.w3.org/1999/xlink","xlink:actuate",i);break;case"xlinkArcrole":la(t,"http://www.w3.org/1999/xlink","xlink:arcrole",i);break;case"xlinkRole":la(t,"http://www.w3.org/1999/xlink","xlink:role",i);break;case"xlinkShow":la(t,"http://www.w3.org/1999/xlink","xlink:show",i);break;case"xlinkTitle":la(t,"http://www.w3.org/1999/xlink","xlink:title",i);break;case"xlinkType":la(t,"http://www.w3.org/1999/xlink","xlink:type",i);break;case"xmlBase":la(t,"http://www.w3.org/XML/1998/namespace","xml:base",i);break;case"xmlLang":la(t,"http://www.w3.org/XML/1998/namespace","xml:lang",i);break;case"xmlSpace":la(t,"http://www.w3.org/XML/1998/namespace","xml:space",i);break;case"is":ou(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=$M.get(n)||n,ou(t,n,i))}}function tp(t,e,n,i,a,s){switch(n){case"style":L_(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"?fl(t,i):(typeof i=="number"||typeof i=="bigint")&&fl(t,""+i);break;case"onScroll":i!=null&&dt("scroll",t);break;case"onScrollEnd":i!=null&&dt("scrollend",t);break;case"onClick":i!=null&&(t.onclick=Sa);break;case"suppressContentEditableWarning":case"suppressHydrationWarning":case"innerHTML":case"ref":break;case"innerText":case"textContent":break;default:if(!A_.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[Zn]||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,""):ou(t,n,i)}}}function wn(t,e,n){switch(e){case"div":case"span":case"svg":case"path":case"a":case"g":case"p":case"li":break;case"img":dt("error",t),dt("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:Lt(t,e,s,l,n,null)}}a&&Lt(t,e,"srcSet",n.srcSet,n,null),i&&Lt(t,e,"src",n.src,n,null);return;case"input":dt("invalid",t);var o=s=l=a=null,c=null,u=null;for(i in n)if(n.hasOwnProperty(i)){var f=n[i];if(f!=null)switch(i){case"name":a=f;break;case"type":l=f;break;case"checked":c=f;break;case"defaultChecked":u=f;break;case"value":s=f;break;case"defaultValue":o=f;break;case"children":case"dangerouslySetInnerHTML":if(f!=null)throw Error(fe(137,e));break;default:Lt(t,e,i,f,n,null)}}C_(t,s,o,c,u,l,a,!1);return;case"select":dt("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:Lt(t,e,a,o,n,null)}e=s,n=l,t.multiple=!!i,e!=null?el(t,!!i,e,!1):n!=null&&el(t,!!i,n,!0);return;case"textarea":dt("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:Lt(t,e,l,o,n,null)}D_(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":dt("beforetoggle",t),dt("toggle",t),dt("cancel",t),dt("close",t);break;case"iframe":case"object":dt("load",t);break;case"video":case"audio":for(i=0;i<jo.length;i++)dt(jo[i],t);break;case"image":dt("error",t),dt("load",t);break;case"details":dt("toggle",t);break;case"embed":case"source":case"link":dt("error",t),dt("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:Lt(t,e,u,i,n,null)}return;default:if(Mm(e)){for(f in n)n.hasOwnProperty(f)&&(i=n[f],i!==void 0&&tp(t,e,f,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 M1(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,f=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 d in i){var p=i[d];if(m=n[d],i.hasOwnProperty(d)&&(p!=null||m!=null))switch(d){case"type":s=p;break;case"name":a=p;break;case"checked":u=p;break;case"defaultChecked":f=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!==m&&Lt(t,e,d,p,i,m)}}Ah(t,l,o,c,u,f,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)||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":d=s;break;case"defaultValue":o=s;break;case"multiple":l=s;default:s!==c&&Lt(t,e,a,s,i,c)}e=o,n=l,i=p,d!=null?el(t,!!n,d,!1):!!i!=!!n&&(e!=null?el(t,!!n,e,!0):el(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:Lt(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&&Lt(t,e,l,a,i,s)}R_(t,d,p);return;case"option":for(var g in n)if(d=n[g],n.hasOwnProperty(g)&&d!=null&&!i.hasOwnProperty(g))switch(g){case"selected":t.selected=!1;break;default:Lt(t,e,g,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:Lt(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 b in n)d=n[b],n.hasOwnProperty(b)&&d!=null&&!i.hasOwnProperty(b)&&Lt(t,e,b,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:Lt(t,e,u,d,i,p)}return;default:if(Mm(e)){for(var x in n)d=n[x],n.hasOwnProperty(x)&&d!==void 0&&!i.hasOwnProperty(x)&&tp(t,e,x,void 0,i,d);for(f in i)d=i[f],p=n[f],!i.hasOwnProperty(f)||d===p||d===void 0&&p===void 0||tp(t,e,f,d,i,p);return}}for(var h in n)d=n[h],n.hasOwnProperty(h)&&d!=null&&!i.hasOwnProperty(h)&&Lt(t,e,h,null,i,d);for(m in i)d=i[m],p=n[m],!i.hasOwnProperty(m)||d===p||d==null&&p==null||Lt(t,e,m,d,i,p)}function Xx(t){switch(t){case"css":case"script":case"font":case"img":case"image":case"input":case"link":return!0;default:return!1}}function E1(){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&&Xx(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 f=c.transferSize,m=c.initiatorType;f&&Xx(m)&&(c=c.responseEnd,l+=f*(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 np=null,ip=null;function nd(t){return t.nodeType===9?t:t.ownerDocument}function qx(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 zb(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 ap(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 wf=null;function T1(){var t=window.event;return t&&t.type==="popstate"?t===wf?!1:(wf=t,!0):(wf=null,!1)}var kb=typeof setTimeout=="function"?setTimeout:void 0,A1=typeof clearTimeout=="function"?clearTimeout:void 0,Yx=typeof Promise=="function"?Promise:void 0,w1=typeof queueMicrotask=="function"?queueMicrotask:typeof Yx<"u"?function(t){return Yx.resolve(null).then(t).catch(N1)}:kb;function N1(t){setTimeout(function(){throw t})}function Ns(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),yl(e);return}i--}else if(n==="$"||n==="$?"||n==="$~"||n==="$!"||n==="&")i++;else if(n==="html")wo(t.ownerDocument.documentElement);else if(n==="head"){n=t.ownerDocument.head,wo(n);for(var s=n.firstChild;s;){var l=s.nextSibling,o=s.nodeName;s[ec]||o==="SCRIPT"||o==="STYLE"||o==="LINK"&&s.rel.toLowerCase()==="stylesheet"||n.removeChild(s),s=l}}else n==="body"&&wo(t.ownerDocument.body);n=a}while(n);yl(e)}function $x(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 sp(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":sp(n),Sm(n);continue;case"SCRIPT":case"STYLE":continue;case"LINK":if(n.rel.toLowerCase()==="stylesheet")continue}t.removeChild(n)}}function C1(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[ec])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 R1(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 Ib(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 rp(t){return t.data==="$?"||t.data==="$~"}function lp(t){return t.data==="$!"||t.data==="$?"&&t.ownerDocument.readyState!=="loading"}function D1(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 op=null;function Zx(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 Qx(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 Bb(t,e,n){switch(e=nd(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 wo(t){for(var e=t.attributes;e.length;)t.removeAttributeNode(e[0]);Sm(t)}var Ci=new Map,Jx=new Set;function id(t){return typeof t.getRootNode=="function"?t.getRootNode():t.nodeType===9?t:t.ownerDocument}var ka=At.d;At.d={f:L1,r:U1,D:O1,C:P1,L:z1,m:k1,X:B1,S:I1,M:j1};function L1(){var t=ka.f(),e=Pd();return t||e}function U1(t){var e=Cl(t);e!==null&&e.tag===5&&e.type==="form"?Ly(e):ka.r(t)}var Ul=typeof document>"u"?null:document;function jb(t,e,n){var i=Ul;if(i&&typeof e=="string"&&e){var a=bi(e);a='link[rel="'+t+'"][href="'+a+'"]',typeof n=="string"&&(a+='[crossorigin="'+n+'"]'),Jx.has(a)||(Jx.add(a),t={rel:t,crossOrigin:n,href:e},i.querySelector(a)===null&&(e=i.createElement("link"),wn(e,"link",t),vn(e),i.head.appendChild(e)))}}function O1(t){ka.D(t),jb("dns-prefetch",t,null)}function P1(t,e){ka.C(t,e),jb("preconnect",t,e)}function z1(t,e,n){ka.L(t,e,n);var i=Ul;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=_l(t);break;case"script":s=Ol(t)}Ci.has(s)||(t=Gt({rel:"preload",href:e==="image"&&n&&n.imageSrcSet?void 0:t,as:e},n),Ci.set(s,t),i.querySelector(a)!==null||e==="style"&&i.querySelector(rc(s))||e==="script"&&i.querySelector(lc(s))||(e=i.createElement("link"),wn(e,"link",t),vn(e),i.head.appendChild(e)))}}function k1(t,e){ka.m(t,e);var n=Ul;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=Ol(t)}if(!Ci.has(s)&&(t=Gt({rel:"modulepreload",href:t},e),Ci.set(s,t),n.querySelector(a)===null)){switch(i){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(n.querySelector(lc(s)))return}i=n.createElement("link"),wn(i,"link",t),vn(i),n.head.appendChild(i)}}}function I1(t,e,n){ka.S(t,e,n);var i=Ul;if(i&&t){var a=Jr(i).hoistableStyles,s=_l(t);e=e||"default";var l=a.get(s);if(!l){var o={loading:0,preload:null};if(l=i.querySelector(rc(s)))o.loading=5;else{t=Gt({rel:"stylesheet",href:t,"data-precedence":e},n),(n=Ci.get(s))&&lg(t,n);var c=l=i.createElement("link");vn(c),wn(c,"link",t),c._p=new Promise(function(u,f){c.onload=u,c.onerror=f}),c.addEventListener("load",function(){o.loading|=1}),c.addEventListener("error",function(){o.loading|=2}),o.loading|=4,_u(l,e,i)}l={type:"stylesheet",instance:l,count:1,state:o},a.set(s,l)}}}function B1(t,e){ka.X(t,e);var n=Ul;if(n&&t){var i=Jr(n).hoistableScripts,a=Ol(t),s=i.get(a);s||(s=n.querySelector(lc(a)),s||(t=Gt({src:t,async:!0},e),(e=Ci.get(a))&&og(t,e),s=n.createElement("script"),vn(s),wn(s,"link",t),n.head.appendChild(s)),s={type:"script",instance:s,count:1,state:null},i.set(a,s))}}function j1(t,e){ka.M(t,e);var n=Ul;if(n&&t){var i=Jr(n).hoistableScripts,a=Ol(t),s=i.get(a);s||(s=n.querySelector(lc(a)),s||(t=Gt({src:t,async:!0,type:"module"},e),(e=Ci.get(a))&&og(t,e),s=n.createElement("script"),vn(s),wn(s,"link",t),n.head.appendChild(s)),s={type:"script",instance:s,count:1,state:null},i.set(a,s))}}function e0(t,e,n,i){var a=(a=hs.current)?id(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=_l(n.href),n=Jr(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=_l(n.href);var s=Jr(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(rc(t)))&&!s._p&&(l.instance=s,l.state.loading=5),Ci.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},Ci.set(t,n),s||F1(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=Ol(n),n=Jr(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 _l(t){return'href="'+bi(t)+'"'}function rc(t){return'link[rel="stylesheet"]['+t+"]"}function Fb(t){return Gt({},t,{"data-precedence":t.precedence,precedence:null})}function F1(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}),wn(e,"link",n),vn(e),t.head.appendChild(e))}function Ol(t){return'[src="'+bi(t)+'"]'}function lc(t){return"script[async]"+t}function t0(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,vn(i),i;var a=Gt({},n,{"data-href":n.href,"data-precedence":n.precedence,href:null,precedence:null});return i=(t.ownerDocument||t).createElement("style"),vn(i),wn(i,"style",a),_u(i,n.precedence,t),e.instance=i;case"stylesheet":a=_l(n.href);var s=t.querySelector(rc(a));if(s)return e.state.loading|=4,e.instance=s,vn(s),s;i=Fb(n),(a=Ci.get(a))&&lg(i,a),s=(t.ownerDocument||t).createElement("link"),vn(s);var l=s;return l._p=new Promise(function(o,c){l.onload=o,l.onerror=c}),wn(s,"link",i),e.state.loading|=4,_u(s,n.precedence,t),e.instance=s;case"script":return s=Ol(n.src),(a=t.querySelector(lc(s)))?(e.instance=a,vn(a),a):(i=n,(a=Ci.get(s))&&(i=Gt({},n),og(i,a)),t=t.ownerDocument||t,a=t.createElement("script"),vn(a),wn(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,_u(i,n.precedence,t));return e.instance}function _u(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 lg(t,e){t.crossOrigin==null&&(t.crossOrigin=e.crossOrigin),t.referrerPolicy==null&&(t.referrerPolicy=e.referrerPolicy),t.title==null&&(t.title=e.title)}function og(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 n0(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[ec]||s[Mn]||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 i0(t,e,n){t=t.ownerDocument||t,t.head.insertBefore(n,e==="title"?t.querySelector("head > title"):null)}function H1(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 Hb(t){return!(t.type==="stylesheet"&&!(t.state.loading&3))}function G1(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=_l(i.href),s=e.querySelector(rc(a));if(s){e=s._p,e!==null&&typeof e=="object"&&typeof e.then=="function"&&(t.count++,t=ad.bind(t),e.then(t,t)),n.state.loading|=4,n.instance=s,vn(s);return}s=e.ownerDocument||e,i=Fb(i),(a=Ci.get(a))&&lg(i,a),s=s.createElement("link"),vn(s);var l=s;l._p=new Promise(function(o,c){l.onload=o,l.onerror=c}),wn(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=ad.bind(t),e.addEventListener("load",n),e.addEventListener("error",n))}}var Nf=0;function V1(t,e){return t.stylesheets&&t.count===0&&bu(t,t.stylesheets),0<t.count||0<t.imgCount?function(n){var i=setTimeout(function(){if(t.stylesheets&&bu(t,t.stylesheets),t.unsuspend){var s=t.unsuspend;t.unsuspend=null,s()}},6e4+e);0<t.imgBytes&&Nf===0&&(Nf=62500*E1());var a=setTimeout(function(){if(t.waitingForImages=!1,t.count===0&&(t.stylesheets&&bu(t,t.stylesheets),t.unsuspend)){var s=t.unsuspend;t.unsuspend=null,s()}},(t.imgBytes>Nf?50:800)+e);return t.unsuspend=n,function(){t.unsuspend=null,clearTimeout(i),clearTimeout(a)}}:null}function ad(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)bu(this,this.stylesheets);else if(this.unsuspend){var t=this.unsuspend;this.unsuspend=null,t()}}}var sd=null;function bu(t,e){t.stylesheets=null,t.unsuspend!==null&&(t.count++,sd=new Map,e.forEach(W1,t),sd=null,ad.call(t))}function W1(t,e){if(!(e.state.loading&4)){var n=sd.get(t);if(n)var i=n.get(null);else{n=new Map,sd.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=ad.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 Ho={$$typeof:ba,Provider:null,Consumer:null,_currentValue:Ws,_currentValue2:Ws,_threadCount:0};function X1(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=$d(-1),this.entangledLanes=this.shellSuspendCounter=this.errorRecoveryDisabledLanes=this.expiredLanes=this.warmLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=$d(0),this.hiddenUpdates=$d(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 Gb(t,e,n,i,a,s,l,o,c,u,f,m){return t=new X1(t,e,n,l,c,u,f,m,o),e=1,s===!0&&(e|=24),s=si(3,null,null,e),t.current=s,s.stateNode=t,e=Om(),e.refCount++,t.pooledCache=e,e.refCount++,s.memoizedState={element:i,isDehydrated:n,cache:e},km(s),t}function Vb(t){return t?(t=qr,t):qr}function Wb(t,e,n,i,a,s){a=Vb(a),i.context===null?i.context=a:i.pendingContext=a,i=ms(e),i.payload={element:n},s=s===void 0?null:s,s!==null&&(i.callback=s),n=gs(t,i,e),n!==null&&(Kn(n,t,e),_o(n,t,e))}function a0(t,e){if(t=t.memoizedState,t!==null&&t.dehydrated!==null){var n=t.retryLane;t.retryLane=n!==0&&n<e?n:e}}function cg(t,e){a0(t,e),(t=t.alternate)&&a0(t,e)}function Xb(t){if(t.tag===13||t.tag===31){var e=cr(t,67108864);e!==null&&Kn(e,t,67108864),cg(t,67108864)}}function s0(t){if(t.tag===13||t.tag===31){var e=ui();e=ym(e);var n=cr(t,e);n!==null&&Kn(n,t,e),cg(t,e)}}var rd=!0;function q1(t,e,n,i){var a=nt.T;nt.T=null;var s=At.p;try{At.p=2,ug(t,e,n,i)}finally{At.p=s,nt.T=a}}function Y1(t,e,n,i){var a=nt.T;nt.T=null;var s=At.p;try{At.p=8,ug(t,e,n,i)}finally{At.p=s,nt.T=a}}function ug(t,e,n,i){if(rd){var a=cp(i);if(a===null)Af(t,e,i,ld,n),r0(t,i);else if($1(a,t,e,n,i))i.stopPropagation();else if(r0(t,i),e&4&&-1<K1.indexOf(t)){for(;a!==null;){var s=Cl(a);if(s!==null)switch(s.tag){case 3:if(s=s.stateNode,s.current.memoizedState.isDehydrated){var l=ks(s.pendingLanes);if(l!==0){var o=s;for(o.pendingLanes|=2,o.entangledLanes|=2;l;){var c=1<<31-ci(l);o.entanglements[1]|=c,l&=~c}ra(s),!(Tt&6)&&($u=li()+500,sc(0))}}break;case 31:case 13:o=cr(s,2),o!==null&&Kn(o,s,2),Pd(),cg(s,2)}if(s=cp(i),s===null&&Af(t,e,i,ld,n),s===a)break;a=s}a!==null&&i.stopPropagation()}else Af(t,e,i,null,n)}}function cp(t){return t=Em(t),dg(t)}var ld=null;function dg(t){if(ld=null,t=Fr(t),t!==null){var e=$o(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=h_(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 ld=t,null}function qb(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(OM()){case x_:return 2;case v_:return 8;case zu:case PM:return 32;case __:return 268435456;default:return 32}default:return 32}}var up=!1,_s=null,ys=null,bs=null,Go=new Map,Vo=new Map,ns=[],K1="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 r0(t,e){switch(t){case"focusin":case"focusout":_s=null;break;case"dragenter":case"dragleave":ys=null;break;case"mouseover":case"mouseout":bs=null;break;case"pointerover":case"pointerout":Go.delete(e.pointerId);break;case"gotpointercapture":case"lostpointercapture":Vo.delete(e.pointerId)}}function Vl(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=Cl(e),e!==null&&Xb(e)),t):(t.eventSystemFlags|=i,e=t.targetContainers,a!==null&&e.indexOf(a)===-1&&e.push(a),t)}function $1(t,e,n,i,a){switch(e){case"focusin":return _s=Vl(_s,t,e,n,i,a),!0;case"dragenter":return ys=Vl(ys,t,e,n,i,a),!0;case"mouseover":return bs=Vl(bs,t,e,n,i,a),!0;case"pointerover":var s=a.pointerId;return Go.set(s,Vl(Go.get(s)||null,t,e,n,i,a)),!0;case"gotpointercapture":return s=a.pointerId,Vo.set(s,Vl(Vo.get(s)||null,t,e,n,i,a)),!0}return!1}function Yb(t){var e=Fr(t.target);if(e!==null){var n=$o(e);if(n!==null){if(e=n.tag,e===13){if(e=f_(n),e!==null){t.blockedOn=e,Vg(t.priority,function(){s0(n)});return}}else if(e===31){if(e=h_(n),e!==null){t.blockedOn=e,Vg(t.priority,function(){s0(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 Su(t){if(t.blockedOn!==null)return!1;for(var e=t.targetContainers;0<e.length;){var n=cp(t.nativeEvent);if(n===null){n=t.nativeEvent;var i=new n.constructor(n.type,n);Nh=i,n.target.dispatchEvent(i),Nh=null}else return e=Cl(n),e!==null&&Xb(e),t.blockedOn=n,!1;e.shift()}return!0}function l0(t,e,n){Su(t)&&n.delete(e)}function Z1(){up=!1,_s!==null&&Su(_s)&&(_s=null),ys!==null&&Su(ys)&&(ys=null),bs!==null&&Su(bs)&&(bs=null),Go.forEach(l0),Vo.forEach(l0)}function Mc(t,e){t.blockedOn===e&&(t.blockedOn=null,up||(up=!0,fn.unstable_scheduleCallback(fn.unstable_NormalPriority,Z1)))}var Ec=null;function o0(t){Ec!==t&&(Ec=t,fn.unstable_scheduleCallback(fn.unstable_NormalPriority,function(){Ec===t&&(Ec=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(dg(i||n)===null)continue;break}var s=Cl(n);s!==null&&(t.splice(e,3),e-=3,Vh(s,{pending:!0,data:a,method:n.method,action:i},i,a))}}))}function yl(t){function e(c){return Mc(c,t)}_s!==null&&Mc(_s,t),ys!==null&&Mc(ys,t),bs!==null&&Mc(bs,t),Go.forEach(e),Vo.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);)Yb(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],l=a[Zn]||null;if(typeof s=="function")l||o0(n);else if(l){var o=null;if(s&&s.hasAttribute("formAction")){if(a=s,l=s[Zn]||null)o=l.formAction;else if(dg(a)!==null)continue}else o=l.action;typeof o=="function"?n[i+1]=o:(n.splice(i,3),i-=3),o0(n)}}}function Kb(){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=ui();Wb(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;Wb(t.current,2,null,t,null,null),Pd(),e[Nl]=null}};function Id(t){this._internalRoot=t}Id.prototype.unstable_scheduleHydration=function(t){if(t){var e=E_();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&&Yb(t)}};var c0=u_.version;if(c0!=="19.2.8")throw Error(fe(527,c0,"19.2.8"));At.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=wM(e),t=t!==null?p_(t):null,t=t===null?null:t.stateNode,t};var Q1={bundleType:0,version:"19.2.8",rendererPackageName:"react-dom",currentDispatcherRef:nt,reconcilerVersion:"19.2.8"};if(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__<"u"){var Tc=__REACT_DEVTOOLS_GLOBAL_HOOK__;if(!Tc.isDisabled&&Tc.supportsFiber)try{Zo=Tc.inject(Q1),oi=Tc}catch{}}bd.createRoot=function(t,e){if(!d_(t))throw Error(fe(299));var n=!1,i="",a=jy,s=Fy,l=Hy;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=Gb(t,1,!1,null,null,n,i,null,a,s,l,Kb),t[Nl]=e.current,rg(t),new fg(e)};bd.hydrateRoot=function(t,e,n){if(!d_(t))throw Error(fe(299));var i=!1,a="",s=jy,l=Fy,o=Hy,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=Gb(t,1,!0,e,n??null,i,a,c,s,l,o,Kb),e.context=Vb(null),n=e.current,i=ui(),i=ym(i),a=ms(i),a.callback=null,gs(n,a,i),n=i,e.current.lanes=n,Jo(e,n),ra(e),t[Nl]=e.current,rg(t),new Id(e)};bd.version="19.2.8";function $b(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE($b)}catch(t){console.error(t)}}$b(),a_.exports=bd;var J1=a_.exports;function Zb(t){if(typeof t!="string"||t==="")return t;const e=t.split(/[\\/]/).filter(Boolean);return e.length>0?e[e.length-1]:t}const Qb="plugbrain.workspace",eT="plugbrain.auth_token";function Jb(){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(eT))==null?void 0:a.trim())||""}catch{}return{"Content-Type":"application/json",...t?{Authorization:`Bearer ${t}`,"x-plug-auth-token":t}:{}}}function tT(){try{return localStorage.getItem(Qb)||""}catch{return""}}function nT(t){try{localStorage.setItem(Qb,t)}catch{}}async function u0(){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 d0(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 iT(t,e){var i;const n=d0(e);return!n||!Array.isArray(t)?"":((i=t.find(a=>typeof(a==null?void 0:a.id)=="string"&&d0(a.root)===n))==null?void 0:i.id)??""}async function aT(t,e,n){var s;const i=await fetch("/api/workspaces",{method:"POST",headers:Jb(),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 eS(a.workspace.id,n),a.workspace.id}function dp(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 sT(t,e){return t!=null&&t.running||t!=null&&t.stale?dp(t):e.startsWith("Indexiert:")||e.startsWith("Indexlauf ohne neuen Fortschritt;")?"":e}async function rT(t){const e=await fetch(`/api/index/progress?workspace=${encodeURIComponent(t)}`);return e.ok?e.json():null}const lT=t=>new Promise(e=>setTimeout(e,t));async function oT(t,e){for(;;){await lT(900);const n=await rT(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 eS(t,e){const n=await fetch("/api/reindex",{method:"POST",headers:Jb(),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:oT(t,e)}const tS="plugbrain.auth_token",fp="plugbrain.agent_id";function cT(){var t,e;try{return((e=(t=window.__PLUGBRAIN__)==null?void 0:t.token)==null?void 0:e.trim())??""}catch{return""}}function nS(){var n,i;let t="";try{t=((n=new URLSearchParams(window.location.search).get("token"))==null?void 0:n.trim())??""}catch{}if(t)return iS(t),t;const e=cT();if(e)return e;try{return((i=localStorage.getItem(tS))==null?void 0:i.trim())??""}catch{return""}}function iS(t){try{localStorage.setItem(tS,t)}catch{}}function ji(){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 uT(t){try{localStorage.setItem(fp,t)}catch{}}function Nn(){const t=nS(),e={"Content-Type":"application/json"};return t&&(e.Authorization=`Bearer ${t}`,e["x-plug-auth-token"]=t),e}async function dT(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 fT(t,e){const n=await fetch("/api/planet/selection",{method:"POST",headers:Nn(),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 aS(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 hT(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 pT(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 f0(t){var p;const e=`?workspace=${encodeURIComponent(t)}`,n=Nn(),[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(g=>!g.ok))throw new Error("Die Fleet-Zeitleiste ist nicht verfügbar.");const[c,u,f,m,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)||!(f!=null&&f.ok)||!Array.isArray(f.messages)||!(m!=null&&m.ok)||!Array.isArray(m.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:f.messages,approvals:m.approvals,tasks:d.tasks}}async function mT(t){if(!(await fetch("/api/agent/message",{method:"POST",headers:Nn(),body:JSON.stringify(t)})).ok)throw new Error("Nachricht konnte nicht gesendet werden.")}async function gT(t){if(!(await fetch("/api/queue",{method:"POST",headers:Nn(),body:JSON.stringify(t)})).ok)throw new Error("Aufgabe konnte nicht eingereiht werden.")}async function xT(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 vT=6e4;async function _T(t,e=ji(),n="AGY",{maxWaitMs:i=vT}={}){const a=Date.now()+i;for(;;){const s=await fetch("/api/agent/attach",{method:"POST",headers:Nn(),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 Mu=new Map;function oc(t,e=ji()){const n=`${t}\0${e}`,i=Mu.get(n);if(i)return i;const a=_T(t,e).then(()=>{}).catch(()=>{Mu.delete(n)});return Mu.set(n,a),a}function yT(){Mu.clear()}async function hp(t,e,n=ji()){await oc(t,n);const i=await fetch("/api/agent/search",{method:"POST",headers:Nn(),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 bT(t,e,n=ji()){await oc(t,n);const i=await fetch("/api/agent/read",{method:"POST",headers:Nn(),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 h0(t,e,n=ji()){const i=await fetch("/api/context/pack",{method:"POST",headers:Nn(),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 ST(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 MT(t,e){const n=await fetch(`/api/notes/query?workspace=${encodeURIComponent(t)}&q=${encodeURIComponent(e)}`,{headers:Nn()});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 pp(t,e,n=30){const i=await fetch(`/api/notes/search?workspace=${encodeURIComponent(t)}&q=${encodeURIComponent(e)}&limit=${n}&lines=1`,{headers:Nn()});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 ET(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 TT(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 Ac(t,e,n=ji()){await oc(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 Wl(t,e,n,i,a=ji(),s=!1){await oc(t,a);const l=await fetch("/api/notes/write",{method:"POST",headers:Nn(),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 AT(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 p0(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 wT(t,e,n,i=ji()){await oc(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:Nn(),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 NT(t,e,n,i=ji()){return`/api/notes/attachment?workspace=${encodeURIComponent(t)}¬e=${encodeURIComponent(e)}&name=${encodeURIComponent(n)}&agentId=${encodeURIComponent(i)}`}function m0(t,e,n=ji()){const i=new URLSearchParams({workspace:t,agentId:n});return e&&i.set("note",e),`/api/notes/export?${i}`}async function CT(t){try{const e=await fetch(`/api/briefing?workspace=${encodeURIComponent(t)}`,{headers:Nn()});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 RT(t,e,n=5){try{const i=await fetch("/api/ask",{method:"POST",headers:Nn(),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 DT(t){try{const e=await fetch(`/api/hygiene?workspace=${encodeURIComponent(t)}`,{headers:Nn()});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 LT(){try{const t=await fetch("/api/machine",{headers:Nn()});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 UT(t=!1){try{const n=await fetch(`/api/repos${t?"?dirty=1&page=1":"?page=1"}`,{headers:Nn()});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 Wt=[],cs=[],xa=[],Wo=[],No={},_n=[],Eu=[],Ki=7.2,Zr=6,Xo=["--k1","--k2","--k3","--k4","--k5","--k6"],hg=t=>getComputedStyle(document.documentElement).getPropertyValue(t).trim(),OT=t=>t.agentColor||hg(Xo[(t.ki??0)%Xo.length]),g0=t=>hg(Xo[t.ki%Xo.length]),sS=new Map;let rS="loc";function PT(t){rS=t}const zT=t=>{const e=Math.max(1,...Wt.map(i=>i.loc)),n=Math.max(1,...Wt.map(i=>i.usedBy.length));return t.dying?0:rS==="loc"?1.5+t.loc/e*26:1.5+t.usedBy.length/n*26},mp=new Set,kT=t=>(mp.add(t),()=>mp.delete(t)),bl=()=>mp.forEach(t=>t());function gp(t,e,n="ok"){Eu.unshift({t:new Date,ws:t,msg:e,kind:n,id:Math.random().toString(36).slice(2)}),Eu.length>60&&Eu.pop()}function Bd(){var o;let e=0,n=0,i=0;const a=Wo.filter(c=>_n.find(u=>u.id===c)),s=new Set;for(const c of a){const u=Wt.filter(g=>g.dir===c&&!g.dying);if(!u.length&&((o=_n.find(g=>g.id===c))!=null&&o.dying))continue;const f=Math.max(1,Math.ceil(Math.sqrt(Math.max(1,u.length)))),m=f*Ki+Zr,d=Math.max(1,Math.ceil(Math.max(1,u.length)/f))*Ki+Zr;n+m>74&&n>0&&(e+=i,n=0,i=0);let p=xa.find(g=>g.dir===c);p||(p={dir:c,x:n+m/2,z:e+d/2,w:.01,h:.01},xa.push(p)),Object.assign(p,{tx:n,tz:e,tw:m,th:d,cols:f}),s.add(c),n+=m,i=Math.max(i,d)}const l=xa.filter(c=>s.has(c.dir));if(l.length){const c=Math.max(...l.map(f=>f.tx+f.tw))/2,u=Math.max(...l.map(f=>f.tz+f.th))/2;for(const f of l)f.tx-=c,f.tz-=u;for(const f of l)Wt.filter(d=>d.dir===f.dir).forEach((d,p)=>{d.tx=f.tx+Zr/2+p%f.cols*Ki+Ki/2,d.tz=f.tz+Zr/2+Math.floor(p/f.cols)*Ki+Ki/2,d.x===void 0&&(d.x=d.tx,d.z=d.tz)})}for(let c=xa.length-1;c>=0;c--)!s.has(xa[c].dir)&&!Wt.some(u=>u.dir===xa[c].dir)&&xa.splice(c,1);for(const c of a)sS.set(c,.5)}function IT(t,e,n=!1){let i=_n.find(a=>a.id===t);return i||(i={id:t,name:e||t,ki:_n.length,load:0,events:0,createdAt:new Date,dying:!1,sim:n},_n.push(i),Wo.includes(t)||Wo.push(t),gp(e||t,"workspace registered","reg"),Bd(),bl(),i)}function BT(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 f=No[u];if(f)return f.loc+=Math.max(2,Math.round(n*.25)),f.pulse=1,s&&(f.agentColor=s,f.agentName=l,f.access=o),f;f={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 m of i){m.includes("/")||(m=`${t.id}/${m}`);const d=No[m];d&&(f.deps.push(m),cs.push({from:f,to:d}),d.usedBy.push(u))}return Wt.push(f),No[u]=f,Bd(),bl(),f}function jT(){let t=!1;for(let e=Wt.length-1;e>=0;e--){const n=Wt[e];if(n.dying&&n.h<.25){Wt.splice(e,1),delete No[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 Wt){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=_n.length-1;e>=0;e--){const n=_n[e];if(n.dying&&!Wt.some(i=>i.dir===n.id)){_n.splice(e,1);const i=Wo.indexOf(n.id);i>=0&&Wo.splice(i,1),t=!0}}t&&(Bd(),bl())}setInterval(()=>{let t=!1;for(const e of _n)e.load>.01&&(e.load*=.82,t=!0);t&&bl()},600);const oo={register({id:t,name:e}={}){return t?IT(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=_n.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++,BT(c,{path:e,loc:n,deps:i,note:a,agentColor:s,agentName:l,access:o}))},event(t,e){const n=_n.find(a=>a.id===t);if(!n)return;const i=Wt.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++,gp(n.name,String(e||"event")),bl()},unregister(t){const e=_n.find(n=>n.id===t);e&&(e.dying=!0,Wt.filter(n=>n.dir===t).forEach(n=>{n.dying=!0}),gp(e.name,"workspace unregistered","sys"),Bd(),bl())},list:()=>_n.map(t=>({id:t.id,name:t.name,buildings:Wt.filter(e=>e.dir===t.id).length})),simulated:()=>!1};window.PlugBrainCity=oo;/** + * @license + * Copyright 2010-2026 Three.js Authors + * SPDX-License-Identifier: MIT + */const pg="185",FT=0,x0=1,HT=2,Tu=1,GT=2,co=3,As=0,$n=1,ya=2,Na=0,rl=1,v0=2,_0=3,y0=4,VT=5,Fs=100,WT=101,XT=102,qT=103,YT=104,KT=200,$T=201,ZT=202,QT=203,xp=204,vp=205,JT=206,eA=207,tA=208,nA=209,iA=210,aA=211,sA=212,rA=213,lA=214,_p=0,yp=1,bp=2,Sl=3,Sp=4,Mp=5,Ep=6,Tp=7,lS=0,oA=1,cA=2,na=0,oS=1,cS=2,uS=3,dS=4,fS=5,hS=6,pS=7,mS=300,tr=301,Ml=302,Cf=303,Rf=304,jd=306,Ap=1e3,Ea=1001,wp=1002,An=1003,uA=1004,wc=1005,Ln=1006,Df=1007,Gs=1008,Ei=1009,gS=1010,xS=1011,qo=1012,mg=1013,aa=1014,ki=1015,Pa=1016,gg=1017,xg=1018,Yo=1020,vS=35902,_S=35899,yS=1021,bS=1022,Ii=1023,za=1026,Vs=1027,vg=1028,_g=1029,nr=1030,yg=1031,bg=1033,Au=33776,wu=33777,Nu=33778,Cu=33779,Np=35840,Cp=35841,Rp=35842,Dp=35843,Lp=36196,Up=37492,Op=37496,Pp=37488,zp=37489,od=37490,kp=37491,Ip=37808,Bp=37809,jp=37810,Fp=37811,Hp=37812,Gp=37813,Vp=37814,Wp=37815,Xp=37816,qp=37817,Yp=37818,Kp=37819,$p=37820,Zp=37821,Qp=36492,Jp=36494,em=36495,tm=36283,nm=36284,cd=36285,im=36286,dA=3200,b0=0,fA=1,is="",xi="srgb",ud="srgb-linear",dd="linear",Nt="srgb",vr=7680,S0=519,hA=512,pA=513,mA=514,Sg=515,gA=516,xA=517,Mg=518,vA=519,M0=35044,E0="300 es",ea=2e3,fd=2001;function _A(t){for(let e=t.length-1;e>=0;--e)if(t[e]>=65535)return!0;return!1}function hd(t){return document.createElementNS("http://www.w3.org/1999/xhtml",t)}function yA(){const t=hd("canvas");return t.style.display="block",t}const T0={};function A0(...t){const e="THREE."+t.shift();console.log(e,...t)}function SS(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 et(...t){t=SS(t);const e="THREE."+t.shift();{const n=t[0];n&&n.isStackTrace?console.warn(n.getError(e)):console.warn(e,...t)}}function Mt(...t){t=SS(t);const e="THREE."+t.shift();{const n=t[0];n&&n.isStackTrace?console.error(n.getError(e)):console.error(e,...t)}}function ll(...t){const e=t.join(" ");e in T0||(T0[e]=!0,et(...t))}function bA(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 SA={[_p]:yp,[bp]:Ep,[Sp]:Tp,[Sl]:Mp,[yp]:_p,[Ep]:bp,[Tp]:Sp,[Mp]:Sl};class dr{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 Rn=["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"],Ru=Math.PI/180,am=180/Math.PI;function cc(){const t=Math.random()*4294967295|0,e=Math.random()*4294967295|0,n=Math.random()*4294967295|0,i=Math.random()*4294967295|0;return(Rn[t&255]+Rn[t>>8&255]+Rn[t>>16&255]+Rn[t>>24&255]+"-"+Rn[e&255]+Rn[e>>8&255]+"-"+Rn[e>>16&15|64]+Rn[e>>24&255]+"-"+Rn[n&63|128]+Rn[n>>8&255]+"-"+Rn[n>>16&255]+Rn[n>>24&255]+Rn[i&255]+Rn[i>>8&255]+Rn[i>>16&255]+Rn[i>>24&255]).toLowerCase()}function gt(t,e,n){return Math.max(e,Math.min(n,t))}function MA(t,e){return(t%e+e)%e}function Lf(t,e,n){return(1-n)*t+n*e}function Xl(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 Vn(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 Rg=class Rg{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=gt(this.x,e.x,n.x),this.y=gt(this.y,e.y,n.y),this}clampScalar(e,n){return this.x=gt(this.x,e,n),this.y=gt(this.y,e,n),this}clampLength(e,n){const i=this.length();return this.divideScalar(i||1).multiplyScalar(gt(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(gt(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}};Rg.prototype.isVector2=!0;let wt=Rg;class Pl{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],f=i[a+2],m=i[a+3],d=s[l+0],p=s[l+1],g=s[l+2],b=s[l+3];if(m!==b||c!==d||u!==p||f!==g){let x=c*d+u*p+f*g+m*b;x<0&&(d=-d,p=-p,g=-g,b=-b,x=-x);let h=1-o;if(x<.9995){const v=Math.acos(x),y=Math.sin(v);h=Math.sin(h*v)/y,o=Math.sin(o*v)/y,c=c*h+d*o,u=u*h+p*o,f=f*h+g*o,m=m*h+b*o}else{c=c*h+d*o,u=u*h+p*o,f=f*h+g*o,m=m*h+b*o;const v=1/Math.sqrt(c*c+u*u+f*f+m*m);c*=v,u*=v,f*=v,m*=v}}e[n]=c,e[n+1]=u,e[n+2]=f,e[n+3]=m}static multiplyQuaternionsFlat(e,n,i,a,s,l){const o=i[a],c=i[a+1],u=i[a+2],f=i[a+3],m=s[l],d=s[l+1],p=s[l+2],g=s[l+3];return e[n]=o*g+f*m+c*p-u*d,e[n+1]=c*g+f*d+u*m-o*p,e[n+2]=u*g+f*p+o*d-c*m,e[n+3]=f*g-o*m-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),f=o(a/2),m=o(s/2),d=c(i/2),p=c(a/2),g=c(s/2);switch(l){case"XYZ":this._x=d*f*m+u*p*g,this._y=u*p*m-d*f*g,this._z=u*f*g+d*p*m,this._w=u*f*m-d*p*g;break;case"YXZ":this._x=d*f*m+u*p*g,this._y=u*p*m-d*f*g,this._z=u*f*g-d*p*m,this._w=u*f*m+d*p*g;break;case"ZXY":this._x=d*f*m-u*p*g,this._y=u*p*m+d*f*g,this._z=u*f*g+d*p*m,this._w=u*f*m-d*p*g;break;case"ZYX":this._x=d*f*m-u*p*g,this._y=u*p*m+d*f*g,this._z=u*f*g-d*p*m,this._w=u*f*m+d*p*g;break;case"YZX":this._x=d*f*m+u*p*g,this._y=u*p*m+d*f*g,this._z=u*f*g-d*p*m,this._w=u*f*m-d*p*g;break;case"XZY":this._x=d*f*m-u*p*g,this._y=u*p*m-d*f*g,this._z=u*f*g+d*p*m,this._w=u*f*m+d*p*g;break;default:et("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],f=n[6],m=n[10],d=i+o+m;if(d>0){const p=.5/Math.sqrt(d+1);this._w=.25/p,this._x=(f-c)*p,this._y=(s-u)*p,this._z=(l-a)*p}else if(i>o&&i>m){const p=2*Math.sqrt(1+i-o-m);this._w=(f-c)/p,this._x=.25*p,this._y=(a+l)/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+l)/p,this._y=.25*p,this._z=(c+f)/p}else{const p=2*Math.sqrt(1+m-i-o);this._w=(l-a)/p,this._x=(s+u)/p,this._y=(c+f)/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(gt(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,f=n._w;return this._x=i*f+l*o+a*u-s*c,this._y=a*f+l*c+s*o-i*u,this._z=s*f+l*u+i*c-a*o,this._w=l*f-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),f=Math.sin(u);c=Math.sin(c*u)/f,n=Math.sin(n*u)/f,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 Dg=class Dg{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(w0.setFromEuler(e))}applyAxisAngle(e,n){return this.applyQuaternion(w0.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),f=2*(o*n-s*a),m=2*(s*i-l*n);return this.x=n+c*u+l*m-o*f,this.y=i+c*f+o*u-s*m,this.z=a+c*m+s*f-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=gt(this.x,e.x,n.x),this.y=gt(this.y,e.y,n.y),this.z=gt(this.z,e.z,n.z),this}clampScalar(e,n){return this.x=gt(this.x,e,n),this.y=gt(this.y,e,n),this.z=gt(this.z,e,n),this}clampLength(e,n){const i=this.length();return this.divideScalar(i||1).multiplyScalar(gt(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 Uf.copy(this).projectOnVector(e),this.sub(Uf)}reflect(e){return this.sub(Uf.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(gt(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}};Dg.prototype.isVector3=!0;let Z=Dg;const Uf=new Z,w0=new Pl,Lg=class Lg{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 f=this.elements;return f[0]=e,f[1]=a,f[2]=o,f[3]=n,f[4]=s,f[5]=c,f[6]=i,f[7]=l,f[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],f=i[4],m=i[7],d=i[2],p=i[5],g=i[8],b=a[0],x=a[3],h=a[6],v=a[1],y=a[4],_=a[7],A=a[2],M=a[5],T=a[8];return s[0]=l*b+o*v+c*A,s[3]=l*x+o*y+c*M,s[6]=l*h+o*_+c*T,s[1]=u*b+f*v+m*A,s[4]=u*x+f*y+m*M,s[7]=u*h+f*_+m*T,s[2]=d*b+p*v+g*A,s[5]=d*x+p*y+g*M,s[8]=d*h+p*_+g*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],l=e[4],o=e[5],c=e[6],u=e[7],f=e[8];return n*l*f-n*o*u-i*s*f+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],f=e[8],m=f*l-o*u,d=o*c-f*s,p=u*s-l*c,g=n*m+i*d+a*p;if(g===0)return this.set(0,0,0,0,0,0,0,0,0);const b=1/g;return e[0]=m*b,e[1]=(a*u-f*i)*b,e[2]=(o*i-a*l)*b,e[3]=d*b,e[4]=(f*n-a*c)*b,e[5]=(a*s-o*n)*b,e[6]=p*b,e[7]=(i*c-u*n)*b,e[8]=(l*n-i*s)*b,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 ll("Matrix3: .scale() is deprecated. Use .makeScale() instead."),this.premultiply(Of.makeScale(e,n)),this}rotate(e){return ll("Matrix3: .rotate() is deprecated. Use .makeRotation() instead."),this.premultiply(Of.makeRotation(-e)),this}translate(e,n){return ll("Matrix3: .translate() is deprecated. Use .makeTranslation() instead."),this.premultiply(Of.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)}};Lg.prototype.isMatrix3=!0;let at=Lg;const Of=new at,N0=new at().set(.4123908,.3575843,.1804808,.212639,.7151687,.0721923,.0193308,.1191948,.9505322),C0=new at().set(3.2409699,-1.5373832,-.4986108,-.9692436,1.8759675,.0415551,.0556301,-.203977,1.0569715);function EA(){const t={enabled:!0,workingColorSpace:ud,spaces:{},convert:function(a,s,l){return this.enabled===!1||s===l||!s||!l||(this.spaces[s].transfer===Nt&&(a.r=Ca(a.r),a.g=Ca(a.g),a.b=Ca(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===Nt&&(a.r=ol(a.r),a.g=ol(a.g),a.b=ol(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?dd: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 ll("ColorManagement: .fromWorkingColorSpace() has been renamed to .workingToColorSpace()."),t.workingToColorSpace(a,s)},toWorkingColorSpace:function(a,s){return ll("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({[ud]:{primaries:e,whitePoint:i,transfer:dd,toXYZ:N0,fromXYZ:C0,luminanceCoefficients:n,workingColorSpaceConfig:{unpackColorSpace:xi},outputColorSpaceConfig:{drawingBufferColorSpace:xi}},[xi]:{primaries:e,whitePoint:i,transfer:Nt,toXYZ:N0,fromXYZ:C0,luminanceCoefficients:n,outputColorSpaceConfig:{drawingBufferColorSpace:xi}}}),t}const mt=EA();function Ca(t){return t<.04045?t*.0773993808:Math.pow(t*.9478672986+.0521327014,2.4)}function ol(t){return t<.0031308?t*12.92:1.055*Math.pow(t,.41666)-.055}let _r;class TA{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{_r===void 0&&(_r=hd("canvas")),_r.width=e.width,_r.height=e.height;const a=_r.getContext("2d");e instanceof ImageData?a.putImageData(e,0,0):a.drawImage(e,0,0,e.width,e.height),i=_r}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=hd("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]=Ca(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(Ca(n[i]/255)*255):n[i]=Ca(n[i]);return{data:n,width:e.width,height:e.height}}else return et("ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied."),e}}let AA=0;class Eg{constructor(e=null){this.isSource=!0,Object.defineProperty(this,"id",{value:AA++}),this.uuid=cc(),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(Pf(a[l].image)):s.push(Pf(a[l]))}else s=Pf(a);i.url=s}return n||(e.images[this.uuid]=i),i}}function Pf(t){return typeof HTMLImageElement<"u"&&t instanceof HTMLImageElement||typeof HTMLCanvasElement<"u"&&t instanceof HTMLCanvasElement||typeof ImageBitmap<"u"&&t instanceof ImageBitmap?TA.getDataURL(t):t.data?{data:Array.from(t.data),width:t.width,height:t.height,type:t.data.constructor.name}:(et("Texture: Unable to serialize Texture."),{})}let wA=0;const zf=new Z;class In extends dr{constructor(e=In.DEFAULT_IMAGE,n=In.DEFAULT_MAPPING,i=Ea,a=Ea,s=Ln,l=Gs,o=Ii,c=Ei,u=In.DEFAULT_ANISOTROPY,f=is){super(),this.isTexture=!0,Object.defineProperty(this,"id",{value:wA++}),this.uuid=cc(),this.name="",this.source=new Eg(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 wt(0,0),this.repeat=new wt(1,1),this.center=new wt(0,0),this.rotation=0,this.matrixAutoUpdate=!0,this.matrix=new at,this.generateMipmaps=!0,this.premultiplyAlpha=!1,this.flipY=!0,this.unpackAlignment=4,this.colorSpace=f,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(zf).x}get height(){return this.source.getSize(zf).y}get depth(){return this.source.getSize(zf).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){et(`Texture.setValues(): parameter '${n}' has value of undefined.`);continue}const a=this[n];if(a===void 0){et(`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!==mS)return e;if(e.applyMatrix3(this.matrix),e.x<0||e.x>1)switch(this.wrapS){case Ap:e.x=e.x-Math.floor(e.x);break;case Ea:e.x=e.x<0?0:1;break;case wp: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 Ap:e.y=e.y-Math.floor(e.y);break;case Ea:e.y=e.y<0?0:1;break;case wp: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++}}In.DEFAULT_IMAGE=null;In.DEFAULT_MAPPING=mS;In.DEFAULT_ANISOTROPY=1;const Ug=class Ug{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],f=c[4],m=c[8],d=c[1],p=c[5],g=c[9],b=c[2],x=c[6],h=c[10];if(Math.abs(f-d)<.01&&Math.abs(m-b)<.01&&Math.abs(g-x)<.01){if(Math.abs(f+d)<.1&&Math.abs(m+b)<.1&&Math.abs(g+x)<.1&&Math.abs(u+p+h-3)<.1)return this.set(1,0,0,0),this;n=Math.PI;const y=(u+1)/2,_=(p+1)/2,A=(h+1)/2,M=(f+d)/4,T=(m+b)/4,S=(g+x)/4;return y>_&&y>A?y<.01?(i=0,a=.707106781,s=.707106781):(i=Math.sqrt(y),a=M/i,s=T/i):_>A?_<.01?(i=.707106781,a=0,s=.707106781):(a=Math.sqrt(_),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((x-g)*(x-g)+(m-b)*(m-b)+(d-f)*(d-f));return Math.abs(v)<.001&&(v=1),this.x=(x-g)/v,this.y=(m-b)/v,this.z=(d-f)/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=gt(this.x,e.x,n.x),this.y=gt(this.y,e.y,n.y),this.z=gt(this.z,e.z,n.z),this.w=gt(this.w,e.w,n.w),this}clampScalar(e,n){return this.x=gt(this.x,e,n),this.y=gt(this.y,e,n),this.z=gt(this.z,e,n),this.w=gt(this.w,e,n),this}clampLength(e,n){const i=this.length();return this.divideScalar(i||1).multiplyScalar(gt(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}};Ug.prototype.isVector4=!0;let Zt=Ug;class NA extends dr{constructor(e=1,n=1,i={}){super(),i=Object.assign({generateMipmaps:!1,internalFormat:null,minFilter:Ln,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 Zt(0,0,e,n),this.scissorTest=!1,this.viewport=new Zt(0,0,e,n),this.textures=[];const a={width:e,height:n,depth:i.depth},s=new In(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:Ln,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 Eg(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 ia extends NA{constructor(e=1,n=1,i={}){super(e,n,i),this.isWebGLRenderTarget=!0}}class MS extends In{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=An,this.minFilter=An,this.wrapR=Ea,this.generateMipmaps=!1,this.flipY=!1,this.unpackAlignment=1,this.layerUpdates=new Set}addLayerUpdate(e){this.layerUpdates.add(e)}clearLayerUpdates(){this.layerUpdates.clear()}}class CA extends In{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=An,this.minFilter=An,this.wrapR=Ea,this.generateMipmaps=!1,this.flipY=!1,this.unpackAlignment=1}}const _d=class _d{constructor(e,n,i,a,s,l,o,c,u,f,m,d,p,g,b,x){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,f,m,d,p,g,b,x)}set(e,n,i,a,s,l,o,c,u,f,m,d,p,g,b,x){const h=this.elements;return h[0]=e,h[4]=n,h[8]=i,h[12]=a,h[1]=s,h[5]=l,h[9]=o,h[13]=c,h[2]=u,h[6]=f,h[10]=m,h[14]=d,h[3]=p,h[7]=g,h[11]=b,h[15]=x,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 _d().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/yr.setFromMatrixColumn(e,0).length(),s=1/yr.setFromMatrixColumn(e,1).length(),l=1/yr.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),f=Math.cos(s),m=Math.sin(s);if(e.order==="XYZ"){const d=l*f,p=l*m,g=o*f,b=o*m;n[0]=c*f,n[4]=-c*m,n[8]=u,n[1]=p+g*u,n[5]=d-b*u,n[9]=-o*c,n[2]=b-d*u,n[6]=g+p*u,n[10]=l*c}else if(e.order==="YXZ"){const d=c*f,p=c*m,g=u*f,b=u*m;n[0]=d+b*o,n[4]=g*o-p,n[8]=l*u,n[1]=l*m,n[5]=l*f,n[9]=-o,n[2]=p*o-g,n[6]=b+d*o,n[10]=l*c}else if(e.order==="ZXY"){const d=c*f,p=c*m,g=u*f,b=u*m;n[0]=d-b*o,n[4]=-l*m,n[8]=g+p*o,n[1]=p+g*o,n[5]=l*f,n[9]=b-d*o,n[2]=-l*u,n[6]=o,n[10]=l*c}else if(e.order==="ZYX"){const d=l*f,p=l*m,g=o*f,b=o*m;n[0]=c*f,n[4]=g*u-p,n[8]=d*u+b,n[1]=c*m,n[5]=b*u+d,n[9]=p*u-g,n[2]=-u,n[6]=o*c,n[10]=l*c}else if(e.order==="YZX"){const d=l*c,p=l*u,g=o*c,b=o*u;n[0]=c*f,n[4]=b-d*m,n[8]=g*m+p,n[1]=m,n[5]=l*f,n[9]=-o*f,n[2]=-u*f,n[6]=p*m+g,n[10]=d-b*m}else if(e.order==="XZY"){const d=l*c,p=l*u,g=o*c,b=o*u;n[0]=c*f,n[4]=-m,n[8]=u*f,n[1]=d*m+b,n[5]=l*f,n[9]=p*m-g,n[2]=g*m-p,n[6]=o*f,n[10]=b*m+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(RA,e,DA)}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(),Nc.crossVectors(ti,Va),a[0]=Va.x,a[4]=Nc.x,a[8]=ti.x,a[1]=Va.y,a[5]=Nc.y,a[9]=ti.y,a[2]=Va.z,a[6]=Nc.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,l=i[0],o=i[4],c=i[8],u=i[12],f=i[1],m=i[5],d=i[9],p=i[13],g=i[2],b=i[6],x=i[10],h=i[14],v=i[3],y=i[7],_=i[11],A=i[15],M=a[0],T=a[4],S=a[8],w=a[12],R=a[1],D=a[5],L=a[9],q=a[13],P=a[2],O=a[6],B=a[10],k=a[14],W=a[3],Y=a[7],he=a[11],_e=a[15];return s[0]=l*M+o*R+c*P+u*W,s[4]=l*T+o*D+c*O+u*Y,s[8]=l*S+o*L+c*B+u*he,s[12]=l*w+o*q+c*k+u*_e,s[1]=f*M+m*R+d*P+p*W,s[5]=f*T+m*D+d*O+p*Y,s[9]=f*S+m*L+d*B+p*he,s[13]=f*w+m*q+d*k+p*_e,s[2]=g*M+b*R+x*P+h*W,s[6]=g*T+b*D+x*O+h*Y,s[10]=g*S+b*L+x*B+h*he,s[14]=g*w+b*q+x*k+h*_e,s[3]=v*M+y*R+_*P+A*W,s[7]=v*T+y*D+_*O+A*Y,s[11]=v*S+y*L+_*B+A*he,s[15]=v*w+y*q+_*k+A*_e,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],f=e[2],m=e[6],d=e[10],p=e[14],g=e[3],b=e[7],x=e[11],h=e[15],v=c*p-u*d,y=o*p-u*m,_=o*d-c*m,A=l*p-u*f,M=l*d-c*f,T=l*m-o*f;return n*(b*v-x*y+h*_)-i*(g*v-x*A+h*M)+a*(g*y-b*A+h*T)-s*(g*_-b*M+x*T)}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],f=e[10];return n*(l*f-o*u)-i*(s*f-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],f=e[8],m=e[9],d=e[10],p=e[11],g=e[12],b=e[13],x=e[14],h=e[15],v=n*o-i*l,y=n*c-a*l,_=n*u-s*l,A=i*c-a*o,M=i*u-s*o,T=a*u-s*c,S=f*b-m*g,w=f*x-d*g,R=f*h-p*g,D=m*x-d*b,L=m*h-p*b,q=d*h-p*x,P=v*q-y*L+_*D+A*R-M*w+T*S;if(P===0)return this.set(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);const O=1/P;return e[0]=(o*q-c*L+u*D)*O,e[1]=(a*L-i*q-s*D)*O,e[2]=(b*T-x*M+h*A)*O,e[3]=(d*M-m*T-p*A)*O,e[4]=(c*R-l*q-u*w)*O,e[5]=(n*q-a*R+s*w)*O,e[6]=(x*_-g*T-h*y)*O,e[7]=(f*T-d*_+p*y)*O,e[8]=(l*L-o*R+u*S)*O,e[9]=(i*R-n*L-s*S)*O,e[10]=(g*M-b*_+h*v)*O,e[11]=(m*_-f*M-p*v)*O,e[12]=(o*w-l*D-c*S)*O,e[13]=(n*D-i*w+a*S)*O,e[14]=(b*y-g*A-x*v)*O,e[15]=(f*A-m*y+d*v)*O,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,f=s*o;return this.set(u*l+i,u*o-a*c,u*c+a*o,0,u*o+a*c,f*o+i,f*c-a*l,0,u*c-a*o,f*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,f=l+l,m=o+o,d=s*u,p=s*f,g=s*m,b=l*f,x=l*m,h=o*m,v=c*u,y=c*f,_=c*m,A=i.x,M=i.y,T=i.z;return a[0]=(1-(b+h))*A,a[1]=(p+_)*A,a[2]=(g-y)*A,a[3]=0,a[4]=(p-_)*M,a[5]=(1-(d+h))*M,a[6]=(x+v)*M,a[7]=0,a[8]=(g+y)*T,a[9]=(x-v)*T,a[10]=(1-(d+b))*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 l=yr.set(a[0],a[1],a[2]).length();const o=yr.set(a[4],a[5],a[6]).length(),c=yr.set(a[8],a[9],a[10]).length();s<0&&(l=-l),Li.copy(this);const u=1/l,f=1/o,m=1/c;return Li.elements[0]*=u,Li.elements[1]*=u,Li.elements[2]*=u,Li.elements[4]*=f,Li.elements[5]*=f,Li.elements[6]*=f,Li.elements[8]*=m,Li.elements[9]*=m,Li.elements[10]*=m,n.setFromRotationMatrix(Li),i.x=l,i.y=o,i.z=c,this}makePerspective(e,n,i,a,s,l,o=ea,c=!1){const u=this.elements,f=2*s/(n-e),m=2*s/(i-a),d=(n+e)/(n-e),p=(i+a)/(i-a);let g,b;if(c)g=s/(l-s),b=l*s/(l-s);else if(o===ea)g=-(l+s)/(l-s),b=-2*l*s/(l-s);else if(o===fd)g=-l/(l-s),b=-l*s/(l-s);else throw new Error("THREE.Matrix4.makePerspective(): Invalid coordinate system: "+o);return u[0]=f,u[4]=0,u[8]=d,u[12]=0,u[1]=0,u[5]=m,u[9]=p,u[13]=0,u[2]=0,u[6]=0,u[10]=g,u[14]=b,u[3]=0,u[7]=0,u[11]=-1,u[15]=0,this}makeOrthographic(e,n,i,a,s,l,o=ea,c=!1){const u=this.elements,f=2/(n-e),m=2/(i-a),d=-(n+e)/(n-e),p=-(i+a)/(i-a);let g,b;if(c)g=1/(l-s),b=l/(l-s);else if(o===ea)g=-2/(l-s),b=-(l+s)/(l-s);else if(o===fd)g=-1/(l-s),b=-s/(l-s);else throw new Error("THREE.Matrix4.makeOrthographic(): Invalid coordinate system: "+o);return u[0]=f,u[4]=0,u[8]=0,u[12]=d,u[1]=0,u[5]=m,u[9]=0,u[13]=p,u[2]=0,u[6]=0,u[10]=g,u[14]=b,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}};_d.prototype.isMatrix4=!0;let Bt=_d;const yr=new Z,Li=new Bt,RA=new Z(0,0,0),DA=new Z(1,1,1),Va=new Z,Nc=new Z,ti=new Z,R0=new Bt,D0=new Pl;class ir{constructor(e=0,n=0,i=0,a=ir.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],f=a[9],m=a[2],d=a[6],p=a[10];switch(n){case"XYZ":this._y=Math.asin(gt(o,-1,1)),Math.abs(o)<.9999999?(this._x=Math.atan2(-f,p),this._z=Math.atan2(-l,s)):(this._x=Math.atan2(d,u),this._z=0);break;case"YXZ":this._x=Math.asin(-gt(f,-1,1)),Math.abs(f)<.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(gt(d,-1,1)),Math.abs(d)<.9999999?(this._y=Math.atan2(-m,p),this._z=Math.atan2(-l,u)):(this._y=0,this._z=Math.atan2(c,s));break;case"ZYX":this._y=Math.asin(-gt(m,-1,1)),Math.abs(m)<.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(gt(c,-1,1)),Math.abs(c)<.9999999?(this._x=Math.atan2(-f,u),this._y=Math.atan2(-m,s)):(this._x=0,this._y=Math.atan2(o,p));break;case"XZY":this._z=Math.asin(-gt(l,-1,1)),Math.abs(l)<.9999999?(this._x=Math.atan2(d,u),this._y=Math.atan2(o,s)):(this._x=Math.atan2(-f,p),this._y=0);break;default:et("Euler: .setFromRotationMatrix() encountered an unknown order: "+n)}return this._order=n,i===!0&&this._onChangeCallback(),this}setFromQuaternion(e,n,i){return R0.makeRotationFromQuaternion(e),this.setFromRotationMatrix(R0,n,i)}setFromVector3(e,n=this._order){return this.set(e.x,e.y,e.z,n)}reorder(e){return D0.setFromEuler(this),this.setFromQuaternion(D0,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}}ir.DEFAULT_ORDER="XYZ";class ES{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 LA=0;const L0=new Z,br=new Pl,da=new Bt,Cc=new Z,ql=new Z,UA=new Z,OA=new Pl,U0=new Z(1,0,0),O0=new Z(0,1,0),P0=new Z(0,0,1),z0={type:"added"},PA={type:"removed"},Sr={type:"childadded",child:null},kf={type:"childremoved",child:null};class Bn extends dr{constructor(){super(),this.isObject3D=!0,Object.defineProperty(this,"id",{value:LA++}),this.uuid=cc(),this.name="",this.type="Object3D",this.parent=null,this.children=[],this.up=Bn.DEFAULT_UP.clone();const e=new Z,n=new ir,i=new Pl,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 Bt},normalMatrix:{value:new at}}),this.matrix=new Bt,this.matrixWorld=new Bt,this.matrixAutoUpdate=Bn.DEFAULT_MATRIX_AUTO_UPDATE,this.matrixWorldAutoUpdate=Bn.DEFAULT_MATRIX_WORLD_AUTO_UPDATE,this.matrixWorldNeedsUpdate=!1,this.layers=new ES,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 br.setFromAxisAngle(e,n),this.quaternion.multiply(br),this}rotateOnWorldAxis(e,n){return br.setFromAxisAngle(e,n),this.quaternion.premultiply(br),this}rotateX(e){return this.rotateOnAxis(U0,e)}rotateY(e){return this.rotateOnAxis(O0,e)}rotateZ(e){return this.rotateOnAxis(P0,e)}translateOnAxis(e,n){return L0.copy(e).applyQuaternion(this.quaternion),this.position.add(L0.multiplyScalar(n)),this}translateX(e){return this.translateOnAxis(U0,e)}translateY(e){return this.translateOnAxis(O0,e)}translateZ(e){return this.translateOnAxis(P0,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?Cc.copy(e):Cc.set(e,n,i);const a=this.parent;this.updateWorldMatrix(!0,!1),ql.setFromMatrixPosition(this.matrixWorld),this.isCamera||this.isLight?da.lookAt(ql,Cc,this.up):da.lookAt(Cc,ql,this.up),this.quaternion.setFromRotationMatrix(da),a&&(da.extractRotation(a.matrixWorld),br.setFromRotationMatrix(da),this.quaternion.premultiply(br.invert()))}add(e){if(arguments.length>1){for(let n=0;n<arguments.length;n++)this.add(arguments[n]);return this}return e===this?(Mt("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(z0),Sr.child=e,this.dispatchEvent(Sr),Sr.child=null):Mt("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(PA),kf.child=e,this.dispatchEvent(kf),kf.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(z0),Sr.child=e,this.dispatchEvent(Sr),Sr.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(ql,e,UA),e}getWorldScale(e){return this.updateWorldMatrix(!0,!1),this.matrixWorld.decompose(ql,OA,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,f=c.length;u<f;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=l(e.geometries),c=l(e.materials),u=l(e.textures),f=l(e.images),m=l(e.shapes),d=l(e.skeletons),p=l(e.animations),g=l(e.nodes);o.length>0&&(i.geometries=o),c.length>0&&(i.materials=c),u.length>0&&(i.textures=u),f.length>0&&(i.images=f),m.length>0&&(i.shapes=m),d.length>0&&(i.skeletons=d),p.length>0&&(i.animations=p),g.length>0&&(i.nodes=g)}return i.object=a,i;function l(o){const c=[];for(const u in o){const f=o[u];delete f.metadata,c.push(f)}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}}Bn.DEFAULT_UP=new Z(0,1,0);Bn.DEFAULT_MATRIX_AUTO_UPDATE=!0;Bn.DEFAULT_MATRIX_WORLD_AUTO_UPDATE=!0;class Rc extends Bn{constructor(){super(),this.isGroup=!0,this.type="Group"}}const zA={type:"move"};class If{constructor(){this._targetRay=null,this._grip=null,this._hand=null}getHandSpace(){return this._hand===null&&(this._hand=new Rc,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 Rc,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 Rc,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 b of e.hand.values()){const x=n.getJointPose(b,i),h=this._getHandJoint(u,b);x!==null&&(h.matrix.fromArray(x.transform.matrix),h.matrix.decompose(h.position,h.rotation,h.scale),h.matrixWorldNeedsUpdate=!0,h.jointRadius=x.radius),h.visible=x!==null}const f=u.joints["index-finger-tip"],m=u.joints["thumb-tip"],d=f.position.distanceTo(m.position),p=.02,g=.005;u.inputState.pinching&&d>p+g?(u.inputState.pinching=!1,this.dispatchEvent({type:"pinchend",handedness:e.handedness,target:this})):!u.inputState.pinching&&d<=p-g&&(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(zA)))}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 Rc;i.matrixAutoUpdate=!1,i.visible=!1,e.joints[n.jointName]=i,e.add(i)}return e.joints[n.jointName]}}const TS={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},Dc={h:0,s:0,l:0};function Bf(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 vt{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,mt.colorSpaceToWorking(this,n),this}setRGB(e,n,i,a=mt.workingColorSpace){return this.r=e,this.g=n,this.b=i,mt.colorSpaceToWorking(this,a),this}setHSL(e,n,i,a=mt.workingColorSpace){if(e=MA(e,1),n=gt(n,0,1),i=gt(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=Bf(l,s,e+1/3),this.g=Bf(l,s,e),this.b=Bf(l,s,e-1/3)}return mt.colorSpaceToWorking(this,a),this}setStyle(e,n=xi){function i(s){s!==void 0&&parseFloat(s)<1&&et("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:et("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);et("Color: Invalid hex color "+e)}else if(e&&e.length>0)return this.setColorName(e,n);return this}setColorName(e,n=xi){const i=TS[e.toLowerCase()];return i!==void 0?this.setHex(i,n):et("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=Ca(e.r),this.g=Ca(e.g),this.b=Ca(e.b),this}copyLinearToSRGB(e){return this.r=ol(e.r),this.g=ol(e.g),this.b=ol(e.b),this}convertSRGBToLinear(){return this.copySRGBToLinear(this),this}convertLinearToSRGB(){return this.copyLinearToSRGB(this),this}getHex(e=xi){return mt.workingToColorSpace(Dn.copy(this),e),Math.round(gt(Dn.r*255,0,255))*65536+Math.round(gt(Dn.g*255,0,255))*256+Math.round(gt(Dn.b*255,0,255))}getHexString(e=xi){return("000000"+this.getHex(e).toString(16)).slice(-6)}getHSL(e,n=mt.workingColorSpace){mt.workingToColorSpace(Dn.copy(this),n);const i=Dn.r,a=Dn.g,s=Dn.b,l=Math.max(i,a,s),o=Math.min(i,a,s);let c,u;const f=(o+l)/2;if(o===l)c=0,u=0;else{const m=l-o;switch(u=f<=.5?m/(l+o):m/(2-l-o),l){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=f,e}getRGB(e,n=mt.workingColorSpace){return mt.workingToColorSpace(Dn.copy(this),n),e.r=Dn.r,e.g=Dn.g,e.b=Dn.b,e}getStyle(e=xi){mt.workingToColorSpace(Dn.copy(this),e);const n=Dn.r,i=Dn.g,a=Dn.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(Dc);const i=Lf(Wa.h,Dc.h,n),a=Lf(Wa.s,Dc.s,n),s=Lf(Wa.l,Dc.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 Dn=new vt;vt.NAMES=TS;class kA extends Bn{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 ir,this.environmentIntensity=1,this.environmentRotation=new ir,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 Z,fa=new Z,jf=new Z,ha=new Z,Mr=new Z,Er=new Z,k0=new Z,Ff=new Z,Hf=new Z,Gf=new Z,Vf=new Zt,Wf=new Zt,Xf=new Zt;class Ti{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),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),fa.subVectors(i,n),jf.subVectors(e,n);const l=Ui.dot(Ui),o=Ui.dot(fa),c=Ui.dot(jf),u=fa.dot(fa),f=fa.dot(jf),m=l*u-o*o;if(m===0)return s.set(0,0,0),null;const d=1/m,p=(u*c-o*f)*d,g=(l*f-o*c)*d;return s.set(1-p-g,g,p)}static containsPoint(e,n,i,a){return this.getBarycoord(e,n,i,a,ha)===null?!1:ha.x>=0&&ha.y>=0&&ha.x+ha.y<=1}static getInterpolation(e,n,i,a,s,l,o,c){return this.getBarycoord(e,n,i,a,ha)===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,ha.x),c.addScaledVector(l,ha.y),c.addScaledVector(o,ha.z),c)}static getInterpolatedAttribute(e,n,i,a,s,l){return Vf.setScalar(0),Wf.setScalar(0),Xf.setScalar(0),Vf.fromBufferAttribute(e,n),Wf.fromBufferAttribute(e,i),Xf.fromBufferAttribute(e,a),l.setScalar(0),l.addScaledVector(Vf,s.x),l.addScaledVector(Wf,s.y),l.addScaledVector(Xf,s.z),l}static isFrontFacing(e,n,i,a){return Ui.subVectors(i,n),fa.subVectors(e,n),Ui.cross(fa).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),fa.subVectors(this.a,this.b),Ui.cross(fa).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 l,o;Mr.subVectors(a,i),Er.subVectors(s,i),Ff.subVectors(e,i);const c=Mr.dot(Ff),u=Er.dot(Ff);if(c<=0&&u<=0)return n.copy(i);Hf.subVectors(e,a);const f=Mr.dot(Hf),m=Er.dot(Hf);if(f>=0&&m<=f)return n.copy(a);const d=c*m-f*u;if(d<=0&&c>=0&&f<=0)return l=c/(c-f),n.copy(i).addScaledVector(Mr,l);Gf.subVectors(e,s);const p=Mr.dot(Gf),g=Er.dot(Gf);if(g>=0&&p<=g)return n.copy(s);const b=p*u-c*g;if(b<=0&&u>=0&&g<=0)return o=u/(u-g),n.copy(i).addScaledVector(Er,o);const x=f*g-p*m;if(x<=0&&m-f>=0&&p-g>=0)return k0.subVectors(s,a),o=(m-f)/(m-f+(p-g)),n.copy(a).addScaledVector(k0,o);const h=1/(x+b+d);return l=b*h,o=d*h,n.copy(i).addScaledVector(Mr,l).addScaledVector(Er,o)}equals(e){return e.a.equals(this.a)&&e.b.equals(this.b)&&e.c.equals(this.c)}}class fr{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(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 l=0,o=s.count;l<o;l++)e.isMesh===!0?e.getVertexPosition(l,Oi):Oi.fromBufferAttribute(s,l),Oi.applyMatrix4(e.matrixWorld),this.expandByPoint(Oi);else e.boundingBox!==void 0?(e.boundingBox===null&&e.computeBoundingBox(),Lc.copy(e.boundingBox)):(i.boundingBox===null&&i.computeBoundingBox(),Lc.copy(i.boundingBox)),Lc.applyMatrix4(e.matrixWorld),this.union(Lc)}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,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(Yl),Uc.subVectors(this.max,Yl),Tr.subVectors(e.a,Yl),Ar.subVectors(e.b,Yl),wr.subVectors(e.c,Yl),Xa.subVectors(Ar,Tr),qa.subVectors(wr,Ar),Ds.subVectors(Tr,wr);let n=[0,-Xa.z,Xa.y,0,-qa.z,qa.y,0,-Ds.z,Ds.y,Xa.z,0,-Xa.x,qa.z,0,-qa.x,Ds.z,0,-Ds.x,-Xa.y,Xa.x,0,-qa.y,qa.x,0,-Ds.y,Ds.x,0];return!qf(n,Tr,Ar,wr,Uc)||(n=[1,0,0,0,1,0,0,0,1],!qf(n,Tr,Ar,wr,Uc))?!1:(Oc.crossVectors(Xa,qa),n=[Oc.x,Oc.y,Oc.z],qf(n,Tr,Ar,wr,Uc))}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:(pa[0].set(this.min.x,this.min.y,this.min.z).applyMatrix4(e),pa[1].set(this.min.x,this.min.y,this.max.z).applyMatrix4(e),pa[2].set(this.min.x,this.max.y,this.min.z).applyMatrix4(e),pa[3].set(this.min.x,this.max.y,this.max.z).applyMatrix4(e),pa[4].set(this.max.x,this.min.y,this.min.z).applyMatrix4(e),pa[5].set(this.max.x,this.min.y,this.max.z).applyMatrix4(e),pa[6].set(this.max.x,this.max.y,this.min.z).applyMatrix4(e),pa[7].set(this.max.x,this.max.y,this.max.z).applyMatrix4(e),this.setFromPoints(pa),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 pa=[new Z,new Z,new Z,new Z,new Z,new Z,new Z,new Z],Oi=new Z,Lc=new fr,Tr=new Z,Ar=new Z,wr=new Z,Xa=new Z,qa=new Z,Ds=new Z,Yl=new Z,Uc=new Z,Oc=new Z,Ls=new Z;function qf(t,e,n,i,a){for(let s=0,l=t.length-3;s<=l;s+=3){Ls.fromArray(t,s);const o=a.x*Math.abs(Ls.x)+a.y*Math.abs(Ls.y)+a.z*Math.abs(Ls.z),c=e.dot(Ls),u=n.dot(Ls),f=i.dot(Ls);if(Math.max(-Math.max(c,u,f),Math.min(c,u,f))>o)return!1}return!0}const sn=new Z,Pc=new wt;let IA=0;class un extends dr{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:IA++}),this.name="",this.array=e,this.itemSize=n,this.count=e!==void 0?e.length/n:0,this.normalized=i,this.usage=M0,this.updateRanges=[],this.gpuType=ki,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++)Pc.fromBufferAttribute(this,n),Pc.applyMatrix3(e),this.setXY(n,Pc.x,Pc.y);else if(this.itemSize===3)for(let n=0,i=this.count;n<i;n++)sn.fromBufferAttribute(this,n),sn.applyMatrix3(e),this.setXYZ(n,sn.x,sn.y,sn.z);return this}applyMatrix4(e){for(let n=0,i=this.count;n<i;n++)sn.fromBufferAttribute(this,n),sn.applyMatrix4(e),this.setXYZ(n,sn.x,sn.y,sn.z);return this}applyNormalMatrix(e){for(let n=0,i=this.count;n<i;n++)sn.fromBufferAttribute(this,n),sn.applyNormalMatrix(e),this.setXYZ(n,sn.x,sn.y,sn.z);return this}transformDirection(e){for(let n=0,i=this.count;n<i;n++)sn.fromBufferAttribute(this,n),sn.transformDirection(e),this.setXYZ(n,sn.x,sn.y,sn.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=Xl(i,this.array)),i}setComponent(e,n,i){return this.normalized&&(i=Vn(i,this.array)),this.array[e*this.itemSize+n]=i,this}getX(e){let n=this.array[e*this.itemSize];return this.normalized&&(n=Xl(n,this.array)),n}setX(e,n){return this.normalized&&(n=Vn(n,this.array)),this.array[e*this.itemSize]=n,this}getY(e){let n=this.array[e*this.itemSize+1];return this.normalized&&(n=Xl(n,this.array)),n}setY(e,n){return this.normalized&&(n=Vn(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=Xl(n,this.array)),n}setZ(e,n){return this.normalized&&(n=Vn(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=Xl(n,this.array)),n}setW(e,n){return this.normalized&&(n=Vn(n,this.array)),this.array[e*this.itemSize+3]=n,this}setXY(e,n,i){return e*=this.itemSize,this.normalized&&(n=Vn(n,this.array),i=Vn(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=Vn(n,this.array),i=Vn(i,this.array),a=Vn(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=Vn(n,this.array),i=Vn(i,this.array),a=Vn(a,this.array),s=Vn(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!==M0&&(e.usage=this.usage),e}dispose(){this.dispatchEvent({type:"dispose"})}}class AS extends un{constructor(e,n,i){super(new Uint16Array(e),n,i)}}class wS extends un{constructor(e,n,i){super(new Uint32Array(e),n,i)}}class Ni extends un{constructor(e,n,i){super(new Float32Array(e),n,i)}}const BA=new fr,Kl=new Z,Yf=new Z;class hr{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):BA.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;Kl.subVectors(e,this.center);const n=Kl.lengthSq();if(n>this.radius*this.radius){const i=Math.sqrt(n),a=(i-this.radius)*.5;this.center.addScaledVector(Kl,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):(Yf.subVectors(e.center,this.center).setLength(e.radius),this.expandByPoint(Kl.copy(e.center).add(Yf)),this.expandByPoint(Kl.copy(e.center).sub(Yf))),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 jA=0;const mi=new Bt,Kf=new Bn,Nr=new Z,ni=new fr,$l=new fr,gn=new Z;class Qn extends dr{constructor(){super(),this.isBufferGeometry=!0,Object.defineProperty(this,"id",{value:jA++}),this.uuid=cc(),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(_A(e)?wS:AS)(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 at().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 Kf.lookAt(e),Kf.updateMatrix(),this.applyMatrix4(Kf.matrix),this}center(){return this.computeBoundingBox(),this.boundingBox.getCenter(Nr).negate(),this.translate(Nr.x,Nr.y,Nr.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 Ni(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&&et("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 fr);const e=this.attributes.position,n=this.morphAttributes.position;if(e&&e.isGLBufferAttribute){Mt("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];ni.setFromBufferAttribute(s),this.morphTargetsRelative?(gn.addVectors(this.boundingBox.min,ni.min),this.boundingBox.expandByPoint(gn),gn.addVectors(this.boundingBox.max,ni.max),this.boundingBox.expandByPoint(gn)):(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))&&Mt('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 hr);const e=this.attributes.position,n=this.morphAttributes.position;if(e&&e.isGLBufferAttribute){Mt("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(ni.setFromBufferAttribute(e),n)for(let s=0,l=n.length;s<l;s++){const o=n[s];$l.setFromBufferAttribute(o),this.morphTargetsRelative?(gn.addVectors(ni.min,$l.min),ni.expandByPoint(gn),gn.addVectors(ni.max,$l.max),ni.expandByPoint(gn)):(ni.expandByPoint($l.min),ni.expandByPoint($l.max))}ni.getCenter(i);let a=0;for(let s=0,l=e.count;s<l;s++)gn.fromBufferAttribute(e,s),a=Math.max(a,i.distanceToSquared(gn));if(n)for(let s=0,l=n.length;s<l;s++){const o=n[s],c=this.morphTargetsRelative;for(let u=0,f=o.count;u<f;u++)gn.fromBufferAttribute(o,u),c&&(Nr.fromBufferAttribute(e,u),gn.add(Nr)),a=Math.max(a,i.distanceToSquared(gn))}this.boundingSphere.radius=Math.sqrt(a),isNaN(this.boundingSphere.radius)&&Mt('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){Mt("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 un(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,f=new Z,m=new Z,d=new wt,p=new wt,g=new wt,b=new Z,x=new Z;function h(S,w,R){u.fromBufferAttribute(i,S),f.fromBufferAttribute(i,w),m.fromBufferAttribute(i,R),d.fromBufferAttribute(s,S),p.fromBufferAttribute(s,w),g.fromBufferAttribute(s,R),f.sub(u),m.sub(u),p.sub(d),g.sub(d);const D=1/(p.x*g.y-g.x*p.y);isFinite(D)&&(b.copy(f).multiplyScalar(g.y).addScaledVector(m,-p.y).multiplyScalar(D),x.copy(m).multiplyScalar(p.x).addScaledVector(f,-g.x).multiplyScalar(D),o[S].add(b),o[w].add(b),o[R].add(b),c[S].add(x),c[w].add(x),c[R].add(x))}let v=this.groups;v.length===0&&(v=[{start:0,count:e.count}]);for(let S=0,w=v.length;S<w;++S){const R=v[S],D=R.start,L=R.count;for(let q=D,P=D+L;q<P;q+=3)h(e.getX(q+0),e.getX(q+1),e.getX(q+2))}const y=new Z,_=new Z,A=new Z,M=new Z;function T(S){A.fromBufferAttribute(a,S),M.copy(A);const w=o[S];y.copy(w),y.sub(A.multiplyScalar(A.dot(w))).normalize(),_.crossVectors(M,w);const D=_.dot(c[S])<0?-1:1;l.setXYZW(S,y.x,y.y,y.z,D)}for(let S=0,w=v.length;S<w;++S){const R=v[S],D=R.start,L=R.count;for(let q=D,P=D+L;q<P;q+=3)T(e.getX(q+0)),T(e.getX(q+1)),T(e.getX(q+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 un(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,f=new Z,m=new Z;if(e)for(let d=0,p=e.count;d<p;d+=3){const g=e.getX(d+0),b=e.getX(d+1),x=e.getX(d+2);a.fromBufferAttribute(n,g),s.fromBufferAttribute(n,b),l.fromBufferAttribute(n,x),f.subVectors(l,s),m.subVectors(a,s),f.cross(m),o.fromBufferAttribute(i,g),c.fromBufferAttribute(i,b),u.fromBufferAttribute(i,x),o.add(f),c.add(f),u.add(f),i.setXYZ(g,o.x,o.y,o.z),i.setXYZ(b,c.x,c.y,c.z),i.setXYZ(x,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),f.subVectors(l,s),m.subVectors(a,s),f.cross(m),i.setXYZ(d+0,f.x,f.y,f.z),i.setXYZ(d+1,f.x,f.y,f.z),i.setXYZ(d+2,f.x,f.y,f.z);this.normalizeNormals(),i.needsUpdate=!0}}normalizeNormals(){const e=this.attributes.normal;for(let n=0,i=e.count;n<i;n++)gn.fromBufferAttribute(e,n),gn.normalize(),e.setXYZ(n,gn.x,gn.y,gn.z)}toNonIndexed(){function e(o,c){const u=o.array,f=o.itemSize,m=o.normalized,d=new u.constructor(c.length*f);let p=0,g=0;for(let b=0,x=c.length;b<x;b++){o.isInterleavedBufferAttribute?p=c[b]*o.data.stride+o.offset:p=c[b]*f;for(let h=0;h<f;h++)d[g++]=u[p++]}return new un(d,f,m)}if(this.index===null)return et("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 f=0,m=u.length;f<m;f++){const d=u[f],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],f=[];for(let m=0,d=u.length;m<d;m++){const p=u[m];f.push(p.toJSON(e.data))}f.length>0&&(a[c]=f,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 f=a[u];this.setAttribute(u,f.clone(n))}const s=e.morphAttributes;for(const u in s){const f=[],m=s[u];for(let d=0,p=m.length;d<p;d++)f.push(m[d].clone(n));this.morphAttributes[u]=f}this.morphTargetsRelative=e.morphTargetsRelative;const l=e.groups;for(let u=0,f=l.length;u<f;u++){const m=l[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 FA=0;class zl extends dr{constructor(){super(),this.isMaterial=!0,Object.defineProperty(this,"id",{value:FA++}),this.uuid=cc(),this.name="",this.type="Material",this.blending=rl,this.side=As,this.vertexColors=!1,this.opacity=1,this.transparent=!1,this.alphaHash=!1,this.blendSrc=xp,this.blendDst=vp,this.blendEquation=Fs,this.blendSrcAlpha=null,this.blendDstAlpha=null,this.blendEquationAlpha=null,this.blendColor=new vt(0,0,0),this.blendAlpha=0,this.depthFunc=Sl,this.depthTest=!0,this.depthWrite=!0,this.stencilWriteMask=255,this.stencilFunc=S0,this.stencilRef=0,this.stencilFuncMask=255,this.stencilFail=vr,this.stencilZFail=vr,this.stencilZPass=vr,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){et(`Material: parameter '${n}' has value of undefined.`);continue}const a=this[n];if(a===void 0){et(`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!==rl&&(i.blending=this.blending),this.side!==As&&(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!==xp&&(i.blendSrc=this.blendSrc),this.blendDst!==vp&&(i.blendDst=this.blendDst),this.blendEquation!==Fs&&(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!==Sl&&(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!==S0&&(i.stencilFunc=this.stencilFunc),this.stencilRef!==0&&(i.stencilRef=this.stencilRef),this.stencilFuncMask!==255&&(i.stencilFuncMask=this.stencilFuncMask),this.stencilFail!==vr&&(i.stencilFail=this.stencilFail),this.stencilZFail!==vr&&(i.stencilZFail=this.stencilZFail),this.stencilZPass!==vr&&(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 vt().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 wt().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 wt().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 ma=new Z,$f=new Z,zc=new Z,Ya=new Z,Zf=new Z,kc=new Z,Qf=new Z;class Tg{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,ma)),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=ma.subVectors(e,this.origin).dot(this.direction);return n<0?this.origin.distanceToSquared(e):(ma.copy(this.origin).addScaledVector(this.direction,n),ma.distanceToSquared(e))}distanceSqToSegment(e,n,i,a){$f.copy(e).add(n).multiplyScalar(.5),zc.copy(n).sub(e).normalize(),Ya.copy(this.origin).sub($f);const s=e.distanceTo(n)*.5,l=-this.direction.dot(zc),o=Ya.dot(this.direction),c=-Ya.dot(zc),u=Ya.lengthSq(),f=Math.abs(1-l*l);let m,d,p,g;if(f>0)if(m=l*c-o,d=l*o-c,g=s*f,m>=0)if(d>=-g)if(d<=g){const b=1/f;m*=b,d*=b,p=m*(m+l*d+2*o)+d*(l*m+d+2*c)+u}else d=s,m=Math.max(0,-(l*d+o)),p=-m*m+d*(d+2*c)+u;else d=-s,m=Math.max(0,-(l*d+o)),p=-m*m+d*(d+2*c)+u;else d<=-g?(m=Math.max(0,-(-l*s+o)),d=m>0?-s:Math.min(Math.max(-s,-c),s),p=-m*m+d*(d+2*c)+u):d<=g?(m=0,d=Math.min(Math.max(-s,-c),s),p=d*(d+2*c)+u):(m=Math.max(0,-(l*s+o)),d=m>0?s:Math.min(Math.max(-s,-c),s),p=-m*m+d*(d+2*c)+u);else d=l>0?-s:s,m=Math.max(0,-(l*d+o)),p=-m*m+d*(d+2*c)+u;return i&&i.copy(this.origin).addScaledVector(this.direction,m),a&&a.copy($f).addScaledVector(zc,d),p}intersectSphere(e,n){ma.subVectors(e.center,this.origin);const i=ma.dot(this.direction),a=ma.dot(ma)-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,f=1/this.direction.y,m=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),f>=0?(s=(e.min.y-d.y)*f,l=(e.max.y-d.y)*f):(s=(e.max.y-d.y)*f,l=(e.min.y-d.y)*f),i>l||s>a||((s>i||isNaN(i))&&(i=s),(l<a||isNaN(a))&&(a=l),m>=0?(o=(e.min.z-d.z)*m,c=(e.max.z-d.z)*m):(o=(e.max.z-d.z)*m,c=(e.min.z-d.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,ma)!==null}intersectTriangle(e,n,i,a,s){Zf.subVectors(n,e),kc.subVectors(i,e),Qf.crossVectors(Zf,kc);let l=this.direction.dot(Qf),o;if(l>0){if(a)return null;o=1}else if(l<0)o=-1,l=-l;else return null;Ya.subVectors(this.origin,e);const c=o*this.direction.dot(kc.crossVectors(Ya,kc));if(c<0)return null;const u=o*this.direction.dot(Zf.cross(Ya));if(u<0||c+u>l)return null;const f=-o*Ya.dot(Qf);return f<0?null:this.at(f/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 NS extends zl{constructor(e){super(),this.isMeshBasicMaterial=!0,this.type="MeshBasicMaterial",this.color=new vt(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 ir,this.combine=lS,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 I0=new Bt,Us=new Tg,Ic=new hr,B0=new Z,Bc=new Z,jc=new Z,Fc=new Z,Jf=new Z,Hc=new Z,j0=new Z,Gc=new Z;class Ri extends Bn{constructor(e=new Qn,n=new NS){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){Hc.set(0,0,0);for(let c=0,u=s.length;c<u;c++){const f=o[c],m=s[c];f!==0&&(Jf.fromBufferAttribute(m,e),l?Hc.addScaledVector(Jf,f):Hc.addScaledVector(Jf.sub(n),f))}n.add(Hc)}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),Us.copy(e.ray).recast(e.near),!(Ic.containsPoint(Us.origin)===!1&&(Us.intersectSphere(Ic,B0)===null||Us.origin.distanceToSquared(B0)>(e.far-e.near)**2))&&(I0.copy(s).invert(),Us.copy(e.ray).applyMatrix4(I0),!(i.boundingBox!==null&&Us.intersectsBox(i.boundingBox)===!1)&&this._computeIntersections(e,n,Us)))}_computeIntersections(e,n,i){let a;const s=this.geometry,l=this.material,o=s.index,c=s.attributes.position,u=s.attributes.uv,f=s.attributes.uv1,m=s.attributes.normal,d=s.groups,p=s.drawRange;if(o!==null)if(Array.isArray(l))for(let g=0,b=d.length;g<b;g++){const x=d[g],h=l[x.materialIndex],v=Math.max(x.start,p.start),y=Math.min(o.count,Math.min(x.start+x.count,p.start+p.count));for(let _=v,A=y;_<A;_+=3){const M=o.getX(_),T=o.getX(_+1),S=o.getX(_+2);a=Vc(this,h,e,i,u,f,m,M,T,S),a&&(a.faceIndex=Math.floor(_/3),a.face.materialIndex=x.materialIndex,n.push(a))}}else{const g=Math.max(0,p.start),b=Math.min(o.count,p.start+p.count);for(let x=g,h=b;x<h;x+=3){const v=o.getX(x),y=o.getX(x+1),_=o.getX(x+2);a=Vc(this,l,e,i,u,f,m,v,y,_),a&&(a.faceIndex=Math.floor(x/3),n.push(a))}}else if(c!==void 0)if(Array.isArray(l))for(let g=0,b=d.length;g<b;g++){const x=d[g],h=l[x.materialIndex],v=Math.max(x.start,p.start),y=Math.min(c.count,Math.min(x.start+x.count,p.start+p.count));for(let _=v,A=y;_<A;_+=3){const M=_,T=_+1,S=_+2;a=Vc(this,h,e,i,u,f,m,M,T,S),a&&(a.faceIndex=Math.floor(_/3),a.face.materialIndex=x.materialIndex,n.push(a))}}else{const g=Math.max(0,p.start),b=Math.min(c.count,p.start+p.count);for(let x=g,h=b;x<h;x+=3){const v=x,y=x+1,_=x+2;a=Vc(this,l,e,i,u,f,m,v,y,_),a&&(a.faceIndex=Math.floor(x/3),n.push(a))}}}}function HA(t,e,n,i,a,s,l,o){let c;if(e.side===$n?c=i.intersectTriangle(l,s,a,!0,o):c=i.intersectTriangle(a,s,l,e.side===As,o),c===null)return null;Gc.copy(o),Gc.applyMatrix4(t.matrixWorld);const u=n.ray.origin.distanceTo(Gc);return u<n.near||u>n.far?null:{distance:u,point:Gc.clone(),object:t}}function Vc(t,e,n,i,a,s,l,o,c,u){t.getVertexPosition(o,Bc),t.getVertexPosition(c,jc),t.getVertexPosition(u,Fc);const f=HA(t,e,n,i,Bc,jc,Fc,j0);if(f){const m=new Z;Ti.getBarycoord(j0,Bc,jc,Fc,m),a&&(f.uv=Ti.getInterpolatedAttribute(a,o,c,u,m,new wt)),s&&(f.uv1=Ti.getInterpolatedAttribute(s,o,c,u,m,new wt)),l&&(f.normal=Ti.getInterpolatedAttribute(l,o,c,u,m,new Z),f.normal.dot(i.direction)>0&&f.normal.multiplyScalar(-1));const d={a:o,b:c,c:u,normal:new Z,materialIndex:0};Ti.getNormal(Bc,jc,Fc,d.normal),f.face=d,f.barycoord=m}return f}class CS extends In{constructor(e=null,n=1,i=1,a,s,l,o,c,u=An,f=An,m,d){super(null,l,o,c,u,f,a,s,m,d),this.isDataTexture=!0,this.image={data:e,width:n,height:i},this.generateMipmaps=!1,this.flipY=!1,this.unpackAlignment=1}}class Qr extends un{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 Cr=new Bt,F0=new Bt,Wc=[],H0=new fr,GA=new Bt,Zl=new Ri,Ql=new hr;class G0 extends Ri{constructor(e,n,i){super(e,n),this.isInstancedMesh=!0,this.instanceMatrix=new Qr(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,GA)}computeBoundingBox(){const e=this.geometry,n=this.count;this.boundingBox===null&&(this.boundingBox=new fr),e.boundingBox===null&&e.computeBoundingBox(),this.boundingBox.makeEmpty();for(let i=0;i<n;i++)this.getMatrixAt(i,Cr),H0.copy(e.boundingBox).applyMatrix4(Cr),this.boundingBox.union(H0)}computeBoundingSphere(){const e=this.geometry,n=this.count;this.boundingSphere===null&&(this.boundingSphere=new hr),e.boundingSphere===null&&e.computeBoundingSphere(),this.boundingSphere.makeEmpty();for(let i=0;i<n;i++)this.getMatrixAt(i,Cr),Ql.copy(e.boundingSphere).applyMatrix4(Cr),this.boundingSphere.union(Ql)}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(Zl.geometry=this.geometry,Zl.material=this.material,Zl.material!==void 0&&(this.boundingSphere===null&&this.computeBoundingSphere(),Ql.copy(this.boundingSphere),Ql.applyMatrix4(i),e.ray.intersectsSphere(Ql)!==!1))for(let s=0;s<a;s++){this.getMatrixAt(s,Cr),F0.multiplyMatrices(i,Cr),Zl.matrixWorld=F0,Zl.raycast(e,Wc);for(let l=0,o=Wc.length;l<o;l++){const c=Wc[l];c.instanceId=s,c.object=this,n.push(c)}Wc.length=0}}setColorAt(e,n){return this.instanceColor===null&&(this.instanceColor=new Qr(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 CS(new Float32Array(a*this.count),a,this.count,vg,ki));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 eh=new Z,VA=new Z,WA=new at;class js{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=eh.subVectors(i,n).cross(VA.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(eh),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||WA.getNormalMatrix(e),a=this.coplanarPoint(eh).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 Os=new hr,XA=new wt(.5,.5),Xc=new Z;class RS{constructor(e=new js,n=new js,i=new js,a=new js,s=new js,l=new js){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=ea,i=!1){const a=this.planes,s=e.elements,l=s[0],o=s[1],c=s[2],u=s[3],f=s[4],m=s[5],d=s[6],p=s[7],g=s[8],b=s[9],x=s[10],h=s[11],v=s[12],y=s[13],_=s[14],A=s[15];if(a[0].setComponents(u-l,p-f,h-g,A-v).normalize(),a[1].setComponents(u+l,p+f,h+g,A+v).normalize(),a[2].setComponents(u+o,p+m,h+b,A+y).normalize(),a[3].setComponents(u-o,p-m,h-b,A-y).normalize(),i)a[4].setComponents(c,d,x,_).normalize(),a[5].setComponents(u-c,p-d,h-x,A-_).normalize();else if(a[4].setComponents(u-c,p-d,h-x,A-_).normalize(),n===ea)a[5].setComponents(u+c,p+d,h+x,A+_).normalize();else if(n===fd)a[5].setComponents(c,d,x,_).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(),Os.copy(e.boundingSphere).applyMatrix4(e.matrixWorld);else{const n=e.geometry;n.boundingSphere===null&&n.computeBoundingSphere(),Os.copy(n.boundingSphere).applyMatrix4(e.matrixWorld)}return this.intersectsSphere(Os)}intersectsSprite(e){Os.center.set(0,0,0);const n=XA.distanceTo(e.center);return Os.radius=.7071067811865476+n,Os.applyMatrix4(e.matrixWorld),this.intersectsSphere(Os)}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(Xc.x=a.normal.x>0?e.max.x:e.min.x,Xc.y=a.normal.y>0?e.max.y:e.min.y,Xc.z=a.normal.z>0?e.max.z:e.min.z,a.distanceToPoint(Xc)<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 DS extends zl{constructor(e){super(),this.isLineBasicMaterial=!0,this.type="LineBasicMaterial",this.color=new vt(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 pd=new Z,md=new Z,V0=new Bt,Jl=new Tg,qc=new hr,th=new Z,W0=new Z;class qA extends Bn{constructor(e=new Qn,n=new DS){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++)pd.fromBufferAttribute(n,a-1),md.fromBufferAttribute(n,a),i[a]=i[a-1],i[a]+=pd.distanceTo(md);e.setAttribute("lineDistance",new Ni(i,1))}else et("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(),qc.copy(i.boundingSphere),qc.applyMatrix4(a),qc.radius+=s,e.ray.intersectsSphere(qc)===!1)return;V0.copy(a).invert(),Jl.copy(e.ray).applyMatrix4(V0);const o=s/((this.scale.x+this.scale.y+this.scale.z)/3),c=o*o,u=this.isLineSegments?2:1,f=i.index,d=i.attributes.position;if(f!==null){const p=Math.max(0,l.start),g=Math.min(f.count,l.start+l.count);for(let b=p,x=g-1;b<x;b+=u){const h=f.getX(b),v=f.getX(b+1),y=Yc(this,e,Jl,c,h,v,b);y&&n.push(y)}if(this.isLineLoop){const b=f.getX(g-1),x=f.getX(p),h=Yc(this,e,Jl,c,b,x,g-1);h&&n.push(h)}}else{const p=Math.max(0,l.start),g=Math.min(d.count,l.start+l.count);for(let b=p,x=g-1;b<x;b+=u){const h=Yc(this,e,Jl,c,b,b+1,b);h&&n.push(h)}if(this.isLineLoop){const b=Yc(this,e,Jl,c,g-1,p,g-1);b&&n.push(b)}}}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 Yc(t,e,n,i,a,s,l){const o=t.geometry.attributes.position;if(pd.fromBufferAttribute(o,a),md.fromBufferAttribute(o,s),n.distanceSqToSegment(pd,md,th,W0)>i)return;th.applyMatrix4(t.matrixWorld);const u=e.ray.origin.distanceTo(th);if(!(u<e.near||u>e.far))return{distance:u,point:W0.clone().applyMatrix4(t.matrixWorld),index:l,face:null,faceIndex:null,barycoord:null,object:t}}const X0=new Z,q0=new Z;class nh 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)X0.fromBufferAttribute(n,a),q0.fromBufferAttribute(n,a+1),i[a]=a===0?0:i[a-1],i[a+1]=i[a]+X0.distanceTo(q0);e.setAttribute("lineDistance",new Ni(i,1))}else et("LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.");return this}}class YA extends zl{constructor(e){super(),this.isPointsMaterial=!0,this.type="PointsMaterial",this.color=new vt(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 Y0=new Bt,sm=new Tg,Kc=new hr,$c=new Z;class KA extends Bn{constructor(e=new Qn,n=new YA){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;Y0.copy(a).invert(),sm.copy(e.ray).applyMatrix4(Y0);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 d=Math.max(0,l.start),p=Math.min(u.count,l.start+l.count);for(let g=d,b=p;g<b;g++){const x=u.getX(g);$c.fromBufferAttribute(m,x),K0($c,x,c,a,e,n,this)}}else{const d=Math.max(0,l.start),p=Math.min(m.count,l.start+l.count);for(let g=d,b=p;g<b;g++)$c.fromBufferAttribute(m,g),K0($c,g,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 K0(t,e,n,i,a,s,l){const o=sm.distanceSqToPoint(t);if(o<n){const c=new Z;sm.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 LS extends In{constructor(e=[],n=tr,i,a,s,l,o,c,u,f){super(e,n,i,a,s,l,o,c,u,f),this.isCubeTexture=!0,this.flipY=!1}get images(){return this.image}set images(e){this.image=e}}class El extends In{constructor(e,n,i=aa,a,s,l,o=An,c=An,u,f=za,m=1){if(f!==za&&f!==Vs)throw new Error("THREE.DepthTexture: format must be either THREE.DepthFormat or THREE.DepthStencilFormat");const d={width:e,height:n,depth:m};super(d,a,s,l,o,c,f,i,u),this.isDepthTexture=!0,this.flipY=!1,this.generateMipmaps=!1,this.compareFunction=null}copy(e){return super.copy(e),this.source=new Eg(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 $A extends El{constructor(e,n=aa,i=tr,a,s,l=An,o=An,c,u=za){const f={width:e,height:e,depth:1},m=[f,f,f,f,f,f];super(e,e,n,i,a,s,l,o,c,u),this.image=m,this.isCubeDepthTexture=!0,this.isCubeTexture=!0}get images(){return this.image}set images(e){this.image=e}}class US extends In{constructor(e=null){super(),this.sourceTexture=e,this.isExternalTexture=!0}copy(e){return super.copy(e),this.sourceTexture=e.sourceTexture,this}}class ar 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=[],f=[],m=[];let d=0,p=0;g("z","y","x",-1,-1,i,n,e,l,s,0),g("z","y","x",1,-1,i,n,-e,l,s,1),g("x","z","y",1,1,e,i,n,a,l,2),g("x","z","y",1,-1,e,i,-n,a,l,3),g("x","y","z",1,-1,e,n,i,a,s,4),g("x","y","z",-1,-1,e,n,-i,a,s,5),this.setIndex(c),this.setAttribute("position",new Ni(u,3)),this.setAttribute("normal",new Ni(f,3)),this.setAttribute("uv",new Ni(m,2));function g(b,x,h,v,y,_,A,M,T,S,w){const R=_/T,D=A/S,L=_/2,q=A/2,P=M/2,O=T+1,B=S+1;let k=0,W=0;const Y=new Z;for(let he=0;he<B;he++){const _e=he*D-q;for(let Ce=0;Ce<O;Ce++){const X=Ce*R-L;Y[b]=X*v,Y[x]=_e*y,Y[h]=P,u.push(Y.x,Y.y,Y.z),Y[b]=0,Y[x]=0,Y[h]=M>0?1:-1,f.push(Y.x,Y.y,Y.z),m.push(Ce/T),m.push(1-he/S),k+=1}}for(let he=0;he<S;he++)for(let _e=0;_e<T;_e++){const Ce=d+_e+O*he,X=d+_e+O*(he+1),de=d+(_e+1)+O*(he+1),Ne=d+(_e+1)+O*he;c.push(Ce,X,Ne),c.push(X,de,Ne),W+=6}o.addGroup(p,W,w),p+=W,d+=k}}copy(e){return super.copy(e),this.parameters=Object.assign({},e.parameters),this}static fromJSON(e){return new ar(e.width,e.height,e.depth,e.widthSegments,e.heightSegments,e.depthSegments)}}const Zc=new Z,Qc=new Z,ih=new Z,Jc=new Ti;class ZA 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(Ru*n),l=e.getIndex(),o=e.getAttribute("position"),c=l?l.count:o.count,u=[0,0,0],f=["a","b","c"],m=new Array(3),d={},p=[];for(let g=0;g<c;g+=3){l?(u[0]=l.getX(g),u[1]=l.getX(g+1),u[2]=l.getX(g+2)):(u[0]=g,u[1]=g+1,u[2]=g+2);const{a:b,b:x,c:h}=Jc;if(b.fromBufferAttribute(o,u[0]),x.fromBufferAttribute(o,u[1]),h.fromBufferAttribute(o,u[2]),Jc.getNormal(ih),m[0]=`${Math.round(b.x*a)},${Math.round(b.y*a)},${Math.round(b.z*a)}`,m[1]=`${Math.round(x.x*a)},${Math.round(x.y*a)},${Math.round(x.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 y=(v+1)%3,_=m[v],A=m[y],M=Jc[f[v]],T=Jc[f[y]],S=`${_}_${A}`,w=`${A}_${_}`;w in d&&d[w]?(ih.dot(d[w].normal)<=s&&(p.push(M.x,M.y,M.z),p.push(T.x,T.y,T.z)),d[w]=null):S in d||(d[S]={index0:u[v],index1:u[y],normal:ih.clone()})}}for(const g in d)if(d[g]){const{index0:b,index1:x}=d[g];Zc.fromBufferAttribute(o,b),Qc.fromBufferAttribute(o,x),p.push(Zc.x,Zc.y,Zc.z),p.push(Qc.x,Qc.y,Qc.z)}this.setAttribute("position",new Ni(p,3))}}copy(e){return super.copy(e),this.parameters=Object.assign({},e.parameters),this}}class Fd 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,f=c+1,m=e/o,d=n/c,p=[],g=[],b=[],x=[];for(let h=0;h<f;h++){const v=h*d-l;for(let y=0;y<u;y++){const _=y*m-s;g.push(_,-v,0),b.push(0,0,1),x.push(y/o),x.push(1-h/c)}}for(let h=0;h<c;h++)for(let v=0;v<o;v++){const y=v+u*h,_=v+u*(h+1),A=v+1+u*(h+1),M=v+1+u*h;p.push(y,_,M),p.push(_,A,M)}this.setIndex(p),this.setAttribute("position",new Ni(g,3)),this.setAttribute("normal",new Ni(b,3)),this.setAttribute("uv",new Ni(x,2))}copy(e){return super.copy(e),this.parameters=Object.assign({},e.parameters),this}static fromJSON(e){return new Fd(e.width,e.height,e.widthSegments,e.heightSegments)}}function Tl(t){const e={};for(const n in t){e[n]={};for(const i in t[n]){const a=t[n][i];if($0(a))a.isRenderTargetTexture?(et("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($0(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 Un(t){const e={};for(let n=0;n<t.length;n++){const i=Tl(t[n]);for(const a in i)e[a]=i[a]}return e}function $0(t){return t&&(t.isColor||t.isMatrix3||t.isMatrix4||t.isVector2||t.isVector3||t.isVector4||t.isTexture||t.isQuaternion)}function QA(t){const e=[];for(let n=0;n<t.length;n++)e.push(t[n].clone());return e}function OS(t){const e=t.getRenderTarget();return e===null?t.outputColorSpace:e.isXRRenderTarget===!0?e.texture.colorSpace:mt.workingColorSpace}const JA={clone:Tl,merge:Un};var ew=`void main() { + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); +}`,tw=`void main() { + gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 ); +}`;class Yn extends zl{constructor(e){super(),this.isShaderMaterial=!0,this.type="ShaderMaterial",this.defines={},this.uniforms={},this.uniformsGroups=[],this.vertexShader=ew,this.fragmentShader=tw,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=Tl(e.uniforms),this.uniformsGroups=QA(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 vt().setHex(a.value);break;case"v2":this.uniforms[i].value=new wt().fromArray(a.value);break;case"v3":this.uniforms[i].value=new Z().fromArray(a.value);break;case"v4":this.uniforms[i].value=new Zt().fromArray(a.value);break;case"m3":this.uniforms[i].value=new at().fromArray(a.value);break;case"m4":this.uniforms[i].value=new Bt().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 nw extends Yn{constructor(e){super(e),this.isRawShaderMaterial=!0,this.type="RawShaderMaterial"}}class iw extends zl{constructor(e){super(),this.isMeshDepthMaterial=!0,this.type="MeshDepthMaterial",this.depthPacking=dA,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 aw extends zl{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 eu=new Z,tu=new Pl,Wi=new Z;class PS extends Bn{constructor(){super(),this.isCamera=!0,this.type="Camera",this.matrixWorldInverse=new Bt,this.projectionMatrix=new Bt,this.projectionMatrixInverse=new Bt,this.coordinateSystem=ea,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(eu,tu,Wi),Wi.x===1&&Wi.y===1&&Wi.z===1?this.matrixWorldInverse.copy(this.matrixWorld).invert():this.matrixWorldInverse.compose(eu,tu,Wi.set(1,1,1)).invert()}updateWorldMatrix(e,n,i=!1){super.updateWorldMatrix(e,n,i),this.matrixWorld.decompose(eu,tu,Wi),Wi.x===1&&Wi.y===1&&Wi.z===1?this.matrixWorldInverse.copy(this.matrixWorld).invert():this.matrixWorldInverse.compose(eu,tu,Wi.set(1,1,1)).invert()}clone(){return new this.constructor().copy(this)}}const Ka=new Z,Z0=new wt,Q0=new wt;class zi extends PS{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=am*2*Math.atan(n),this.updateProjectionMatrix()}getFocalLength(){const e=Math.tan(Ru*.5*this.fov);return .5*this.getFilmHeight()/e}getEffectiveFOV(){return am*2*Math.atan(Math.tan(Ru*.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,Z0,Q0),n.subVectors(Q0,Z0)}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(Ru*.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 Ag extends PS{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,f=(this.top-this.bottom)/this.view.fullHeight/this.zoom;s+=u*this.view.offsetX,l=s+u*this.view.width,o-=f*this.view.offsetY,c=o-f*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 Rr=-90,Dr=1;class sw extends Bn{constructor(e,n,i){super(),this.type="CubeCamera",this.renderTarget=i,this.coordinateSystem=null,this.activeMipmapLevel=0;const a=new zi(Rr,Dr,e,n);a.layers=this.layers,this.add(a);const s=new zi(Rr,Dr,e,n);s.layers=this.layers,this.add(s);const l=new zi(Rr,Dr,e,n);l.layers=this.layers,this.add(l);const o=new zi(Rr,Dr,e,n);o.layers=this.layers,this.add(o);const c=new zi(Rr,Dr,e,n);c.layers=this.layers,this.add(c);const u=new zi(Rr,Dr,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===ea)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,f]=this.children,m=e.getRenderTarget(),d=e.getActiveCubeFace(),p=e.getActiveMipmapLevel(),g=e.xr.enabled;e.xr.enabled=!1;const b=i.texture.generateMipmaps;i.texture.generateMipmaps=!1;let x=!1;e.isWebGLRenderer===!0?x=e.state.buffers.depth.getReversed():x=e.reversedDepthBuffer,e.setRenderTarget(i,0,a),x&&e.autoClear===!1&&e.clearDepth(),e.render(n,s),e.setRenderTarget(i,1,a),x&&e.autoClear===!1&&e.clearDepth(),e.render(n,l),e.setRenderTarget(i,2,a),x&&e.autoClear===!1&&e.clearDepth(),e.render(n,o),e.setRenderTarget(i,3,a),x&&e.autoClear===!1&&e.clearDepth(),e.render(n,c),e.setRenderTarget(i,4,a),x&&e.autoClear===!1&&e.clearDepth(),e.render(n,u),i.texture.generateMipmaps=b,e.setRenderTarget(i,5,a),x&&e.autoClear===!1&&e.clearDepth(),e.render(n,f),e.setRenderTarget(m,d,p),e.xr.enabled=g,i.texture.needsPMREMUpdate=!0}}class rw extends zi{constructor(e=[]){super(),this.isArrayCamera=!0,this.isMultiViewCamera=!1,this.cameras=e}}const Og=class Og{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}};Og.prototype.isMatrix2=!0;let J0=Og;function ev(t,e,n,i){const a=lw(i);switch(n){case yS:return t*e;case vg:return t*e/a.components*a.byteLength;case _g:return t*e/a.components*a.byteLength;case nr:return t*e*2/a.components*a.byteLength;case yg:return t*e*2/a.components*a.byteLength;case bS:return t*e*3/a.components*a.byteLength;case Ii:return t*e*4/a.components*a.byteLength;case bg:return t*e*4/a.components*a.byteLength;case Au:case wu:return Math.floor((t+3)/4)*Math.floor((e+3)/4)*8;case Nu:case Cu:return Math.floor((t+3)/4)*Math.floor((e+3)/4)*16;case Cp:case Dp:return Math.max(t,16)*Math.max(e,8)/4;case Np:case Rp:return Math.max(t,8)*Math.max(e,8)/2;case Lp:case Up:case Pp:case zp:return Math.floor((t+3)/4)*Math.floor((e+3)/4)*8;case Op:case od: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 jp:return Math.floor((t+4)/5)*Math.floor((e+4)/5)*16;case Fp:return Math.floor((t+5)/6)*Math.floor((e+4)/5)*16;case Hp:return Math.floor((t+5)/6)*Math.floor((e+5)/6)*16;case Gp:return Math.floor((t+7)/8)*Math.floor((e+4)/5)*16;case Vp:return Math.floor((t+7)/8)*Math.floor((e+5)/6)*16;case Wp:return Math.floor((t+7)/8)*Math.floor((e+7)/8)*16;case Xp:return Math.floor((t+9)/10)*Math.floor((e+4)/5)*16;case qp:return Math.floor((t+9)/10)*Math.floor((e+5)/6)*16;case Yp: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 $p:return Math.floor((t+11)/12)*Math.floor((e+9)/10)*16;case Zp:return Math.floor((t+11)/12)*Math.floor((e+11)/12)*16;case Qp:case Jp:case em:return Math.ceil(t/4)*Math.ceil(e/4)*16;case tm:case nm:return Math.ceil(t/4)*Math.ceil(e/4)*8;case cd:case im:return Math.ceil(t/4)*Math.ceil(e/4)*16}throw new Error(`Unable to determine texture byte length for ${n} format.`)}function lw(t){switch(t){case Ei:case gS:return{byteLength:1,components:1};case qo:case xS:case Pa:return{byteLength:2,components:1};case gg:case xg:return{byteLength:2,components:4};case aa:case mg:case ki:return{byteLength:4,components:1};case vS:case _S: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:pg}}));typeof window<"u"&&(window.__THREE__?et("WARNING: Multiple instances of Three.js being imported."):window.__THREE__=pg);/** + * @license + * Copyright 2010-2026 Three.js Authors + * SPDX-License-Identifier: MIT + */function zS(){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 ow(t){const e=new WeakMap;function n(o,c){const u=o.array,f=o.usage,m=u.byteLength,d=t.createBuffer();t.bindBuffer(c,d),t.bufferData(c,u,f),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:m}}function i(o,c,u){const f=c.array,m=c.updateRanges;if(t.bindBuffer(u,o),m.length===0)t.bufferSubData(u,0,f);else{m.sort((p,g)=>p.start-g.start);let d=0;for(let p=1;p<m.length;p++){const g=m[d],b=m[p];b.start<=g.start+g.count+1?g.count=Math.max(g.count,b.start+b.count-g.start):(++d,m[d]=b)}m.length=d+1;for(let p=0,g=m.length;p<g;p++){const b=m[p];t.bufferSubData(u,b.start*f.BYTES_PER_ELEMENT,f,b.start,b.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 f=e.get(o);(!f||f.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 cw=`#ifdef USE_ALPHAHASH + if ( diffuseColor.a < getAlphaHashThreshold( vPosition ) ) discard; +#endif`,uw=`#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`,dw=`#ifdef USE_ALPHAMAP + diffuseColor.a *= texture2D( alphaMap, vAlphaMapUv ).g; +#endif`,fw=`#ifdef USE_ALPHAMAP + uniform sampler2D alphaMap; +#endif`,hw=`#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`,pw=`#ifdef USE_ALPHATEST + uniform float alphaTest; +#endif`,mw=`#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`,gw=`#ifdef USE_AOMAP + uniform sampler2D aoMap; + uniform float aoMapIntensity; +#endif`,xw=`#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`,vw=`#ifdef USE_BATCHING + mat4 batchingMatrix = getBatchingMatrix( getIndirectIndex( gl_DrawID ) ); +#endif`,_w=`vec3 transformed = vec3( position ); +#ifdef USE_ALPHAHASH + vPosition = vec3( position ); +#endif`,yw=`vec3 objectNormal = vec3( normal ); +#ifdef USE_TANGENT + vec3 objectTangent = vec3( tangent.xyz ); +#endif`,bw=`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`,Sw=`#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`,Mw=`#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`,Ew=`#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`,Tw=`#if NUM_CLIPPING_PLANES > 0 + varying vec3 vClipPosition; + uniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ]; +#endif`,Aw=`#if NUM_CLIPPING_PLANES > 0 + varying vec3 vClipPosition; +#endif`,ww=`#if NUM_CLIPPING_PLANES > 0 + vClipPosition = - mvPosition.xyz; +#endif`,Nw=`#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA ) + diffuseColor *= vColor; +#endif`,Cw=`#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA ) + varying vec4 vColor; +#endif`,Rw=`#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA ) || defined( USE_INSTANCING_COLOR ) || defined( USE_BATCHING_COLOR ) + varying vec4 vColor; +#endif`,Dw=`#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`,Lw=`#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`,Uw=`#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`,Ow=`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`,Pw=`#ifdef USE_DISPLACEMENTMAP + uniform sampler2D displacementMap; + uniform float displacementScale; + uniform float displacementBias; +#endif`,zw=`#ifdef USE_DISPLACEMENTMAP + transformed += normalize( objectNormal ) * ( texture2D( displacementMap, vDisplacementMapUv ).x * displacementScale + displacementBias ); +#endif`,kw=`#ifdef USE_EMISSIVEMAP + vec4 emissiveColor = texture2D( emissiveMap, vEmissiveMapUv ); + #ifdef DECODE_VIDEO_TEXTURE_EMISSIVE + emissiveColor = sRGBTransferEOTF( emissiveColor ); + #endif + totalEmissiveRadiance *= emissiveColor.rgb; +#endif`,Iw=`#ifdef USE_EMISSIVEMAP + uniform sampler2D emissiveMap; +#endif`,Bw="gl_FragColor = linearToOutputTexel( gl_FragColor );",jw=`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 ); +}`,Fw=`#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`,Hw=`#ifdef USE_ENVMAP + uniform float envMapIntensity; + uniform mat3 envMapRotation; + #ifdef ENVMAP_TYPE_CUBE + uniform samplerCube envMap; + #else + uniform sampler2D envMap; + #endif +#endif`,Gw=`#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`,Vw=`#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`,Ww=`#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`,Xw=`#ifdef USE_FOG + vFogDepth = - mvPosition.z; +#endif`,qw=`#ifdef USE_FOG + varying float vFogDepth; +#endif`,Yw=`#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`,Kw=`#ifdef USE_FOG + uniform vec3 fogColor; + varying float vFogDepth; + #ifdef FOG_EXP2 + uniform float fogDensity; + #else + uniform float fogNear; + uniform float fogFar; + #endif +#endif`,$w=`#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 +}`,Zw=`#ifdef USE_LIGHTMAP + uniform sampler2D lightMap; + uniform float lightMapIntensity; +#endif`,Qw=`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`,eN=`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>`,tN=`#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`,nN=`ToonMaterial material; +material.diffuseColor = diffuseColor.rgb;`,iN=`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`,aN=`BlinnPhongMaterial material; +material.diffuseColor = diffuseColor.rgb; +material.specularColor = specular; +material.specularShininess = shininess; +material.specularStrength = specularStrength;`,sN=`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`,rN=`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`,lN=`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 ); +}`,oN=` +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`,cN=`#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`,uN=`#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`,dN=`#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`,fN=`#if defined( USE_LOGARITHMIC_DEPTH_BUFFER ) + gl_FragDepth = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5; +#endif`,hN=`#if defined( USE_LOGARITHMIC_DEPTH_BUFFER ) + uniform float logDepthBufFC; + varying float vFragDepth; + varying float vIsPerspective; +#endif`,pN=`#ifdef USE_LOGARITHMIC_DEPTH_BUFFER + varying float vFragDepth; + varying float vIsPerspective; +#endif`,mN=`#ifdef USE_LOGARITHMIC_DEPTH_BUFFER + vFragDepth = 1.0 + gl_Position.w; + vIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) ); +#endif`,gN=`#ifdef USE_MAP + vec4 sampledDiffuseColor = texture2D( map, vMapUv ); + #ifdef DECODE_VIDEO_TEXTURE + sampledDiffuseColor = sRGBTransferEOTF( sampledDiffuseColor ); + #endif + diffuseColor *= sampledDiffuseColor; +#endif`,xN=`#ifdef USE_MAP + uniform sampler2D map; +#endif`,vN=`#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`,_N=`#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`,yN=`float metalnessFactor = metalness; +#ifdef USE_METALNESSMAP + vec4 texelMetalness = texture2D( metalnessMap, vMetalnessMapUv ); + metalnessFactor *= texelMetalness.b; +#endif`,bN=`#ifdef USE_METALNESSMAP + uniform sampler2D metalnessMap; +#endif`,SN=`#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`,MN=`#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`,EN=`#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`,TN=`#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`,AN=`#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`,wN=`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;`,NN=`#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`,CN=`#ifndef FLAT_SHADED + varying vec3 vNormal; + #ifdef USE_TANGENT + varying vec3 vTangent; + varying vec3 vBitangent; + #endif +#endif`,RN=`#ifndef FLAT_SHADED + varying vec3 vNormal; + #ifdef USE_TANGENT + varying vec3 vTangent; + varying vec3 vBitangent; + #endif +#endif`,DN=`#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`,LN=`#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`,UN=`#ifdef USE_CLEARCOAT + vec3 clearcoatNormal = nonPerturbedNormal; +#endif`,ON=`#ifdef USE_CLEARCOAT_NORMALMAP + vec3 clearcoatMapN = texture2D( clearcoatNormalMap, vClearcoatNormalMapUv ).xyz * 2.0 - 1.0; + clearcoatMapN.xy *= clearcoatNormalScale; + clearcoatNormal = normalize( tbn2 * clearcoatMapN ); +#endif`,PN=`#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`,zN=`#ifdef USE_IRIDESCENCEMAP + uniform sampler2D iridescenceMap; +#endif +#ifdef USE_IRIDESCENCE_THICKNESSMAP + uniform sampler2D iridescenceThicknessMap; +#endif`,kN=`#ifdef OPAQUE +diffuseColor.a = 1.0; +#endif +#ifdef USE_TRANSMISSION +diffuseColor.a *= material.transmissionAlpha; +#endif +gl_FragColor = vec4( outgoingLight, diffuseColor.a );`,IN=`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 +}`,BN=`#ifdef PREMULTIPLIED_ALPHA + gl_FragColor.rgb *= gl_FragColor.a; +#endif`,jN=`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;`,FN=`#ifdef DITHERING + gl_FragColor.rgb = dithering( gl_FragColor.rgb ); +#endif`,HN=`#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`,GN=`float roughnessFactor = roughness; +#ifdef USE_ROUGHNESSMAP + vec4 texelRoughness = texture2D( roughnessMap, vRoughnessMapUv ); + roughnessFactor *= texelRoughness.g; +#endif`,VN=`#ifdef USE_ROUGHNESSMAP + uniform sampler2D roughnessMap; +#endif`,WN=`#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`,XN=`#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`,YN=`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; +}`,KN=`#ifdef USE_SKINNING + mat4 boneMatX = getBoneMatrix( skinIndex.x ); + mat4 boneMatY = getBoneMatrix( skinIndex.y ); + mat4 boneMatZ = getBoneMatrix( skinIndex.z ); + mat4 boneMatW = getBoneMatrix( skinIndex.w ); +#endif`,$N=`#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`,ZN=`#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`,QN=`#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`,JN=`float specularStrength; +#ifdef USE_SPECULARMAP + vec4 texelSpecular = texture2D( specularMap, vSpecularMapUv ); + specularStrength = texelSpecular.r; +#else + specularStrength = 1.0; +#endif`,eC=`#ifdef USE_SPECULARMAP + uniform sampler2D specularMap; +#endif`,tC=`#if defined( TONE_MAPPING ) + gl_FragColor.rgb = toneMapping( gl_FragColor.rgb ); +#endif`,nC=`#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; }`,iC=`#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`,aC=`#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`,sC=`#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`,rC=`#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`,lC=`#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`,oC=`#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 cC=`varying vec2 vUv; +uniform mat3 uvTransform; +void main() { + vUv = ( uvTransform * vec3( uv, 1 ) ).xy; + gl_Position = vec4( position.xy, 1.0, 1.0 ); +}`,uC=`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> +}`,dC=`varying vec3 vWorldDirection; +#include <common> +void main() { + vWorldDirection = transformDirection( position, modelMatrix ); + #include <begin_vertex> + #include <project_vertex> + gl_Position.z = gl_Position.w; +}`,fC=`#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> +}`,hC=`varying vec3 vWorldDirection; +#include <common> +void main() { + vWorldDirection = transformDirection( position, modelMatrix ); + #include <begin_vertex> + #include <project_vertex> + gl_Position.z = gl_Position.w; +}`,pC=`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> +}`,mC=`#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; +}`,gC=`#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 +}`,xC=`#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; +}`,vC=`#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 ); +}`,_C=`varying vec3 vWorldDirection; +#include <common> +void main() { + vWorldDirection = transformDirection( position, modelMatrix ); + #include <begin_vertex> + #include <project_vertex> +}`,yC=`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> +}`,bC=`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> +}`,SC=`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> +}`,MC=`#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> +}`,EC=`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> +}`,TC=`#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> +}`,AC=`#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> +}`,wC=`#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; +}`,NC=`#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> +}`,CC=`#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 +}`,RC=`#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 +}`,DC=`#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> +}`,LC=`#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> +}`,UC=`#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 +}`,OC=`#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> +}`,PC=`#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> +}`,zC=`#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> +}`,kC=`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> +}`,IC=`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> +}`,BC=`#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> +}`,jC=`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> +}`,FC=`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> +}`,HC=`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> +}`,ct={alphahash_fragment:cw,alphahash_pars_fragment:uw,alphamap_fragment:dw,alphamap_pars_fragment:fw,alphatest_fragment:hw,alphatest_pars_fragment:pw,aomap_fragment:mw,aomap_pars_fragment:gw,batching_pars_vertex:xw,batching_vertex:vw,begin_vertex:_w,beginnormal_vertex:yw,bsdfs:bw,iridescence_fragment:Sw,bumpmap_pars_fragment:Mw,clipping_planes_fragment:Ew,clipping_planes_pars_fragment:Tw,clipping_planes_pars_vertex:Aw,clipping_planes_vertex:ww,color_fragment:Nw,color_pars_fragment:Cw,color_pars_vertex:Rw,color_vertex:Dw,common:Lw,cube_uv_reflection_fragment:Uw,defaultnormal_vertex:Ow,displacementmap_pars_vertex:Pw,displacementmap_vertex:zw,emissivemap_fragment:kw,emissivemap_pars_fragment:Iw,colorspace_fragment:Bw,colorspace_pars_fragment:jw,envmap_fragment:Fw,envmap_common_pars_fragment:Hw,envmap_pars_fragment:Gw,envmap_pars_vertex:Vw,envmap_physical_pars_fragment:tN,envmap_vertex:Ww,fog_vertex:Xw,fog_pars_vertex:qw,fog_fragment:Yw,fog_pars_fragment:Kw,gradientmap_pars_fragment:$w,lightmap_pars_fragment:Zw,lights_lambert_fragment:Qw,lights_lambert_pars_fragment:Jw,lights_pars_begin:eN,lights_toon_fragment:nN,lights_toon_pars_fragment:iN,lights_phong_fragment:aN,lights_phong_pars_fragment:sN,lights_physical_fragment:rN,lights_physical_pars_fragment:lN,lights_fragment_begin:oN,lights_fragment_maps:cN,lights_fragment_end:uN,lightprobes_pars_fragment:dN,logdepthbuf_fragment:fN,logdepthbuf_pars_fragment:hN,logdepthbuf_pars_vertex:pN,logdepthbuf_vertex:mN,map_fragment:gN,map_pars_fragment:xN,map_particle_fragment:vN,map_particle_pars_fragment:_N,metalnessmap_fragment:yN,metalnessmap_pars_fragment:bN,morphinstance_vertex:SN,morphcolor_vertex:MN,morphnormal_vertex:EN,morphtarget_pars_vertex:TN,morphtarget_vertex:AN,normal_fragment_begin:wN,normal_fragment_maps:NN,normal_pars_fragment:CN,normal_pars_vertex:RN,normal_vertex:DN,normalmap_pars_fragment:LN,clearcoat_normal_fragment_begin:UN,clearcoat_normal_fragment_maps:ON,clearcoat_pars_fragment:PN,iridescence_pars_fragment:zN,opaque_fragment:kN,packing:IN,premultiplied_alpha_fragment:BN,project_vertex:jN,dithering_fragment:FN,dithering_pars_fragment:HN,roughnessmap_fragment:GN,roughnessmap_pars_fragment:VN,shadowmap_pars_fragment:WN,shadowmap_pars_vertex:XN,shadowmap_vertex:qN,shadowmask_pars_fragment:YN,skinbase_vertex:KN,skinning_pars_vertex:$N,skinning_vertex:ZN,skinnormal_vertex:QN,specularmap_fragment:JN,specularmap_pars_fragment:eC,tonemapping_fragment:tC,tonemapping_pars_fragment:nC,transmission_fragment:iC,transmission_pars_fragment:aC,uv_pars_fragment:sC,uv_pars_vertex:rC,uv_vertex:lC,worldpos_vertex:oC,background_vert:cC,background_frag:uC,backgroundCube_vert:dC,backgroundCube_frag:fC,cube_vert:hC,cube_frag:pC,depth_vert:mC,depth_frag:gC,distance_vert:xC,distance_frag:vC,equirect_vert:_C,equirect_frag:yC,linedashed_vert:bC,linedashed_frag:SC,meshbasic_vert:MC,meshbasic_frag:EC,meshlambert_vert:TC,meshlambert_frag:AC,meshmatcap_vert:wC,meshmatcap_frag:NC,meshnormal_vert:CC,meshnormal_frag:RC,meshphong_vert:DC,meshphong_frag:LC,meshphysical_vert:UC,meshphysical_frag:OC,meshtoon_vert:PC,meshtoon_frag:zC,points_vert:kC,points_frag:IC,shadow_vert:BC,shadow_frag:jC,sprite_vert:FC,sprite_frag:HC},Oe={common:{diffuse:{value:new vt(16777215)},opacity:{value:1},map:{value:null},mapTransform:{value:new at},alphaMap:{value:null},alphaMapTransform:{value:new at},alphaTest:{value:0}},specularmap:{specularMap:{value:null},specularMapTransform:{value:new at}},envmap:{envMap:{value:null},envMapRotation:{value:new at},reflectivity:{value:1},ior:{value:1.5},refractionRatio:{value:.98},dfgLUT:{value:null}},aomap:{aoMap:{value:null},aoMapIntensity:{value:1},aoMapTransform:{value:new at}},lightmap:{lightMap:{value:null},lightMapIntensity:{value:1},lightMapTransform:{value:new at}},bumpmap:{bumpMap:{value:null},bumpMapTransform:{value:new at},bumpScale:{value:1}},normalmap:{normalMap:{value:null},normalMapTransform:{value:new at},normalScale:{value:new wt(1,1)}},displacementmap:{displacementMap:{value:null},displacementMapTransform:{value:new at},displacementScale:{value:1},displacementBias:{value:0}},emissivemap:{emissiveMap:{value:null},emissiveMapTransform:{value:new at}},metalnessmap:{metalnessMap:{value:null},metalnessMapTransform:{value:new at}},roughnessmap:{roughnessMap:{value:null},roughnessMapTransform:{value:new at}},gradientmap:{gradientMap:{value:null}},fog:{fogDensity:{value:25e-5},fogNear:{value:1},fogFar:{value:2e3},fogColor:{value:new vt(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 vt(16777215)},opacity:{value:1},size:{value:1},scale:{value:1},map:{value:null},alphaMap:{value:null},alphaMapTransform:{value:new at},alphaTest:{value:0},uvTransform:{value:new at}},sprite:{diffuse:{value:new vt(16777215)},opacity:{value:1},center:{value:new wt(.5,.5)},rotation:{value:0},map:{value:null},mapTransform:{value:new at},alphaMap:{value:null},alphaMapTransform:{value:new at},alphaTest:{value:0}}},$i={basic:{uniforms:Un([Oe.common,Oe.specularmap,Oe.envmap,Oe.aomap,Oe.lightmap,Oe.fog]),vertexShader:ct.meshbasic_vert,fragmentShader:ct.meshbasic_frag},lambert:{uniforms:Un([Oe.common,Oe.specularmap,Oe.envmap,Oe.aomap,Oe.lightmap,Oe.emissivemap,Oe.bumpmap,Oe.normalmap,Oe.displacementmap,Oe.fog,Oe.lights,{emissive:{value:new vt(0)},envMapIntensity:{value:1}}]),vertexShader:ct.meshlambert_vert,fragmentShader:ct.meshlambert_frag},phong:{uniforms:Un([Oe.common,Oe.specularmap,Oe.envmap,Oe.aomap,Oe.lightmap,Oe.emissivemap,Oe.bumpmap,Oe.normalmap,Oe.displacementmap,Oe.fog,Oe.lights,{emissive:{value:new vt(0)},specular:{value:new vt(1118481)},shininess:{value:30},envMapIntensity:{value:1}}]),vertexShader:ct.meshphong_vert,fragmentShader:ct.meshphong_frag},standard:{uniforms:Un([Oe.common,Oe.envmap,Oe.aomap,Oe.lightmap,Oe.emissivemap,Oe.bumpmap,Oe.normalmap,Oe.displacementmap,Oe.roughnessmap,Oe.metalnessmap,Oe.fog,Oe.lights,{emissive:{value:new vt(0)},roughness:{value:1},metalness:{value:0},envMapIntensity:{value:1}}]),vertexShader:ct.meshphysical_vert,fragmentShader:ct.meshphysical_frag},toon:{uniforms:Un([Oe.common,Oe.aomap,Oe.lightmap,Oe.emissivemap,Oe.bumpmap,Oe.normalmap,Oe.displacementmap,Oe.gradientmap,Oe.fog,Oe.lights,{emissive:{value:new vt(0)}}]),vertexShader:ct.meshtoon_vert,fragmentShader:ct.meshtoon_frag},matcap:{uniforms:Un([Oe.common,Oe.bumpmap,Oe.normalmap,Oe.displacementmap,Oe.fog,{matcap:{value:null}}]),vertexShader:ct.meshmatcap_vert,fragmentShader:ct.meshmatcap_frag},points:{uniforms:Un([Oe.points,Oe.fog]),vertexShader:ct.points_vert,fragmentShader:ct.points_frag},dashed:{uniforms:Un([Oe.common,Oe.fog,{scale:{value:1},dashSize:{value:1},totalSize:{value:2}}]),vertexShader:ct.linedashed_vert,fragmentShader:ct.linedashed_frag},depth:{uniforms:Un([Oe.common,Oe.displacementmap]),vertexShader:ct.depth_vert,fragmentShader:ct.depth_frag},normal:{uniforms:Un([Oe.common,Oe.bumpmap,Oe.normalmap,Oe.displacementmap,{opacity:{value:1}}]),vertexShader:ct.meshnormal_vert,fragmentShader:ct.meshnormal_frag},sprite:{uniforms:Un([Oe.sprite,Oe.fog]),vertexShader:ct.sprite_vert,fragmentShader:ct.sprite_frag},background:{uniforms:{uvTransform:{value:new at},t2D:{value:null},backgroundIntensity:{value:1}},vertexShader:ct.background_vert,fragmentShader:ct.background_frag},backgroundCube:{uniforms:{envMap:{value:null},backgroundBlurriness:{value:0},backgroundIntensity:{value:1},backgroundRotation:{value:new at}},vertexShader:ct.backgroundCube_vert,fragmentShader:ct.backgroundCube_frag},cube:{uniforms:{tCube:{value:null},tFlip:{value:-1},opacity:{value:1}},vertexShader:ct.cube_vert,fragmentShader:ct.cube_frag},equirect:{uniforms:{tEquirect:{value:null}},vertexShader:ct.equirect_vert,fragmentShader:ct.equirect_frag},distance:{uniforms:Un([Oe.common,Oe.displacementmap,{referencePosition:{value:new Z},nearDistance:{value:1},farDistance:{value:1e3}}]),vertexShader:ct.distance_vert,fragmentShader:ct.distance_frag},shadow:{uniforms:Un([Oe.lights,Oe.fog,{color:{value:new vt(0)},opacity:{value:1}}]),vertexShader:ct.shadow_vert,fragmentShader:ct.shadow_frag}};$i.physical={uniforms:Un([$i.standard.uniforms,{clearcoat:{value:0},clearcoatMap:{value:null},clearcoatMapTransform:{value:new at},clearcoatNormalMap:{value:null},clearcoatNormalMapTransform:{value:new at},clearcoatNormalScale:{value:new wt(1,1)},clearcoatRoughness:{value:0},clearcoatRoughnessMap:{value:null},clearcoatRoughnessMapTransform:{value:new at},dispersion:{value:0},iridescence:{value:0},iridescenceMap:{value:null},iridescenceMapTransform:{value:new at},iridescenceIOR:{value:1.3},iridescenceThicknessMinimum:{value:100},iridescenceThicknessMaximum:{value:400},iridescenceThicknessMap:{value:null},iridescenceThicknessMapTransform:{value:new at},sheen:{value:0},sheenColor:{value:new vt(0)},sheenColorMap:{value:null},sheenColorMapTransform:{value:new at},sheenRoughness:{value:1},sheenRoughnessMap:{value:null},sheenRoughnessMapTransform:{value:new at},transmission:{value:0},transmissionMap:{value:null},transmissionMapTransform:{value:new at},transmissionSamplerSize:{value:new wt},transmissionSamplerMap:{value:null},thickness:{value:0},thicknessMap:{value:null},thicknessMapTransform:{value:new at},attenuationDistance:{value:0},attenuationColor:{value:new vt(0)},specularColor:{value:new vt(1,1,1)},specularColorMap:{value:null},specularColorMapTransform:{value:new at},specularIntensity:{value:1},specularIntensityMap:{value:null},specularIntensityMapTransform:{value:new at},anisotropyVector:{value:new wt},anisotropyMap:{value:null},anisotropyMapTransform:{value:new at}}]),vertexShader:ct.meshphysical_vert,fragmentShader:ct.meshphysical_frag};const nu={r:0,b:0,g:0},GC=new Bt,kS=new at;kS.set(-1,0,0,0,1,0,0,0,1);function VC(t,e,n,i,a,s){const l=new vt(0);let o=a===!0?0:1,c,u,f=null,m=0,d=null;function p(v){let y=v.isScene===!0?v.background:null;if(y&&y.isTexture){const _=v.backgroundBlurriness>0;y=e.get(y,_)}return y}function g(v){let y=!1;const _=p(v);_===null?x(l,o):_&&_.isColor&&(x(_,1),y=!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||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 b(v,y){const _=p(y);_&&(_.isCubeTexture||_.mapping===jd)?(u===void 0&&(u=new Ri(new ar(1,1,1),new Yn({name:"BackgroundCubeMaterial",uniforms:Tl($i.backgroundCube.uniforms),vertexShader:$i.backgroundCube.vertexShader,fragmentShader:$i.backgroundCube.fragmentShader,side:$n,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=_,u.material.uniforms.backgroundBlurriness.value=y.backgroundBlurriness,u.material.uniforms.backgroundIntensity.value=y.backgroundIntensity,u.material.uniforms.backgroundRotation.value.setFromMatrix4(GC.makeRotationFromEuler(y.backgroundRotation)).transpose(),_.isCubeTexture&&_.isRenderTargetTexture===!1&&u.material.uniforms.backgroundRotation.value.premultiply(kS),u.material.toneMapped=mt.getTransfer(_.colorSpace)!==Nt,(f!==_||m!==_.version||d!==t.toneMapping)&&(u.material.needsUpdate=!0,f=_,m=_.version,d=t.toneMapping),u.layers.enableAll(),v.unshift(u,u.geometry,u.material,0,0,null)):_&&_.isTexture&&(c===void 0&&(c=new Ri(new Fd(2,2),new Yn({name:"BackgroundMaterial",uniforms:Tl($i.background.uniforms),vertexShader:$i.background.vertexShader,fragmentShader:$i.background.fragmentShader,side:As,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=_,c.material.uniforms.backgroundIntensity.value=y.backgroundIntensity,c.material.toneMapped=mt.getTransfer(_.colorSpace)!==Nt,_.matrixAutoUpdate===!0&&_.updateMatrix(),c.material.uniforms.uvTransform.value.copy(_.matrix),(f!==_||m!==_.version||d!==t.toneMapping)&&(c.material.needsUpdate=!0,f=_,m=_.version,d=t.toneMapping),c.layers.enableAll(),v.unshift(c,c.geometry,c.material,0,0,null))}function x(v,y){v.getRGB(nu,OS(t)),n.buffers.color.setClear(nu.r,nu.g,nu.b,y,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 l},setClearColor:function(v,y=1){l.set(v),o=y,x(l,o)},getClearAlpha:function(){return o},setClearAlpha:function(v){o=v,x(l,o)},render:g,addToRenderList:b,dispose:h}}function WC(t,e){const n=t.getParameter(t.MAX_VERTEX_ATTRIBS),i={},a=d(null);let s=a,l=!1;function o(D,L,q,P,O){let B=!1;const k=m(D,P,q,L);s!==k&&(s=k,u(s.object)),B=p(D,P,q,O),B&&g(D,P,q,O),O!==null&&e.update(O,t.ELEMENT_ARRAY_BUFFER),(B||l)&&(l=!1,_(D,L,q,P),O!==null&&t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,e.get(O).buffer))}function c(){return t.createVertexArray()}function u(D){return t.bindVertexArray(D)}function f(D){return t.deleteVertexArray(D)}function m(D,L,q,P){const O=P.wireframe===!0;let B=i[L.id];B===void 0&&(B={},i[L.id]=B);const k=D.isInstancedMesh===!0?D.id:0;let W=B[k];W===void 0&&(W={},B[k]=W);let Y=W[q.id];Y===void 0&&(Y={},W[q.id]=Y);let he=Y[O];return he===void 0&&(he=d(c()),Y[O]=he),he}function d(D){const L=[],q=[],P=[];for(let O=0;O<n;O++)L[O]=0,q[O]=0,P[O]=0;return{geometry:null,program:null,wireframe:!1,newAttributes:L,enabledAttributes:q,attributeDivisors:P,object:D,attributes:{},index:null}}function p(D,L,q,P){const O=s.attributes,B=L.attributes;let k=0;const W=q.getAttributes();for(const Y in W)if(W[Y].location>=0){const _e=O[Y];let Ce=B[Y];if(Ce===void 0&&(Y==="instanceMatrix"&&D.instanceMatrix&&(Ce=D.instanceMatrix),Y==="instanceColor"&&D.instanceColor&&(Ce=D.instanceColor)),_e===void 0||_e.attribute!==Ce||Ce&&_e.data!==Ce.data)return!0;k++}return s.attributesNum!==k||s.index!==P}function g(D,L,q,P){const O={},B=L.attributes;let k=0;const W=q.getAttributes();for(const Y in W)if(W[Y].location>=0){let _e=B[Y];_e===void 0&&(Y==="instanceMatrix"&&D.instanceMatrix&&(_e=D.instanceMatrix),Y==="instanceColor"&&D.instanceColor&&(_e=D.instanceColor));const Ce={};Ce.attribute=_e,_e&&_e.data&&(Ce.data=_e.data),O[Y]=Ce,k++}s.attributes=O,s.attributesNum=k,s.index=P}function b(){const D=s.newAttributes;for(let L=0,q=D.length;L<q;L++)D[L]=0}function x(D){h(D,0)}function h(D,L){const q=s.newAttributes,P=s.enabledAttributes,O=s.attributeDivisors;q[D]=1,P[D]===0&&(t.enableVertexAttribArray(D),P[D]=1),O[D]!==L&&(t.vertexAttribDivisor(D,L),O[D]=L)}function v(){const D=s.newAttributes,L=s.enabledAttributes;for(let q=0,P=L.length;q<P;q++)L[q]!==D[q]&&(t.disableVertexAttribArray(q),L[q]=0)}function y(D,L,q,P,O,B,k){k===!0?t.vertexAttribIPointer(D,L,q,O,B):t.vertexAttribPointer(D,L,q,P,O,B)}function _(D,L,q,P){b();const O=P.attributes,B=q.getAttributes(),k=L.defaultAttributeValues;for(const W in B){const Y=B[W];if(Y.location>=0){let he=O[W];if(he===void 0&&(W==="instanceMatrix"&&D.instanceMatrix&&(he=D.instanceMatrix),W==="instanceColor"&&D.instanceColor&&(he=D.instanceColor)),he!==void 0){const _e=he.normalized,Ce=he.itemSize,X=e.get(he);if(X===void 0)continue;const de=X.buffer,Ne=X.type,ae=X.bytesPerElement,j=Ne===t.INT||Ne===t.UNSIGNED_INT||he.gpuType===mg;if(he.isInterleavedBufferAttribute){const ee=he.data,xe=ee.stride,Te=he.offset;if(ee.isInstancedInterleavedBuffer){for(let Ie=0;Ie<Y.locationSize;Ie++)h(Y.location+Ie,ee.meshPerAttribute);D.isInstancedMesh!==!0&&P._maxInstanceCount===void 0&&(P._maxInstanceCount=ee.meshPerAttribute*ee.count)}else for(let Ie=0;Ie<Y.locationSize;Ie++)x(Y.location+Ie);t.bindBuffer(t.ARRAY_BUFFER,de);for(let Ie=0;Ie<Y.locationSize;Ie++)y(Y.location+Ie,Ce/Y.locationSize,Ne,_e,xe*ae,(Te+Ce/Y.locationSize*Ie)*ae,j)}else{if(he.isInstancedBufferAttribute){for(let ee=0;ee<Y.locationSize;ee++)h(Y.location+ee,he.meshPerAttribute);D.isInstancedMesh!==!0&&P._maxInstanceCount===void 0&&(P._maxInstanceCount=he.meshPerAttribute*he.count)}else for(let ee=0;ee<Y.locationSize;ee++)x(Y.location+ee);t.bindBuffer(t.ARRAY_BUFFER,de);for(let ee=0;ee<Y.locationSize;ee++)y(Y.location+ee,Ce/Y.locationSize,Ne,_e,Ce*ae,Ce/Y.locationSize*ee*ae,j)}}else if(k!==void 0){const _e=k[W];if(_e!==void 0)switch(_e.length){case 2:t.vertexAttrib2fv(Y.location,_e);break;case 3:t.vertexAttrib3fv(Y.location,_e);break;case 4:t.vertexAttrib4fv(Y.location,_e);break;default:t.vertexAttrib1fv(Y.location,_e)}}}}v()}function A(){w();for(const D in i){const L=i[D];for(const q in L){const P=L[q];for(const O in P){const B=P[O];for(const k in B)f(B[k].object),delete B[k];delete P[O]}}delete i[D]}}function M(D){if(i[D.id]===void 0)return;const L=i[D.id];for(const q in L){const P=L[q];for(const O in P){const B=P[O];for(const k in B)f(B[k].object),delete B[k];delete P[O]}}delete i[D.id]}function T(D){for(const L in i){const q=i[L];for(const P in q){const O=q[P];if(O[D.id]===void 0)continue;const B=O[D.id];for(const k in B)f(B[k].object),delete B[k];delete O[D.id]}}}function S(D){for(const L in i){const q=i[L],P=D.isInstancedMesh===!0?D.id:0,O=q[P];if(O!==void 0){for(const B in O){const k=O[B];for(const W in k)f(k[W].object),delete k[W];delete O[B]}delete q[P],Object.keys(q).length===0&&delete i[L]}}}function w(){R(),l=!0,s!==a&&(s=a,u(s.object))}function R(){a.geometry=null,a.program=null,a.wireframe=!1}return{setup:o,reset:w,resetDefaultState:R,dispose:A,releaseStatesOfGeometry:M,releaseStatesOfObject:S,releaseStatesOfProgram:T,initAttributes:b,enableAttribute:x,disableUnusedAttributes:v}}function XC(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,f){f!==0&&(t.drawArraysInstanced(i,c,u,f),n.update(u,i,f))}function o(c,u,f){if(f===0)return;e.get("WEBGL_multi_draw").multiDrawArraysWEBGL(i,c,0,u,0,f);let d=0;for(let p=0;p<f;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 T=e.get("EXT_texture_filter_anisotropic");a=t.getParameter(T.MAX_TEXTURE_MAX_ANISOTROPY_EXT)}else a=0;return a}function l(T){return!(T!==Ii&&i.convert(T)!==t.getParameter(t.IMPLEMENTATION_COLOR_READ_FORMAT))}function o(T){const S=T===Pa&&(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!==ki&&!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 f=c(u);f!==u&&(et("WebGLRenderer:",u,"not supported, using",f,"instead."),u=f);const m=n.logarithmicDepthBuffer===!0,d=n.reversedDepthBuffer===!0&&e.has("EXT_clip_control");n.reversedDepthBuffer===!0&&d===!1&&et("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),g=t.getParameter(t.MAX_VERTEX_TEXTURE_IMAGE_UNITS),b=t.getParameter(t.MAX_TEXTURE_SIZE),x=t.getParameter(t.MAX_CUBE_MAP_TEXTURE_SIZE),h=t.getParameter(t.MAX_VERTEX_ATTRIBS),v=t.getParameter(t.MAX_VERTEX_UNIFORM_VECTORS),y=t.getParameter(t.MAX_VARYING_VECTORS),_=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:l,textureTypeReadable:o,precision:u,logarithmicDepthBuffer:m,reversedDepthBuffer:d,maxTextures:p,maxVertexTextures:g,maxTextureSize:b,maxCubemapSize:x,maxAttributes:h,maxVertexUniforms:v,maxVaryings:y,maxFragmentUniforms:_,maxSamples:A,samples:M}}function YC(t){const e=this;let n=null,i=0,a=!1,s=!1;const l=new js,o=new at,c={value:null,needsUpdate:!1};this.uniform=c,this.numPlanes=0,this.numIntersection=0,this.init=function(m,d){const p=m.length!==0||d||i!==0||a;return a=d,i=m.length,p},this.beginShadows=function(){s=!0,f(null)},this.endShadows=function(){s=!1},this.setGlobalState=function(m,d){n=f(m,d,0)},this.setState=function(m,d,p){const g=m.clippingPlanes,b=m.clipIntersection,x=m.clipShadows,h=t.get(m);if(!a||g===null||g.length===0||s&&!x)s?f(null):u();else{const v=s?0:i,y=v*4;let _=h.clippingState||null;c.value=_,_=f(g,d,y,p);for(let A=0;A!==y;++A)_[A]=n[A];h.clippingState=_,this.numIntersection=b?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 f(m,d,p,g){const b=m!==null?m.length:0;let x=null;if(b!==0){if(x=c.value,g!==!0||x===null){const h=p+b*4,v=d.matrixWorldInverse;o.getNormalMatrix(v),(x===null||x.length<h)&&(x=new Float32Array(h));for(let y=0,_=p;y!==b;++y,_+=4)l.copy(m[y]).applyMatrix4(v,o),l.normal.toArray(x,_),x[_+3]=l.constant}c.value=x,c.needsUpdate=!0}return e.numPlanes=b,e.numIntersection=0,x}}const us=4,tv=[.125,.215,.35,.446,.526,.582],Hs=20,KC=256,eo=new Ag,nv=new vt;let ah=null,sh=0,rh=0,lh=!1;const $C=new Z;class iv{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=$C}=s;ah=this._renderer.getRenderTarget(),sh=this._renderer.getActiveCubeFace(),rh=this._renderer.getActiveMipmapLevel(),lh=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=rv(),this._compileMaterial(this._cubemapMaterial))}compileEquirectangularShader(){this._equirectMaterial===null&&(this._equirectMaterial=sv(),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(ah,sh,rh),this._renderer.xr.enabled=lh,e.scissorTest=!1,Lr(e,0,0,e.width,e.height)}_fromTexture(e,n){e.mapping===tr||e.mapping===Ml?this._setSize(e.image.length===0?16:e.image[0].width||e.image[0].image.width):this._setSize(e.image.width/4),ah=this._renderer.getRenderTarget(),sh=this._renderer.getActiveCubeFace(),rh=this._renderer.getActiveMipmapLevel(),lh=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:Ln,minFilter:Ln,generateMipmaps:!1,type:Pa,format:Ii,colorSpace:ud,depthBuffer:!1},a=av(e,n,i);if(this._pingPongRenderTarget===null||this._pingPongRenderTarget.width!==e||this._pingPongRenderTarget.height!==n){this._pingPongRenderTarget!==null&&this._dispose(),this._pingPongRenderTarget=av(e,n,i);const{_lodMax:s}=this;({lodMeshes:this._lodMeshes,sizeLods:this._sizeLods,sigmas:this._sigmas}=ZC(s)),this._blurMaterial=JC(s,e,n),this._ggxMaterial=QC(s,e,n)}return a}_compileMaterial(e){const n=new Ri(new Qn,e);this._renderer.compile(n,eo)}_sceneToCubeUV(e,n,i,a,s){const c=new zi(90,1,n,i),u=[1,-1,1,1,1,1],f=[1,1,1,-1,-1,-1],m=this._renderer,d=m.autoClear,p=m.toneMapping;m.getClearColor(nv),m.toneMapping=na,m.autoClear=!1,m.state.buffers.depth.getReversed()&&(m.setRenderTarget(a),m.clearDepth(),m.setRenderTarget(null)),this._backgroundBox===null&&(this._backgroundBox=new Ri(new ar,new NS({name:"PMREM.Background",side:$n,depthWrite:!1,depthTest:!1})));const b=this._backgroundBox,x=b.material;let h=!1;const v=e.background;v?v.isColor&&(x.color.copy(v),e.background=null,h=!0):(x.color.copy(nv),h=!0);for(let y=0;y<6;y++){const _=y%3;_===0?(c.up.set(0,u[y],0),c.position.set(s.x,s.y,s.z),c.lookAt(s.x+f[y],s.y,s.z)):_===1?(c.up.set(0,0,u[y]),c.position.set(s.x,s.y,s.z),c.lookAt(s.x,s.y+f[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+f[y]));const A=this._cubeSize;Lr(a,_*A,y>2?A:0,A,A),m.setRenderTarget(a),h&&m.render(b,c),m.render(e,c)}m.toneMapping=p,m.autoClear=d,e.background=v}_textureToCubeUV(e,n){const i=this._renderer,a=e.mapping===tr||e.mapping===Ml;a?(this._cubemapMaterial===null&&(this._cubemapMaterial=rv()),this._cubemapMaterial.uniforms.flipEnvMap.value=e.isRenderTargetTexture===!1?-1:1):this._equirectMaterial===null&&(this._equirectMaterial=sv());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;Lr(n,0,0,3*c,2*c),i.setRenderTarget(n),i.render(l,eo)}_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),f=n/(this._lodMeshes.length-1),m=Math.sqrt(u*u-f*f),d=0+u*1.25,p=m*d,{_lodMax:g}=this,b=this._sizeLods[i],x=3*b*(i>g-us?i-g+us:0),h=4*(this._cubeSize-b);c.envMap.value=e.texture,c.roughness.value=p,c.mipInt.value=g-n,Lr(s,x,h,3*b,2*b),a.setRenderTarget(s),a.render(o,eo),c.envMap.value=s.texture,c.roughness.value=0,c.mipInt.value=g-i,Lr(e,x,h,3*b,2*b),a.setRenderTarget(e),a.render(o,eo)}_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"&&Mt("blur direction must be either latitudinal or longitudinal!");const f=3,m=this._lodMeshes[a];m.material=u;const d=u.uniforms,p=this._sizeLods[i]-1,g=isFinite(s)?Math.PI/(2*p):2*Math.PI/(2*Hs-1),b=s/g,x=isFinite(s)?1+Math.floor(f*b):Hs;x>Hs&&et(`sigmaRadians, ${s}, is too large and will clip, as it requested ${x} samples when the maximum is set to ${Hs}`);const h=[];let v=0;for(let T=0;T<Hs;++T){const S=T/b,w=Math.exp(-S*S/2);h.push(w),T===0?v+=w:T<x&&(v+=2*w)}for(let T=0;T<h.length;T++)h[T]=h[T]/v;d.envMap.value=e.texture,d.samples.value=x,d.weights.value=h,d.latitudinal.value=l==="latitudinal",o&&(d.poleAxis.value=o);const{_lodMax:y}=this;d.dTheta.value=g,d.mipInt.value=y-i;const _=this._sizeLods[a],A=3*_*(a>y-us?a-y+us:0),M=4*(this._cubeSize-_);Lr(n,A,M,3*_,2*_),c.setRenderTarget(n),c.render(m,eo)}}function ZC(t){const e=[],n=[],i=[];let a=t;const s=t-us+1+tv.length;for(let l=0;l<s;l++){const o=Math.pow(2,a);e.push(o);let c=1/o;l>t-us?c=tv[l-t+us-1]:l===0&&(c=0),n.push(c);const u=1/(o-2),f=-u,m=1+u,d=[f,f,m,f,m,m,f,f,m,m,f,m],p=6,g=6,b=3,x=2,h=1,v=new Float32Array(b*g*p),y=new Float32Array(x*g*p),_=new Float32Array(h*g*p);for(let M=0;M<p;M++){const T=M%3*2/3-1,S=M>2?0:-1,w=[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(w,b*g*M),y.set(d,x*g*M);const R=[M,M,M,M,M,M];_.set(R,h*g*M)}const A=new Qn;A.setAttribute("position",new un(v,b)),A.setAttribute("uv",new un(y,x)),A.setAttribute("faceIndex",new un(_,h)),i.push(new Ri(A,null)),a>us&&a--}return{lodMeshes:i,sizeLods:e,sigmas:n}}function av(t,e,n){const i=new ia(t,e,n);return i.texture.mapping=jd,i.texture.name="PMREM.cubeUv",i.scissorTest=!0,i}function Lr(t,e,n,i,a){t.viewport.set(e,n,i,a),t.scissor.set(e,n,i,a)}function QC(t,e,n){return new Yn({name:"PMREMGGXConvolution",defines:{GGX_SAMPLES:KC,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:Hd(),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 JC(t,e,n){const i=new Float32Array(Hs),a=new Z(0,1,0);return new Yn({name:"SphericalGaussianBlur",defines:{n:Hs,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:Hd(),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 sv(){return new Yn({name:"EquirectangularToCubeUV",uniforms:{envMap:{value:null}},vertexShader:Hd(),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 rv(){return new Yn({name:"CubemapToCubeUV",uniforms:{envMap:{value:null},flipEnvMap:{value:-1}},vertexShader:Hd(),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 Hd(){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 IS extends ia{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 LS(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 ar(5,5,5),s=new Yn({name:"CubemapFromEquirect",uniforms:Tl(i.uniforms),vertexShader:i.vertexShader,fragmentShader:i.fragmentShader,side:$n,blending:Na});s.uniforms.tEquirect.value=n;const l=new Ri(a,s),o=n.minFilter;return n.minFilter===Gs&&(n.minFilter=Ln),new sw(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 eR(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===Cf||p===Rf)if(e.has(d)){const g=e.get(d).texture;return o(g,d.mapping)}else{const g=d.image;if(g&&g.height>0){const b=new IS(g.height);return b.fromEquirectangularTexture(t,d),e.set(d,b),d.addEventListener("dispose",u),o(b.texture,d.mapping)}else return null}}return d}function l(d){if(d&&d.isTexture){const p=d.mapping,g=p===Cf||p===Rf,b=p===tr||p===Ml;if(g||b){let x=n.get(d);const h=x!==void 0?x.texture.pmremVersion:0;if(d.isRenderTargetTexture&&d.pmremVersion!==h)return i===null&&(i=new iv(t)),x=g?i.fromEquirectangular(d,x):i.fromCubemap(d,x),x.texture.pmremVersion=d.pmremVersion,n.set(d,x),x.texture;if(x!==void 0)return x.texture;{const v=d.image;return g&&v&&v.height>0||b&&v&&c(v)?(i===null&&(i=new iv(t)),x=g?i.fromEquirectangular(d):i.fromCubemap(d),x.texture.pmremVersion=d.pmremVersion,n.set(d,x),d.addEventListener("dispose",f),x.texture):null}}}return d}function o(d,p){return p===Cf?d.mapping=tr:p===Rf&&(d.mapping=Ml),d}function c(d){let p=0;const g=6;for(let b=0;b<g;b++)d[b]!==void 0&&p++;return p===g}function u(d){const p=d.target;p.removeEventListener("dispose",u);const g=e.get(p);g!==void 0&&(e.delete(p),g.dispose())}function f(d){const p=d.target;p.removeEventListener("dispose",f);const g=n.get(p);g!==void 0&&(n.delete(p),g.dispose())}function m(){e=new WeakMap,n=new WeakMap,i!==null&&(i.dispose(),i=null)}return{get:a,dispose:m}}function tR(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&&ll("WebGLRenderer: "+i+" extension not supported."),a}}}function nR(t,e,n,i){const a={},s=new WeakMap;function l(m){const d=m.target;d.index!==null&&e.remove(d.index);for(const g in d.attributes)e.remove(d.attributes[g]);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(m,d){return a[d.id]===!0||(d.addEventListener("dispose",l),a[d.id]=!0,n.memory.geometries++),d}function c(m){const d=m.attributes;for(const p in d)e.update(d[p],t.ARRAY_BUFFER)}function u(m){const d=[],p=m.index,g=m.attributes.position;let b=0;if(g===void 0)return;if(p!==null){const v=p.array;b=p.version;for(let y=0,_=v.length;y<_;y+=3){const A=v[y+0],M=v[y+1],T=v[y+2];d.push(A,M,M,T,T,A)}}else{const v=g.array;b=g.version;for(let y=0,_=v.length/3-1;y<_;y+=3){const A=y+0,M=y+1,T=y+2;d.push(A,M,M,T,T,A)}}const x=new(g.count>=65535?wS:AS)(d,1);x.version=b;const h=s.get(m);h&&e.remove(h),s.set(m,x)}function f(m){const d=s.get(m);if(d){const p=m.index;p!==null&&d.version<p.version&&u(m)}else u(m);return s.get(m)}return{get:o,update:c,getWireframeAttribute:f}}function iR(t,e,n){let i;function a(m){i=m}let s,l;function o(m){s=m.type,l=m.bytesPerElement}function c(m,d){t.drawElements(i,d,s,m*l),n.update(d,i,1)}function u(m,d,p){p!==0&&(t.drawElementsInstanced(i,d,s,m*l,p),n.update(d,i,p))}function f(m,d,p){if(p===0)return;e.get("WEBGL_multi_draw").multiDrawElementsWEBGL(i,d,0,s,m,0,p);let b=0;for(let x=0;x<p;x++)b+=d[x];n.update(b,i,1)}this.setMode=a,this.setIndex=o,this.render=c,this.renderInstances=u,this.renderMultiDraw=f}function aR(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:Mt("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 sR(t,e,n){const i=new WeakMap,a=new Zt;function s(l,o,c){const u=l.morphTargetInfluences,f=o.morphAttributes.position||o.morphAttributes.normal||o.morphAttributes.color,m=f!==void 0?f.length:0;let d=i.get(o);if(d===void 0||d.count!==m){let R=function(){S.dispose(),i.delete(o),o.removeEventListener("dispose",R)};var p=R;d!==void 0&&d.texture.dispose();const g=o.morphAttributes.position!==void 0,b=o.morphAttributes.normal!==void 0,x=o.morphAttributes.color!==void 0,h=o.morphAttributes.position||[],v=o.morphAttributes.normal||[],y=o.morphAttributes.color||[];let _=0;g===!0&&(_=1),b===!0&&(_=2),x===!0&&(_=3);let A=o.attributes.position.count*_,M=1;A>e.maxTextureSize&&(M=Math.ceil(A/e.maxTextureSize),A=e.maxTextureSize);const T=new Float32Array(A*M*4*m),S=new MS(T,A,M,m);S.type=ki,S.needsUpdate=!0;const w=_*4;for(let D=0;D<m;D++){const L=h[D],q=v[D],P=y[D],O=A*M*4*D;for(let B=0;B<L.count;B++){const k=B*w;g===!0&&(a.fromBufferAttribute(L,B),T[O+k+0]=a.x,T[O+k+1]=a.y,T[O+k+2]=a.z,T[O+k+3]=0),b===!0&&(a.fromBufferAttribute(q,B),T[O+k+4]=a.x,T[O+k+5]=a.y,T[O+k+6]=a.z,T[O+k+7]=0),x===!0&&(a.fromBufferAttribute(P,B),T[O+k+8]=a.x,T[O+k+9]=a.y,T[O+k+10]=a.z,T[O+k+11]=P.itemSize===4?a.w:1)}}d={count:m,texture:S,size:new wt(A,M)},i.set(o,d),o.addEventListener("dispose",R)}if(l.isInstancedMesh===!0&&l.morphTexture!==null)c.getUniforms().setValue(t,"morphTexture",l.morphTexture,n);else{let g=0;for(let x=0;x<u.length;x++)g+=u[x];const b=o.morphTargetsRelative?1:1-g;c.getUniforms().setValue(t,"morphTargetBaseInfluence",b),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 rR(t,e,n,i,a){let s=new WeakMap;function l(u){const f=a.render.frame,m=u.geometry,d=e.get(u,m);if(s.get(d)!==f&&(e.update(d),s.set(d,f)),u.isInstancedMesh&&(u.hasEventListener("dispose",c)===!1&&u.addEventListener("dispose",c),s.get(u)!==f&&(n.update(u.instanceMatrix,t.ARRAY_BUFFER),u.instanceColor!==null&&n.update(u.instanceColor,t.ARRAY_BUFFER),s.set(u,f))),u.isSkinnedMesh){const p=u.skeleton;s.get(p)!==f&&(p.update(),s.set(p,f))}return d}function o(){s=new WeakMap}function c(u){const f=u.target;f.removeEventListener("dispose",c),i.releaseStatesOfObject(f),n.remove(f.instanceMatrix),f.instanceColor!==null&&n.remove(f.instanceColor)}return{update:l,dispose:o}}const lR={[oS]:"LINEAR_TONE_MAPPING",[cS]:"REINHARD_TONE_MAPPING",[uS]:"CINEON_TONE_MAPPING",[dS]:"ACES_FILMIC_TONE_MAPPING",[hS]:"AGX_TONE_MAPPING",[pS]:"NEUTRAL_TONE_MAPPING",[fS]:"CUSTOM_TONE_MAPPING"};function oR(t,e,n,i,a,s){const l=new ia(e,n,{type:t,depthBuffer:a,stencilBuffer:s,samples:i?4:0,depthTexture:a?new El(e,n):void 0}),o=new ia(e,n,{type:Pa,depthBuffer:!1,stencilBuffer:!1}),c=new Qn;c.setAttribute("position",new Ni([-1,3,0,-1,-1,0,3,-1,0],3)),c.setAttribute("uv",new Ni([0,2,0,0,2,0],2));const u=new nw({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}),f=new Ri(c,u),m=new Ag(-1,1,1,-1,0,1);let d=null,p=null,g=!1,b,x=null,h=[],v=!1;this.setSize=function(y,_){l.setSize(y,_),o.setSize(y,_);for(let A=0;A<h.length;A++){const M=h[A];M.setSize&&M.setSize(y,_)}},this.setEffects=function(y){h=y,v=h.length>0&&h[0].isRenderPass===!0;const _=l.width,A=l.height;for(let M=0;M<h.length;M++){const T=h[M];T.setSize&&T.setSize(_,A)}},this.begin=function(y,_){if(g||y.toneMapping===na&&h.length===0)return!1;if(x=_,_!==null){const A=_.width,M=_.height;(l.width!==A||l.height!==M)&&this.setSize(A,M)}return v===!1&&y.setRenderTarget(l),b=y.toneMapping,y.toneMapping=na,!0},this.hasRenderPass=function(){return v},this.end=function(y,_){y.toneMapping=b,g=!0;let A=l,M=o;for(let T=0;T<h.length;T++){const S=h[T];if(S.enabled!==!1&&(S.render(y,M,A,_),S.needsSwap!==!1)){const w=A;A=M,M=w}}if(d!==y.outputColorSpace||p!==y.toneMapping){d=y.outputColorSpace,p=y.toneMapping,u.defines={},mt.getTransfer(d)===Nt&&(u.defines.SRGB_TRANSFER="");const T=lR[p];T&&(u.defines[T]=""),u.needsUpdate=!0}u.uniforms.tDiffuse.value=A.texture,y.setRenderTarget(x),y.render(f,m),x=null,g=!1},this.isCompositing=function(){return g},this.dispose=function(){l.depthTexture&&l.depthTexture.dispose(),l.dispose(),o.dispose(),c.dispose(),u.dispose()}}const BS=new In,rm=new El(1,1),jS=new MS,FS=new CA,HS=new LS,lv=[],ov=[],cv=new Float32Array(16),uv=new Float32Array(9),dv=new Float32Array(4);function kl(t,e,n){const i=t[0];if(i<=0||i>0)return t;const a=e*n;let s=lv[a];if(s===void 0&&(s=new Float32Array(a),lv[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 hn(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 pn(t,e){for(let n=0,i=e.length;n<i;n++)t[n]=e[n]}function Gd(t,e){let n=ov[e];n===void 0&&(n=new Int32Array(e),ov[e]=n);for(let i=0;i!==e;++i)n[i]=t.allocateTextureUnit();return n}function cR(t,e){const n=this.cache;n[0]!==e&&(t.uniform1f(this.addr,e),n[0]=e)}function uR(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(hn(n,e))return;t.uniform2fv(this.addr,e),pn(n,e)}}function dR(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(hn(n,e))return;t.uniform3fv(this.addr,e),pn(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.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(hn(n,e))return;t.uniform4fv(this.addr,e),pn(n,e)}}function hR(t,e){const n=this.cache,i=e.elements;if(i===void 0){if(hn(n,e))return;t.uniformMatrix2fv(this.addr,!1,e),pn(n,e)}else{if(hn(n,i))return;dv.set(i),t.uniformMatrix2fv(this.addr,!1,dv),pn(n,i)}}function pR(t,e){const n=this.cache,i=e.elements;if(i===void 0){if(hn(n,e))return;t.uniformMatrix3fv(this.addr,!1,e),pn(n,e)}else{if(hn(n,i))return;uv.set(i),t.uniformMatrix3fv(this.addr,!1,uv),pn(n,i)}}function mR(t,e){const n=this.cache,i=e.elements;if(i===void 0){if(hn(n,e))return;t.uniformMatrix4fv(this.addr,!1,e),pn(n,e)}else{if(hn(n,i))return;cv.set(i),t.uniformMatrix4fv(this.addr,!1,cv),pn(n,i)}}function gR(t,e){const n=this.cache;n[0]!==e&&(t.uniform1i(this.addr,e),n[0]=e)}function xR(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(hn(n,e))return;t.uniform2iv(this.addr,e),pn(n,e)}}function vR(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(hn(n,e))return;t.uniform3iv(this.addr,e),pn(n,e)}}function _R(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(hn(n,e))return;t.uniform4iv(this.addr,e),pn(n,e)}}function yR(t,e){const n=this.cache;n[0]!==e&&(t.uniform1ui(this.addr,e),n[0]=e)}function bR(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(hn(n,e))return;t.uniform2uiv(this.addr,e),pn(n,e)}}function SR(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(hn(n,e))return;t.uniform3uiv(this.addr,e),pn(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||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(hn(n,e))return;t.uniform4uiv(this.addr,e),pn(n,e)}}function ER(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?(rm.compareFunction=n.isReversedDepthBuffer()?Mg:Sg,s=rm):s=BS,n.setTexture2D(e||s,a)}function TR(t,e,n){const i=this.cache,a=n.allocateTextureUnit();i[0]!==a&&(t.uniform1i(this.addr,a),i[0]=a),n.setTexture3D(e||FS,a)}function AR(t,e,n){const i=this.cache,a=n.allocateTextureUnit();i[0]!==a&&(t.uniform1i(this.addr,a),i[0]=a),n.setTextureCube(e||HS,a)}function wR(t,e,n){const i=this.cache,a=n.allocateTextureUnit();i[0]!==a&&(t.uniform1i(this.addr,a),i[0]=a),n.setTexture2DArray(e||jS,a)}function NR(t){switch(t){case 5126:return cR;case 35664:return uR;case 35665:return dR;case 35666:return fR;case 35674:return hR;case 35675:return pR;case 35676:return mR;case 5124:case 35670:return gR;case 35667:case 35671:return xR;case 35668:case 35672:return vR;case 35669:case 35673:return _R;case 5125:return yR;case 36294:return bR;case 36295:return SR;case 36296:return MR;case 35678:case 36198:case 36298:case 36306:case 35682:return ER;case 35679:case 36299:case 36307:return TR;case 35680:case 36300:case 36308:case 36293:return AR;case 36289:case 36303:case 36311:case 36292:return wR}}function CR(t,e){t.uniform1fv(this.addr,e)}function RR(t,e){const n=kl(e,this.size,2);t.uniform2fv(this.addr,n)}function DR(t,e){const n=kl(e,this.size,3);t.uniform3fv(this.addr,n)}function LR(t,e){const n=kl(e,this.size,4);t.uniform4fv(this.addr,n)}function UR(t,e){const n=kl(e,this.size,4);t.uniformMatrix2fv(this.addr,!1,n)}function OR(t,e){const n=kl(e,this.size,9);t.uniformMatrix3fv(this.addr,!1,n)}function PR(t,e){const n=kl(e,this.size,16);t.uniformMatrix4fv(this.addr,!1,n)}function zR(t,e){t.uniform1iv(this.addr,e)}function kR(t,e){t.uniform2iv(this.addr,e)}function IR(t,e){t.uniform3iv(this.addr,e)}function BR(t,e){t.uniform4iv(this.addr,e)}function jR(t,e){t.uniform1uiv(this.addr,e)}function FR(t,e){t.uniform2uiv(this.addr,e)}function HR(t,e){t.uniform3uiv(this.addr,e)}function GR(t,e){t.uniform4uiv(this.addr,e)}function VR(t,e,n){const i=this.cache,a=e.length,s=Gd(n,a);hn(i,s)||(t.uniform1iv(this.addr,s),pn(i,s));let l;this.type===t.SAMPLER_2D_SHADOW?l=rm:l=BS;for(let o=0;o!==a;++o)n.setTexture2D(e[o]||l,s[o])}function WR(t,e,n){const i=this.cache,a=e.length,s=Gd(n,a);hn(i,s)||(t.uniform1iv(this.addr,s),pn(i,s));for(let l=0;l!==a;++l)n.setTexture3D(e[l]||FS,s[l])}function XR(t,e,n){const i=this.cache,a=e.length,s=Gd(n,a);hn(i,s)||(t.uniform1iv(this.addr,s),pn(i,s));for(let l=0;l!==a;++l)n.setTextureCube(e[l]||HS,s[l])}function qR(t,e,n){const i=this.cache,a=e.length,s=Gd(n,a);hn(i,s)||(t.uniform1iv(this.addr,s),pn(i,s));for(let l=0;l!==a;++l)n.setTexture2DArray(e[l]||jS,s[l])}function YR(t){switch(t){case 5126:return CR;case 35664:return RR;case 35665:return DR;case 35666:return LR;case 35674:return UR;case 35675:return OR;case 35676:return PR;case 5124:case 35670:return zR;case 35667:case 35671:return kR;case 35668:case 35672:return IR;case 35669:case 35673:return BR;case 5125:return jR;case 36294:return FR;case 36295:return HR;case 36296:return GR;case 35678:case 36198:case 36298:case 36306:case 35682:return VR;case 35679:case 36299:case 36307:return WR;case 35680:case 36300:case 36308:case 36293:return XR;case 36289:case 36303:case 36311:case 36292:return qR}}class KR{constructor(e,n,i){this.id=e,this.addr=i,this.cache=[],this.type=n.type,this.setValue=NR(n.type)}}class $R{constructor(e,n,i){this.id=e,this.addr=i,this.cache=[],this.type=n.type,this.size=n.size,this.setValue=YR(n.type)}}class ZR{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 oh=/(\w+)(\])?(\[|\.)?/g;function fv(t,e){t.seq.push(e),t.map[e.id]=e}function QR(t,e,n){const i=t.name,a=i.length;for(oh.lastIndex=0;;){const s=oh.exec(i),l=oh.lastIndex;let o=s[1];const c=s[2]==="]",u=s[3];if(c&&(o=o|0),u===void 0||u==="["&&l+2===a){fv(n,u===void 0?new KR(o,t,e):new $R(o,t,e));break}else{let m=n.map[o];m===void 0&&(m=new ZR(o),fv(n,m)),n=m}}}class Du{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);QR(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 hv(t,e,n){const i=t.createShader(e);return t.shaderSource(i,n),t.compileShader(i),i}const JR=37297;let e3=0;function t3(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 pv=new at;function n3(t){mt._getMatrix(pv,mt.workingColorSpace,t);const e=`mat3( ${pv.elements.map(n=>n.toFixed(4))} )`;switch(mt.getTransfer(t)){case dd:return[e,"LinearTransferOETF"];case Nt:return[e,"sRGBTransferOETF"];default:return et("WebGLProgram: Unsupported color space: ",t),[e,"LinearTransferOETF"]}}function mv(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+` + +`+t3(t.getShaderSource(e),o)}else return s}function i3(t,e){const n=n3(e);return[`vec4 ${t}( vec4 value ) {`,` return ${n[1]}( vec4( value.rgb * ${n[0]}, value.a ) );`,"}"].join(` +`)}const a3={[oS]:"Linear",[cS]:"Reinhard",[uS]:"Cineon",[dS]:"ACESFilmic",[hS]:"AgX",[pS]:"Neutral",[fS]:"Custom"};function s3(t,e){const n=a3[e];return n===void 0?(et("WebGLProgram: Unsupported toneMapping:",e),"vec3 "+t+"( vec3 color ) { return LinearToneMapping( color ); }"):"vec3 "+t+"( vec3 color ) { return "+n+"ToneMapping( color ); }"}const iu=new Z;function r3(){mt.getLuminanceCoefficients(iu);const t=iu.x.toFixed(4),e=iu.y.toFixed(4),n=iu.z.toFixed(4);return["float luminance( const in vec3 rgb ) {",` const vec3 weights = vec3( ${t}, ${e}, ${n} );`," return dot( weights, rgb );","}"].join(` +`)}function l3(t){return[t.extensionClipCullDistance?"#extension GL_ANGLE_clip_cull_distance : require":"",t.extensionMultiDraw?"#extension GL_ANGLE_multi_draw : require":""].filter(uo).join(` +`)}function o3(t){const e=[];for(const n in t){const i=t[n];i!==!1&&e.push("#define "+n+" "+i)}return e.join(` +`)}function c3(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 uo(t){return t!==""}function gv(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 xv(t,e){return t.replace(/NUM_CLIPPING_PLANES/g,e.numClippingPlanes).replace(/UNION_CLIPPING_PLANES/g,e.numClippingPlanes-e.numClipIntersection)}const u3=/^[ \t]*#include +<([\w\d./]+)>/gm;function lm(t){return t.replace(u3,f3)}const d3=new Map;function f3(t,e){let n=ct[e];if(n===void 0){const i=d3.get(e);if(i!==void 0)n=ct[i],et('WebGLRenderer: Shader chunk "%s" has been deprecated. Use "%s" instead.',e,i);else throw new Error("THREE.WebGLProgram: Can not resolve #include <"+e+">")}return lm(n)}const h3=/#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 vv(t){return t.replace(h3,p3)}function p3(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 _v(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 m3={[Tu]:"SHADOWMAP_TYPE_PCF",[co]:"SHADOWMAP_TYPE_VSM"};function g3(t){return m3[t.shadowMapType]||"SHADOWMAP_TYPE_BASIC"}const x3={[tr]:"ENVMAP_TYPE_CUBE",[Ml]:"ENVMAP_TYPE_CUBE",[jd]:"ENVMAP_TYPE_CUBE_UV"};function v3(t){return t.envMap===!1?"ENVMAP_TYPE_CUBE":x3[t.envMapMode]||"ENVMAP_TYPE_CUBE"}const _3={[Ml]:"ENVMAP_MODE_REFRACTION"};function y3(t){return t.envMap===!1?"ENVMAP_MODE_REFLECTION":_3[t.envMapMode]||"ENVMAP_MODE_REFLECTION"}const b3={[lS]:"ENVMAP_BLENDING_MULTIPLY",[oA]:"ENVMAP_BLENDING_MIX",[cA]:"ENVMAP_BLENDING_ADD"};function S3(t){return t.envMap===!1?"ENVMAP_BLENDING_NONE":b3[t.combine]||"ENVMAP_BLENDING_NONE"}function M3(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 E3(t,e,n,i){const a=t.getContext(),s=n.defines;let l=n.vertexShader,o=n.fragmentShader;const c=g3(n),u=v3(n),f=y3(n),m=S3(n),d=M3(n),p=l3(n),g=o3(s),b=a.createProgram();let x,h,v=n.glslVersion?"#version "+n.glslVersion+` +`:"";n.isRawShaderMaterial?(x=["#define SHADER_TYPE "+n.shaderType,"#define SHADER_NAME "+n.shaderName,g].filter(uo).join(` +`),x.length>0&&(x+=` +`),h=["#define SHADER_TYPE "+n.shaderType,"#define SHADER_NAME "+n.shaderName,g].filter(uo).join(` +`),h.length>0&&(h+=` +`)):(x=[_v(n),"#define SHADER_TYPE "+n.shaderType,"#define SHADER_NAME "+n.shaderName,g,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 "+f:"",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(uo).join(` +`),h=[_v(n),"#define SHADER_TYPE "+n.shaderType,"#define SHADER_NAME "+n.shaderName,g,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 "+f:"",n.envMap?"#define "+m:"",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!==na?"#define TONE_MAPPING":"",n.toneMapping!==na?ct.tonemapping_pars_fragment:"",n.toneMapping!==na?s3("toneMapping",n.toneMapping):"",n.dithering?"#define DITHERING":"",n.opaque?"#define OPAQUE":"",ct.colorspace_pars_fragment,i3("linearToOutputTexel",n.outputColorSpace),r3(),n.useDepthPacking?"#define DEPTH_PACKING "+n.depthPacking:"",` +`].filter(uo).join(` +`)),l=lm(l),l=gv(l,n),l=xv(l,n),o=lm(o),o=gv(o,n),o=xv(o,n),l=vv(l),o=vv(o),n.isRawShaderMaterial!==!0&&(v=`#version 300 es +`,x=[p,"#define attribute in","#define varying out","#define texture2D texture"].join(` +`)+` +`+x,h=["#define varying in",n.glslVersion===E0?"":"layout(location = 0) out highp vec4 pc_fragColor;",n.glslVersion===E0?"":"#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 y=v+x+l,_=v+h+o,A=hv(a,a.VERTEX_SHADER,y),M=hv(a,a.FRAGMENT_SHADER,_);a.attachShader(b,A),a.attachShader(b,M),n.index0AttributeName!==void 0?a.bindAttribLocation(b,0,n.index0AttributeName):n.hasPositionAttribute===!0&&a.bindAttribLocation(b,0,"position"),a.linkProgram(b);function T(D){if(t.debug.checkShaderErrors){const L=a.getProgramInfoLog(b)||"",q=a.getShaderInfoLog(A)||"",P=a.getShaderInfoLog(M)||"",O=L.trim(),B=q.trim(),k=P.trim();let W=!0,Y=!0;if(a.getProgramParameter(b,a.LINK_STATUS)===!1)if(W=!1,typeof t.debug.onShaderError=="function")t.debug.onShaderError(a,b,A,M);else{const he=mv(a,A,"vertex"),_e=mv(a,M,"fragment");Mt("WebGLProgram: Shader Error "+a.getError()+" - VALIDATE_STATUS "+a.getProgramParameter(b,a.VALIDATE_STATUS)+` + +Material Name: `+D.name+` +Material Type: `+D.type+` + +Program Info Log: `+O+` +`+he+` +`+_e)}else O!==""?et("WebGLProgram: Program Info Log:",O):(B===""||k==="")&&(Y=!1);Y&&(D.diagnostics={runnable:W,programLog:O,vertexShader:{log:B,prefix:x},fragmentShader:{log:k,prefix:h}})}a.deleteShader(A),a.deleteShader(M),S=new Du(a,b),w=c3(a,b)}let S;this.getUniforms=function(){return S===void 0&&T(this),S};let w;this.getAttributes=function(){return w===void 0&&T(this),w};let R=n.rendererExtensionParallelShaderCompile===!1;return this.isReady=function(){return R===!1&&(R=a.getProgramParameter(b,JR)),R},this.destroy=function(){i.releaseStatesOfProgram(this),a.deleteProgram(b),this.program=void 0},this.type=n.shaderType,this.name=n.shaderName,this.id=e3++,this.cacheKey=e,this.usedTimes=1,this.program=b,this.vertexShader=A,this.fragmentShader=M,this}let T3=0;class A3{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 w3(e),n.set(e,i)),i}}class w3{constructor(e){this.id=T3++,this.code=e,this.usedTimes=0}}function N3(t){return t===nr||t===od||t===cd}function C3(t,e,n,i,a,s){const l=new ES,o=new A3,c=new Set,u=[],f=new Map,m=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 g(S){return c.add(S),S===0?"uv":`uv${S}`}function b(S,w,R,D,L,q){const P=D.fog,O=L.geometry,B=S.isMeshStandardMaterial||S.isMeshLambertMaterial||S.isMeshPhongMaterial?D.environment:null,k=S.isMeshStandardMaterial||S.isMeshLambertMaterial&&!S.envMap||S.isMeshPhongMaterial&&!S.envMap,W=e.get(S.envMap||B,k),Y=W&&W.mapping===jd?W.image.height:null,he=p[S.type];S.precision!==null&&(d=i.getMaxPrecision(S.precision),d!==S.precision&&et("WebGLProgram.getParameters:",S.precision,"not supported, using",d,"instead."));const _e=O.morphAttributes.position||O.morphAttributes.normal||O.morphAttributes.color,Ce=_e!==void 0?_e.length:0;let X=0;O.morphAttributes.position!==void 0&&(X=1),O.morphAttributes.normal!==void 0&&(X=2),O.morphAttributes.color!==void 0&&(X=3);let de,Ne,ae,j;if(he){const Fe=$i[he];de=Fe.vertexShader,Ne=Fe.fragmentShader}else{de=S.vertexShader,Ne=S.fragmentShader;const Fe=o.getVertexShaderStage(S),bt=o.getFragmentShaderStage(S);o.update(S,Fe,bt),ae=Fe.id,j=bt.id}const ee=t.getRenderTarget(),xe=t.state.buffers.depth.getReversed(),Te=L.isInstancedMesh===!0,Ie=L.isBatchedMesh===!0,it=!!S.map,re=!!S.matcap,me=!!W,Ae=!!S.aoMap,se=!!S.lightMap,Le=!!S.bumpMap&&S.wireframe===!1,ke=!!S.normalMap,Ue=!!S.displacementMap,Re=!!S.emissiveMap,qe=!!S.metalnessMap,Se=!!S.roughnessMap,I=S.anisotropy>0,Qe=S.clearcoat>0,Je=S.dispersion>0,C=S.iridescence>0,E=S.sheen>0,V=S.transmission>0,K=I&&!!S.anisotropyMap,ne=Qe&&!!S.clearcoatMap,ye=Qe&&!!S.clearcoatNormalMap,be=Qe&&!!S.clearcoatRoughnessMap,ie=C&&!!S.iridescenceMap,Q=C&&!!S.iridescenceThicknessMap,le=E&&!!S.sheenColorMap,pe=E&&!!S.sheenRoughnessMap,ve=!!S.specularMap,Me=!!S.specularColorMap,Be=!!S.specularIntensityMap,Ve=V&&!!S.transmissionMap,tt=V&&!!S.thicknessMap,H=!!S.gradientMap,Ee=!!S.alphaMap,oe=S.alphaTest>0,we=!!S.alphaHash,De=!!S.extensions;let ge=na;S.toneMapped&&(ee===null||ee.isXRRenderTarget===!0)&&(ge=t.toneMapping);const We={shaderID:he,shaderType:S.type,shaderName:S.name,vertexShader:de,fragmentShader:Ne,defines:S.defines,customVertexShaderID:ae,customFragmentShaderID:j,isRawShaderMaterial:S.isRawShaderMaterial===!0,glslVersion:S.glslVersion,precision:d,batching:Ie,batchingColor:Ie&&L._colorsTexture!==null,instancing:Te,instancingColor:Te&&L.instanceColor!==null,instancingMorph:Te&&L.morphTexture!==null,outputColorSpace:ee===null?t.outputColorSpace:ee.isXRRenderTarget===!0?ee.texture.colorSpace:mt.workingColorSpace,alphaToCoverage:!!S.alphaToCoverage,map:it,matcap:re,envMap:me,envMapMode:me&&W.mapping,envMapCubeUVHeight:Y,aoMap:Ae,lightMap:se,bumpMap:Le,normalMap:ke,displacementMap:Ue,emissiveMap:Re,normalMapObjectSpace:ke&&S.normalMapType===fA,normalMapTangentSpace:ke&&S.normalMapType===b0,packedNormalMap:ke&&S.normalMapType===b0&&N3(S.normalMap.format),metalnessMap:qe,roughnessMap:Se,anisotropy:I,anisotropyMap:K,clearcoat:Qe,clearcoatMap:ne,clearcoatNormalMap:ye,clearcoatRoughnessMap:be,dispersion:Je,iridescence:C,iridescenceMap:ie,iridescenceThicknessMap:Q,sheen:E,sheenColorMap:le,sheenRoughnessMap:pe,specularMap:ve,specularColorMap:Me,specularIntensityMap:Be,transmission:V,transmissionMap:Ve,thicknessMap:tt,gradientMap:H,opaque:S.transparent===!1&&S.blending===rl&&S.alphaToCoverage===!1,alphaMap:Ee,alphaTest:oe,alphaHash:we,combine:S.combine,mapUv:it&&g(S.map.channel),aoMapUv:Ae&&g(S.aoMap.channel),lightMapUv:se&&g(S.lightMap.channel),bumpMapUv:Le&&g(S.bumpMap.channel),normalMapUv:ke&&g(S.normalMap.channel),displacementMapUv:Ue&&g(S.displacementMap.channel),emissiveMapUv:Re&&g(S.emissiveMap.channel),metalnessMapUv:qe&&g(S.metalnessMap.channel),roughnessMapUv:Se&&g(S.roughnessMap.channel),anisotropyMapUv:K&&g(S.anisotropyMap.channel),clearcoatMapUv:ne&&g(S.clearcoatMap.channel),clearcoatNormalMapUv:ye&&g(S.clearcoatNormalMap.channel),clearcoatRoughnessMapUv:be&&g(S.clearcoatRoughnessMap.channel),iridescenceMapUv:ie&&g(S.iridescenceMap.channel),iridescenceThicknessMapUv:Q&&g(S.iridescenceThicknessMap.channel),sheenColorMapUv:le&&g(S.sheenColorMap.channel),sheenRoughnessMapUv:pe&&g(S.sheenRoughnessMap.channel),specularMapUv:ve&&g(S.specularMap.channel),specularColorMapUv:Me&&g(S.specularColorMap.channel),specularIntensityMapUv:Be&&g(S.specularIntensityMap.channel),transmissionMapUv:Ve&&g(S.transmissionMap.channel),thicknessMapUv:tt&&g(S.thicknessMap.channel),alphaMapUv:Ee&&g(S.alphaMap.channel),vertexTangents:!!O.attributes.tangent&&(ke||I),vertexNormals:!!O.attributes.normal,vertexColors:S.vertexColors,vertexAlphas:S.vertexColors===!0&&!!O.attributes.color&&O.attributes.color.itemSize===4,pointsUvs:L.isPoints===!0&&!!O.attributes.uv&&(it||Ee),fog:!!P,useFog:S.fog===!0,fogExp2:!!P&&P.isFogExp2,flatShading:S.wireframe===!1&&(S.flatShading===!0||O.attributes.normal===void 0&&ke===!1&&(S.isMeshLambertMaterial||S.isMeshPhongMaterial||S.isMeshStandardMaterial||S.isMeshPhysicalMaterial)),sizeAttenuation:S.sizeAttenuation===!0,logarithmicDepthBuffer:m,reversedDepthBuffer:xe,skinning:L.isSkinnedMesh===!0,hasPositionAttribute:O.attributes.position!==void 0,morphTargets:O.morphAttributes.position!==void 0,morphNormals:O.morphAttributes.normal!==void 0,morphColors:O.morphAttributes.color!==void 0,morphTargetsCount:Ce,morphTextureStride:X,numDirLights:w.directional.length,numPointLights:w.point.length,numSpotLights:w.spot.length,numSpotLightMaps:w.spotLightMap.length,numRectAreaLights:w.rectArea.length,numHemiLights:w.hemi.length,numDirLightShadows:w.directionalShadowMap.length,numPointLightShadows:w.pointShadowMap.length,numSpotLightShadows:w.spotShadowMap.length,numSpotLightShadowsWithMaps:w.numSpotLightShadowsWithMaps,numLightProbes:w.numLightProbes,numLightProbeGrids:q.length,numClippingPlanes:s.numPlanes,numClipIntersection:s.numIntersection,dithering:S.dithering,shadowMapEnabled:t.shadowMap.enabled&&R.length>0,shadowMapType:t.shadowMap.type,toneMapping:ge,decodeVideoTexture:it&&S.map.isVideoTexture===!0&&mt.getTransfer(S.map.colorSpace)===Nt,decodeVideoTextureEmissive:Re&&S.emissiveMap.isVideoTexture===!0&&mt.getTransfer(S.emissiveMap.colorSpace)===Nt,premultipliedAlpha:S.premultipliedAlpha,doubleSided:S.side===ya,flipSided:S.side===$n,useDepthPacking:S.depthPacking>=0,depthPacking:S.depthPacking||0,index0AttributeName:S.index0AttributeName,extensionClipCullDistance:De&&S.extensions.clipCullDistance===!0&&n.has("WEBGL_clip_cull_distance"),extensionMultiDraw:(De&&S.extensions.multiDraw===!0||Ie)&&n.has("WEBGL_multi_draw"),rendererExtensionParallelShaderCompile:n.has("KHR_parallel_shader_compile"),customProgramCacheKey:S.customProgramCacheKey()};return We.vertexUv1s=c.has(1),We.vertexUv2s=c.has(2),We.vertexUv3s=c.has(3),c.clear(),We}function x(S){const w=[];if(S.shaderID?w.push(S.shaderID):(w.push(S.customVertexShaderID),w.push(S.customFragmentShaderID)),S.defines!==void 0)for(const R in S.defines)w.push(R),w.push(S.defines[R]);return S.isRawShaderMaterial===!1&&(h(w,S),v(w,S),w.push(t.outputColorSpace)),w.push(S.customProgramCacheKey),w.join()}function h(S,w){S.push(w.precision),S.push(w.outputColorSpace),S.push(w.envMapMode),S.push(w.envMapCubeUVHeight),S.push(w.mapUv),S.push(w.alphaMapUv),S.push(w.lightMapUv),S.push(w.aoMapUv),S.push(w.bumpMapUv),S.push(w.normalMapUv),S.push(w.displacementMapUv),S.push(w.emissiveMapUv),S.push(w.metalnessMapUv),S.push(w.roughnessMapUv),S.push(w.anisotropyMapUv),S.push(w.clearcoatMapUv),S.push(w.clearcoatNormalMapUv),S.push(w.clearcoatRoughnessMapUv),S.push(w.iridescenceMapUv),S.push(w.iridescenceThicknessMapUv),S.push(w.sheenColorMapUv),S.push(w.sheenRoughnessMapUv),S.push(w.specularMapUv),S.push(w.specularColorMapUv),S.push(w.specularIntensityMapUv),S.push(w.transmissionMapUv),S.push(w.thicknessMapUv),S.push(w.combine),S.push(w.fogExp2),S.push(w.sizeAttenuation),S.push(w.morphTargetsCount),S.push(w.morphAttributeCount),S.push(w.numDirLights),S.push(w.numPointLights),S.push(w.numSpotLights),S.push(w.numSpotLightMaps),S.push(w.numHemiLights),S.push(w.numRectAreaLights),S.push(w.numDirLightShadows),S.push(w.numPointLightShadows),S.push(w.numSpotLightShadows),S.push(w.numSpotLightShadowsWithMaps),S.push(w.numLightProbes),S.push(w.shadowMapType),S.push(w.toneMapping),S.push(w.numClippingPlanes),S.push(w.numClipIntersection),S.push(w.depthPacking)}function v(S,w){l.disableAll(),w.instancing&&l.enable(0),w.instancingColor&&l.enable(1),w.instancingMorph&&l.enable(2),w.matcap&&l.enable(3),w.envMap&&l.enable(4),w.normalMapObjectSpace&&l.enable(5),w.normalMapTangentSpace&&l.enable(6),w.clearcoat&&l.enable(7),w.iridescence&&l.enable(8),w.alphaTest&&l.enable(9),w.vertexColors&&l.enable(10),w.vertexAlphas&&l.enable(11),w.vertexUv1s&&l.enable(12),w.vertexUv2s&&l.enable(13),w.vertexUv3s&&l.enable(14),w.vertexTangents&&l.enable(15),w.anisotropy&&l.enable(16),w.alphaHash&&l.enable(17),w.batching&&l.enable(18),w.dispersion&&l.enable(19),w.batchingColor&&l.enable(20),w.gradientMap&&l.enable(21),w.packedNormalMap&&l.enable(22),w.vertexNormals&&l.enable(23),S.push(l.mask),l.disableAll(),w.fog&&l.enable(0),w.useFog&&l.enable(1),w.flatShading&&l.enable(2),w.logarithmicDepthBuffer&&l.enable(3),w.reversedDepthBuffer&&l.enable(4),w.skinning&&l.enable(5),w.morphTargets&&l.enable(6),w.morphNormals&&l.enable(7),w.morphColors&&l.enable(8),w.premultipliedAlpha&&l.enable(9),w.shadowMapEnabled&&l.enable(10),w.doubleSided&&l.enable(11),w.flipSided&&l.enable(12),w.useDepthPacking&&l.enable(13),w.dithering&&l.enable(14),w.transmission&&l.enable(15),w.sheen&&l.enable(16),w.opaque&&l.enable(17),w.pointsUvs&&l.enable(18),w.decodeVideoTexture&&l.enable(19),w.decodeVideoTextureEmissive&&l.enable(20),w.alphaToCoverage&&l.enable(21),w.numLightProbeGrids>0&&l.enable(22),w.hasPositionAttribute&&l.enable(23),S.push(l.mask)}function y(S){const w=p[S.type];let R;if(w){const D=$i[w];R=JA.clone(D.uniforms)}else R=S.uniforms;return R}function _(S,w){let R=f.get(w);return R!==void 0?++R.usedTimes:(R=new E3(t,w,S,a),u.push(R),f.set(w,R)),R}function A(S){if(--S.usedTimes===0){const w=u.indexOf(S);u[w]=u[u.length-1],u.pop(),f.delete(S.cacheKey),S.destroy()}}function M(S){o.remove(S)}function T(){o.dispose()}return{getParameters:b,getProgramCacheKey:x,getUniforms:y,acquireProgram:_,releaseProgram:A,releaseShaderCache:M,programs:u,dispose:T}}function R3(){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 D3(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 yv(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 bv(){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,g,b,x,h){let v=t[e];return v===void 0?(v={id:d.id,object:d,geometry:p,material:g,materialVariant:l(d),groupOrder:b,renderOrder:d.renderOrder,z:x,group:h},t[e]=v):(v.id=d.id,v.object=d,v.geometry=p,v.material=g,v.materialVariant=l(d),v.groupOrder=b,v.renderOrder=d.renderOrder,v.z=x,v.group=h),e++,v}function c(d,p,g,b,x,h){const v=o(d,p,g,b,x,h);g.transmission>0?i.push(v):g.transparent===!0?a.push(v):n.push(v)}function u(d,p,g,b,x,h){const v=o(d,p,g,b,x,h);g.transmission>0?i.unshift(v):g.transparent===!0?a.unshift(v):n.unshift(v)}function f(d,p,g){n.length>1&&n.sort(d||D3),i.length>1&&i.sort(p||yv),a.length>1&&a.sort(p||yv),g&&(n.reverse(),i.reverse(),a.reverse())}function m(){for(let d=e,p=t.length;d<p;d++){const g=t[d];if(g.id===null)break;g.id=null,g.object=null,g.geometry=null,g.material=null,g.group=null}}return{opaque:n,transmissive:i,transparent:a,init:s,push:c,unshift:u,finish:m,sort:f}}function L3(){let t=new WeakMap;function e(i,a){const s=t.get(i);let l;return s===void 0?(l=new bv,t.set(i,[l])):a>=s.length?(l=new bv,s.push(l)):l=s[a],l}function n(){t=new WeakMap}return{get:e,dispose:n}}function U3(){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 vt};break;case"SpotLight":n={position:new Z,direction:new Z,color:new vt,distance:0,coneCos:0,penumbraCos:0,decay:0};break;case"PointLight":n={position:new Z,color:new vt,distance:0,decay:0};break;case"HemisphereLight":n={direction:new Z,skyColor:new vt,groundColor:new vt};break;case"RectAreaLight":n={color:new vt,position:new Z,halfWidth:new Z,halfHeight:new Z};break}return t[e.id]=n,n}}}function O3(){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 wt};break;case"SpotLight":n={shadowIntensity:1,shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new wt};break;case"PointLight":n={shadowIntensity:1,shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new wt,shadowCameraNear:1,shadowCameraFar:1e3};break}return t[e.id]=n,n}}}let P3=0;function z3(t,e){return(e.castShadow?2:0)-(t.castShadow?2:0)+(e.map?1:0)-(t.map?1:0)}function k3(t){const e=new U3,n=O3(),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 Bt,l=new Bt;function o(u){let f=0,m=0,d=0;for(let w=0;w<9;w++)i.probe[w].set(0,0,0);let p=0,g=0,b=0,x=0,h=0,v=0,y=0,_=0,A=0,M=0,T=0;u.sort(z3);for(let w=0,R=u.length;w<R;w++){const D=u[w],L=D.color,q=D.intensity,P=D.distance;let O=null;if(D.shadow&&D.shadow.map&&(D.shadow.map.texture.format===nr?O=D.shadow.map.texture:O=D.shadow.map.depthTexture||D.shadow.map.texture),D.isAmbientLight)f+=L.r*q,m+=L.g*q,d+=L.b*q;else if(D.isLightProbe){for(let B=0;B<9;B++)i.probe[B].addScaledVector(D.sh.coefficients[B],q);T++}else if(D.isDirectionalLight){const B=e.get(D);if(B.color.copy(D.color).multiplyScalar(D.intensity),D.castShadow){const k=D.shadow,W=n.get(D);W.shadowIntensity=k.intensity,W.shadowBias=k.bias,W.shadowNormalBias=k.normalBias,W.shadowRadius=k.radius,W.shadowMapSize=k.mapSize,i.directionalShadow[p]=W,i.directionalShadowMap[p]=O,i.directionalShadowMatrix[p]=D.shadow.matrix,v++}i.directional[p]=B,p++}else if(D.isSpotLight){const B=e.get(D);B.position.setFromMatrixPosition(D.matrixWorld),B.color.copy(L).multiplyScalar(q),B.distance=P,B.coneCos=Math.cos(D.angle),B.penumbraCos=Math.cos(D.angle*(1-D.penumbra)),B.decay=D.decay,i.spot[b]=B;const k=D.shadow;if(D.map&&(i.spotLightMap[A]=D.map,A++,k.updateMatrices(D),D.castShadow&&M++),i.spotLightMatrix[b]=k.matrix,D.castShadow){const W=n.get(D);W.shadowIntensity=k.intensity,W.shadowBias=k.bias,W.shadowNormalBias=k.normalBias,W.shadowRadius=k.radius,W.shadowMapSize=k.mapSize,i.spotShadow[b]=W,i.spotShadowMap[b]=O,_++}b++}else if(D.isRectAreaLight){const B=e.get(D);B.color.copy(L).multiplyScalar(q),B.halfWidth.set(D.width*.5,0,0),B.halfHeight.set(0,D.height*.5,0),i.rectArea[x]=B,x++}else if(D.isPointLight){const B=e.get(D);if(B.color.copy(D.color).multiplyScalar(D.intensity),B.distance=D.distance,B.decay=D.decay,D.castShadow){const k=D.shadow,W=n.get(D);W.shadowIntensity=k.intensity,W.shadowBias=k.bias,W.shadowNormalBias=k.normalBias,W.shadowRadius=k.radius,W.shadowMapSize=k.mapSize,W.shadowCameraNear=k.camera.near,W.shadowCameraFar=k.camera.far,i.pointShadow[g]=W,i.pointShadowMap[g]=O,i.pointShadowMatrix[g]=D.shadow.matrix,y++}i.point[g]=B,g++}else if(D.isHemisphereLight){const B=e.get(D);B.skyColor.copy(D.color).multiplyScalar(q),B.groundColor.copy(D.groundColor).multiplyScalar(q),i.hemi[h]=B,h++}}x>0&&(t.has("OES_texture_float_linear")===!0?(i.rectAreaLTC1=Oe.LTC_FLOAT_1,i.rectAreaLTC2=Oe.LTC_FLOAT_2):(i.rectAreaLTC1=Oe.LTC_HALF_1,i.rectAreaLTC2=Oe.LTC_HALF_2)),i.ambient[0]=f,i.ambient[1]=m,i.ambient[2]=d;const S=i.hash;(S.directionalLength!==p||S.pointLength!==g||S.spotLength!==b||S.rectAreaLength!==x||S.hemiLength!==h||S.numDirectionalShadows!==v||S.numPointShadows!==y||S.numSpotShadows!==_||S.numSpotMaps!==A||S.numLightProbes!==T)&&(i.directional.length=p,i.spot.length=b,i.rectArea.length=x,i.point.length=g,i.hemi.length=h,i.directionalShadow.length=v,i.directionalShadowMap.length=v,i.pointShadow.length=y,i.pointShadowMap.length=y,i.spotShadow.length=_,i.spotShadowMap.length=_,i.directionalShadowMatrix.length=v,i.pointShadowMatrix.length=y,i.spotLightMatrix.length=_+A-M,i.spotLightMap.length=A,i.numSpotLightShadowsWithMaps=M,i.numLightProbes=T,S.directionalLength=p,S.pointLength=g,S.spotLength=b,S.rectAreaLength=x,S.hemiLength=h,S.numDirectionalShadows=v,S.numPointShadows=y,S.numSpotShadows=_,S.numSpotMaps=A,S.numLightProbes=T,i.version=P3++)}function c(u,f){let m=0,d=0,p=0,g=0,b=0;const x=f.matrixWorldInverse;for(let h=0,v=u.length;h<v;h++){const y=u[h];if(y.isDirectionalLight){const _=i.directional[m];_.direction.setFromMatrixPosition(y.matrixWorld),a.setFromMatrixPosition(y.target.matrixWorld),_.direction.sub(a),_.direction.transformDirection(x),m++}else if(y.isSpotLight){const _=i.spot[p];_.position.setFromMatrixPosition(y.matrixWorld),_.position.applyMatrix4(x),_.direction.setFromMatrixPosition(y.matrixWorld),a.setFromMatrixPosition(y.target.matrixWorld),_.direction.sub(a),_.direction.transformDirection(x),p++}else if(y.isRectAreaLight){const _=i.rectArea[g];_.position.setFromMatrixPosition(y.matrixWorld),_.position.applyMatrix4(x),l.identity(),s.copy(y.matrixWorld),s.premultiply(x),l.extractRotation(s),_.halfWidth.set(y.width*.5,0,0),_.halfHeight.set(0,y.height*.5,0),_.halfWidth.applyMatrix4(l),_.halfHeight.applyMatrix4(l),g++}else if(y.isPointLight){const _=i.point[d];_.position.setFromMatrixPosition(y.matrixWorld),_.position.applyMatrix4(x),d++}else if(y.isHemisphereLight){const _=i.hemi[b];_.direction.setFromMatrixPosition(y.matrixWorld),_.direction.transformDirection(x),b++}}}return{setup:o,setupView:c,state:i}}function Sv(t){const e=new k3(t),n=[],i=[],a=[];function s(d){m.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 f(d){e.setupView(n,d)}const m={lightsArray:n,shadowsArray:i,lightProbeGridArray:a,camera:null,lights:e,transmissionRenderTarget:{},textureUnits:0};return{init:s,state:m,setupLights:u,setupLightsView:f,pushLight:l,pushShadow:o,pushLightProbeGrid:c}}function I3(t){let e=new WeakMap;function n(a,s=0){const l=e.get(a);let o;return l===void 0?(o=new Sv(t),e.set(a,[o])):s>=l.length?(o=new Sv(t),l.push(o)):o=l[s],o}function i(){e=new WeakMap}return{get:n,dispose:i}}const B3=`void main() { + gl_Position = vec4( position, 1.0 ); +}`,j3=`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 ); +}`,F3=[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)],H3=[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)],Mv=new Bt,to=new Z,ch=new Z;function G3(t,e,n){let i=new RS;const a=new wt,s=new wt,l=new Zt,o=new iw,c=new aw,u={},f=n.maxTextureSize,m={[As]:$n,[$n]:As,[ya]:ya},d=new Yn({defines:{VSM_SAMPLES:8},uniforms:{shadow_pass:{value:null},resolution:{value:new wt},radius:{value:4}},vertexShader:B3,fragmentShader:j3}),p=d.clone();p.defines.HORIZONTAL_PASS=1;const g=new Qn;g.setAttribute("position",new un(new Float32Array([-1,-1,.5,3,-1,.5,-1,3,.5]),3));const b=new Ri(g,d),x=this;this.enabled=!1,this.autoUpdate=!0,this.needsUpdate=!1,this.type=Tu;let h=this.type;this.render=function(M,T,S){if(x.enabled===!1||x.autoUpdate===!1&&x.needsUpdate===!1||M.length===0)return;this.type===GT&&(et("WebGLShadowMap: PCFSoftShadowMap has been deprecated. Using PCFShadowMap instead."),this.type=Tu);const w=t.getRenderTarget(),R=t.getActiveCubeFace(),D=t.getActiveMipmapLevel(),L=t.state;L.setBlending(Na),L.buffers.depth.getReversed()===!0?L.buffers.color.setClear(0,0,0,0):L.buffers.color.setClear(1,1,1,1),L.buffers.depth.setTest(!0),L.setScissorTest(!1);const q=h!==this.type;q&&T.traverse(function(P){P.material&&(Array.isArray(P.material)?P.material.forEach(O=>O.needsUpdate=!0):P.material.needsUpdate=!0)});for(let P=0,O=M.length;P<O;P++){const B=M[P],k=B.shadow;if(k===void 0){et("WebGLShadowMap:",B,"has no shadow.");continue}if(k.autoUpdate===!1&&k.needsUpdate===!1)continue;a.copy(k.mapSize);const W=k.getFrameExtents();a.multiply(W),s.copy(k.mapSize),(a.x>f||a.y>f)&&(a.x>f&&(s.x=Math.floor(f/W.x),a.x=s.x*W.x,k.mapSize.x=s.x),a.y>f&&(s.y=Math.floor(f/W.y),a.y=s.y*W.y,k.mapSize.y=s.y));const Y=t.state.buffers.depth.getReversed();if(k.camera._reversedDepth=Y,k.map===null||q===!0){if(k.map!==null&&(k.map.depthTexture!==null&&(k.map.depthTexture.dispose(),k.map.depthTexture=null),k.map.dispose()),this.type===co){if(B.isPointLight){et("WebGLShadowMap: VSM shadow maps are not supported for PointLights. Use PCF or BasicShadowMap instead.");continue}k.map=new ia(a.x,a.y,{format:nr,type:Pa,minFilter:Ln,magFilter:Ln,generateMipmaps:!1}),k.map.texture.name=B.name+".shadowMap",k.map.depthTexture=new El(a.x,a.y,ki),k.map.depthTexture.name=B.name+".shadowMapDepth",k.map.depthTexture.format=za,k.map.depthTexture.compareFunction=null,k.map.depthTexture.minFilter=An,k.map.depthTexture.magFilter=An}else B.isPointLight?(k.map=new IS(a.x),k.map.depthTexture=new $A(a.x,aa)):(k.map=new ia(a.x,a.y),k.map.depthTexture=new El(a.x,a.y,aa)),k.map.depthTexture.name=B.name+".shadowMap",k.map.depthTexture.format=za,this.type===Tu?(k.map.depthTexture.compareFunction=Y?Mg:Sg,k.map.depthTexture.minFilter=Ln,k.map.depthTexture.magFilter=Ln):(k.map.depthTexture.compareFunction=null,k.map.depthTexture.minFilter=An,k.map.depthTexture.magFilter=An);k.camera.updateProjectionMatrix()}const he=k.map.isWebGLCubeRenderTarget?6:1;for(let _e=0;_e<he;_e++){if(k.map.isWebGLCubeRenderTarget)t.setRenderTarget(k.map,_e),t.clear();else{_e===0&&(t.setRenderTarget(k.map),t.clear());const Ce=k.getViewport(_e);l.set(s.x*Ce.x,s.y*Ce.y,s.x*Ce.z,s.y*Ce.w),L.viewport(l)}if(B.isPointLight){const Ce=k.camera,X=k.matrix,de=B.distance||Ce.far;de!==Ce.far&&(Ce.far=de,Ce.updateProjectionMatrix()),to.setFromMatrixPosition(B.matrixWorld),Ce.position.copy(to),ch.copy(Ce.position),ch.add(F3[_e]),Ce.up.copy(H3[_e]),Ce.lookAt(ch),Ce.updateMatrixWorld(),X.makeTranslation(-to.x,-to.y,-to.z),Mv.multiplyMatrices(Ce.projectionMatrix,Ce.matrixWorldInverse),k._frustum.setFromProjectionMatrix(Mv,Ce.coordinateSystem,Ce.reversedDepth)}else k.updateMatrices(B);i=k.getFrustum(),_(T,S,k.camera,B,this.type)}k.isPointLightShadow!==!0&&this.type===co&&v(k,S),k.needsUpdate=!1}h=this.type,x.needsUpdate=!1,t.setRenderTarget(w,R,D)};function v(M,T){const S=e.update(b);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 ia(a.x,a.y,{format:nr,type:Pa})),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(T,null,S,d,b,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,b,null)}function y(M,T,S,w){let R=null;const D=S.isPointLight===!0?M.customDistanceMaterial:M.customDepthMaterial;if(D!==void 0)R=D;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 L=R.uuid,q=T.uuid;let P=u[L];P===void 0&&(P={},u[L]=P);let O=P[q];O===void 0&&(O=R.clone(),P[q]=O,T.addEventListener("dispose",A)),R=O}if(R.visible=T.visible,R.wireframe=T.wireframe,w===co?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 L=t.properties.get(R);L.light=S}return R}function _(M,T,S,w,R){if(M.visible===!1)return;if(M.layers.test(T.layers)&&(M.isMesh||M.isLine||M.isPoints)&&(M.castShadow||M.receiveShadow&&R===co)&&(!M.frustumCulled||i.intersectsObject(M))){M.modelViewMatrix.multiplyMatrices(S.matrixWorldInverse,M.matrixWorld);const q=e.update(M),P=M.material;if(Array.isArray(P)){const O=q.groups;for(let B=0,k=O.length;B<k;B++){const W=O[B],Y=P[W.materialIndex];if(Y&&Y.visible){const he=y(M,Y,w,R);M.onBeforeShadow(t,M,T,S,q,he,W),t.renderBufferDirect(S,null,q,he,M,W),M.onAfterShadow(t,M,T,S,q,he,W)}}}else if(P.visible){const O=y(M,P,w,R);M.onBeforeShadow(t,M,T,S,q,O,null),t.renderBufferDirect(S,null,q,O,M,null),M.onAfterShadow(t,M,T,S,q,O,null)}}const L=M.children;for(let q=0,P=L.length;q<P;q++)_(L[q],T,S,w,R)}function A(M){M.target.removeEventListener("dispose",A);for(const S in u){const w=u[S],R=M.target.uuid;R in w&&(w[R].dispose(),delete w[R])}}}function V3(t,e){function n(){let H=!1;const Ee=new Zt;let oe=null;const we=new Zt(0,0,0,0);return{setMask:function(De){oe!==De&&!H&&(t.colorMask(De,De,De,De),oe=De)},setLocked:function(De){H=De},setClear:function(De,ge,We,Fe,bt){bt===!0&&(De*=Fe,ge*=Fe,We*=Fe),Ee.set(De,ge,We,Fe),we.equals(Ee)===!1&&(t.clearColor(De,ge,We,Fe),we.copy(Ee))},reset:function(){H=!1,oe=null,we.set(-1,0,0,0)}}}function i(){let H=!1,Ee=!1,oe=null,we=null,De=null;return{setReversed:function(ge){if(Ee!==ge){const We=e.get("EXT_clip_control");ge?We.clipControlEXT(We.LOWER_LEFT_EXT,We.ZERO_TO_ONE_EXT):We.clipControlEXT(We.LOWER_LEFT_EXT,We.NEGATIVE_ONE_TO_ONE_EXT),Ee=ge;const Fe=De;De=null,this.setClear(Fe)}},getReversed:function(){return Ee},setTest:function(ge){ge?ee(t.DEPTH_TEST):xe(t.DEPTH_TEST)},setMask:function(ge){oe!==ge&&!H&&(t.depthMask(ge),oe=ge)},setFunc:function(ge){if(Ee&&(ge=SA[ge]),we!==ge){switch(ge){case _p:t.depthFunc(t.NEVER);break;case yp:t.depthFunc(t.ALWAYS);break;case bp:t.depthFunc(t.LESS);break;case Sl:t.depthFunc(t.LEQUAL);break;case Sp:t.depthFunc(t.EQUAL);break;case Mp:t.depthFunc(t.GEQUAL);break;case Ep:t.depthFunc(t.GREATER);break;case Tp:t.depthFunc(t.NOTEQUAL);break;default:t.depthFunc(t.LEQUAL)}we=ge}},setLocked:function(ge){H=ge},setClear:function(ge){De!==ge&&(De=ge,Ee&&(ge=1-ge),t.clearDepth(ge))},reset:function(){H=!1,oe=null,we=null,De=null,Ee=!1}}}function a(){let H=!1,Ee=null,oe=null,we=null,De=null,ge=null,We=null,Fe=null,bt=null;return{setTest:function(rt){H||(rt?ee(t.STENCIL_TEST):xe(t.STENCIL_TEST))},setMask:function(rt){Ee!==rt&&!H&&(t.stencilMask(rt),Ee=rt)},setFunc:function(rt,nn,bn){(oe!==rt||we!==nn||De!==bn)&&(t.stencilFunc(rt,nn,bn),oe=rt,we=nn,De=bn)},setOp:function(rt,nn,bn){(ge!==rt||We!==nn||Fe!==bn)&&(t.stencilOp(rt,nn,bn),ge=rt,We=nn,Fe=bn)},setLocked:function(rt){H=rt},setClear:function(rt){bt!==rt&&(t.clearStencil(rt),bt=rt)},reset:function(){H=!1,Ee=null,oe=null,we=null,De=null,ge=null,We=null,Fe=null,bt=null}}}const s=new n,l=new i,o=new a,c=new WeakMap,u=new WeakMap;let f={},m={},d={},p=new WeakMap,g=[],b=null,x=!1,h=null,v=null,y=null,_=null,A=null,M=null,T=null,S=new vt(0,0,0),w=0,R=!1,D=null,L=null,q=null,P=null,O=null;const B=t.getParameter(t.MAX_COMBINED_TEXTURE_IMAGE_UNITS);let k=!1,W=0;const Y=t.getParameter(t.VERSION);Y.indexOf("WebGL")!==-1?(W=parseFloat(/^WebGL (\d)/.exec(Y)[1]),k=W>=1):Y.indexOf("OpenGL ES")!==-1&&(W=parseFloat(/^OpenGL ES (\d)/.exec(Y)[1]),k=W>=2);let he=null,_e={};const Ce=t.getParameter(t.SCISSOR_BOX),X=t.getParameter(t.VIEWPORT),de=new Zt().fromArray(Ce),Ne=new Zt().fromArray(X);function ae(H,Ee,oe,we){const De=new Uint8Array(4),ge=t.createTexture();t.bindTexture(H,ge),t.texParameteri(H,t.TEXTURE_MIN_FILTER,t.NEAREST),t.texParameteri(H,t.TEXTURE_MAG_FILTER,t.NEAREST);for(let We=0;We<oe;We++)H===t.TEXTURE_3D||H===t.TEXTURE_2D_ARRAY?t.texImage3D(Ee,0,t.RGBA,1,1,we,0,t.RGBA,t.UNSIGNED_BYTE,De):t.texImage2D(Ee+We,0,t.RGBA,1,1,0,t.RGBA,t.UNSIGNED_BYTE,De);return ge}const j={};j[t.TEXTURE_2D]=ae(t.TEXTURE_2D,t.TEXTURE_2D,1),j[t.TEXTURE_CUBE_MAP]=ae(t.TEXTURE_CUBE_MAP,t.TEXTURE_CUBE_MAP_POSITIVE_X,6),j[t.TEXTURE_2D_ARRAY]=ae(t.TEXTURE_2D_ARRAY,t.TEXTURE_2D_ARRAY,1,1),j[t.TEXTURE_3D]=ae(t.TEXTURE_3D,t.TEXTURE_3D,1,1),s.setClear(0,0,0,1),l.setClear(1),o.setClear(0),ee(t.DEPTH_TEST),l.setFunc(Sl),Le(!1),ke(x0),ee(t.CULL_FACE),Ae(Na);function ee(H){f[H]!==!0&&(t.enable(H),f[H]=!0)}function xe(H){f[H]!==!1&&(t.disable(H),f[H]=!1)}function Te(H,Ee){return d[H]!==Ee?(t.bindFramebuffer(H,Ee),d[H]=Ee,H===t.DRAW_FRAMEBUFFER&&(d[t.FRAMEBUFFER]=Ee),H===t.FRAMEBUFFER&&(d[t.DRAW_FRAMEBUFFER]=Ee),!0):!1}function Ie(H,Ee){let oe=g,we=!1;if(H){oe=p.get(Ee),oe===void 0&&(oe=[],p.set(Ee,oe));const De=H.textures;if(oe.length!==De.length||oe[0]!==t.COLOR_ATTACHMENT0){for(let ge=0,We=De.length;ge<We;ge++)oe[ge]=t.COLOR_ATTACHMENT0+ge;oe.length=De.length,we=!0}}else oe[0]!==t.BACK&&(oe[0]=t.BACK,we=!0);we&&t.drawBuffers(oe)}function it(H){return b!==H?(t.useProgram(H),b=H,!0):!1}const re={[Fs]:t.FUNC_ADD,[WT]:t.FUNC_SUBTRACT,[XT]:t.FUNC_REVERSE_SUBTRACT};re[qT]=t.MIN,re[YT]=t.MAX;const me={[KT]:t.ZERO,[$T]:t.ONE,[ZT]:t.SRC_COLOR,[xp]:t.SRC_ALPHA,[iA]:t.SRC_ALPHA_SATURATE,[tA]:t.DST_COLOR,[JT]:t.DST_ALPHA,[QT]:t.ONE_MINUS_SRC_COLOR,[vp]:t.ONE_MINUS_SRC_ALPHA,[nA]:t.ONE_MINUS_DST_COLOR,[eA]:t.ONE_MINUS_DST_ALPHA,[aA]:t.CONSTANT_COLOR,[sA]:t.ONE_MINUS_CONSTANT_COLOR,[rA]:t.CONSTANT_ALPHA,[lA]:t.ONE_MINUS_CONSTANT_ALPHA};function Ae(H,Ee,oe,we,De,ge,We,Fe,bt,rt){if(H===Na){x===!0&&(xe(t.BLEND),x=!1);return}if(x===!1&&(ee(t.BLEND),x=!0),H!==VT){if(H!==h||rt!==R){if((v!==Fs||A!==Fs)&&(t.blendEquation(t.FUNC_ADD),v=Fs,A=Fs),rt)switch(H){case rl:t.blendFuncSeparate(t.ONE,t.ONE_MINUS_SRC_ALPHA,t.ONE,t.ONE_MINUS_SRC_ALPHA);break;case v0:t.blendFunc(t.ONE,t.ONE);break;case _0:t.blendFuncSeparate(t.ZERO,t.ONE_MINUS_SRC_COLOR,t.ZERO,t.ONE);break;case y0:t.blendFuncSeparate(t.DST_COLOR,t.ONE_MINUS_SRC_ALPHA,t.ZERO,t.ONE);break;default:Mt("WebGLState: Invalid blending: ",H);break}else switch(H){case rl:t.blendFuncSeparate(t.SRC_ALPHA,t.ONE_MINUS_SRC_ALPHA,t.ONE,t.ONE_MINUS_SRC_ALPHA);break;case v0:t.blendFuncSeparate(t.SRC_ALPHA,t.ONE,t.ONE,t.ONE);break;case _0:Mt("WebGLState: SubtractiveBlending requires material.premultipliedAlpha = true");break;case y0:Mt("WebGLState: MultiplyBlending requires material.premultipliedAlpha = true");break;default:Mt("WebGLState: Invalid blending: ",H);break}y=null,_=null,M=null,T=null,S.set(0,0,0),w=0,h=H,R=rt}return}De=De||Ee,ge=ge||oe,We=We||we,(Ee!==v||De!==A)&&(t.blendEquationSeparate(re[Ee],re[De]),v=Ee,A=De),(oe!==y||we!==_||ge!==M||We!==T)&&(t.blendFuncSeparate(me[oe],me[we],me[ge],me[We]),y=oe,_=we,M=ge,T=We),(Fe.equals(S)===!1||bt!==w)&&(t.blendColor(Fe.r,Fe.g,Fe.b,bt),S.copy(Fe),w=bt),h=H,R=!1}function se(H,Ee){H.side===ya?xe(t.CULL_FACE):ee(t.CULL_FACE);let oe=H.side===$n;Ee&&(oe=!oe),Le(oe),H.blending===rl&&H.transparent===!1?Ae(Na):Ae(H.blending,H.blendEquation,H.blendSrc,H.blendDst,H.blendEquationAlpha,H.blendSrcAlpha,H.blendDstAlpha,H.blendColor,H.blendAlpha,H.premultipliedAlpha),l.setFunc(H.depthFunc),l.setTest(H.depthTest),l.setMask(H.depthWrite),s.setMask(H.colorWrite);const we=H.stencilWrite;o.setTest(we),we&&(o.setMask(H.stencilWriteMask),o.setFunc(H.stencilFunc,H.stencilRef,H.stencilFuncMask),o.setOp(H.stencilFail,H.stencilZFail,H.stencilZPass)),Re(H.polygonOffset,H.polygonOffsetFactor,H.polygonOffsetUnits),H.alphaToCoverage===!0?ee(t.SAMPLE_ALPHA_TO_COVERAGE):xe(t.SAMPLE_ALPHA_TO_COVERAGE)}function Le(H){D!==H&&(H?t.frontFace(t.CW):t.frontFace(t.CCW),D=H)}function ke(H){H!==FT?(ee(t.CULL_FACE),H!==L&&(H===x0?t.cullFace(t.BACK):H===HT?t.cullFace(t.FRONT):t.cullFace(t.FRONT_AND_BACK))):xe(t.CULL_FACE),L=H}function Ue(H){H!==q&&(k&&t.lineWidth(H),q=H)}function Re(H,Ee,oe){H?(ee(t.POLYGON_OFFSET_FILL),(P!==Ee||O!==oe)&&(P=Ee,O=oe,l.getReversed()&&(Ee=-Ee),t.polygonOffset(Ee,oe))):xe(t.POLYGON_OFFSET_FILL)}function qe(H){H?ee(t.SCISSOR_TEST):xe(t.SCISSOR_TEST)}function Se(H){H===void 0&&(H=t.TEXTURE0+B-1),he!==H&&(t.activeTexture(H),he=H)}function I(H,Ee,oe){oe===void 0&&(he===null?oe=t.TEXTURE0+B-1:oe=he);let we=_e[oe];we===void 0&&(we={type:void 0,texture:void 0},_e[oe]=we),(we.type!==H||we.texture!==Ee)&&(he!==oe&&(t.activeTexture(oe),he=oe),t.bindTexture(H,Ee||j[H]),we.type=H,we.texture=Ee)}function Qe(){const H=_e[he];H!==void 0&&H.type!==void 0&&(t.bindTexture(H.type,null),H.type=void 0,H.texture=void 0)}function Je(){try{t.compressedTexImage2D(...arguments)}catch(H){Mt("WebGLState:",H)}}function C(){try{t.compressedTexImage3D(...arguments)}catch(H){Mt("WebGLState:",H)}}function E(){try{t.texSubImage2D(...arguments)}catch(H){Mt("WebGLState:",H)}}function V(){try{t.texSubImage3D(...arguments)}catch(H){Mt("WebGLState:",H)}}function K(){try{t.compressedTexSubImage2D(...arguments)}catch(H){Mt("WebGLState:",H)}}function ne(){try{t.compressedTexSubImage3D(...arguments)}catch(H){Mt("WebGLState:",H)}}function ye(){try{t.texStorage2D(...arguments)}catch(H){Mt("WebGLState:",H)}}function be(){try{t.texStorage3D(...arguments)}catch(H){Mt("WebGLState:",H)}}function ie(){try{t.texImage2D(...arguments)}catch(H){Mt("WebGLState:",H)}}function Q(){try{t.texImage3D(...arguments)}catch(H){Mt("WebGLState:",H)}}function le(H){return m[H]!==void 0?m[H]:t.getParameter(H)}function pe(H,Ee){m[H]!==Ee&&(t.pixelStorei(H,Ee),m[H]=Ee)}function ve(H){de.equals(H)===!1&&(t.scissor(H.x,H.y,H.z,H.w),de.copy(H))}function Me(H){Ne.equals(H)===!1&&(t.viewport(H.x,H.y,H.z,H.w),Ne.copy(H))}function Be(H,Ee){let oe=u.get(Ee);oe===void 0&&(oe=new WeakMap,u.set(Ee,oe));let we=oe.get(H);we===void 0&&(we=t.getUniformBlockIndex(Ee,H.name),oe.set(H,we))}function Ve(H,Ee){const we=u.get(Ee).get(H);c.get(Ee)!==we&&(t.uniformBlockBinding(Ee,we,H.__bindingPointIndex),c.set(Ee,we))}function tt(){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),f={},m={},he=null,_e={},d={},p=new WeakMap,g=[],b=null,x=!1,h=null,v=null,y=null,_=null,A=null,M=null,T=null,S=new vt(0,0,0),w=0,R=!1,D=null,L=null,q=null,P=null,O=null,de.set(0,0,t.canvas.width,t.canvas.height),Ne.set(0,0,t.canvas.width,t.canvas.height),s.reset(),l.reset(),o.reset()}return{buffers:{color:s,depth:l,stencil:o},enable:ee,disable:xe,bindFramebuffer:Te,drawBuffers:Ie,useProgram:it,setBlending:Ae,setMaterial:se,setFlipSided:Le,setCullFace:ke,setLineWidth:Ue,setPolygonOffset:Re,setScissorTest:qe,activeTexture:Se,bindTexture:I,unbindTexture:Qe,compressedTexImage2D:Je,compressedTexImage3D:C,texImage2D:ie,texImage3D:Q,pixelStorei:pe,getParameter:le,updateUBOMapping:Be,uniformBlockBinding:Ve,texStorage2D:ye,texStorage3D:be,texSubImage2D:E,texSubImage3D:V,compressedTexSubImage2D:K,compressedTexSubImage3D:ne,scissor:ve,viewport:Me,reset:tt}}function W3(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 wt,f=new WeakMap,m=new Set;let d;const p=new WeakMap;let g=!1;try{g=typeof OffscreenCanvas<"u"&&new OffscreenCanvas(1,1).getContext("2d")!==null}catch{}function b(C,E){return g?new OffscreenCanvas(C,E):hd("canvas")}function x(C,E,V){let K=1;const ne=Je(C);if((ne.width>V||ne.height>V)&&(K=V/Math.max(ne.width,ne.height)),K<1)if(typeof HTMLImageElement<"u"&&C instanceof HTMLImageElement||typeof HTMLCanvasElement<"u"&&C instanceof HTMLCanvasElement||typeof ImageBitmap<"u"&&C instanceof ImageBitmap||typeof VideoFrame<"u"&&C instanceof VideoFrame){const ye=Math.floor(K*ne.width),be=Math.floor(K*ne.height);d===void 0&&(d=b(ye,be));const ie=E?b(ye,be):d;return ie.width=ye,ie.height=be,ie.getContext("2d").drawImage(C,0,0,ye,be),et("WebGLRenderer: Texture has been resized from ("+ne.width+"x"+ne.height+") to ("+ye+"x"+be+")."),ie}else return"data"in C&&et("WebGLRenderer: Image in DataTexture is too big ("+ne.width+"x"+ne.height+")."),C;return C}function h(C){return C.generateMipmaps}function v(C){t.generateMipmap(C)}function y(C){return C.isWebGLCubeRenderTarget?t.TEXTURE_CUBE_MAP:C.isWebGL3DRenderTarget?t.TEXTURE_3D:C.isWebGLArrayRenderTarget||C.isCompressedArrayTexture?t.TEXTURE_2D_ARRAY:t.TEXTURE_2D}function _(C,E,V,K,ne,ye=!1){if(C!==null){if(t[C]!==void 0)return t[C];et("WebGLRenderer: Attempt to use non-existing WebGL internal format '"+C+"'")}let be;K&&(be=e.get("EXT_texture_norm16"),be||et("WebGLRenderer: Unable to use normalized textures without EXT_texture_norm16 extension"));let ie=E;if(E===t.RED&&(V===t.FLOAT&&(ie=t.R32F),V===t.HALF_FLOAT&&(ie=t.R16F),V===t.UNSIGNED_BYTE&&(ie=t.R8),V===t.UNSIGNED_SHORT&&be&&(ie=be.R16_EXT),V===t.SHORT&&be&&(ie=be.R16_SNORM_EXT)),E===t.RED_INTEGER&&(V===t.UNSIGNED_BYTE&&(ie=t.R8UI),V===t.UNSIGNED_SHORT&&(ie=t.R16UI),V===t.UNSIGNED_INT&&(ie=t.R32UI),V===t.BYTE&&(ie=t.R8I),V===t.SHORT&&(ie=t.R16I),V===t.INT&&(ie=t.R32I)),E===t.RG&&(V===t.FLOAT&&(ie=t.RG32F),V===t.HALF_FLOAT&&(ie=t.RG16F),V===t.UNSIGNED_BYTE&&(ie=t.RG8),V===t.UNSIGNED_SHORT&&be&&(ie=be.RG16_EXT),V===t.SHORT&&be&&(ie=be.RG16_SNORM_EXT)),E===t.RG_INTEGER&&(V===t.UNSIGNED_BYTE&&(ie=t.RG8UI),V===t.UNSIGNED_SHORT&&(ie=t.RG16UI),V===t.UNSIGNED_INT&&(ie=t.RG32UI),V===t.BYTE&&(ie=t.RG8I),V===t.SHORT&&(ie=t.RG16I),V===t.INT&&(ie=t.RG32I)),E===t.RGB_INTEGER&&(V===t.UNSIGNED_BYTE&&(ie=t.RGB8UI),V===t.UNSIGNED_SHORT&&(ie=t.RGB16UI),V===t.UNSIGNED_INT&&(ie=t.RGB32UI),V===t.BYTE&&(ie=t.RGB8I),V===t.SHORT&&(ie=t.RGB16I),V===t.INT&&(ie=t.RGB32I)),E===t.RGBA_INTEGER&&(V===t.UNSIGNED_BYTE&&(ie=t.RGBA8UI),V===t.UNSIGNED_SHORT&&(ie=t.RGBA16UI),V===t.UNSIGNED_INT&&(ie=t.RGBA32UI),V===t.BYTE&&(ie=t.RGBA8I),V===t.SHORT&&(ie=t.RGBA16I),V===t.INT&&(ie=t.RGBA32I)),E===t.RGB&&(V===t.UNSIGNED_SHORT&&be&&(ie=be.RGB16_EXT),V===t.SHORT&&be&&(ie=be.RGB16_SNORM_EXT),V===t.UNSIGNED_INT_5_9_9_9_REV&&(ie=t.RGB9_E5),V===t.UNSIGNED_INT_10F_11F_11F_REV&&(ie=t.R11F_G11F_B10F)),E===t.RGBA){const Q=ye?dd:mt.getTransfer(ne);V===t.FLOAT&&(ie=t.RGBA32F),V===t.HALF_FLOAT&&(ie=t.RGBA16F),V===t.UNSIGNED_BYTE&&(ie=Q===Nt?t.SRGB8_ALPHA8:t.RGBA8),V===t.UNSIGNED_SHORT&&be&&(ie=be.RGBA16_EXT),V===t.SHORT&&be&&(ie=be.RGBA16_SNORM_EXT),V===t.UNSIGNED_SHORT_4_4_4_4&&(ie=t.RGBA4),V===t.UNSIGNED_SHORT_5_5_5_1&&(ie=t.RGB5_A1)}return(ie===t.R16F||ie===t.R32F||ie===t.RG16F||ie===t.RG32F||ie===t.RGBA16F||ie===t.RGBA32F)&&e.get("EXT_color_buffer_float"),ie}function A(C,E){let V;return C?E===null||E===aa||E===Yo?V=t.DEPTH24_STENCIL8:E===ki?V=t.DEPTH32F_STENCIL8:E===qo&&(V=t.DEPTH24_STENCIL8,et("DepthTexture: 16 bit depth attachment is not supported with stencil. Using 24-bit attachment.")):E===null||E===aa||E===Yo?V=t.DEPTH_COMPONENT24:E===ki?V=t.DEPTH_COMPONENT32F:E===qo&&(V=t.DEPTH_COMPONENT16),V}function M(C,E){return h(C)===!0||C.isFramebufferTexture&&C.minFilter!==An&&C.minFilter!==Ln?Math.log2(Math.max(E.width,E.height))+1:C.mipmaps!==void 0&&C.mipmaps.length>0?C.mipmaps.length:C.isCompressedTexture&&Array.isArray(C.image)?E.mipmaps.length:1}function T(C){const E=C.target;E.removeEventListener("dispose",T),w(E),E.isVideoTexture&&f.delete(E),E.isHTMLTexture&&m.delete(E)}function S(C){const E=C.target;E.removeEventListener("dispose",S),D(E)}function w(C){const E=i.get(C);if(E.__webglInit===void 0)return;const V=C.source,K=p.get(V);if(K){const ne=K[E.__cacheKey];ne.usedTimes--,ne.usedTimes===0&&R(C),Object.keys(K).length===0&&p.delete(V)}i.remove(C)}function R(C){const E=i.get(C);t.deleteTexture(E.__webglTexture);const V=C.source,K=p.get(V);delete K[E.__cacheKey],l.memory.textures--}function D(C){const E=i.get(C);if(C.depthTexture&&(C.depthTexture.dispose(),i.remove(C.depthTexture)),C.isWebGLCubeRenderTarget)for(let K=0;K<6;K++){if(Array.isArray(E.__webglFramebuffer[K]))for(let ne=0;ne<E.__webglFramebuffer[K].length;ne++)t.deleteFramebuffer(E.__webglFramebuffer[K][ne]);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=C.textures;for(let K=0,ne=V.length;K<ne;K++){const ye=i.get(V[K]);ye.__webglTexture&&(t.deleteTexture(ye.__webglTexture),l.memory.textures--),i.remove(V[K])}i.remove(C)}let L=0;function q(){L=0}function P(){return L}function O(C){L=C}function B(){const C=L;return C>=a.maxTextures&&et("WebGLTextures: Trying to use "+C+" texture units while this GPU supports only "+a.maxTextures),L+=1,C}function k(C){const E=[];return E.push(C.wrapS),E.push(C.wrapT),E.push(C.wrapR||0),E.push(C.magFilter),E.push(C.minFilter),E.push(C.anisotropy),E.push(C.internalFormat),E.push(C.format),E.push(C.type),E.push(C.generateMipmaps),E.push(C.premultiplyAlpha),E.push(C.flipY),E.push(C.unpackAlignment),E.push(C.colorSpace),E.join()}function W(C,E){const V=i.get(C);if(C.isVideoTexture&&I(C),C.isRenderTargetTexture===!1&&C.isExternalTexture!==!0&&C.version>0&&V.__version!==C.version){const K=C.image;if(K===null)et("WebGLRenderer: Texture marked for update but no image data found.");else if(K.complete===!1)et("WebGLRenderer: Texture marked for update but image is incomplete");else{xe(V,C,E);return}}else C.isExternalTexture&&(V.__webglTexture=C.sourceTexture?C.sourceTexture:null);n.bindTexture(t.TEXTURE_2D,V.__webglTexture,t.TEXTURE0+E)}function Y(C,E){const V=i.get(C);if(C.isRenderTargetTexture===!1&&C.version>0&&V.__version!==C.version){xe(V,C,E);return}else C.isExternalTexture&&(V.__webglTexture=C.sourceTexture?C.sourceTexture:null);n.bindTexture(t.TEXTURE_2D_ARRAY,V.__webglTexture,t.TEXTURE0+E)}function he(C,E){const V=i.get(C);if(C.isRenderTargetTexture===!1&&C.version>0&&V.__version!==C.version){xe(V,C,E);return}n.bindTexture(t.TEXTURE_3D,V.__webglTexture,t.TEXTURE0+E)}function _e(C,E){const V=i.get(C);if(C.isCubeDepthTexture!==!0&&C.version>0&&V.__version!==C.version){Te(V,C,E);return}n.bindTexture(t.TEXTURE_CUBE_MAP,V.__webglTexture,t.TEXTURE0+E)}const Ce={[Ap]:t.REPEAT,[Ea]:t.CLAMP_TO_EDGE,[wp]:t.MIRRORED_REPEAT},X={[An]:t.NEAREST,[uA]:t.NEAREST_MIPMAP_NEAREST,[wc]:t.NEAREST_MIPMAP_LINEAR,[Ln]:t.LINEAR,[Df]:t.LINEAR_MIPMAP_NEAREST,[Gs]:t.LINEAR_MIPMAP_LINEAR},de={[hA]:t.NEVER,[vA]:t.ALWAYS,[pA]:t.LESS,[Sg]:t.LEQUAL,[mA]:t.EQUAL,[Mg]:t.GEQUAL,[gA]:t.GREATER,[xA]:t.NOTEQUAL};function Ne(C,E){if(E.type===ki&&e.has("OES_texture_float_linear")===!1&&(E.magFilter===Ln||E.magFilter===Df||E.magFilter===wc||E.magFilter===Gs||E.minFilter===Ln||E.minFilter===Df||E.minFilter===wc||E.minFilter===Gs)&&et("WebGLRenderer: Unable to use linear filtering with floating point textures. OES_texture_float_linear not supported on this device."),t.texParameteri(C,t.TEXTURE_WRAP_S,Ce[E.wrapS]),t.texParameteri(C,t.TEXTURE_WRAP_T,Ce[E.wrapT]),(C===t.TEXTURE_3D||C===t.TEXTURE_2D_ARRAY)&&t.texParameteri(C,t.TEXTURE_WRAP_R,Ce[E.wrapR]),t.texParameteri(C,t.TEXTURE_MAG_FILTER,X[E.magFilter]),t.texParameteri(C,t.TEXTURE_MIN_FILTER,X[E.minFilter]),E.compareFunction&&(t.texParameteri(C,t.TEXTURE_COMPARE_MODE,t.COMPARE_REF_TO_TEXTURE),t.texParameteri(C,t.TEXTURE_COMPARE_FUNC,de[E.compareFunction])),e.has("EXT_texture_filter_anisotropic")===!0){if(E.magFilter===An||E.minFilter!==wc&&E.minFilter!==Gs||E.type===ki&&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(C,V.TEXTURE_MAX_ANISOTROPY_EXT,Math.min(E.anisotropy,a.getMaxAnisotropy())),i.get(E).__currentAnisotropy=E.anisotropy}}}function ae(C,E){let V=!1;C.__webglInit===void 0&&(C.__webglInit=!0,E.addEventListener("dispose",T));const K=E.source;let ne=p.get(K);ne===void 0&&(ne={},p.set(K,ne));const ye=k(E);if(ye!==C.__cacheKey){ne[ye]===void 0&&(ne[ye]={texture:t.createTexture(),usedTimes:0},l.memory.textures++,V=!0),ne[ye].usedTimes++;const be=ne[C.__cacheKey];be!==void 0&&(ne[C.__cacheKey].usedTimes--,be.usedTimes===0&&R(E)),C.__cacheKey=ye,C.__webglTexture=ne[ye].texture}return V}function j(C,E,V){return Math.floor(Math.floor(C/V)/E)}function ee(C,E,V,K){const ye=C.updateRanges;if(ye.length===0)n.texSubImage2D(t.TEXTURE_2D,0,0,0,E.width,E.height,V,K,E.data);else{ye.sort((pe,ve)=>pe.start-ve.start);let be=0;for(let pe=1;pe<ye.length;pe++){const ve=ye[be],Me=ye[pe],Be=ve.start+ve.count,Ve=j(Me.start,E.width,4),tt=j(ve.start,E.width,4);Me.start<=Be+1&&Ve===tt&&j(Me.start+Me.count-1,E.width,4)===Ve?ve.count=Math.max(ve.count,Me.start+Me.count-ve.start):(++be,ye[be]=Me)}ye.length=be+1;const ie=n.getParameter(t.UNPACK_ROW_LENGTH),Q=n.getParameter(t.UNPACK_SKIP_PIXELS),le=n.getParameter(t.UNPACK_SKIP_ROWS);n.pixelStorei(t.UNPACK_ROW_LENGTH,E.width);for(let pe=0,ve=ye.length;pe<ve;pe++){const Me=ye[pe],Be=Math.floor(Me.start/4),Ve=Math.ceil(Me.count/4),tt=Be%E.width,H=Math.floor(Be/E.width),Ee=Ve,oe=1;n.pixelStorei(t.UNPACK_SKIP_PIXELS,tt),n.pixelStorei(t.UNPACK_SKIP_ROWS,H),n.texSubImage2D(t.TEXTURE_2D,0,tt,H,Ee,oe,V,K,E.data)}C.clearUpdateRanges(),n.pixelStorei(t.UNPACK_ROW_LENGTH,ie),n.pixelStorei(t.UNPACK_SKIP_PIXELS,Q),n.pixelStorei(t.UNPACK_SKIP_ROWS,le)}}function xe(C,E,V){let K=t.TEXTURE_2D;(E.isDataArrayTexture||E.isCompressedArrayTexture)&&(K=t.TEXTURE_2D_ARRAY),E.isData3DTexture&&(K=t.TEXTURE_3D);const ne=ae(C,E),ye=E.source;n.bindTexture(K,C.__webglTexture,t.TEXTURE0+V);const be=i.get(ye);if(ye.version!==be.__version||ne===!0){if(n.activeTexture(t.TEXTURE0+V),(typeof ImageBitmap<"u"&&E.image instanceof ImageBitmap)===!1){const oe=mt.getPrimaries(mt.workingColorSpace),we=E.colorSpace===is?null:mt.getPrimaries(E.colorSpace),De=E.colorSpace===is||oe===we?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,De)}n.pixelStorei(t.UNPACK_ALIGNMENT,E.unpackAlignment);let Q=x(E.image,!1,a.maxTextureSize);Q=Qe(E,Q);const le=s.convert(E.format,E.colorSpace),pe=s.convert(E.type);let ve=_(E.internalFormat,le,pe,E.normalized,E.colorSpace,E.isVideoTexture);Ne(K,E);let Me;const Be=E.mipmaps,Ve=E.isVideoTexture!==!0,tt=be.__version===void 0||ne===!0,H=ye.dataReady,Ee=M(E,Q);if(E.isDepthTexture)ve=A(E.format===Vs,E.type),tt&&(Ve?n.texStorage2D(t.TEXTURE_2D,1,ve,Q.width,Q.height):n.texImage2D(t.TEXTURE_2D,0,ve,Q.width,Q.height,0,le,pe,null));else if(E.isDataTexture)if(Be.length>0){Ve&&tt&&n.texStorage2D(t.TEXTURE_2D,Ee,ve,Be[0].width,Be[0].height);for(let oe=0,we=Be.length;oe<we;oe++)Me=Be[oe],Ve?H&&n.texSubImage2D(t.TEXTURE_2D,oe,0,0,Me.width,Me.height,le,pe,Me.data):n.texImage2D(t.TEXTURE_2D,oe,ve,Me.width,Me.height,0,le,pe,Me.data);E.generateMipmaps=!1}else Ve?(tt&&n.texStorage2D(t.TEXTURE_2D,Ee,ve,Q.width,Q.height),H&&ee(E,Q,le,pe)):n.texImage2D(t.TEXTURE_2D,0,ve,Q.width,Q.height,0,le,pe,Q.data);else if(E.isCompressedTexture)if(E.isCompressedArrayTexture){Ve&&tt&&n.texStorage3D(t.TEXTURE_2D_ARRAY,Ee,ve,Be[0].width,Be[0].height,Q.depth);for(let oe=0,we=Be.length;oe<we;oe++)if(Me=Be[oe],E.format!==Ii)if(le!==null)if(Ve){if(H)if(E.layerUpdates.size>0){const De=ev(Me.width,Me.height,E.format,E.type);for(const ge of E.layerUpdates){const We=Me.data.subarray(ge*De/Me.data.BYTES_PER_ELEMENT,(ge+1)*De/Me.data.BYTES_PER_ELEMENT);n.compressedTexSubImage3D(t.TEXTURE_2D_ARRAY,oe,0,0,ge,Me.width,Me.height,1,le,We)}E.clearLayerUpdates()}else n.compressedTexSubImage3D(t.TEXTURE_2D_ARRAY,oe,0,0,0,Me.width,Me.height,Q.depth,le,Me.data)}else n.compressedTexImage3D(t.TEXTURE_2D_ARRAY,oe,ve,Me.width,Me.height,Q.depth,0,Me.data,0,0);else et("WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()");else Ve?H&&n.texSubImage3D(t.TEXTURE_2D_ARRAY,oe,0,0,0,Me.width,Me.height,Q.depth,le,pe,Me.data):n.texImage3D(t.TEXTURE_2D_ARRAY,oe,ve,Me.width,Me.height,Q.depth,0,le,pe,Me.data)}else{Ve&&tt&&n.texStorage2D(t.TEXTURE_2D,Ee,ve,Be[0].width,Be[0].height);for(let oe=0,we=Be.length;oe<we;oe++)Me=Be[oe],E.format!==Ii?le!==null?Ve?H&&n.compressedTexSubImage2D(t.TEXTURE_2D,oe,0,0,Me.width,Me.height,le,Me.data):n.compressedTexImage2D(t.TEXTURE_2D,oe,ve,Me.width,Me.height,0,Me.data):et("WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()"):Ve?H&&n.texSubImage2D(t.TEXTURE_2D,oe,0,0,Me.width,Me.height,le,pe,Me.data):n.texImage2D(t.TEXTURE_2D,oe,ve,Me.width,Me.height,0,le,pe,Me.data)}else if(E.isDataArrayTexture)if(Ve){if(tt&&n.texStorage3D(t.TEXTURE_2D_ARRAY,Ee,ve,Q.width,Q.height,Q.depth),H)if(E.layerUpdates.size>0){const oe=ev(Q.width,Q.height,E.format,E.type);for(const we of E.layerUpdates){const De=Q.data.subarray(we*oe/Q.data.BYTES_PER_ELEMENT,(we+1)*oe/Q.data.BYTES_PER_ELEMENT);n.texSubImage3D(t.TEXTURE_2D_ARRAY,0,0,0,we,Q.width,Q.height,1,le,pe,De)}E.clearLayerUpdates()}else n.texSubImage3D(t.TEXTURE_2D_ARRAY,0,0,0,0,Q.width,Q.height,Q.depth,le,pe,Q.data)}else n.texImage3D(t.TEXTURE_2D_ARRAY,0,ve,Q.width,Q.height,Q.depth,0,le,pe,Q.data);else if(E.isData3DTexture)Ve?(tt&&n.texStorage3D(t.TEXTURE_3D,Ee,ve,Q.width,Q.height,Q.depth),H&&n.texSubImage3D(t.TEXTURE_3D,0,0,0,0,Q.width,Q.height,Q.depth,le,pe,Q.data)):n.texImage3D(t.TEXTURE_3D,0,ve,Q.width,Q.height,Q.depth,0,le,pe,Q.data);else if(E.isFramebufferTexture){if(tt)if(Ve)n.texStorage2D(t.TEXTURE_2D,Ee,ve,Q.width,Q.height);else{let oe=Q.width,we=Q.height;for(let De=0;De<Ee;De++)n.texImage2D(t.TEXTURE_2D,De,ve,oe,we,0,le,pe,null),oe>>=1,we>>=1}}else if(E.isHTMLTexture){if("texElementImage2D"in t){const oe=t.canvas;if(oe.hasAttribute("layoutsubtree")||oe.setAttribute("layoutsubtree","true"),Q.parentNode!==oe){oe.appendChild(Q),m.add(E),oe.onpaint=we=>{const De=we.changedElements;for(const ge of m)De.includes(ge.image)&&(ge.needsUpdate=!0)},oe.requestPaint();return}if(t.texElementImage2D.length===3)t.texElementImage2D(t.TEXTURE_2D,t.RGBA8,Q);else{const De=t.RGBA,ge=t.RGBA,We=t.UNSIGNED_BYTE;t.texElementImage2D(t.TEXTURE_2D,0,De,ge,We,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&&tt){const oe=Je(Be[0]);n.texStorage2D(t.TEXTURE_2D,Ee,ve,oe.width,oe.height)}for(let oe=0,we=Be.length;oe<we;oe++)Me=Be[oe],Ve?H&&n.texSubImage2D(t.TEXTURE_2D,oe,0,0,le,pe,Me):n.texImage2D(t.TEXTURE_2D,oe,ve,le,pe,Me);E.generateMipmaps=!1}else if(Ve){if(tt){const oe=Je(Q);n.texStorage2D(t.TEXTURE_2D,Ee,ve,oe.width,oe.height)}H&&n.texSubImage2D(t.TEXTURE_2D,0,0,0,le,pe,Q)}else n.texImage2D(t.TEXTURE_2D,0,ve,le,pe,Q);h(E)&&v(K),be.__version=ye.version,E.onUpdate&&E.onUpdate(E)}C.__version=E.version}function Te(C,E,V){if(E.image.length!==6)return;const K=ae(C,E),ne=E.source;n.bindTexture(t.TEXTURE_CUBE_MAP,C.__webglTexture,t.TEXTURE0+V);const ye=i.get(ne);if(ne.version!==ye.__version||K===!0){n.activeTexture(t.TEXTURE0+V);const be=mt.getPrimaries(mt.workingColorSpace),ie=E.colorSpace===is?null:mt.getPrimaries(E.colorSpace),Q=E.colorSpace===is||be===ie?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 le=E.isCompressedTexture||E.image[0].isCompressedTexture,pe=E.image[0]&&E.image[0].isDataTexture,ve=[];for(let ge=0;ge<6;ge++)!le&&!pe?ve[ge]=x(E.image[ge],!0,a.maxCubemapSize):ve[ge]=pe?E.image[ge].image:E.image[ge],ve[ge]=Qe(E,ve[ge]);const Me=ve[0],Be=s.convert(E.format,E.colorSpace),Ve=s.convert(E.type),tt=_(E.internalFormat,Be,Ve,E.normalized,E.colorSpace),H=E.isVideoTexture!==!0,Ee=ye.__version===void 0||K===!0,oe=ne.dataReady;let we=M(E,Me);Ne(t.TEXTURE_CUBE_MAP,E);let De;if(le){H&&Ee&&n.texStorage2D(t.TEXTURE_CUBE_MAP,we,tt,Me.width,Me.height);for(let ge=0;ge<6;ge++){De=ve[ge].mipmaps;for(let We=0;We<De.length;We++){const Fe=De[We];E.format!==Ii?Be!==null?H?oe&&n.compressedTexSubImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+ge,We,0,0,Fe.width,Fe.height,Be,Fe.data):n.compressedTexImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+ge,We,tt,Fe.width,Fe.height,0,Fe.data):et("WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()"):H?oe&&n.texSubImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+ge,We,0,0,Fe.width,Fe.height,Be,Ve,Fe.data):n.texImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+ge,We,tt,Fe.width,Fe.height,0,Be,Ve,Fe.data)}}}else{if(De=E.mipmaps,H&&Ee){De.length>0&&we++;const ge=Je(ve[0]);n.texStorage2D(t.TEXTURE_CUBE_MAP,we,tt,ge.width,ge.height)}for(let ge=0;ge<6;ge++)if(pe){H?oe&&n.texSubImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+ge,0,0,0,ve[ge].width,ve[ge].height,Be,Ve,ve[ge].data):n.texImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+ge,0,tt,ve[ge].width,ve[ge].height,0,Be,Ve,ve[ge].data);for(let We=0;We<De.length;We++){const bt=De[We].image[ge].image;H?oe&&n.texSubImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+ge,We+1,0,0,bt.width,bt.height,Be,Ve,bt.data):n.texImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+ge,We+1,tt,bt.width,bt.height,0,Be,Ve,bt.data)}}else{H?oe&&n.texSubImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+ge,0,0,0,Be,Ve,ve[ge]):n.texImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+ge,0,tt,Be,Ve,ve[ge]);for(let We=0;We<De.length;We++){const Fe=De[We];H?oe&&n.texSubImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+ge,We+1,0,0,Be,Ve,Fe.image[ge]):n.texImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+ge,We+1,tt,Be,Ve,Fe.image[ge])}}}h(E)&&v(t.TEXTURE_CUBE_MAP),ye.__version=ne.version,E.onUpdate&&E.onUpdate(E)}C.__version=E.version}function Ie(C,E,V,K,ne,ye){const be=s.convert(V.format,V.colorSpace),ie=s.convert(V.type),Q=_(V.internalFormat,be,ie,V.normalized,V.colorSpace),le=i.get(E),pe=i.get(V);if(pe.__renderTarget=E,!le.__hasExternalTextures){const ve=Math.max(1,E.width>>ye),Me=Math.max(1,E.height>>ye);ne===t.TEXTURE_3D||ne===t.TEXTURE_2D_ARRAY?n.texImage3D(ne,ye,Q,ve,Me,E.depth,0,be,ie,null):n.texImage2D(ne,ye,Q,ve,Me,0,be,ie,null)}n.bindFramebuffer(t.FRAMEBUFFER,C),Se(E)?o.framebufferTexture2DMultisampleEXT(t.FRAMEBUFFER,K,ne,pe.__webglTexture,0,qe(E)):(ne===t.TEXTURE_2D||ne>=t.TEXTURE_CUBE_MAP_POSITIVE_X&&ne<=t.TEXTURE_CUBE_MAP_NEGATIVE_Z)&&t.framebufferTexture2D(t.FRAMEBUFFER,K,ne,pe.__webglTexture,ye),n.bindFramebuffer(t.FRAMEBUFFER,null)}function it(C,E,V){if(t.bindRenderbuffer(t.RENDERBUFFER,C),E.depthBuffer){const K=E.depthTexture,ne=K&&K.isDepthTexture?K.type:null,ye=A(E.stencilBuffer,ne),be=E.stencilBuffer?t.DEPTH_STENCIL_ATTACHMENT:t.DEPTH_ATTACHMENT;Se(E)?o.renderbufferStorageMultisampleEXT(t.RENDERBUFFER,qe(E),ye,E.width,E.height):V?t.renderbufferStorageMultisample(t.RENDERBUFFER,qe(E),ye,E.width,E.height):t.renderbufferStorage(t.RENDERBUFFER,ye,E.width,E.height),t.framebufferRenderbuffer(t.FRAMEBUFFER,be,t.RENDERBUFFER,C)}else{const K=E.textures;for(let ne=0;ne<K.length;ne++){const ye=K[ne],be=s.convert(ye.format,ye.colorSpace),ie=s.convert(ye.type),Q=_(ye.internalFormat,be,ie,ye.normalized,ye.colorSpace);Se(E)?o.renderbufferStorageMultisampleEXT(t.RENDERBUFFER,qe(E),Q,E.width,E.height):V?t.renderbufferStorageMultisample(t.RENDERBUFFER,qe(E),Q,E.width,E.height):t.renderbufferStorage(t.RENDERBUFFER,Q,E.width,E.height)}}t.bindRenderbuffer(t.RENDERBUFFER,null)}function re(C,E,V){const K=E.isWebGLCubeRenderTarget===!0;if(n.bindFramebuffer(t.FRAMEBUFFER,C),!(E.depthTexture&&E.depthTexture.isDepthTexture))throw new Error("THREE.WebGLTextures: renderTarget.depthTexture must be an instance of THREE.DepthTexture.");const ne=i.get(E.depthTexture);if(ne.__renderTarget=E,(!ne.__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(ne.__webglInit===void 0&&(ne.__webglInit=!0,E.depthTexture.addEventListener("dispose",T)),ne.__webglTexture===void 0){ne.__webglTexture=t.createTexture(),n.bindTexture(t.TEXTURE_CUBE_MAP,ne.__webglTexture),Ne(t.TEXTURE_CUBE_MAP,E.depthTexture);const le=s.convert(E.depthTexture.format),pe=s.convert(E.depthTexture.type);let ve;E.depthTexture.format===za?ve=t.DEPTH_COMPONENT24:E.depthTexture.format===Vs&&(ve=t.DEPTH24_STENCIL8);for(let Me=0;Me<6;Me++)t.texImage2D(t.TEXTURE_CUBE_MAP_POSITIVE_X+Me,0,ve,E.width,E.height,0,le,pe,null)}}else W(E.depthTexture,0);const ye=ne.__webglTexture,be=qe(E),ie=K?t.TEXTURE_CUBE_MAP_POSITIVE_X+V:t.TEXTURE_2D,Q=E.depthTexture.format===Vs?t.DEPTH_STENCIL_ATTACHMENT:t.DEPTH_ATTACHMENT;if(E.depthTexture.format===za)Se(E)?o.framebufferTexture2DMultisampleEXT(t.FRAMEBUFFER,Q,ie,ye,0,be):t.framebufferTexture2D(t.FRAMEBUFFER,Q,ie,ye,0);else if(E.depthTexture.format===Vs)Se(E)?o.framebufferTexture2DMultisampleEXT(t.FRAMEBUFFER,Q,ie,ye,0,be):t.framebufferTexture2D(t.FRAMEBUFFER,Q,ie,ye,0);else throw new Error("THREE.WebGLTextures: Unknown depthTexture format.")}function me(C){const E=i.get(C),V=C.isWebGLCubeRenderTarget===!0;if(E.__boundDepthTexture!==C.depthTexture){const K=C.depthTexture;if(E.__depthDisposeCallback&&E.__depthDisposeCallback(),K){const ne=()=>{delete E.__boundDepthTexture,delete E.__depthDisposeCallback,K.removeEventListener("dispose",ne)};K.addEventListener("dispose",ne),E.__depthDisposeCallback=ne}E.__boundDepthTexture=K}if(C.depthTexture&&!E.__autoAllocateDepthBuffer)if(V)for(let K=0;K<6;K++)re(E.__webglFramebuffer[K],C,K);else{const K=C.texture.mipmaps;K&&K.length>0?re(E.__webglFramebuffer[0],C,0):re(E.__webglFramebuffer,C,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(),it(E.__webglDepthbuffer[K],C,!1);else{const ne=C.stencilBuffer?t.DEPTH_STENCIL_ATTACHMENT:t.DEPTH_ATTACHMENT,ye=E.__webglDepthbuffer[K];t.bindRenderbuffer(t.RENDERBUFFER,ye),t.framebufferRenderbuffer(t.FRAMEBUFFER,ne,t.RENDERBUFFER,ye)}}else{const K=C.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(),it(E.__webglDepthbuffer,C,!1);else{const ne=C.stencilBuffer?t.DEPTH_STENCIL_ATTACHMENT:t.DEPTH_ATTACHMENT,ye=E.__webglDepthbuffer;t.bindRenderbuffer(t.RENDERBUFFER,ye),t.framebufferRenderbuffer(t.FRAMEBUFFER,ne,t.RENDERBUFFER,ye)}}n.bindFramebuffer(t.FRAMEBUFFER,null)}function Ae(C,E,V){const K=i.get(C);E!==void 0&&Ie(K.__webglFramebuffer,C,C.texture,t.COLOR_ATTACHMENT0,t.TEXTURE_2D,0),V!==void 0&&me(C)}function se(C){const E=C.texture,V=i.get(C),K=i.get(E);C.addEventListener("dispose",S);const ne=C.textures,ye=C.isWebGLCubeRenderTarget===!0,be=ne.length>1;if(be||(K.__webglTexture===void 0&&(K.__webglTexture=t.createTexture()),K.__version=E.version,l.memory.textures++),ye){V.__webglFramebuffer=[];for(let ie=0;ie<6;ie++)if(E.mipmaps&&E.mipmaps.length>0){V.__webglFramebuffer[ie]=[];for(let Q=0;Q<E.mipmaps.length;Q++)V.__webglFramebuffer[ie][Q]=t.createFramebuffer()}else V.__webglFramebuffer[ie]=t.createFramebuffer()}else{if(E.mipmaps&&E.mipmaps.length>0){V.__webglFramebuffer=[];for(let ie=0;ie<E.mipmaps.length;ie++)V.__webglFramebuffer[ie]=t.createFramebuffer()}else V.__webglFramebuffer=t.createFramebuffer();if(be)for(let ie=0,Q=ne.length;ie<Q;ie++){const le=i.get(ne[ie]);le.__webglTexture===void 0&&(le.__webglTexture=t.createTexture(),l.memory.textures++)}if(C.samples>0&&Se(C)===!1){V.__webglMultisampledFramebuffer=t.createFramebuffer(),V.__webglColorRenderbuffer=[],n.bindFramebuffer(t.FRAMEBUFFER,V.__webglMultisampledFramebuffer);for(let ie=0;ie<ne.length;ie++){const Q=ne[ie];V.__webglColorRenderbuffer[ie]=t.createRenderbuffer(),t.bindRenderbuffer(t.RENDERBUFFER,V.__webglColorRenderbuffer[ie]);const le=s.convert(Q.format,Q.colorSpace),pe=s.convert(Q.type),ve=_(Q.internalFormat,le,pe,Q.normalized,Q.colorSpace,C.isXRRenderTarget===!0),Me=qe(C);t.renderbufferStorageMultisample(t.RENDERBUFFER,Me,ve,C.width,C.height),t.framebufferRenderbuffer(t.FRAMEBUFFER,t.COLOR_ATTACHMENT0+ie,t.RENDERBUFFER,V.__webglColorRenderbuffer[ie])}t.bindRenderbuffer(t.RENDERBUFFER,null),C.depthBuffer&&(V.__webglDepthRenderbuffer=t.createRenderbuffer(),it(V.__webglDepthRenderbuffer,C,!0)),n.bindFramebuffer(t.FRAMEBUFFER,null)}}if(ye){n.bindTexture(t.TEXTURE_CUBE_MAP,K.__webglTexture),Ne(t.TEXTURE_CUBE_MAP,E);for(let ie=0;ie<6;ie++)if(E.mipmaps&&E.mipmaps.length>0)for(let Q=0;Q<E.mipmaps.length;Q++)Ie(V.__webglFramebuffer[ie][Q],C,E,t.COLOR_ATTACHMENT0,t.TEXTURE_CUBE_MAP_POSITIVE_X+ie,Q);else Ie(V.__webglFramebuffer[ie],C,E,t.COLOR_ATTACHMENT0,t.TEXTURE_CUBE_MAP_POSITIVE_X+ie,0);h(E)&&v(t.TEXTURE_CUBE_MAP),n.unbindTexture()}else if(be){for(let ie=0,Q=ne.length;ie<Q;ie++){const le=ne[ie],pe=i.get(le);let ve=t.TEXTURE_2D;(C.isWebGL3DRenderTarget||C.isWebGLArrayRenderTarget)&&(ve=C.isWebGL3DRenderTarget?t.TEXTURE_3D:t.TEXTURE_2D_ARRAY),n.bindTexture(ve,pe.__webglTexture),Ne(ve,le),Ie(V.__webglFramebuffer,C,le,t.COLOR_ATTACHMENT0+ie,ve,0),h(le)&&v(ve)}n.unbindTexture()}else{let ie=t.TEXTURE_2D;if((C.isWebGL3DRenderTarget||C.isWebGLArrayRenderTarget)&&(ie=C.isWebGL3DRenderTarget?t.TEXTURE_3D:t.TEXTURE_2D_ARRAY),n.bindTexture(ie,K.__webglTexture),Ne(ie,E),E.mipmaps&&E.mipmaps.length>0)for(let Q=0;Q<E.mipmaps.length;Q++)Ie(V.__webglFramebuffer[Q],C,E,t.COLOR_ATTACHMENT0,ie,Q);else Ie(V.__webglFramebuffer,C,E,t.COLOR_ATTACHMENT0,ie,0);h(E)&&v(ie),n.unbindTexture()}C.depthBuffer&&me(C)}function Le(C){const E=C.textures;for(let V=0,K=E.length;V<K;V++){const ne=E[V];if(h(ne)){const ye=y(C),be=i.get(ne).__webglTexture;n.bindTexture(ye,be),v(ye),n.unbindTexture()}}}const ke=[],Ue=[];function Re(C){if(C.samples>0){if(Se(C)===!1){const E=C.textures,V=C.width,K=C.height;let ne=t.COLOR_BUFFER_BIT;const ye=C.stencilBuffer?t.DEPTH_STENCIL_ATTACHMENT:t.DEPTH_ATTACHMENT,be=i.get(C),ie=E.length>1;if(ie)for(let le=0;le<E.length;le++)n.bindFramebuffer(t.FRAMEBUFFER,be.__webglMultisampledFramebuffer),t.framebufferRenderbuffer(t.FRAMEBUFFER,t.COLOR_ATTACHMENT0+le,t.RENDERBUFFER,null),n.bindFramebuffer(t.FRAMEBUFFER,be.__webglFramebuffer),t.framebufferTexture2D(t.DRAW_FRAMEBUFFER,t.COLOR_ATTACHMENT0+le,t.TEXTURE_2D,null,0);n.bindFramebuffer(t.READ_FRAMEBUFFER,be.__webglMultisampledFramebuffer);const Q=C.texture.mipmaps;Q&&Q.length>0?n.bindFramebuffer(t.DRAW_FRAMEBUFFER,be.__webglFramebuffer[0]):n.bindFramebuffer(t.DRAW_FRAMEBUFFER,be.__webglFramebuffer);for(let le=0;le<E.length;le++){if(C.resolveDepthBuffer&&(C.depthBuffer&&(ne|=t.DEPTH_BUFFER_BIT),C.stencilBuffer&&C.resolveStencilBuffer&&(ne|=t.STENCIL_BUFFER_BIT)),ie){t.framebufferRenderbuffer(t.READ_FRAMEBUFFER,t.COLOR_ATTACHMENT0,t.RENDERBUFFER,be.__webglColorRenderbuffer[le]);const pe=i.get(E[le]).__webglTexture;t.framebufferTexture2D(t.DRAW_FRAMEBUFFER,t.COLOR_ATTACHMENT0,t.TEXTURE_2D,pe,0)}t.blitFramebuffer(0,0,V,K,0,0,V,K,ne,t.NEAREST),c===!0&&(ke.length=0,Ue.length=0,ke.push(t.COLOR_ATTACHMENT0+le),C.depthBuffer&&C.resolveDepthBuffer===!1&&(ke.push(ye),Ue.push(ye),t.invalidateFramebuffer(t.DRAW_FRAMEBUFFER,Ue)),t.invalidateFramebuffer(t.READ_FRAMEBUFFER,ke))}if(n.bindFramebuffer(t.READ_FRAMEBUFFER,null),n.bindFramebuffer(t.DRAW_FRAMEBUFFER,null),ie)for(let le=0;le<E.length;le++){n.bindFramebuffer(t.FRAMEBUFFER,be.__webglMultisampledFramebuffer),t.framebufferRenderbuffer(t.FRAMEBUFFER,t.COLOR_ATTACHMENT0+le,t.RENDERBUFFER,be.__webglColorRenderbuffer[le]);const pe=i.get(E[le]).__webglTexture;n.bindFramebuffer(t.FRAMEBUFFER,be.__webglFramebuffer),t.framebufferTexture2D(t.DRAW_FRAMEBUFFER,t.COLOR_ATTACHMENT0+le,t.TEXTURE_2D,pe,0)}n.bindFramebuffer(t.DRAW_FRAMEBUFFER,be.__webglMultisampledFramebuffer)}else if(C.depthBuffer&&C.resolveDepthBuffer===!1&&c){const E=C.stencilBuffer?t.DEPTH_STENCIL_ATTACHMENT:t.DEPTH_ATTACHMENT;t.invalidateFramebuffer(t.DRAW_FRAMEBUFFER,[E])}}}function qe(C){return Math.min(a.maxSamples,C.samples)}function Se(C){const E=i.get(C);return C.samples>0&&e.has("WEBGL_multisampled_render_to_texture")===!0&&E.__useRenderToTexture!==!1}function I(C){const E=l.render.frame;f.get(C)!==E&&(f.set(C,E),C.update())}function Qe(C,E){const V=C.colorSpace,K=C.format,ne=C.type;return C.isCompressedTexture===!0||C.isVideoTexture===!0||V!==ud&&V!==is&&(mt.getTransfer(V)===Nt?(K!==Ii||ne!==Ei)&&et("WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType."):Mt("WebGLTextures: Unsupported texture color space:",V)),E}function Je(C){return typeof HTMLImageElement<"u"&&C instanceof HTMLImageElement?(u.width=C.naturalWidth||C.width,u.height=C.naturalHeight||C.height):typeof VideoFrame<"u"&&C instanceof VideoFrame?(u.width=C.displayWidth,u.height=C.displayHeight):(u.width=C.width,u.height=C.height),u}this.allocateTextureUnit=B,this.resetTextureUnits=q,this.getTextureUnits=P,this.setTextureUnits=O,this.setTexture2D=W,this.setTexture2DArray=Y,this.setTexture3D=he,this.setTextureCube=_e,this.rebindTextures=Ae,this.setupRenderTarget=se,this.updateRenderTargetMipmap=Le,this.updateMultisampleRenderTarget=Re,this.setupDepthRenderbuffer=me,this.setupFrameBufferTexture=Ie,this.useMultisampledRTT=Se,this.isReversedDepthBuffer=function(){return n.buffers.depth.getReversed()}}function X3(t,e){function n(i,a=is){let s;const l=mt.getTransfer(a);if(i===Ei)return t.UNSIGNED_BYTE;if(i===gg)return t.UNSIGNED_SHORT_4_4_4_4;if(i===xg)return t.UNSIGNED_SHORT_5_5_5_1;if(i===vS)return t.UNSIGNED_INT_5_9_9_9_REV;if(i===_S)return t.UNSIGNED_INT_10F_11F_11F_REV;if(i===gS)return t.BYTE;if(i===xS)return t.SHORT;if(i===qo)return t.UNSIGNED_SHORT;if(i===mg)return t.INT;if(i===aa)return t.UNSIGNED_INT;if(i===ki)return t.FLOAT;if(i===Pa)return t.HALF_FLOAT;if(i===yS)return t.ALPHA;if(i===bS)return t.RGB;if(i===Ii)return t.RGBA;if(i===za)return t.DEPTH_COMPONENT;if(i===Vs)return t.DEPTH_STENCIL;if(i===vg)return t.RED;if(i===_g)return t.RED_INTEGER;if(i===nr)return t.RG;if(i===yg)return t.RG_INTEGER;if(i===bg)return t.RGBA_INTEGER;if(i===Au||i===wu||i===Nu||i===Cu)if(l===Nt)if(s=e.get("WEBGL_compressed_texture_s3tc_srgb"),s!==null){if(i===Au)return s.COMPRESSED_SRGB_S3TC_DXT1_EXT;if(i===wu)return s.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;if(i===Nu)return s.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;if(i===Cu)return s.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}else return null;else if(s=e.get("WEBGL_compressed_texture_s3tc"),s!==null){if(i===Au)return s.COMPRESSED_RGB_S3TC_DXT1_EXT;if(i===wu)return s.COMPRESSED_RGBA_S3TC_DXT1_EXT;if(i===Nu)return s.COMPRESSED_RGBA_S3TC_DXT3_EXT;if(i===Cu)return s.COMPRESSED_RGBA_S3TC_DXT5_EXT}else return null;if(i===Np||i===Cp||i===Rp||i===Dp)if(s=e.get("WEBGL_compressed_texture_pvrtc"),s!==null){if(i===Np)return s.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;if(i===Cp)return s.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;if(i===Rp)return s.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;if(i===Dp)return s.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG}else return null;if(i===Lp||i===Up||i===Op||i===Pp||i===zp||i===od||i===kp)if(s=e.get("WEBGL_compressed_texture_etc"),s!==null){if(i===Lp||i===Up)return l===Nt?s.COMPRESSED_SRGB8_ETC2:s.COMPRESSED_RGB8_ETC2;if(i===Op)return l===Nt?s.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:s.COMPRESSED_RGBA8_ETC2_EAC;if(i===Pp)return s.COMPRESSED_R11_EAC;if(i===zp)return s.COMPRESSED_SIGNED_R11_EAC;if(i===od)return s.COMPRESSED_RG11_EAC;if(i===kp)return s.COMPRESSED_SIGNED_RG11_EAC}else return null;if(i===Ip||i===Bp||i===jp||i===Fp||i===Hp||i===Gp||i===Vp||i===Wp||i===Xp||i===qp||i===Yp||i===Kp||i===$p||i===Zp)if(s=e.get("WEBGL_compressed_texture_astc"),s!==null){if(i===Ip)return l===Nt?s.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:s.COMPRESSED_RGBA_ASTC_4x4_KHR;if(i===Bp)return l===Nt?s.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:s.COMPRESSED_RGBA_ASTC_5x4_KHR;if(i===jp)return l===Nt?s.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:s.COMPRESSED_RGBA_ASTC_5x5_KHR;if(i===Fp)return l===Nt?s.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:s.COMPRESSED_RGBA_ASTC_6x5_KHR;if(i===Hp)return l===Nt?s.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:s.COMPRESSED_RGBA_ASTC_6x6_KHR;if(i===Gp)return l===Nt?s.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:s.COMPRESSED_RGBA_ASTC_8x5_KHR;if(i===Vp)return l===Nt?s.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:s.COMPRESSED_RGBA_ASTC_8x6_KHR;if(i===Wp)return l===Nt?s.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:s.COMPRESSED_RGBA_ASTC_8x8_KHR;if(i===Xp)return l===Nt?s.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:s.COMPRESSED_RGBA_ASTC_10x5_KHR;if(i===qp)return l===Nt?s.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:s.COMPRESSED_RGBA_ASTC_10x6_KHR;if(i===Yp)return l===Nt?s.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:s.COMPRESSED_RGBA_ASTC_10x8_KHR;if(i===Kp)return l===Nt?s.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:s.COMPRESSED_RGBA_ASTC_10x10_KHR;if(i===$p)return l===Nt?s.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:s.COMPRESSED_RGBA_ASTC_12x10_KHR;if(i===Zp)return l===Nt?s.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:s.COMPRESSED_RGBA_ASTC_12x12_KHR}else return null;if(i===Qp||i===Jp||i===em)if(s=e.get("EXT_texture_compression_bptc"),s!==null){if(i===Qp)return l===Nt?s.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT:s.COMPRESSED_RGBA_BPTC_UNORM_EXT;if(i===Jp)return s.COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT;if(i===em)return s.COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT}else return null;if(i===tm||i===nm||i===cd||i===im)if(s=e.get("EXT_texture_compression_rgtc"),s!==null){if(i===tm)return s.COMPRESSED_RED_RGTC1_EXT;if(i===nm)return s.COMPRESSED_SIGNED_RED_RGTC1_EXT;if(i===cd)return s.COMPRESSED_RED_GREEN_RGTC2_EXT;if(i===im)return s.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT}else return null;return i===Yo?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 ); + +}`,Y3=` +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 K3{constructor(){this.texture=null,this.mesh=null,this.depthNear=0,this.depthFar=0}init(e,n){if(this.texture===null){const i=new US(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 Yn({vertexShader:q3,fragmentShader:Y3,uniforms:{depthColor:{value:this.texture},depthWidth:{value:n.z},depthHeight:{value:n.w}}});this.mesh=new Ri(new Fd(20,20),i)}return this.mesh}reset(){this.texture=null,this.mesh=null}getDepthTexture(){return this.texture}}class $3 extends dr{constructor(e,n){super();const i=this;let a=null,s=1,l=null,o="local-floor",c=1,u=null,f=null,m=null,d=null,p=null,g=null;const b=typeof XRWebGLBinding<"u",x=new K3,h={},v=n.getContextAttributes();let y=null,_=null;const A=[],M=[],T=new wt;let S=null;const w=new zi;w.viewport=new Zt;const R=new zi;R.viewport=new Zt;const D=[w,R],L=new rw;let q=null,P=null;this.cameraAutoUpdate=!0,this.enabled=!1,this.isPresenting=!1,this.getController=function(ae){let j=A[ae];return j===void 0&&(j=new If,A[ae]=j),j.getTargetRaySpace()},this.getControllerGrip=function(ae){let j=A[ae];return j===void 0&&(j=new If,A[ae]=j),j.getGripSpace()},this.getHand=function(ae){let j=A[ae];return j===void 0&&(j=new If,A[ae]=j),j.getHandSpace()};function O(ae){const j=M.indexOf(ae.inputSource);if(j===-1)return;const ee=A[j];ee!==void 0&&(ee.update(ae.inputSource,ae.frame,u||l),ee.dispatchEvent({type:ae.type,data:ae.inputSource}))}function B(){a.removeEventListener("select",O),a.removeEventListener("selectstart",O),a.removeEventListener("selectend",O),a.removeEventListener("squeeze",O),a.removeEventListener("squeezestart",O),a.removeEventListener("squeezeend",O),a.removeEventListener("end",B),a.removeEventListener("inputsourceschange",k);for(let ae=0;ae<A.length;ae++){const j=M[ae];j!==null&&(M[ae]=null,A[ae].disconnect(j))}q=null,P=null,x.reset();for(const ae in h)delete h[ae];e.setRenderTarget(y),p=null,d=null,m=null,a=null,_=null,Ne.stop(),i.isPresenting=!1,e.setPixelRatio(S),e.setSize(T.width,T.height,!1),i.dispatchEvent({type:"sessionend"})}this.setFramebufferScaleFactor=function(ae){s=ae,i.isPresenting===!0&&et("WebXRManager: Cannot change framebuffer scale while presenting.")},this.setReferenceSpaceType=function(ae){o=ae,i.isPresenting===!0&&et("WebXRManager: Cannot change reference space type while presenting.")},this.getReferenceSpace=function(){return u||l},this.setReferenceSpace=function(ae){u=ae},this.getBaseLayer=function(){return d!==null?d:p},this.getBinding=function(){return m===null&&b&&(m=new XRWebGLBinding(a,n)),m},this.getFrame=function(){return g},this.getSession=function(){return a},this.setSession=async function(ae){if(a=ae,a!==null){if(y=e.getRenderTarget(),a.addEventListener("select",O),a.addEventListener("selectstart",O),a.addEventListener("selectend",O),a.addEventListener("squeeze",O),a.addEventListener("squeezestart",O),a.addEventListener("squeezeend",O),a.addEventListener("end",B),a.addEventListener("inputsourceschange",k),v.xrCompatible!==!0&&await n.makeXRCompatible(),S=e.getPixelRatio(),e.getSize(T),b&&"createProjectionLayer"in XRWebGLBinding.prototype){let ee=null,xe=null,Te=null;v.depth&&(Te=v.stencil?n.DEPTH24_STENCIL8:n.DEPTH_COMPONENT24,ee=v.stencil?Vs:za,xe=v.stencil?Yo:aa);const Ie={colorFormat:n.RGBA8,depthFormat:Te,scaleFactor:s};m=this.getBinding(),d=m.createProjectionLayer(Ie),a.updateRenderState({layers:[d]}),e.setPixelRatio(1),e.setSize(d.textureWidth,d.textureHeight,!1),_=new ia(d.textureWidth,d.textureHeight,{format:Ii,type:Ei,depthTexture:new El(d.textureWidth,d.textureHeight,xe,void 0,void 0,void 0,void 0,void 0,void 0,ee),stencilBuffer:v.stencil,colorSpace:e.outputColorSpace,samples:v.antialias?4:0,resolveDepthBuffer:d.ignoreDepthValues===!1,resolveStencilBuffer:d.ignoreDepthValues===!1})}else{const ee={antialias:v.antialias,alpha:!0,depth:v.depth,stencil:v.stencil,framebufferScaleFactor:s};p=new XRWebGLLayer(a,n,ee),a.updateRenderState({baseLayer:p}),e.setPixelRatio(1),e.setSize(p.framebufferWidth,p.framebufferHeight,!1),_=new ia(p.framebufferWidth,p.framebufferHeight,{format:Ii,type:Ei,colorSpace:e.outputColorSpace,stencilBuffer:v.stencil,resolveDepthBuffer:p.ignoreDepthValues===!1,resolveStencilBuffer:p.ignoreDepthValues===!1})}_.isXRRenderTarget=!0,this.setFoveation(c),u=null,l=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 x.getDepthTexture()};function k(ae){for(let j=0;j<ae.removed.length;j++){const ee=ae.removed[j],xe=M.indexOf(ee);xe>=0&&(M[xe]=null,A[xe].disconnect(ee))}for(let j=0;j<ae.added.length;j++){const ee=ae.added[j];let xe=M.indexOf(ee);if(xe===-1){for(let Ie=0;Ie<A.length;Ie++)if(Ie>=M.length){M.push(ee),xe=Ie;break}else if(M[Ie]===null){M[Ie]=ee,xe=Ie;break}if(xe===-1)break}const Te=A[xe];Te&&Te.connect(ee)}}const W=new Z,Y=new Z;function he(ae,j,ee){W.setFromMatrixPosition(j.matrixWorld),Y.setFromMatrixPosition(ee.matrixWorld);const xe=W.distanceTo(Y),Te=j.projectionMatrix.elements,Ie=ee.projectionMatrix.elements,it=Te[14]/(Te[10]-1),re=Te[14]/(Te[10]+1),me=(Te[9]+1)/Te[5],Ae=(Te[9]-1)/Te[5],se=(Te[8]-1)/Te[0],Le=(Ie[8]+1)/Ie[0],ke=it*se,Ue=it*Le,Re=xe/(-se+Le),qe=Re*-se;if(j.matrixWorld.decompose(ae.position,ae.quaternion,ae.scale),ae.translateX(qe),ae.translateZ(Re),ae.matrixWorld.compose(ae.position,ae.quaternion,ae.scale),ae.matrixWorldInverse.copy(ae.matrixWorld).invert(),Te[10]===-1)ae.projectionMatrix.copy(j.projectionMatrix),ae.projectionMatrixInverse.copy(j.projectionMatrixInverse);else{const Se=it+Re,I=re+Re,Qe=ke-qe,Je=Ue+(xe-qe),C=me*re/I*Se,E=Ae*re/I*Se;ae.projectionMatrix.makePerspective(Qe,Je,C,E,Se,I),ae.projectionMatrixInverse.copy(ae.projectionMatrix).invert()}}function _e(ae,j){j===null?ae.matrixWorld.copy(ae.matrix):ae.matrixWorld.multiplyMatrices(j.matrixWorld,ae.matrix),ae.matrixWorldInverse.copy(ae.matrixWorld).invert()}this.updateCamera=function(ae){if(a===null)return;let j=ae.near,ee=ae.far;x.texture!==null&&(x.depthNear>0&&(j=x.depthNear),x.depthFar>0&&(ee=x.depthFar)),L.near=R.near=w.near=j,L.far=R.far=w.far=ee,(q!==L.near||P!==L.far)&&(a.updateRenderState({depthNear:L.near,depthFar:L.far}),q=L.near,P=L.far),L.layers.mask=ae.layers.mask|6,w.layers.mask=L.layers.mask&-5,R.layers.mask=L.layers.mask&-3;const xe=ae.parent,Te=L.cameras;_e(L,xe);for(let Ie=0;Ie<Te.length;Ie++)_e(Te[Ie],xe);Te.length===2?he(L,w,R):L.projectionMatrix.copy(w.projectionMatrix),Ce(ae,L,xe)};function Ce(ae,j,ee){ee===null?ae.matrix.copy(j.matrixWorld):(ae.matrix.copy(ee.matrixWorld),ae.matrix.invert(),ae.matrix.multiply(j.matrixWorld)),ae.matrix.decompose(ae.position,ae.quaternion,ae.scale),ae.updateMatrixWorld(!0),ae.projectionMatrix.copy(j.projectionMatrix),ae.projectionMatrixInverse.copy(j.projectionMatrixInverse),ae.isPerspectiveCamera&&(ae.fov=am*2*Math.atan(1/ae.projectionMatrix.elements[5]),ae.zoom=1)}this.getCamera=function(){return L},this.getFoveation=function(){if(!(d===null&&p===null))return c},this.setFoveation=function(ae){c=ae,d!==null&&(d.fixedFoveation=ae),p!==null&&p.fixedFoveation!==void 0&&(p.fixedFoveation=ae)},this.hasDepthSensing=function(){return x.texture!==null},this.getDepthSensingMesh=function(){return x.getMesh(L)},this.getCameraTexture=function(ae){return h[ae]};let X=null;function de(ae,j){if(f=j.getViewerPose(u||l),g=j,f!==null){const ee=f.views;p!==null&&(e.setRenderTargetFramebuffer(_,p.framebuffer),e.setRenderTarget(_));let xe=!1;ee.length!==L.cameras.length&&(L.cameras.length=0,xe=!0);for(let re=0;re<ee.length;re++){const me=ee[re];let Ae=null;if(p!==null)Ae=p.getViewport(me);else{const Le=m.getViewSubImage(d,me);Ae=Le.viewport,re===0&&(e.setRenderTargetTextures(_,Le.colorTexture,Le.depthStencilTexture),e.setRenderTarget(_))}let se=D[re];se===void 0&&(se=new zi,se.layers.enable(re),se.viewport=new Zt,D[re]=se),se.matrix.fromArray(me.transform.matrix),se.matrix.decompose(se.position,se.quaternion,se.scale),se.projectionMatrix.fromArray(me.projectionMatrix),se.projectionMatrixInverse.copy(se.projectionMatrix).invert(),se.viewport.set(Ae.x,Ae.y,Ae.width,Ae.height),re===0&&(L.matrix.copy(se.matrix),L.matrix.decompose(L.position,L.quaternion,L.scale)),xe===!0&&L.cameras.push(se)}const Te=a.enabledFeatures;if(Te&&Te.includes("depth-sensing")&&a.depthUsage=="gpu-optimized"&&b){m=i.getBinding();const re=m.getDepthInformation(ee[0]);re&&re.isValid&&re.texture&&x.init(re,a.renderState)}if(Te&&Te.includes("camera-access")&&b){e.state.unbindTexture(),m=i.getBinding();for(let re=0;re<ee.length;re++){const me=ee[re].camera;if(me){let Ae=h[me];Ae||(Ae=new US,h[me]=Ae);const se=m.getCameraImage(me);Ae.sourceTexture=se}}}}for(let ee=0;ee<A.length;ee++){const xe=M[ee],Te=A[ee];xe!==null&&Te!==void 0&&Te.update(xe,j,u||l)}X&&X(ae,j),j.detectedPlanes&&i.dispatchEvent({type:"planesdetected",data:j}),g=null}const Ne=new zS;Ne.setAnimationLoop(de),this.setAnimationLoop=function(ae){X=ae},this.dispose=function(){}}}const Z3=new Bt,GS=new at;GS.set(-1,0,0,0,1,0,0,0,1);function Q3(t,e){function n(x,h){x.matrixAutoUpdate===!0&&x.updateMatrix(),h.value.copy(x.matrix)}function i(x,h){h.color.getRGB(x.fogColor.value,OS(t)),h.isFog?(x.fogNear.value=h.near,x.fogFar.value=h.far):h.isFogExp2&&(x.fogDensity.value=h.density)}function a(x,h,v,y,_){h.isNodeMaterial?h.uniformsNeedUpdate=!1:h.isMeshBasicMaterial?s(x,h):h.isMeshLambertMaterial?(s(x,h),h.envMap&&(x.envMapIntensity.value=h.envMapIntensity)):h.isMeshToonMaterial?(s(x,h),m(x,h)):h.isMeshPhongMaterial?(s(x,h),f(x,h),h.envMap&&(x.envMapIntensity.value=h.envMapIntensity)):h.isMeshStandardMaterial?(s(x,h),d(x,h),h.isMeshPhysicalMaterial&&p(x,h,_)):h.isMeshMatcapMaterial?(s(x,h),g(x,h)):h.isMeshDepthMaterial?s(x,h):h.isMeshDistanceMaterial?(s(x,h),b(x,h)):h.isMeshNormalMaterial?s(x,h):h.isLineBasicMaterial?(l(x,h),h.isLineDashedMaterial&&o(x,h)):h.isPointsMaterial?c(x,h,v,y):h.isSpriteMaterial?u(x,h):h.isShadowMaterial?(x.color.value.copy(h.color),x.opacity.value=h.opacity):h.isShaderMaterial&&(h.uniformsNeedUpdate=!1)}function s(x,h){x.opacity.value=h.opacity,h.color&&x.diffuse.value.copy(h.color),h.emissive&&x.emissive.value.copy(h.emissive).multiplyScalar(h.emissiveIntensity),h.map&&(x.map.value=h.map,n(h.map,x.mapTransform)),h.alphaMap&&(x.alphaMap.value=h.alphaMap,n(h.alphaMap,x.alphaMapTransform)),h.bumpMap&&(x.bumpMap.value=h.bumpMap,n(h.bumpMap,x.bumpMapTransform),x.bumpScale.value=h.bumpScale,h.side===$n&&(x.bumpScale.value*=-1)),h.normalMap&&(x.normalMap.value=h.normalMap,n(h.normalMap,x.normalMapTransform),x.normalScale.value.copy(h.normalScale),h.side===$n&&x.normalScale.value.negate()),h.displacementMap&&(x.displacementMap.value=h.displacementMap,n(h.displacementMap,x.displacementMapTransform),x.displacementScale.value=h.displacementScale,x.displacementBias.value=h.displacementBias),h.emissiveMap&&(x.emissiveMap.value=h.emissiveMap,n(h.emissiveMap,x.emissiveMapTransform)),h.specularMap&&(x.specularMap.value=h.specularMap,n(h.specularMap,x.specularMapTransform)),h.alphaTest>0&&(x.alphaTest.value=h.alphaTest);const v=e.get(h),y=v.envMap,_=v.envMapRotation;y&&(x.envMap.value=y,x.envMapRotation.value.setFromMatrix4(Z3.makeRotationFromEuler(_)).transpose(),y.isCubeTexture&&y.isRenderTargetTexture===!1&&x.envMapRotation.value.premultiply(GS),x.reflectivity.value=h.reflectivity,x.ior.value=h.ior,x.refractionRatio.value=h.refractionRatio),h.lightMap&&(x.lightMap.value=h.lightMap,x.lightMapIntensity.value=h.lightMapIntensity,n(h.lightMap,x.lightMapTransform)),h.aoMap&&(x.aoMap.value=h.aoMap,x.aoMapIntensity.value=h.aoMapIntensity,n(h.aoMap,x.aoMapTransform))}function l(x,h){x.diffuse.value.copy(h.color),x.opacity.value=h.opacity,h.map&&(x.map.value=h.map,n(h.map,x.mapTransform))}function o(x,h){x.dashSize.value=h.dashSize,x.totalSize.value=h.dashSize+h.gapSize,x.scale.value=h.scale}function c(x,h,v,y){x.diffuse.value.copy(h.color),x.opacity.value=h.opacity,x.size.value=h.size*v,x.scale.value=y*.5,h.map&&(x.map.value=h.map,n(h.map,x.uvTransform)),h.alphaMap&&(x.alphaMap.value=h.alphaMap,n(h.alphaMap,x.alphaMapTransform)),h.alphaTest>0&&(x.alphaTest.value=h.alphaTest)}function u(x,h){x.diffuse.value.copy(h.color),x.opacity.value=h.opacity,x.rotation.value=h.rotation,h.map&&(x.map.value=h.map,n(h.map,x.mapTransform)),h.alphaMap&&(x.alphaMap.value=h.alphaMap,n(h.alphaMap,x.alphaMapTransform)),h.alphaTest>0&&(x.alphaTest.value=h.alphaTest)}function f(x,h){x.specular.value.copy(h.specular),x.shininess.value=Math.max(h.shininess,1e-4)}function m(x,h){h.gradientMap&&(x.gradientMap.value=h.gradientMap)}function d(x,h){x.metalness.value=h.metalness,h.metalnessMap&&(x.metalnessMap.value=h.metalnessMap,n(h.metalnessMap,x.metalnessMapTransform)),x.roughness.value=h.roughness,h.roughnessMap&&(x.roughnessMap.value=h.roughnessMap,n(h.roughnessMap,x.roughnessMapTransform)),h.envMap&&(x.envMapIntensity.value=h.envMapIntensity)}function p(x,h,v){x.ior.value=h.ior,h.sheen>0&&(x.sheenColor.value.copy(h.sheenColor).multiplyScalar(h.sheen),x.sheenRoughness.value=h.sheenRoughness,h.sheenColorMap&&(x.sheenColorMap.value=h.sheenColorMap,n(h.sheenColorMap,x.sheenColorMapTransform)),h.sheenRoughnessMap&&(x.sheenRoughnessMap.value=h.sheenRoughnessMap,n(h.sheenRoughnessMap,x.sheenRoughnessMapTransform))),h.clearcoat>0&&(x.clearcoat.value=h.clearcoat,x.clearcoatRoughness.value=h.clearcoatRoughness,h.clearcoatMap&&(x.clearcoatMap.value=h.clearcoatMap,n(h.clearcoatMap,x.clearcoatMapTransform)),h.clearcoatRoughnessMap&&(x.clearcoatRoughnessMap.value=h.clearcoatRoughnessMap,n(h.clearcoatRoughnessMap,x.clearcoatRoughnessMapTransform)),h.clearcoatNormalMap&&(x.clearcoatNormalMap.value=h.clearcoatNormalMap,n(h.clearcoatNormalMap,x.clearcoatNormalMapTransform),x.clearcoatNormalScale.value.copy(h.clearcoatNormalScale),h.side===$n&&x.clearcoatNormalScale.value.negate())),h.dispersion>0&&(x.dispersion.value=h.dispersion),h.iridescence>0&&(x.iridescence.value=h.iridescence,x.iridescenceIOR.value=h.iridescenceIOR,x.iridescenceThicknessMinimum.value=h.iridescenceThicknessRange[0],x.iridescenceThicknessMaximum.value=h.iridescenceThicknessRange[1],h.iridescenceMap&&(x.iridescenceMap.value=h.iridescenceMap,n(h.iridescenceMap,x.iridescenceMapTransform)),h.iridescenceThicknessMap&&(x.iridescenceThicknessMap.value=h.iridescenceThicknessMap,n(h.iridescenceThicknessMap,x.iridescenceThicknessMapTransform))),h.transmission>0&&(x.transmission.value=h.transmission,x.transmissionSamplerMap.value=v.texture,x.transmissionSamplerSize.value.set(v.width,v.height),h.transmissionMap&&(x.transmissionMap.value=h.transmissionMap,n(h.transmissionMap,x.transmissionMapTransform)),x.thickness.value=h.thickness,h.thicknessMap&&(x.thicknessMap.value=h.thicknessMap,n(h.thicknessMap,x.thicknessMapTransform)),x.attenuationDistance.value=h.attenuationDistance,x.attenuationColor.value.copy(h.attenuationColor)),h.anisotropy>0&&(x.anisotropyVector.value.set(h.anisotropy*Math.cos(h.anisotropyRotation),h.anisotropy*Math.sin(h.anisotropyRotation)),h.anisotropyMap&&(x.anisotropyMap.value=h.anisotropyMap,n(h.anisotropyMap,x.anisotropyMapTransform))),x.specularIntensity.value=h.specularIntensity,x.specularColor.value.copy(h.specularColor),h.specularColorMap&&(x.specularColorMap.value=h.specularColorMap,n(h.specularColorMap,x.specularColorMapTransform)),h.specularIntensityMap&&(x.specularIntensityMap.value=h.specularIntensityMap,n(h.specularIntensityMap,x.specularIntensityMapTransform))}function g(x,h){h.matcap&&(x.matcap.value=h.matcap)}function b(x,h){const v=e.get(h).light;x.referencePosition.value.setFromMatrixPosition(v.matrixWorld),x.nearDistance.value=v.shadow.camera.near,x.farDistance.value=v.shadow.camera.far}return{refreshFogUniforms:i,refreshMaterialUniforms:a}}function J3(t,e,n,i){let a={},s={},l=[];const o=t.getParameter(t.MAX_UNIFORM_BUFFER_BINDINGS);function c(_,A){const M=A.program;i.uniformBlockBinding(_,M)}function u(_,A){let M=a[_.id];M===void 0&&(x(_),M=f(_),a[_.id]=M,_.addEventListener("dispose",v));const T=A.program;i.updateUBOMapping(_,T);const S=e.render.frame;s[_.id]!==S&&(d(_),s[_.id]=S)}function f(_){const A=m();_.__bindingPointIndex=A;const M=t.createBuffer(),T=_.__size,S=_.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 _=0;_<o;_++)if(l.indexOf(_)===-1)return l.push(_),_;return Mt("WebGLRenderer: Maximum number of simultaneously usable uniforms groups reached."),0}function d(_){const A=a[_.id],M=_.uniforms,T=_.__cache;t.bindBuffer(t.UNIFORM_BUFFER,A);for(let S=0,w=M.length;S<w;S++){const R=M[S];if(Array.isArray(R))for(let D=0,L=R.length;D<L;D++)p(R[D],S,D,T);else p(R,S,0,T)}t.bindBuffer(t.UNIFORM_BUFFER,null)}function p(_,A,M,T){if(b(_,A,M,T)===!0){const S=_.__offset,w=_.value;if(Array.isArray(w)){let R=0;for(let D=0;D<w.length;D++){const L=w[D],q=h(L);g(L,_.__data,R),typeof L!="number"&&typeof L!="boolean"&&!L.isMatrix3&&!ArrayBuffer.isView(L)&&(R+=q.storage/Float32Array.BYTES_PER_ELEMENT)}}else g(w,_.__data,0);t.bufferSubData(t.UNIFORM_BUFFER,S,_.__data)}}function g(_,A,M){typeof _=="number"||typeof _=="boolean"?A[0]=_:_.isMatrix3?(A[0]=_.elements[0],A[1]=_.elements[1],A[2]=_.elements[2],A[3]=0,A[4]=_.elements[3],A[5]=_.elements[4],A[6]=_.elements[5],A[7]=0,A[8]=_.elements[6],A[9]=_.elements[7],A[10]=_.elements[8],A[11]=0):ArrayBuffer.isView(_)?A.set(new _.constructor(_.buffer,_.byteOffset,A.length)):_.toArray(A,M)}function b(_,A,M,T){const S=_.value,w=A+"_"+M;if(T[w]===void 0)return typeof S=="number"||typeof S=="boolean"?T[w]=S:ArrayBuffer.isView(S)?T[w]=S.slice():T[w]=S.clone(),!0;{const R=T[w];if(typeof S=="number"||typeof S=="boolean"){if(R!==S)return T[w]=S,!0}else{if(ArrayBuffer.isView(S))return!0;if(R.equals(S)===!1)return R.copy(S),!0}}return!1}function x(_){const A=_.uniforms;let M=0;const T=16;for(let w=0,R=A.length;w<R;w++){const D=Array.isArray(A[w])?A[w]:[A[w]];for(let L=0,q=D.length;L<q;L++){const P=D[L],O=Array.isArray(P.value)?P.value:[P.value];for(let B=0,k=O.length;B<k;B++){const W=O[B],Y=h(W),he=M%T,_e=he%Y.boundary,Ce=he+_e;M+=_e,Ce!==0&&T-Ce<Y.storage&&(M+=T-Ce),P.__data=new Float32Array(Y.storage/Float32Array.BYTES_PER_ELEMENT),P.__offset=M,M+=Y.storage}}}const S=M%T;return S>0&&(M+=T-S),_.__size=M,_.__cache={},this}function h(_){const A={boundary:0,storage:0};return typeof _=="number"||typeof _=="boolean"?(A.boundary=4,A.storage=4):_.isVector2?(A.boundary=8,A.storage=8):_.isVector3||_.isColor?(A.boundary=16,A.storage=12):_.isVector4?(A.boundary=16,A.storage=16):_.isMatrix3?(A.boundary=48,A.storage=48):_.isMatrix4?(A.boundary=64,A.storage=64):_.isTexture?et("WebGLRenderer: Texture samplers can not be part of an uniforms group."):ArrayBuffer.isView(_)?(A.boundary=16,A.storage=_.byteLength):et("WebGLRenderer: Unsupported uniform value type.",_),A}function v(_){const A=_.target;A.removeEventListener("dispose",v);const M=l.indexOf(A.__bindingPointIndex);l.splice(M,1),t.deleteBuffer(a[A.id]),delete a[A.id],delete s[A.id]}function y(){for(const _ in a)t.deleteBuffer(a[_]);l=[],a={},s={}}return{bind:c,update:u,dispose:y}}const e2=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 Xi=null;function t2(){return Xi===null&&(Xi=new CS(e2,16,16,nr,Pa),Xi.name="DFG_LUT",Xi.minFilter=Ln,Xi.magFilter=Ln,Xi.wrapS=Ea,Xi.wrapT=Ea,Xi.generateMipmaps=!1,Xi.needsUpdate=!0),Xi}class n2{constructor(e={}){const{canvas:n=yA(),context:i=null,depth:a=!0,stencil:s=!1,alpha:l=!1,antialias:o=!1,premultipliedAlpha:c=!0,preserveDrawingBuffer:u=!1,powerPreference:f="default",failIfMajorPerformanceCaveat:m=!1,reversedDepthBuffer:d=!1,outputBufferType:p=Ei}=e;this.isWebGLRenderer=!0;let g;if(i!==null){if(typeof WebGLRenderingContext<"u"&&i instanceof WebGLRenderingContext)throw new Error("THREE.WebGLRenderer: WebGL 1 is not supported since r163.");g=i.getContextAttributes().alpha}else g=l;const b=p,x=new Set([bg,yg,_g]),h=new Set([Ei,aa,qo,Yo,gg,xg]),v=new Uint32Array(4),y=new Int32Array(4),_=new Z;let A=null,M=null;const T=[],S=[];let w=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=na,this.toneMappingExposure=1,this.transmissionResolutionScale=1;const R=this;let D=!1,L=null,q=null,P=null,O=null;this._outputColorSpace=xi;let B=0,k=0,W=null,Y=-1,he=null;const _e=new Zt,Ce=new Zt;let X=null;const de=new vt(0);let Ne=0,ae=n.width,j=n.height,ee=1,xe=null,Te=null;const Ie=new Zt(0,0,ae,j),it=new Zt(0,0,ae,j);let re=!1;const me=new RS;let Ae=!1,se=!1;const Le=new Bt,ke=new Z,Ue=new Zt,Re={background:null,fog:null,environment:null,overrideMaterial:null,isScene:!0};let qe=!1;function Se(){return W===null?ee:1}let I=i;function Qe(N,G){return n.getContext(N,G)}try{const N={alpha:!0,depth:a,stencil:s,antialias:o,premultipliedAlpha:c,preserveDrawingBuffer:u,powerPreference:f,failIfMajorPerformanceCaveat:m};if("setAttribute"in n&&n.setAttribute("data-engine",`three.js r${pg}`),n.addEventListener("webglcontextlost",bt,!1),n.addEventListener("webglcontextrestored",rt,!1),n.addEventListener("webglcontextcreationerror",nn,!1),I===null){const G="webgl2";if(I=Qe(G,N),I===null)throw Qe(G)?new Error("THREE.WebGLRenderer: Error creating WebGL context with your selected attributes."):new Error("THREE.WebGLRenderer: Error creating WebGL context.")}}catch(N){throw Mt("WebGLRenderer: "+N.message),N}let Je,C,E,V,K,ne,ye,be,ie,Q,le,pe,ve,Me,Be,Ve,tt,H,Ee,oe,we,De,ge;function We(){Je=new tR(I),Je.init(),we=new X3(I,Je),C=new qC(I,Je,e,we),E=new V3(I,Je),C.reversedDepthBuffer&&d&&E.buffers.depth.setReversed(!0),q=I.createFramebuffer(),P=I.createFramebuffer(),O=I.createFramebuffer(),V=new aR(I),K=new R3,ne=new W3(I,Je,E,K,C,we,V),ye=new eR(R),be=new ow(I),De=new WC(I,be),ie=new nR(I,be,V,De),Q=new rR(I,ie,be,De,V),H=new sR(I,C,ne),Be=new YC(K),le=new C3(R,ye,Je,C,De,Be),pe=new Q3(R,K),ve=new L3,Me=new I3(Je),tt=new VC(R,ye,E,Q,g,c),Ve=new G3(R,Q,C),ge=new J3(I,V,C,E),Ee=new XC(I,Je,V),oe=new iR(I,Je,V),V.programs=le.programs,R.capabilities=C,R.extensions=Je,R.properties=K,R.renderLists=ve,R.shadowMap=Ve,R.state=E,R.info=V}We(),b!==Ei&&(w=new oR(b,n.width,n.height,o,a,s));const Fe=new $3(R,I);this.xr=Fe,this.getContext=function(){return I},this.getContextAttributes=function(){return I.getContextAttributes()},this.forceContextLoss=function(){const N=Je.get("WEBGL_lose_context");N&&N.loseContext()},this.forceContextRestore=function(){const N=Je.get("WEBGL_lose_context");N&&N.restoreContext()},this.getPixelRatio=function(){return ee},this.setPixelRatio=function(N){N!==void 0&&(ee=N,this.setSize(ae,j,!1))},this.getSize=function(N){return N.set(ae,j)},this.setSize=function(N,G,te=!0){if(Fe.isPresenting){et("WebGLRenderer: Can't change size while VR device is presenting.");return}ae=N,j=G,n.width=Math.floor(N*ee),n.height=Math.floor(G*ee),te===!0&&(n.style.width=N+"px",n.style.height=G+"px"),w!==null&&w.setSize(n.width,n.height),this.setViewport(0,0,N,G)},this.getDrawingBufferSize=function(N){return N.set(ae*ee,j*ee).floor()},this.setDrawingBufferSize=function(N,G,te){ae=N,j=G,ee=te,n.width=Math.floor(N*te),n.height=Math.floor(G*te),this.setViewport(0,0,N,G)},this.setEffects=function(N){if(b===Ei){Mt("WebGLRenderer: setEffects() requires outputBufferType set to HalfFloatType or FloatType.");return}if(N){for(let G=0;G<N.length;G++)if(N[G].isOutputPass===!0){et("WebGLRenderer: OutputPass is not needed in setEffects(). Tone mapping and color space conversion are applied automatically.");break}}w.setEffects(N||[])},this.getCurrentViewport=function(N){return N.copy(_e)},this.getViewport=function(N){return N.copy(Ie)},this.setViewport=function(N,G,te,$){N.isVector4?Ie.set(N.x,N.y,N.z,N.w):Ie.set(N,G,te,$),E.viewport(_e.copy(Ie).multiplyScalar(ee).round())},this.getScissor=function(N){return N.copy(it)},this.setScissor=function(N,G,te,$){N.isVector4?it.set(N.x,N.y,N.z,N.w):it.set(N,G,te,$),E.scissor(Ce.copy(it).multiplyScalar(ee).round())},this.getScissorTest=function(){return re},this.setScissorTest=function(N){E.setScissorTest(re=N)},this.setOpaqueSort=function(N){xe=N},this.setTransparentSort=function(N){Te=N},this.getClearColor=function(N){return N.copy(tt.getClearColor())},this.setClearColor=function(){tt.setClearColor(...arguments)},this.getClearAlpha=function(){return tt.getClearAlpha()},this.setClearAlpha=function(){tt.setClearAlpha(...arguments)},this.clear=function(N=!0,G=!0,te=!0){let $=0;if(N){let J=!1;if(W!==null){const ze=W.texture.format;J=x.has(ze)}if(J){const ze=W.texture.type,Ge=h.has(ze),Pe=tt.getClearColor(),Xe=tt.getClearAlpha(),Ke=Pe.r,lt=Pe.g,ut=Pe.b;Ge?(v[0]=Ke,v[1]=lt,v[2]=ut,v[3]=Xe,I.clearBufferuiv(I.COLOR,0,v)):(y[0]=Ke,y[1]=lt,y[2]=ut,y[3]=Xe,I.clearBufferiv(I.COLOR,0,y))}else $|=I.COLOR_BUFFER_BIT}G&&($|=I.DEPTH_BUFFER_BIT,this.state.buffers.depth.setMask(!0)),te&&($|=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(N){N.setRenderer(this),L=N},this.dispose=function(){n.removeEventListener("webglcontextlost",bt,!1),n.removeEventListener("webglcontextrestored",rt,!1),n.removeEventListener("webglcontextcreationerror",nn,!1),tt.dispose(),ve.dispose(),Me.dispose(),K.dispose(),ye.dispose(),Q.dispose(),De.dispose(),ge.dispose(),le.dispose(),Fe.dispose(),Fe.removeEventListener("sessionstart",z),Fe.removeEventListener("sessionend",ue),je.stop()};function bt(N){N.preventDefault(),A0("WebGLRenderer: Context Lost."),D=!0}function rt(){A0("WebGLRenderer: Context Restored."),D=!1;const N=V.autoReset,G=Ve.enabled,te=Ve.autoUpdate,$=Ve.needsUpdate,J=Ve.type;We(),V.autoReset=N,Ve.enabled=G,Ve.autoUpdate=te,Ve.needsUpdate=$,Ve.type=J}function nn(N){Mt("WebGLRenderer: A WebGL context could not be created. Reason: ",N.statusMessage)}function bn(N){const G=N.target;G.removeEventListener("dispose",bn),Ia(G)}function Ia(N){_t(N),K.remove(N)}function _t(N){const G=K.get(N).programs;G!==void 0&&(G.forEach(function(te){le.releaseProgram(te)}),N.isShaderMaterial&&le.releaseShaderCache(N))}this.renderBufferDirect=function(N,G,te,$,J,ze){G===null&&(G=Re);const Ge=J.isMesh&&J.matrixWorld.determinantAffine()<0,Pe=Xt(N,G,te,$,J);E.setMaterial($,Ge);let Xe=te.index,Ke=1;if($.wireframe===!0){if(Xe=ie.getWireframeAttribute(te),Xe===void 0)return;Ke=2}const lt=te.drawRange,ut=te.attributes.position;let $e=lt.start*Ke,Dt=(lt.start+lt.count)*Ke;ze!==null&&($e=Math.max($e,ze.start*Ke),Dt=Math.min(Dt,(ze.start+ze.count)*Ke)),Xe!==null?($e=Math.max($e,0),Dt=Math.min(Dt,Xe.count)):ut!=null&&($e=Math.max($e,0),Dt=Math.min(Dt,ut.count));const Qt=Dt-$e;if(Qt<0||Qt===1/0)return;De.setup(J,$,Pe,te,Xe);let Yt,Ot=Ee;if(Xe!==null&&(Yt=be.get(Xe),Ot=oe,Ot.setIndex(Yt)),J.isMesh)$.wireframe===!0?(E.setLineWidth($.wireframeLinewidth*Se()),Ot.setMode(I.LINES)):Ot.setMode(I.TRIANGLES);else if(J.isLine){let Cn=$.linewidth;Cn===void 0&&(Cn=1),E.setLineWidth(Cn*Se()),J.isLineSegments?Ot.setMode(I.LINES):J.isLineLoop?Ot.setMode(I.LINE_LOOP):Ot.setMode(I.LINE_STRIP)}else J.isPoints?Ot.setMode(I.POINTS):J.isSprite&&Ot.setMode(I.TRIANGLES);if(J.isBatchedMesh)if(Je.get("WEBGL_multi_draw"))Ot.renderMultiDraw(J._multiDrawStarts,J._multiDrawCounts,J._multiDrawCount);else{const Cn=J._multiDrawStarts,He=J._multiDrawCounts,ei=J._multiDrawCount,St=Xe?be.get(Xe).bytesPerElement:1,pi=K.get($).currentProgram.getUniforms();for(let Vi=0;Vi<ei;Vi++)pi.setValue(I,"_gl_DrawID",Vi),Ot.render(Cn[Vi]/St,He[Vi])}else if(J.isInstancedMesh)Ot.renderInstances($e,Qt,J.count);else if(te.isInstancedBufferGeometry){const Cn=te._maxInstanceCount!==void 0?te._maxInstanceCount:1/0,He=Math.min(te.instanceCount,Cn);Ot.renderInstances($e,Qt,He)}else Ot.render($e,Qt)};function Ba(N,G,te){N.transparent===!0&&N.side===ya&&N.forceSinglePass===!1?(N.side=$n,N.needsUpdate=!0,F(N,G,te),N.side=As,N.needsUpdate=!0,F(N,G,te),N.side=ya):F(N,G,te)}this.compile=function(N,G,te=null){te===null&&(te=N),M=Me.get(te),M.init(G),S.push(M),te.traverseVisible(function(J){J.isLight&&J.layers.test(G.layers)&&(M.pushLight(J),J.castShadow&&M.pushShadow(J))}),N!==te&&N.traverseVisible(function(J){J.isLight&&J.layers.test(G.layers)&&(M.pushLight(J),J.castShadow&&M.pushShadow(J))}),M.setupLights();const $=new Set;return N.traverse(function(J){if(!(J.isMesh||J.isPoints||J.isLine||J.isSprite))return;const ze=J.material;if(ze)if(Array.isArray(ze))for(let Ge=0;Ge<ze.length;Ge++){const Pe=ze[Ge];Ba(Pe,te,J),$.add(Pe)}else Ba(ze,te,J),$.add(ze)}),M=S.pop(),$},this.compileAsync=function(N,G,te=null){const $=this.compile(N,G,te);return new Promise(J=>{function ze(){if($.forEach(function(Ge){K.get(Ge).currentProgram.isReady()&&$.delete(Ge)}),$.size===0){J(N);return}setTimeout(ze,10)}Je.get("KHR_parallel_shader_compile")!==null?ze():setTimeout(ze,10)})};let Fi=null;function Hi(N){Fi&&Fi(N)}function z(){je.stop()}function ue(){je.start()}const je=new zS;je.setAnimationLoop(Hi),typeof self<"u"&&je.setContext(self),this.setAnimationLoop=function(N){Fi=N,Fe.setAnimationLoop(N),N===null?je.stop():je.start()},Fe.addEventListener("sessionstart",z),Fe.addEventListener("sessionend",ue),this.render=function(N,G){if(G!==void 0&&G.isCamera!==!0){Mt("WebGLRenderer.render: camera is not an instance of THREE.Camera.");return}if(D===!0)return;L!==null&&L.renderStart(N,G);const te=Fe.enabled===!0&&Fe.isPresenting===!0,$=w!==null&&(W===null||te)&&w.begin(R,W);if(N.matrixWorldAutoUpdate===!0&&N.updateMatrixWorld(),G.parent===null&&G.matrixWorldAutoUpdate===!0&&G.updateMatrixWorld(),Fe.enabled===!0&&Fe.isPresenting===!0&&(w===null||w.isCompositing()===!1)&&(Fe.cameraAutoUpdate===!0&&Fe.updateCamera(G),G=Fe.getCamera()),N.isScene===!0&&N.onBeforeRender(R,N,G,W),M=Me.get(N,S.length),M.init(G),M.state.textureUnits=ne.getTextureUnits(),S.push(M),Le.multiplyMatrices(G.projectionMatrix,G.matrixWorldInverse),me.setFromProjectionMatrix(Le,ea,G.reversedDepth),se=this.localClippingEnabled,Ae=Be.init(this.clippingPlanes,se),A=ve.get(N,T.length),A.init(),T.push(A),Fe.enabled===!0&&Fe.isPresenting===!0){const Ge=R.xr.getDepthSensingMesh();Ge!==null&&ht(Ge,G,-1/0,R.sortObjects)}ht(N,G,0,R.sortObjects),A.finish(),R.sortObjects===!0&&A.sort(xe,Te,G.reversedDepth),qe=Fe.enabled===!1||Fe.isPresenting===!1||Fe.hasDepthSensing()===!1,qe&&tt.addToRenderList(A,N),this.info.render.frame++,this.info.autoReset===!0&&this.info.reset(),Ae===!0&&Be.beginShadows();const J=M.state.shadowsArray;if(Ve.render(J,N,G),Ae===!0&&Be.endShadows(),($&&w.hasRenderPass())===!1){const Ge=A.opaque,Pe=A.transmissive;if(M.setupLights(),G.isArrayCamera){const Xe=G.cameras;if(Pe.length>0)for(let Ke=0,lt=Xe.length;Ke<lt;Ke++){const ut=Xe[Ke];an(Ge,Pe,N,ut)}qe&&tt.render(N);for(let Ke=0,lt=Xe.length;Ke<lt;Ke++){const ut=Xe[Ke];qt(A,N,ut,ut.viewport)}}else Pe.length>0&&an(Ge,Pe,N,G),qe&&tt.render(N),qt(A,N,G)}W!==null&&k===0&&(ne.updateMultisampleRenderTarget(W),ne.updateRenderTargetMipmap(W)),$&&w.end(R),N.isScene===!0&&N.onAfterRender(R,N,G),De.resetDefaultState(),Y=-1,he=null,S.pop(),S.length>0?(M=S[S.length-1],ne.setTextureUnits(M.state.textureUnits),Ae===!0&&Be.setGlobalState(R.clippingPlanes,M.state.camera)):M=null,T.pop(),T.length>0?A=T[T.length-1]:A=null,L!==null&&L.renderEnd()};function ht(N,G,te,$){if(N.visible===!1)return;if(N.layers.test(G.layers)){if(N.isGroup)te=N.renderOrder;else if(N.isLOD)N.autoUpdate===!0&&N.update(G);else if(N.isLightProbeGrid)M.pushLightProbeGrid(N);else if(N.isLight)M.pushLight(N),N.castShadow&&M.pushShadow(N);else if(N.isSprite){if(!N.frustumCulled||me.intersectsSprite(N)){$&&Ue.setFromMatrixPosition(N.matrixWorld).applyMatrix4(Le);const Ge=Q.update(N),Pe=N.material;Pe.visible&&A.push(N,Ge,Pe,te,Ue.z,null)}}else if((N.isMesh||N.isLine||N.isPoints)&&(!N.frustumCulled||me.intersectsObject(N))){const Ge=Q.update(N),Pe=N.material;if($&&(N.boundingSphere!==void 0?(N.boundingSphere===null&&N.computeBoundingSphere(),Ue.copy(N.boundingSphere.center)):(Ge.boundingSphere===null&&Ge.computeBoundingSphere(),Ue.copy(Ge.boundingSphere.center)),Ue.applyMatrix4(N.matrixWorld).applyMatrix4(Le)),Array.isArray(Pe)){const Xe=Ge.groups;for(let Ke=0,lt=Xe.length;Ke<lt;Ke++){const ut=Xe[Ke],$e=Pe[ut.materialIndex];$e&&$e.visible&&A.push(N,Ge,$e,te,Ue.z,ut)}}else Pe.visible&&A.push(N,Ge,Pe,te,Ue.z,null)}}const ze=N.children;for(let Ge=0,Pe=ze.length;Ge<Pe;Ge++)ht(ze[Ge],G,te,$)}function qt(N,G,te,$){const{opaque:J,transmissive:ze,transparent:Ge}=N;M.setupLightsView(te),Ae===!0&&Be.setGlobalState(R.clippingPlanes,te),$&&E.viewport(_e.copy($)),J.length>0&&mn(J,G,te),ze.length>0&&mn(ze,G,te),Ge.length>0&&mn(Ge,G,te),E.buffers.depth.setTest(!0),E.buffers.depth.setMask(!0),E.buffers.color.setMask(!0),E.setPolygonOffset(!1)}function an(N,G,te,$){if((te.isScene===!0?te.overrideMaterial:null)!==null)return;if(M.state.transmissionRenderTarget[$.id]===void 0){const $e=Je.has("EXT_color_buffer_half_float")||Je.has("EXT_color_buffer_float");M.state.transmissionRenderTarget[$.id]=new ia(1,1,{generateMipmaps:!0,type:$e?Pa:Ei,minFilter:Gs,samples:Math.max(4,C.samples),stencilBuffer:s,resolveDepthBuffer:!1,resolveStencilBuffer:!1,colorSpace:mt.workingColorSpace})}const ze=M.state.transmissionRenderTarget[$.id],Ge=$.viewport||_e;ze.setSize(Ge.z*R.transmissionResolutionScale,Ge.w*R.transmissionResolutionScale);const Pe=R.getRenderTarget(),Xe=R.getActiveCubeFace(),Ke=R.getActiveMipmapLevel();R.setRenderTarget(ze),R.getClearColor(de),Ne=R.getClearAlpha(),Ne<1&&R.setClearColor(16777215,.5),R.clear(),qe&&tt.render(te);const lt=R.toneMapping;R.toneMapping=na;const ut=$.viewport;if($.viewport!==void 0&&($.viewport=void 0),M.setupLightsView($),Ae===!0&&Be.setGlobalState(R.clippingPlanes,$),mn(N,te,$),ne.updateMultisampleRenderTarget(ze),ne.updateRenderTargetMipmap(ze),Je.has("WEBGL_multisampled_render_to_texture")===!1){let $e=!1;for(let Dt=0,Qt=G.length;Dt<Qt;Dt++){const Yt=G[Dt],{object:Ot,geometry:Cn,material:He,group:ei}=Yt;if(He.side===ya&&Ot.layers.test($.layers)){const St=He.side;He.side=$n,He.needsUpdate=!0,Cs(Ot,te,$,Cn,He,ei),He.side=St,He.needsUpdate=!0,$e=!0}}$e===!0&&(ne.updateMultisampleRenderTarget(ze),ne.updateRenderTargetMipmap(ze))}R.setRenderTarget(Pe,Xe,Ke),R.setClearColor(de,Ne),ut!==void 0&&($.viewport=ut),R.toneMapping=lt}function mn(N,G,te){const $=G.isScene===!0?G.overrideMaterial:null;for(let J=0,ze=N.length;J<ze;J++){const Ge=N[J],{object:Pe,geometry:Xe,group:Ke}=Ge;let lt=Ge.material;lt.allowOverride===!0&&$!==null&&(lt=$),Pe.layers.test(te.layers)&&Cs(Pe,G,te,Xe,lt,Ke)}}function Cs(N,G,te,$,J,ze){N.onBeforeRender(R,G,te,$,J,ze),N.modelViewMatrix.multiplyMatrices(te.matrixWorldInverse,N.matrixWorld),N.normalMatrix.getNormalMatrix(N.modelViewMatrix),J.onBeforeRender(R,G,te,$,N,ze),J.transparent===!0&&J.side===ya&&J.forceSinglePass===!1?(J.side=$n,J.needsUpdate=!0,R.renderBufferDirect(te,G,$,J,N,ze),J.side=As,J.needsUpdate=!0,R.renderBufferDirect(te,G,$,J,N,ze),J.side=ya):R.renderBufferDirect(te,G,$,J,N,ze),N.onAfterRender(R,G,te,$,J,ze)}function F(N,G,te){G.isScene!==!0&&(G=Re);const $=K.get(N),J=M.state.lights,ze=M.state.shadowsArray,Ge=J.state.version,Pe=le.getParameters(N,J.state,ze,G,te,M.state.lightProbeGridArray),Xe=le.getProgramCacheKey(Pe);let Ke=$.programs;$.environment=N.isMeshStandardMaterial||N.isMeshLambertMaterial||N.isMeshPhongMaterial?G.environment:null,$.fog=G.fog;const lt=N.isMeshStandardMaterial||N.isMeshLambertMaterial&&!N.envMap||N.isMeshPhongMaterial&&!N.envMap;$.envMap=ye.get(N.envMap||$.environment,lt),$.envMapRotation=$.environment!==null&&N.envMap===null?G.environmentRotation:N.envMapRotation,Ke===void 0&&(N.addEventListener("dispose",bn),Ke=new Map,$.programs=Ke);let ut=Ke.get(Xe);if(ut!==void 0){if($.currentProgram===ut&&$.lightsStateVersion===Ge)return Ze(N,Pe),ut}else Pe.uniforms=le.getUniforms(N),L!==null&&N.isNodeMaterial&&L.build(N,te,Pe),N.onBeforeCompile(Pe,R),ut=le.acquireProgram(Pe,Xe),Ke.set(Xe,ut),$.uniforms=Pe.uniforms;const $e=$.uniforms;return(!N.isShaderMaterial&&!N.isRawShaderMaterial||N.clipping===!0)&&($e.clippingPlanes=Be.uniform),Ze(N,Pe),$.needsLights=Gi(N),$.lightsStateVersion=Ge,$.needsLights&&($e.ambientLightColor.value=J.state.ambient,$e.lightProbe.value=J.state.probe,$e.directionalLights.value=J.state.directional,$e.directionalLightShadows.value=J.state.directionalShadow,$e.spotLights.value=J.state.spot,$e.spotLightShadows.value=J.state.spotShadow,$e.rectAreaLights.value=J.state.rectArea,$e.ltc_1.value=J.state.rectAreaLTC1,$e.ltc_2.value=J.state.rectAreaLTC2,$e.pointLights.value=J.state.point,$e.pointLightShadows.value=J.state.pointShadow,$e.hemisphereLights.value=J.state.hemi,$e.directionalShadowMatrix.value=J.state.directionalShadowMatrix,$e.spotLightMatrix.value=J.state.spotLightMatrix,$e.spotLightMap.value=J.state.spotLightMap,$e.pointShadowMatrix.value=J.state.pointShadowMatrix),$.lightProbeGrid=M.state.lightProbeGridArray.length>0,$.currentProgram=ut,$.uniformsList=null,ut}function ce(N){if(N.uniformsList===null){const G=N.currentProgram.getUniforms();N.uniformsList=Du.seqWithValue(G.seq,N.uniforms)}return N.uniformsList}function Ze(N,G){const te=K.get(N);te.outputColorSpace=G.outputColorSpace,te.batching=G.batching,te.batchingColor=G.batchingColor,te.instancing=G.instancing,te.instancingColor=G.instancingColor,te.instancingMorph=G.instancingMorph,te.skinning=G.skinning,te.morphTargets=G.morphTargets,te.morphNormals=G.morphNormals,te.morphColors=G.morphColors,te.morphTargetsCount=G.morphTargetsCount,te.numClippingPlanes=G.numClippingPlanes,te.numIntersection=G.numClipIntersection,te.vertexAlphas=G.vertexAlphas,te.vertexTangents=G.vertexTangents,te.toneMapping=G.toneMapping}function Ye(N,G){if(N.length===0)return null;if(N.length===1)return N[0].texture!==null?N[0]:null;_.setFromMatrixPosition(G.matrixWorld);for(let te=0,$=N.length;te<$;te++){const J=N[te];if(J.texture!==null&&J.boundingBox.containsPoint(_))return J}return null}function Xt(N,G,te,$,J){G.isScene!==!0&&(G=Re),ne.resetTextureUnits();const ze=G.fog,Ge=$.isMeshStandardMaterial||$.isMeshLambertMaterial||$.isMeshPhongMaterial?G.environment:null,Pe=W===null?R.outputColorSpace:W.isXRRenderTarget===!0?W.texture.colorSpace:mt.workingColorSpace,Xe=$.isMeshStandardMaterial||$.isMeshLambertMaterial&&!$.envMap||$.isMeshPhongMaterial&&!$.envMap,Ke=ye.get($.envMap||Ge,Xe),lt=$.vertexColors===!0&&!!te.attributes.color&&te.attributes.color.itemSize===4,ut=!!te.attributes.tangent&&(!!$.normalMap||$.anisotropy>0),$e=!!te.morphAttributes.position,Dt=!!te.morphAttributes.normal,Qt=!!te.morphAttributes.color;let Yt=na;$.toneMapped&&(W===null||W.isXRRenderTarget===!0)&&(Yt=R.toneMapping);const Ot=te.morphAttributes.position||te.morphAttributes.normal||te.morphAttributes.color,Cn=Ot!==void 0?Ot.length:0,He=K.get($),ei=M.state.lights;if(Ae===!0&&(se===!0||N!==he)){const kt=N===he&&$.id===Y;Be.setState($,N,kt)}let St=!1;$.version===He.__version?(He.needsLights&&He.lightsStateVersion!==ei.state.version||He.outputColorSpace!==Pe||J.isBatchedMesh&&He.batching===!1||!J.isBatchedMesh&&He.batching===!0||J.isBatchedMesh&&He.batchingColor===!0&&J.colorTexture===null||J.isBatchedMesh&&He.batchingColor===!1&&J.colorTexture!==null||J.isInstancedMesh&&He.instancing===!1||!J.isInstancedMesh&&He.instancing===!0||J.isSkinnedMesh&&He.skinning===!1||!J.isSkinnedMesh&&He.skinning===!0||J.isInstancedMesh&&He.instancingColor===!0&&J.instanceColor===null||J.isInstancedMesh&&He.instancingColor===!1&&J.instanceColor!==null||J.isInstancedMesh&&He.instancingMorph===!0&&J.morphTexture===null||J.isInstancedMesh&&He.instancingMorph===!1&&J.morphTexture!==null||He.envMap!==Ke||$.fog===!0&&He.fog!==ze||He.numClippingPlanes!==void 0&&(He.numClippingPlanes!==Be.numPlanes||He.numIntersection!==Be.numIntersection)||He.vertexAlphas!==lt||He.vertexTangents!==ut||He.morphTargets!==$e||He.morphNormals!==Dt||He.morphColors!==Qt||He.toneMapping!==Yt||He.morphTargetsCount!==Cn||!!He.lightProbeGrid!=M.state.lightProbeGridArray.length>0)&&(St=!0):(St=!0,He.__version=$.version);let pi=He.currentProgram;St===!0&&(pi=F($,G,J),L&&$.isNodeMaterial&&L.onUpdateProgram($,pi,He));let Vi=!1,ja=!1,pr=!1;const Pt=pi.getUniforms(),Jt=He.uniforms;if(E.useProgram(pi.program)&&(Vi=!0,ja=!0,pr=!0),$.id!==Y&&(Y=$.id,ja=!0),He.needsLights){const kt=Ye(M.state.lightProbeGridArray,J);He.lightProbeGrid!==kt&&(He.lightProbeGrid=kt,ja=!0)}if(Vi||he!==N){E.buffers.depth.getReversed()&&N.reversedDepth!==!0&&(N._reversedDepth=!0,N.updateProjectionMatrix()),Pt.setValue(I,"projectionMatrix",N.projectionMatrix),Pt.setValue(I,"viewMatrix",N.matrixWorldInverse);const Ha=Pt.map.cameraPosition;Ha!==void 0&&Ha.setValue(I,ke.setFromMatrixPosition(N.matrixWorld)),C.logarithmicDepthBuffer&&Pt.setValue(I,"logDepthBufFC",2/(Math.log(N.far+1)/Math.LN2)),($.isMeshPhongMaterial||$.isMeshToonMaterial||$.isMeshLambertMaterial||$.isMeshBasicMaterial||$.isMeshStandardMaterial||$.isShaderMaterial)&&Pt.setValue(I,"isOrthographic",N.isOrthographicCamera===!0),he!==N&&(he=N,ja=!0,pr=!0)}if(He.needsLights&&(ei.state.directionalShadowMap.length>0&&Pt.setValue(I,"directionalShadowMap",ei.state.directionalShadowMap,ne),ei.state.spotShadowMap.length>0&&Pt.setValue(I,"spotShadowMap",ei.state.spotShadowMap,ne),ei.state.pointShadowMap.length>0&&Pt.setValue(I,"pointShadowMap",ei.state.pointShadowMap,ne)),J.isSkinnedMesh){Pt.setOptional(I,J,"bindMatrix"),Pt.setOptional(I,J,"bindMatrixInverse");const kt=J.skeleton;kt&&(kt.boneTexture===null&&kt.computeBoneTexture(),Pt.setValue(I,"boneTexture",kt.boneTexture,ne))}J.isBatchedMesh&&(Pt.setOptional(I,J,"batchingTexture"),Pt.setValue(I,"batchingTexture",J._matricesTexture,ne),Pt.setOptional(I,J,"batchingIdTexture"),Pt.setValue(I,"batchingIdTexture",J._indirectTexture,ne),Pt.setOptional(I,J,"batchingColorTexture"),J._colorsTexture!==null&&Pt.setValue(I,"batchingColorTexture",J._colorsTexture,ne));const Fa=te.morphAttributes;if((Fa.position!==void 0||Fa.normal!==void 0||Fa.color!==void 0)&&H.update(J,te,pi),(ja||He.receiveShadow!==J.receiveShadow)&&(He.receiveShadow=J.receiveShadow,Pt.setValue(I,"receiveShadow",J.receiveShadow)),($.isMeshStandardMaterial||$.isMeshLambertMaterial||$.isMeshPhongMaterial)&&$.envMap===null&&G.environment!==null&&(Jt.envMapIntensity.value=G.environmentIntensity),Jt.dfgLUT!==void 0&&(Jt.dfgLUT.value=t2()),ja){if(Pt.setValue(I,"toneMappingExposure",R.toneMappingExposure),He.needsLights&&hi(Jt,pr),ze&&$.fog===!0&&pe.refreshFogUniforms(Jt,ze),pe.refreshMaterialUniforms(Jt,$,ee,j,M.state.transmissionRenderTarget[N.id]),He.needsLights&&He.lightProbeGrid){const kt=He.lightProbeGrid;Jt.probesSH.value=kt.texture,Jt.probesMin.value.copy(kt.boundingBox.min),Jt.probesMax.value.copy(kt.boundingBox.max),Jt.probesResolution.value.copy(kt.resolution)}Du.upload(I,ce(He),Jt,ne)}if($.isShaderMaterial&&$.uniformsNeedUpdate===!0&&(Du.upload(I,ce(He),Jt,ne),$.uniformsNeedUpdate=!1),$.isSpriteMaterial&&Pt.setValue(I,"center",J.center),Pt.setValue(I,"modelViewMatrix",J.modelViewMatrix),Pt.setValue(I,"normalMatrix",J.normalMatrix),Pt.setValue(I,"modelMatrix",J.matrixWorld),$.uniformsGroups!==void 0){const kt=$.uniformsGroups;for(let Ha=0,mr=kt.length;Ha<mr;Ha++){const Pg=kt[Ha];ge.update(Pg,pi),ge.bind(Pg,pi)}}return pi}function hi(N,G){N.ambientLightColor.needsUpdate=G,N.lightProbe.needsUpdate=G,N.directionalLights.needsUpdate=G,N.directionalLightShadows.needsUpdate=G,N.pointLights.needsUpdate=G,N.pointLightShadows.needsUpdate=G,N.spotLights.needsUpdate=G,N.spotLightShadows.needsUpdate=G,N.rectAreaLights.needsUpdate=G,N.hemisphereLights.needsUpdate=G}function Gi(N){return N.isMeshLambertMaterial||N.isMeshToonMaterial||N.isMeshPhongMaterial||N.isMeshStandardMaterial||N.isShadowMaterial||N.isShaderMaterial&&N.lights===!0}this.getActiveCubeFace=function(){return B},this.getActiveMipmapLevel=function(){return k},this.getRenderTarget=function(){return W},this.setRenderTargetTextures=function(N,G,te){const $=K.get(N);$.__autoAllocateDepthBuffer=N.resolveDepthBuffer===!1,$.__autoAllocateDepthBuffer===!1&&($.__useRenderToTexture=!1),K.get(N.texture).__webglTexture=G,K.get(N.depthTexture).__webglTexture=$.__autoAllocateDepthBuffer?void 0:te,$.__hasExternalTextures=!0},this.setRenderTargetFramebuffer=function(N,G){const te=K.get(N);te.__webglFramebuffer=G,te.__useDefaultFramebuffer=G===void 0},this.setRenderTarget=function(N,G=0,te=0){W=N,B=G,k=te;let $=null,J=!1,ze=!1;if(N){const Pe=K.get(N);if(Pe.__useDefaultFramebuffer!==void 0){E.bindFramebuffer(I.FRAMEBUFFER,Pe.__webglFramebuffer),_e.copy(N.viewport),Ce.copy(N.scissor),X=N.scissorTest,E.viewport(_e),E.scissor(Ce),E.setScissorTest(X),Y=-1;return}else if(Pe.__webglFramebuffer===void 0)ne.setupRenderTarget(N);else if(Pe.__hasExternalTextures)ne.rebindTextures(N,K.get(N.texture).__webglTexture,K.get(N.depthTexture).__webglTexture);else if(N.depthBuffer){const lt=N.depthTexture;if(Pe.__boundDepthTexture!==lt){if(lt!==null&&K.has(lt)&&(N.width!==lt.image.width||N.height!==lt.image.height))throw new Error("THREE.WebGLRenderer: Attached DepthTexture is initialized to the incorrect size.");ne.setupDepthRenderbuffer(N)}}const Xe=N.texture;(Xe.isData3DTexture||Xe.isDataArrayTexture||Xe.isCompressedArrayTexture)&&(ze=!0);const Ke=K.get(N).__webglFramebuffer;N.isWebGLCubeRenderTarget?(Array.isArray(Ke[G])?$=Ke[G][te]:$=Ke[G],J=!0):N.samples>0&&ne.useMultisampledRTT(N)===!1?$=K.get(N).__webglMultisampledFramebuffer:Array.isArray(Ke)?$=Ke[te]:$=Ke,_e.copy(N.viewport),Ce.copy(N.scissor),X=N.scissorTest}else _e.copy(Ie).multiplyScalar(ee).floor(),Ce.copy(it).multiplyScalar(ee).floor(),X=re;if(te!==0&&($=q),E.bindFramebuffer(I.FRAMEBUFFER,$)&&E.drawBuffers(N,$),E.viewport(_e),E.scissor(Ce),E.setScissorTest(X),J){const Pe=K.get(N.texture);I.framebufferTexture2D(I.FRAMEBUFFER,I.COLOR_ATTACHMENT0,I.TEXTURE_CUBE_MAP_POSITIVE_X+G,Pe.__webglTexture,te)}else if(ze){const Pe=G;for(let Xe=0;Xe<N.textures.length;Xe++){const Ke=K.get(N.textures[Xe]);I.framebufferTextureLayer(I.FRAMEBUFFER,I.COLOR_ATTACHMENT0+Xe,Ke.__webglTexture,te,Pe)}}else if(N!==null&&te!==0){const Pe=K.get(N.texture);I.framebufferTexture2D(I.FRAMEBUFFER,I.COLOR_ATTACHMENT0,I.TEXTURE_2D,Pe.__webglTexture,te)}Y=-1},this.readRenderTargetPixels=function(N,G,te,$,J,ze,Ge,Pe=0){if(!(N&&N.isWebGLRenderTarget)){Mt("WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.");return}let Xe=K.get(N).__webglFramebuffer;if(N.isWebGLCubeRenderTarget&&Ge!==void 0&&(Xe=Xe[Ge]),Xe){E.bindFramebuffer(I.FRAMEBUFFER,Xe);try{const Ke=N.textures[Pe],lt=Ke.format,ut=Ke.type;if(N.textures.length>1&&I.readBuffer(I.COLOR_ATTACHMENT0+Pe),!C.textureFormatReadable(lt)){Mt("WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.");return}if(!C.textureTypeReadable(ut)){Mt("WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.");return}G>=0&&G<=N.width-$&&te>=0&&te<=N.height-J&&I.readPixels(G,te,$,J,we.convert(lt),we.convert(ut),ze)}finally{const Ke=W!==null?K.get(W).__webglFramebuffer:null;E.bindFramebuffer(I.FRAMEBUFFER,Ke)}}},this.readRenderTargetPixelsAsync=async function(N,G,te,$,J,ze,Ge,Pe=0){if(!(N&&N.isWebGLRenderTarget))throw new Error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.");let Xe=K.get(N).__webglFramebuffer;if(N.isWebGLCubeRenderTarget&&Ge!==void 0&&(Xe=Xe[Ge]),Xe)if(G>=0&&G<=N.width-$&&te>=0&&te<=N.height-J){E.bindFramebuffer(I.FRAMEBUFFER,Xe);const Ke=N.textures[Pe],lt=Ke.format,ut=Ke.type;if(N.textures.length>1&&I.readBuffer(I.COLOR_ATTACHMENT0+Pe),!C.textureFormatReadable(lt))throw new Error("THREE.WebGLRenderer.readRenderTargetPixelsAsync: renderTarget is not in RGBA or implementation defined format.");if(!C.textureTypeReadable(ut))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,ze.byteLength,I.STREAM_READ),I.readPixels(G,te,$,J,we.convert(lt),we.convert(ut),0);const Dt=W!==null?K.get(W).__webglFramebuffer:null;E.bindFramebuffer(I.FRAMEBUFFER,Dt);const Qt=I.fenceSync(I.SYNC_GPU_COMMANDS_COMPLETE,0);return I.flush(),await bA(I,Qt,4),I.bindBuffer(I.PIXEL_PACK_BUFFER,$e),I.getBufferSubData(I.PIXEL_PACK_BUFFER,0,ze),I.deleteBuffer($e),I.deleteSync(Qt),ze}else throw new Error("THREE.WebGLRenderer.readRenderTargetPixelsAsync: requested read bounds are out of range.")},this.copyFramebufferToTexture=function(N,G=null,te=0){const $=Math.pow(2,-te),J=Math.floor(N.image.width*$),ze=Math.floor(N.image.height*$),Ge=G!==null?G.x:0,Pe=G!==null?G.y:0;ne.setTexture2D(N,0),I.copyTexSubImage2D(I.TEXTURE_2D,te,0,0,Ge,Pe,J,ze),E.unbindTexture()},this.copyTextureToTexture=function(N,G,te=null,$=null,J=0,ze=0){let Ge,Pe,Xe,Ke,lt,ut,$e,Dt,Qt;const Yt=N.isCompressedTexture?N.mipmaps[ze]:N.image;if(te!==null)Ge=te.max.x-te.min.x,Pe=te.max.y-te.min.y,Xe=te.isBox3?te.max.z-te.min.z:1,Ke=te.min.x,lt=te.min.y,ut=te.isBox3?te.min.z:0;else{const Jt=Math.pow(2,-J);Ge=Math.floor(Yt.width*Jt),Pe=Math.floor(Yt.height*Jt),N.isDataArrayTexture?Xe=Yt.depth:N.isData3DTexture?Xe=Math.floor(Yt.depth*Jt):Xe=1,Ke=0,lt=0,ut=0}$!==null?($e=$.x,Dt=$.y,Qt=$.z):($e=0,Dt=0,Qt=0);const Ot=we.convert(G.format),Cn=we.convert(G.type);let He;G.isData3DTexture?(ne.setTexture3D(G,0),He=I.TEXTURE_3D):G.isDataArrayTexture||G.isCompressedArrayTexture?(ne.setTexture2DArray(G,0),He=I.TEXTURE_2D_ARRAY):(ne.setTexture2D(G,0),He=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),St=E.getParameter(I.UNPACK_IMAGE_HEIGHT),pi=E.getParameter(I.UNPACK_SKIP_PIXELS),Vi=E.getParameter(I.UNPACK_SKIP_ROWS),ja=E.getParameter(I.UNPACK_SKIP_IMAGES);E.pixelStorei(I.UNPACK_ROW_LENGTH,Yt.width),E.pixelStorei(I.UNPACK_IMAGE_HEIGHT,Yt.height),E.pixelStorei(I.UNPACK_SKIP_PIXELS,Ke),E.pixelStorei(I.UNPACK_SKIP_ROWS,lt),E.pixelStorei(I.UNPACK_SKIP_IMAGES,ut);const pr=N.isDataArrayTexture||N.isData3DTexture,Pt=G.isDataArrayTexture||G.isData3DTexture;if(N.isDepthTexture){const Jt=K.get(N),Fa=K.get(G),kt=K.get(Jt.__renderTarget),Ha=K.get(Fa.__renderTarget);E.bindFramebuffer(I.READ_FRAMEBUFFER,kt.__webglFramebuffer),E.bindFramebuffer(I.DRAW_FRAMEBUFFER,Ha.__webglFramebuffer);for(let mr=0;mr<Xe;mr++)pr&&(I.framebufferTextureLayer(I.READ_FRAMEBUFFER,I.COLOR_ATTACHMENT0,K.get(N).__webglTexture,J,ut+mr),I.framebufferTextureLayer(I.DRAW_FRAMEBUFFER,I.COLOR_ATTACHMENT0,K.get(G).__webglTexture,ze,Qt+mr)),I.blitFramebuffer(Ke,lt,Ge,Pe,$e,Dt,Ge,Pe,I.DEPTH_BUFFER_BIT,I.NEAREST);E.bindFramebuffer(I.READ_FRAMEBUFFER,null),E.bindFramebuffer(I.DRAW_FRAMEBUFFER,null)}else if(J!==0||N.isRenderTargetTexture||K.has(N)){const Jt=K.get(N),Fa=K.get(G);E.bindFramebuffer(I.READ_FRAMEBUFFER,P),E.bindFramebuffer(I.DRAW_FRAMEBUFFER,O);for(let kt=0;kt<Xe;kt++)pr?I.framebufferTextureLayer(I.READ_FRAMEBUFFER,I.COLOR_ATTACHMENT0,Jt.__webglTexture,J,ut+kt):I.framebufferTexture2D(I.READ_FRAMEBUFFER,I.COLOR_ATTACHMENT0,I.TEXTURE_2D,Jt.__webglTexture,J),Pt?I.framebufferTextureLayer(I.DRAW_FRAMEBUFFER,I.COLOR_ATTACHMENT0,Fa.__webglTexture,ze,Qt+kt):I.framebufferTexture2D(I.DRAW_FRAMEBUFFER,I.COLOR_ATTACHMENT0,I.TEXTURE_2D,Fa.__webglTexture,ze),J!==0?I.blitFramebuffer(Ke,lt,Ge,Pe,$e,Dt,Ge,Pe,I.COLOR_BUFFER_BIT,I.NEAREST):Pt?I.copyTexSubImage3D(He,ze,$e,Dt,Qt+kt,Ke,lt,Ge,Pe):I.copyTexSubImage2D(He,ze,$e,Dt,Ke,lt,Ge,Pe);E.bindFramebuffer(I.READ_FRAMEBUFFER,null),E.bindFramebuffer(I.DRAW_FRAMEBUFFER,null)}else Pt?N.isDataTexture||N.isData3DTexture?I.texSubImage3D(He,ze,$e,Dt,Qt,Ge,Pe,Xe,Ot,Cn,Yt.data):G.isCompressedArrayTexture?I.compressedTexSubImage3D(He,ze,$e,Dt,Qt,Ge,Pe,Xe,Ot,Yt.data):I.texSubImage3D(He,ze,$e,Dt,Qt,Ge,Pe,Xe,Ot,Cn,Yt):N.isDataTexture?I.texSubImage2D(I.TEXTURE_2D,ze,$e,Dt,Ge,Pe,Ot,Cn,Yt.data):N.isCompressedTexture?I.compressedTexSubImage2D(I.TEXTURE_2D,ze,$e,Dt,Yt.width,Yt.height,Ot,Yt.data):I.texSubImage2D(I.TEXTURE_2D,ze,$e,Dt,Ge,Pe,Ot,Cn,Yt);E.pixelStorei(I.UNPACK_ROW_LENGTH,ei),E.pixelStorei(I.UNPACK_IMAGE_HEIGHT,St),E.pixelStorei(I.UNPACK_SKIP_PIXELS,pi),E.pixelStorei(I.UNPACK_SKIP_ROWS,Vi),E.pixelStorei(I.UNPACK_SKIP_IMAGES,ja),ze===0&&G.generateMipmaps&&I.generateMipmap(He),E.unbindTexture()},this.initRenderTarget=function(N){K.get(N).__webglFramebuffer===void 0&&ne.setupRenderTarget(N)},this.initTexture=function(N){N.isCubeTexture?ne.setTextureCube(N,0):N.isData3DTexture?ne.setTexture3D(N,0):N.isDataArrayTexture||N.isCompressedArrayTexture?ne.setTexture2DArray(N,0):ne.setTexture2D(N,0),E.unbindTexture()},this.resetState=function(){B=0,k=0,W=null,E.reset(),De.reset()},typeof __THREE_DEVTOOLS__<"u"&&__THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe",{detail:this}))}get coordinateSystem(){return ea}get outputColorSpace(){return this._outputColorSpace}set outputColorSpace(e){this._outputColorSpace=e;const n=this.getContext();n.drawingBufferColorSpace=mt._getDrawingBufferColorSpace(e),n.unpackColorSpace=mt._getUnpackColorSpace()}}const i2=1024,a2=2048,Ev=96,Tv=new Map;function s2(t){if(!t.agentColor)return null;const e=t.agentColor+(t.access||"");let n=Tv.get(e);if(!n){n=new vt;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)}Tv.set(e,n)}return n}function r2(t,e,n,{onSelect:i,onZoom:a}){let s;try{s=new n2({antialias:!0,alpha:!0,canvas:t})}catch{}if(!s)return null;s.setPixelRatio(Math.min(devicePixelRatio,2)),s.setClearColor(0,0);const l=new kA,o=new Ag(-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); + }`,f=new ar(1,1,1),m=new Yn({uniforms:{uHatch:{value:1},uTime:{value:0}},vertexShader:c,fragmentShader:u});let d=i2,p=new G0(f,m,d);p.frustumCulled=!1;let g=new Qr(new Float32Array(d*3),3),b=new Qr(new Float32Array(d*2),2);f.setAttribute("aColor",g),f.setAttribute("aHi",b),l.add(p);const x=new ZA(f),h=new DS({color:3814695,transparent:!0,opacity:.3});let v=[];for(let Se=0;Se<d;Se++){const I=new nh(x,h);I.visible=!1,v.push(I),l.add(I)}const y=(Se,I)=>{let Qe=Math.max(1,Se);for(;Qe<I;)Qe*=2;return Qe};function _(Se){if(Se<=d)return;const I=y(d,Se),Qe=p,Je=v,C=new G0(f,m,I);C.frustumCulled=!1,C.count=0;const E=new Qr(new Float32Array(I*3),3),V=new Qr(new Float32Array(I*2),2);f.setAttribute("aColor",E),f.setAttribute("aHi",V);const K=[];for(let ne=0;ne<I;ne++){const ye=new nh(x,h);ye.visible=!1,K.push(ye),l.add(ye)}l.remove(Qe);for(const ne of Je)l.remove(ne);p=C,g=E,b=V,v=K,d=I}const A=()=>new Yn({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 ar(1,1,1);for(let Se=0;Se<Ev;Se++){const I=new Ri(T,A());I.visible=!1,M.push(I),l.add(I)}const S=3;let w=a2,R=new Float32Array(w*S*3),D=new Float32Array(w*S);const L=new Qn;L.setAttribute("position",new un(R,3)),L.setAttribute("aA",new un(D,1));const q=new KA(L,new Yn({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}));q.frustumCulled=!1,l.add(q);let P=new Float32Array(w*6),O=new Float32Array(w*2);const B=new Qn;B.setAttribute("position",new un(P,3)),B.setAttribute("aA",new un(O,1));const k=new nh(B,new Yn({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,l.add(k);function W(Se){Se<=w||(w=y(w,Se),R=new Float32Array(w*S*3),D=new Float32Array(w*S),P=new Float32Array(w*6),O=new Float32Array(w*2),L.setAttribute("position",new un(R,3)),L.setAttribute("aA",new un(D,1)),B.setAttribute("position",new un(P,3)),B.setAttribute("aA",new un(O,1)))}const Y={yaw:Math.PI*.25,tYaw:Math.PI*.25,zoom:16,tZoom:16},he=Math.atan(1/Math.SQRT2);let _e=!1,Ce=0,X=0,de=!0;const Ne={x:-1,y:-1,live:!1};t.addEventListener("pointerdown",Se=>{_e=!0,X=0,Ce=Se.clientX,t.setPointerCapture(Se.pointerId),t.classList.add("drag")}),t.addEventListener("pointerup",Se=>{_e=!1,t.classList.remove("drag"),t.releasePointerCapture(Se.pointerId)}),t.addEventListener("pointermove",Se=>{const I=t.getBoundingClientRect();Ne.x=Se.clientX-I.left,Ne.y=Se.clientY-I.top,Ne.live=!0,_e&&(X+=Math.abs(Se.clientX-Ce),Y.tYaw-=(Se.clientX-Ce)*.006,Ce=Se.clientX,de=!1,me(!1))}),t.addEventListener("pointerleave",()=>{Ne.live=!1});const ae=Y.tZoom,j=()=>a(Math.round(ae/Y.tZoom*100)),ee=Se=>{Y.tZoom=Math.max(4,Math.min(60,Y.tZoom*Se)),j()};t.addEventListener("wheel",Se=>{Se.preventDefault(),ee(1+Math.sign(Se.deltaY)*.11)},{passive:!1}),j();let xe=null,Te=null,Ie=null,it="",re=null,me=()=>{};t.addEventListener("click",()=>{X>5||i(xe&&Te!==xe?xe:null)});const Ae=Xo.map(Se=>new vt(hg(Se)||"#8a4b2a")),se=new Z,Le=new Bt,ke=new vt;let Ue=!0,Re=performance.now();function qe(Se){requestAnimationFrame(qe);const I=Math.min(.05,(Se-Re)/1e3);Re=Se;const Qe=e.clientWidth,Je=e.clientHeight;if(!Qe||!Je)return;t.width!==Math.round(Qe*s.getPixelRatio())&&s.setSize(Qe,Je,!1);const C=1-Math.pow(.002,I);jT(),_(Wt.length),W(cs.length),de&&(Y.tYaw+=I*.12),Y.yaw+=(Y.tYaw-Y.yaw)*C,Y.zoom+=(Y.tZoom-Y.zoom)*C;const E=Y.zoom*4,V=E*(Qe/Je);o.left=-V,o.right=V,o.top=E,o.bottom=-E,o.updateProjectionMatrix();const K=180;o.position.set(Math.cos(Y.yaw)*Math.cos(he)*K,Math.sin(he)*K,Math.sin(Y.yaw)*Math.cos(he)*K),o.lookAt(0,6,0);for(let Q=0;Q<Ev;Q++){const le=M[Q],pe=xa[Q];if(!pe||Q>=xa.length){le.visible=!1;continue}pe.x=pe.x===void 0?pe.tx+pe.tw/2:pe.x,pe.z=pe.z===void 0?pe.tz+pe.th/2:pe.z;const ve=pe.tx+pe.tw/2,Me=pe.tz+pe.th/2;pe.x+=(ve-pe.x)*C,pe.z+=(Me-pe.z)*C,pe.w+=(pe.tw-pe.w)*C,pe.h+=(pe.th-pe.h)*C,le.visible=!0,le.position.set(pe.x,-.25,pe.z),le.scale.set(Math.max(.01,pe.w-Zr*.45),.5,Math.max(.01,pe.h-Zr*.45))}const ne=Te?new Set([Te.path,...Te.deps,...Te.usedBy]):null,ye=Te||xe||Ie,be=Wt.length;p.count=be;for(let Q=0;Q<be;Q++){const le=Wt[Q];le.x!==le.tx&&(le.x+=(le.tx-le.x)*C*.7),le.z!==le.tz&&(le.z+=(le.tz-le.z)*C*.7);const pe=zT(le);le.h=le.h===void 0?pe:le.h+(pe-le.h)*(le.dying?C*1.4:C*.6),le.pulse=Math.max(0,(le.pulse||0)-I*1.6),Le.makeScale(Ki*.68,Math.max(.01,le.h),Ki*.68),Le.setPosition(le.x,le.h/2,le.z),p.setMatrixAt(Q,Le);const ve=v[Q];ve.visible=!0,ve.scale.set(Ki*.68,Math.max(.01,le.h),Ki*.68),ve.position.set(le.x,le.h/2,le.z);const Me=s2(le);Me?ke.copy(Me):ke.copy(Ae[(le.ki??0)%Ae.length]).offsetHSL(0,0,(sS.get(le.dir)-.5)*.17),g.array[Q*3]=ke.r,g.array[Q*3+1]=ke.g,g.array[Q*3+2]=ke.b;const Be=le===ye?1:Math.min(.85,le.pulse||0);let Ve=ne?ne.has(le.path)?0:1:it&&!le.path.toLowerCase().includes(it)?1:0;!ne&&!it&&re&&(Ve=le.top===re?0:1),b.array[Q*2]+=(Be-b.array[Q*2])*C,b.array[Q*2+1]+=(Ve-b.array[Q*2+1])*C}for(let Q=be;Q<d;Q++)v[Q].visible=!1;p.instanceMatrix.needsUpdate=!0,g.needsUpdate=b.needsUpdate=!0;const ie=cs.length;B.setDrawRange(0,ie*2),L.setDrawRange(0,ie*S);for(let Q=0;Q<ie;Q++){const le=cs[Q],pe=le.from,ve=le.to,Me=Q*6;P[Me]=pe.x,P[Me+1]=pe.h,P[Me+2]=pe.z,P[Me+3]=ve.x,P[Me+4]=ve.h,P[Me+5]=ve.z;const Be=!ne||ne.has(pe.path)&&ne.has(ve.path),Ve=Te&&(pe===Te||ve===Te),tt=Ve?.55:Be?.1:.02;O[Q*2]+=(tt-O[Q*2])*C,O[Q*2+1]=O[Q*2];for(let H=0;H<S;H++){const Ee=Q*S+H,oe=(Se/2600+(Q*.37+H/S))%1,we=Math.sin(oe*Math.PI)*Math.hypot(ve.x-pe.x,ve.z-pe.z)*.22;R[Ee*3]=pe.x+(ve.x-pe.x)*oe,R[Ee*3+1]=pe.h+(ve.h-pe.h)*oe+we+1.2,R[Ee*3+2]=pe.z+(ve.z-pe.z)*oe,D[Ee]=(Ue?1:0)*(Ve?1:Be?.45:.06)*Math.sin(oe*Math.PI)}}if(B.getAttribute("position").needsUpdate=!0,B.getAttribute("aA").needsUpdate=!0,L.getAttribute("position").needsUpdate=!0,L.getAttribute("aA").needsUpdate=!0,q.material.uniforms.uPx.value=3.4*s.getPixelRatio(),Ne.live&&!_e){let Q=null,le=26*26;for(let pe=0;pe<be;pe++){const ve=Wt[pe];if(ve.dying||ve.h<1)continue;se.set(ve.x,ve.h*.6,ve.z).project(o);const Me=(se.x*.5+.5)*Qe,Be=(-se.y*.5+.5)*Je,Ve=(Me-Ne.x)**2+(Be-Ne.y)**2;Ve<le&&(le=Ve,Q=ve,ve.sx=Me,ve.sy=Be)}xe=Q,t.style.cursor=_e?"grabbing":Q?"pointer":"grab"}else Ne.live||(xe=null);xe?(n.style.display="block",n.style.left=xe.sx+"px",n.style.top=xe.sy+"px",n.innerHTML=`<b>${xe.name}</b> · ${xe.loc} lines<br>${xe.dir} · referenced by ${xe.usedBy.length}`):n.style.display="none",s.render(l,o)}return requestAnimationFrame(qe),{setFlow:Se=>{Ue=Se},setHatch:Se=>{m.uniforms.uHatch.value=Se?1:0},setSpin:Se=>{de=Se},spinning:()=>de,onSpinChange:Se=>{me=Se},dolly:ee,reset:()=>{Y.tYaw=Math.PI*.25,Y.tZoom=ae,j()},setSel:Se=>{Te=Se},setRailHover:Se=>{Ie=Se},setQuery:Se=>{it=Se},setFocusTop:Se=>{re=Se}}}const Ur=new Map;function Av(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 l2(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 o2(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 c2(t){var f;const e=t==null?void 0:t.workspace,n=(f=t==null?void 0:t.graph)==null?void 0:f.nodes;if(!(e!=null&&e.id)||!Array.isArray(n))return{workspaces:Ur.size,buildings:0,added:0};const i=Zb(e.name||e.canonicalPath||e.id)||e.id;for(const m of _n.slice())m.sim&&oo.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])),l=new Map;for(const m of a){const d=s.get(m==null?void 0:m.sourceId),p=s.get(m==null?void 0:m.targetId);if(!d||!p)continue;const g=Av(p);g&&(l.has(d.id)||l.set(d.id,[]),l.get(d.id).push(g))}const o=[];for(const m of n){const d=Av(m);d&&o.push({node:m,path:d})}o.sort((m,d)=>m.path.localeCompare(d.path));let c=0,u=0;for(const{node:m,path:d}of o){const p=o2(d,i);Ur.has(p)||(oo.register({id:p,name:p}),Ur.set(p,new Set));const g=Ur.get(p);if(g.has(d))continue;g.add(d),u+=g.size;const b=m.properties||{};oo.grow(p,{path:d,loc:l2(m),deps:l.get(m.id)||[],note:m.type||"",agentColor:b.agentColor||b.readerColor||null,agentName:b.agentName||b.readerName||null,access:b.agentColor?"write":b.readerColor?"read":null}),c+=1}return c>0&&oo.event(String(e.id),`${c} indexed object${c===1?"":"s"} added across ${Ur.size} districts`),{workspaces:Ur.size,buildings:u,added:c,total:o.length,truncated:!1}}function u2({snapshot:t,onSelectFile:e}){U.useEffect(()=>{t&&c2(t)},[t]);const[n,i]=U.useState(!0),[a,s]=U.useState("loc"),[l,o]=U.useState(!0),[c,u]=U.useState(!0),[f,m]=U.useState(!0),[d,p]=U.useState(100),[g,b]=U.useState(null),[x,h]=U.useState(""),[v,y]=U.useState(null),[_,A]=U.useState(!0),[,M]=U.useReducer(P=>P+1,0),T=U.useRef(null),S=U.useRef(null),w=U.useRef(null),R=U.useRef(null);U.useEffect(()=>{const P=r2(T.current,S.current,w.current,{onSelect:O=>b(O),onZoom:O=>p(O)});if(!P){i(!1);return}R.current=P,P.onSpinChange(O=>m(O))},[]),U.useEffect(()=>{const P=kT(()=>M());return()=>{P()}},[]),U.useEffect(()=>{!g&&Wt.length>0&&b(Wt[0])},[Wt.length,g]),U.useEffect(()=>{var P;(P=R.current)==null||P.setSel(g)},[g]),U.useEffect(()=>{var P;(P=R.current)==null||P.setQuery(x)},[x]),U.useEffect(()=>{var P;(P=R.current)==null||P.setFocusTop(v)},[v]),U.useEffect(()=>{const P=O=>{var k,W;const B=O.target;if(/^(INPUT|TEXTAREA)$/.test(B.tagName)){O.key==="Escape"&&B.blur();return}O.key==="Escape"?(b(null),y(null)):O.key==="="||O.key==="+"?(k=R.current)==null||k.dolly(.8474576271186441):O.key==="-"||O.key==="_"?(W=R.current)==null||W.dolly(1.18):(O.key==="e"||O.key==="E")&&A(Y=>!Y)};return addEventListener("keydown",P),()=>removeEventListener("keydown",P)},[]);const D=Wt.reduce((P,O)=>P+O.loc,0),L=_n.reduce((P,O)=>P+O.events,0),q=(P,O)=>O.length?r.jsxs(r.Fragment,{children:[r.jsxs("h3",{children:[P+" ",r.jsx("span",{style:{color:"var(--faint)"},children:O.length})]}),O.map(B=>{const k=No[B];return k&&r.jsxs("div",{className:"dep","data-p":B,onClick:()=>b(k),children:[r.jsx("span",{className:"sw",style:{background:OT(k)}}),r.jsx("span",{children:B})]},B)})]}):null;return r.jsxs("div",{id:"app",className:g?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:_n.length}),r.jsx("i",{children:"Workspaces"})]}),r.jsxs("div",{children:[r.jsx("b",{id:"k-bld",children:Wt.length}),r.jsx("i",{children:"Dateien"})]}),r.jsxs("div",{children:[r.jsx("b",{id:"k-ev",children:L}),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:P=>h(P.target.value.trim().toLowerCase())})}),r.jsx("div",{className:"tree",id:"tree",children:_n.length?_n.map(P=>{const O=Wt.filter(k=>k.dir===P.id),B=O.reduce((k,W)=>k+W.loc,0);return r.jsxs("div",{className:"ws"+(v===P.id?" on":"")+(P.dying?" dying":""),onClick:()=>y(k=>k===P.id?null:P.id),children:[r.jsxs("div",{className:"wsrow",children:[r.jsx("span",{className:"sw",style:{background:g0(P)}}),r.jsx("span",{className:"nm",children:P.name}),P.sim?r.jsx("span",{className:"tag",children:"sim"}):null,r.jsxs("span",{className:"lc",children:[O.length," Dat. · ",B," Z."]})]}),r.jsx("div",{className:"loadbar",children:r.jsx("i",{style:{width:Math.round(P.load*100)+"%",background:g0(P)}})})]},P.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:T}),r.jsx("div",{id:"tip",ref:w}),r.jsxs("div",{id:"crumb",children:["PLUGBRAIN / ",r.jsx("b",{id:"crumb-t",children:g?g.path.toUpperCase():v?v.toUpperCase():"STADT-ÜBERSICHT"})]}),_&&r.jsx("div",{id:"feed",children:Eu.slice(0,9).map(P=>r.jsxs("div",{className:"fe",children:[r.jsx("span",{className:"ft",children:P.t.toLocaleTimeString("de-DE",{hour12:!1})}),r.jsx("span",{className:"fw",style:{color:"var(--accent)"},children:P.ws}),r.jsx("span",{className:"fm",children:P.msg})]},P.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(([P,O])=>r.jsx("button",{className:"tb"+(a===P?" on":""),"data-h":P,type:"button",onClick:()=>{PT(P),s(P)},children:O},P)),r.jsx("div",{className:"vsep"}),r.jsx("button",{className:"tb"+(l?" on":""),id:"t-flow",type:"button",onClick:()=>{o(P=>{var O;return(O=R.current)==null||O.setFlow(!P),!P})},children:"Fluss"}),r.jsx("button",{className:"tb"+(c?" on":""),id:"t-hatch",type:"button",onClick:()=>{u(P=>{var O;return(O=R.current)==null||O.setHatch(!P),!P})},children:"Schraffur"}),r.jsx("button",{className:"tb"+(f?" on":""),id:"t-spin",type:"button",onClick:()=>{m(P=>{var O;return(O=R.current)==null||O.setSpin(!P),!P})},children:"Orbit"}),r.jsx("button",{className:"tb"+(_?" on":""),id:"t-feed",type:"button",title:"Feed ein-/ausblenden (E)",onClick:()=>A(P=>!P),children:"Feed"}),r.jsx("div",{className:"vsep"}),r.jsx("button",{className:"tb",id:"zout",type:"button",title:"Herauszoomen",onClick:()=>{var P;return(P=R.current)==null?void 0:P.dolly(1.18)},children:"−"}),r.jsx("button",{className:"tb",id:"zlvl",type:"button",title:"Zoom zurücksetzen",onClick:()=>{var P;return(P=R.current)==null?void 0:P.reset()},children:d+"%"}),r.jsx("button",{className:"tb",id:"zin",type:"button",title:"Hineinzoomen",onClick:()=>{var P;return(P=R.current)==null?void 0:P.dolly(1/1.18)},children:"+"}),r.jsx("div",{className:"vsep"}),r.jsx("button",{className:"tb",id:"t-reset",type:"button",onClick:()=>{var P;(P=R.current)==null||P.reset(),b(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:g&&r.jsxs("div",{className:"dt",children:[r.jsx("div",{className:"kind",children:g.dir+"/"}),r.jsx("h2",{children:g.name}),g.note?r.jsx("div",{className:"note",children:g.note}):null,e&&r.jsx("button",{type:"button",className:"btn primary",style:{marginTop:"10px",marginBottom:"14px",width:"100%",padding:"8px 12px"},onClick:()=>e(g.path),children:"Datei in Quellansicht öffnen"}),r.jsxs("dl",{children:[r.jsx("dt",{children:"Size"}),r.jsx("dd",{children:g.loc}),r.jsx("dt",{children:"References"}),r.jsx("dd",{children:g.deps.length}),r.jsx("dt",{children:"Referenced by"}),r.jsx("dd",{children:g.usedBy.length}),r.jsx("dt",{children:"Share of total"}),r.jsx("dd",{children:D?(g.loc/D*100).toFixed(1)+"%":"—"})]}),q("References",g.deps),q("Referenced by",g.usedBy)]})})})]})}const wv={agent:"Agent",task:"Aufgabe",worker:"Worker",worktree:"Worktree",file:"Datei",artifact:"Artefakt",route:"Route"},Nv={live:"Live-Ereignis",recovered:"wiederhergestelltes Ereignis","historical-import":"historischer Import"};function uh(t){const e=new Date(t);return Number.isNaN(e.getTime())?t:e.toLocaleString()}function dh(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 d2(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 Cv(t,e){var n;return((n=t.find(i=>i.id===e))==null?void 0:n.label)??e}function f2({mesh:t,workspaceId:e,onSelectFile:n,focusAgentId:i}){const[a,s]=U.useState(null),[l,o]=U.useState([]),[c,u]=U.useState(!1),[f,m]=U.useState(""),d=U.useMemo(()=>(t==null?void 0:t.nodes.find(g=>g.id===a))??null,[t,a]);return U.useEffect(()=>{if(!i)return;const g=`agent:${i}`;t!=null&&t.nodes.some(b=>b.id===g)&&s(g)},[i,t]),U.useEffect(()=>{a!==null&&d===null&&s(null)},[d,a]),U.useEffect(()=>{const g=d?d2(d):null;if(!e||g===null){o([]),m(""),u(!1);return}let b=!0;return u(!0),m(""),pT(e,{...g,limit:12}).then(x=>{b&&o(x)}).catch(()=>{b&&(o([]),m("Die Trace-Zeitleiste ist derzeit nicht verfügbar."))}).finally(()=>{b&&u(!1)}),()=>{b=!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(g=>r.jsxs("li",{children:[r.jsx("code",{children:g.workerId}),g.taskId?r.jsxs(r.Fragment,{children:[" · Aufgabe ",r.jsx("code",{children:g.taskId})]}):""," — ",g.reason]},g.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(g=>{const b=dh(g),x=g.id===a;return r.jsx("li",{children:r.jsxs("button",{type:"button",className:x?"mesh-trace__node is-selected":"mesh-trace__node",onClick:()=>s(g.id),"aria-pressed":x,children:[r.jsx("span",{className:"mesh-trace__kind",children:wv[g.kind]}),r.jsx("span",{className:"mesh-trace__label",title:g.label,children:g.label}),r.jsxs("span",{className:"mesh-trace__events",children:[g.eventCount," Ereignis",g.eventCount===1?"":"se"]}),r.jsx("span",{className:"mesh-trace__provenance",title:"Ereignis-Provenienz, nicht aktueller Prozessstatus",children:Nv[g.provenance]}),b&&r.jsx("span",{className:"mesh-trace__proof",children:b})]})},g.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(g=>r.jsxs("li",{children:[r.jsx("span",{className:"mesh-trace__edge-kind",children:g.kind}),r.jsx("span",{title:g.from,children:Cv(t.nodes,g.from)}),r.jsx("span",{"aria-hidden":"true",children:"→"}),r.jsx("span",{title:g.to,children:Cv(t.nodes,g.to)}),r.jsxs("small",{children:[g.count," Ereignis",g.count===1?"":"se"," · Belege: ",g.evidence.join(", ")]})]},g.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:wv[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:uh(d.firstSeen)})]}),r.jsxs("div",{children:[r.jsx("dt",{children:"Zuletzt"}),r.jsx("dd",{children:uh(d.lastSeen)})]}),r.jsxs("div",{children:[r.jsx("dt",{children:"Provenienz"}),r.jsx("dd",{children:Nv[d.provenance]})]}),dh(d)&&r.jsxs("div",{children:[r.jsx("dt",{children:"Worker-Beweis"}),r.jsx("dd",{children:dh(d)})]}),Object.entries(d.detail).map(([g,b])=>r.jsxs("div",{children:[r.jsx("dt",{children:g}),r.jsx("dd",{children:b??"—"})]},g))]}),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 …"}),f&&r.jsx("p",{role:"status",children:f}),!c&&!f&&l.length===0&&r.jsx("p",{children:"Für diesen Knotentyp gibt es keine gefilterte Zeitleiste."}),r.jsx("ol",{children:l.map(g=>r.jsxs("li",{children:[r.jsx("code",{children:g.type})," ",r.jsx("time",{dateTime:g.occurredAt,children:uh(g.occurredAt)}),r.jsx("span",{children:g.summary})]},g.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 h2({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(au,{value:e,label:"WARTEND",tone:e>8?"hot":void 0}),r.jsx(au,{value:t.filter(n=>n.state==="claimed").length,label:"IN ARBEIT"}),r.jsx(au,{value:t.filter(n=>n.state==="delivered").length,label:"GELIEFERT"}),r.jsx(au,{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:p2[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 p2={pending:"WARTET",claimed:"IN ARBEIT",delivered:"GELIEFERT",cancelled:"ABGEBROCHEN"};function au({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 m2=8e3;function g2(t,e=m2){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 Rv({workspaceId:t,path:e,highlightLine:n,onClose:i,onNavigateFile:a}){const[s,l]=U.useState(!0),[o,c]=U.useState(null),[u,f]=U.useState(null),[m,d]=U.useState(null),[p,g]=U.useState([]),b=U.useRef(null);if(U.useEffect(()=>{let y=!0;return l(!0),c(null),f(null),d(null),g([]),g2(bT(t,e)).then(_=>{y&&(c(_),l(!1))}).catch(_=>{y&&(c({ok:!1,path:e,content:"",bytes:0,lang:null,error:String((_==null?void 0:_.message)??_)}),l(!1))}),aS(t).then(_=>{y&&f(_)}).catch(()=>{}),xT(t,e).then(_=>{y&&d(_)}).catch(()=>{}),ET(t,e).then(_=>{y&&g(_)}).catch(()=>{}),()=>{y=!1}},[t,e]),U.useEffect(()=>{!s&&b.current&&b.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 x=o.content.split(/\r?\n/),h=x.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:[h," 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",")"]}),(m==null?void 0:m.owner)&&r.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:[r.jsx("i",{style:{background:m.owner.color}}),m.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:x.map((y,_)=>{const A=_+1,M=n===A;return r.jsxs("tr",{ref:M?b:void 0,className:`source-line-row ${M?"source-line-row--highlight":""}`,children:[r.jsx("td",{className:"source-line-num mono","data-line":A,children:A}),r.jsx("td",{className:"source-line-code mono",children:r.jsx("pre",{children:y||" "})})]},A)})})})}),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,_)=>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,")"]})]},_))})]})]})]})}function Et({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 yt={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 x2(t){return!t||typeof t!="string"?"":t.replace(/\\/g,"/")}function v2(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 _2({workspaceName:t,files:e,activePath:n,onSelectFile:i}){const[a,s]=U.useState(""),[l,o]=U.useState(new Set),c=U.useMemo(()=>v2(e),[e]),u=U.useMemo(()=>{const x=[];function h(v){v.isDir&&v.path&&x.push(v.path);for(const y of v.children.values())y.isDir&&h(y)}return h(c),x},[c]),f=x=>{o(h=>{const v=new Set(h);return v.has(x)?v.delete(x):v.add(x),v})},m=()=>o(new Set),d=()=>o(new Set(u)),p=x2(n);function g(x,h){if(!h)return!0;const v=h.toLowerCase();if(!x.isDir)return x.name.toLowerCase().includes(v)||x.path.toLowerCase().includes(v);if(x.name&&x.name.toLowerCase().includes(v))return!0;for(const y of x.children.values())if(g(y,h))return!0;return!1}const b=(x,h=0)=>{var y,_,A;if(x.isDir){const M=l.has(x.path),T=Array.from(x.children.values()).sort((w,R)=>w.isDir!==R.isDir?w.isDir?-1:1:w.name.localeCompare(R.name)),S=a?T.filter(w=>g(w,a)):T;return a&&S.length===0&&(!x.name||!x.name.toLowerCase().includes(a.toLowerCase()))?null:r.jsxs("div",{className:"tree-dir-group",children:[x.name&&r.jsxs("div",{className:`tree-item tree-item--dir ${h===0?"tree-item--root":""}`,style:{paddingLeft:`${h*14+10}px`},onClick:()=>f(x.path),role:"treeitem","aria-expanded":!M,children:[r.jsx("span",{className:"tree-chevron",children:M?"▶":"▼"}),r.jsx("span",{className:"tree-icon",children:r.jsx(Et,{path:yt.folder})}),r.jsx("span",{className:"tree-label",children:x.name}),r.jsx("span",{className:"tree-badge tree-badge--count",children:x.children.size})]}),(!M||a)&&r.jsx("div",{className:"tree-dir-children",children:S.map(w=>b(w,x.name?h+1:h))})]},x.path||"root")}const v=p!==""&&p===x.path;return a&&!x.path.toLowerCase().includes(a.toLowerCase())?null:r.jsxs("div",{className:`tree-item tree-item--file ${v?"tree-item--active":""}`,style:{paddingLeft:`${h*14+22}px`},onClick:()=>i(x.path),title:x.path,role:"treeitem",children:[r.jsx("span",{className:"tree-dot","aria-hidden":"true"}),r.jsx("span",{className:"tree-label mono",children:x.name}),((y=x.file)==null?void 0:y.lang)&&r.jsx("span",{className:"tree-badge tree-badge--lang",children:x.file.lang}),((_=x.file)==null?void 0:_.loc)!==void 0&&r.jsxs("span",{className:"tree-badge tree-badge--loc",children:[x.file.loc," L"]}),((A=x.file)==null?void 0:A.agent)&&r.jsx("span",{className:"tree-agent-dot",style:{background:x.file.agent.color},title:`Owner: ${x.file.agent.name}`})]},x.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(Et,{path:yt.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:x=>s(x.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:m,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."}):b(c)})]})}function y2({workspaceId:t,onSelectHit:e}){const[n,i]=U.useState(()=>{const M=new URLSearchParams(window.location.search).get("mode");return M==="code"||M==="notes"||M==="prose"?M:"all"}),[a,s]=U.useState(()=>new URLSearchParams(window.location.search).get("q")||""),[l,o]=U.useState([]),[c,u]=U.useState([]),[f,m]=U.useState(null),[d,p]=U.useState(!1),[g,b]=U.useState(""),[x,h]=U.useState(""),[v,y]=U.useState(""),_=()=>{let M=`# Suchtreffer-Kontext: "${x}" +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 (${l.length}): +`;for(const T of l)M+=`- **${T.name}** (${T.kind}) — \`${T.path}${T.line?`:${T.line}`:""}\` +`,T.snippet&&(M+=` > ${T.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")})},A=async(M,T,S)=>{M&&M.preventDefault();const w=S??n,R=(T??a).trim();if(!R)return;p(!0),b(""),m(null);const D=performance.now();try{if(w==="all"){const[L,q]=await Promise.allSettled([hp(t,R),pp(t,R)]),P=[];if(L.status==="fulfilled")for(const O of L.value)P.push({id:`code-${O.path}-${O.name}-${O.line??0}`,type:"code",name:O.name,kind:O.kind,path:O.path,line:O.line});if(q.status==="fulfilled")for(const O of q.value.hits||[])P.push({id:`note-${O.path}-${O.line??0}`,type:"note",name:O.title,kind:"Notiz",path:O.path,line:O.line,snippet:O.snippet});o(P),u([])}else if(w==="code"){const L=await hp(t,R);o(L.map((q,P)=>({id:`code-${q.path}-${q.name}-${q.line??P}`,type:"code",name:q.name,kind:q.kind,path:q.path,line:q.line}))),u([])}else if(w==="prose"){const L=await pp(t,R);o((L.hits||[]).map((q,P)=>({id:`note-${q.path}-${q.line??P}`,type:"note",name:q.title,kind:"Notiz",path:q.path,line:q.line,snippet:q.snippet}))),u([])}else{const L=await MT(t,R);u(L.notes||[]),o([])}m(Math.round(performance.now()-D)),h(R)}catch(L){b((L==null?void 0:L.message)||`Fehler bei der Suche (${w})`),o([]),u([])}finally{p(!1)}};return U.useEffect(()=>{const M=new URLSearchParams(window.location.search).get("q");M&&t&&A(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(Et,{path:yt.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()&&A(void 0,a,"all")},children:"Alle"}),r.jsx("button",{type:"button",className:`tb ${n==="code"?"on":""}`,onClick:()=>{i("code"),a.trim()&&A(void 0,a,"code")},children:"Code & Symbole"}),r.jsx("button",{type:"button",className:`tb ${n==="prose"?"on":""}`,onClick:()=>{i("prose"),a.trim()&&A(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),A(void 0,M,"notes")},children:"Properties (Bases)"})]})]}),r.jsx("form",{className:"search-form",onSubmit:A,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"})]})}),g&&r.jsx("div",{className:"search-error-alert",role:"alert",children:g}),r.jsxs("div",{className:"search-results",children:[x&&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 "${x}"`:`${c.length} Notiz(en) gefunden für "${x}":`:l.length===0?`Keine Treffer für "${x}" im Brain-Index`:`${l.length} Treffer für "${x}":`,f!==null&&r.jsxs("span",{className:"search-results-time mono",style:{marginLeft:"8px",opacity:.7},children:[f," ms"]})]}),(n==="notes"&&c.length>0||n!=="notes"&&l.length>0)&&r.jsx("button",{type:"button",className:"pb-button pb-button--secondary",onClick:()=>_(),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("div",{className:"search-hit-card",onClick:()=>e(M.path),children:[r.jsxs("div",{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("div",{className:"search-hit-path mono",title:M.path,style:{marginTop:"4px"},children:M.path}),(M.inLinks!==void 0||M.outLinks!==void 0)&&r.jsxs("div",{style:{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("div",{className:"search-hit-card",onClick:()=>e(M.path,M.line),children:[r.jsxs("div",{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("div",{className:"search-hit-snippet",children:M.snippet}),r.jsx("div",{className:"search-hit-path mono",title:M.path,style:{marginTop:"4px"},children:M.path})]},M.id))})]})]})}function b2({workspaceId:t,focus:e,onOpenNote:n}){const[i,a]=U.useState(1),[s,l]=U.useState(120),[o,c]=U.useState(null),[u,f]=U.useState(0),[m,d]=U.useState("");U.useEffect(()=>{let v=!0;return d(""),AT(t,{focus:e,depth:i,limit:s}).then(y=>{v&&(c(y),f(0))}).catch(y=>{v&&(c(null),d(y instanceof Error?y.message:String(y)))}),()=>{v=!1}},[t,e,i,s]);const p=U.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]),g=p[u]??null,b=U.useMemo(()=>!g||!o?[]:o.edges.filter(v=>v.source===g.id||v.target===g.id).map(v=>p.find(y=>y.id===(v.source===g.id?v.target:v.source))).filter(v=>v!==void 0),[g,o,p]),x=v=>f(y=>Math.max(0,Math.min(p.length-1,y+v))),h=v=>{p.length&&(v.key==="ArrowDown"||v.key==="ArrowRight"?(v.preventDefault(),x(1)):v.key==="ArrowUp"||v.key==="ArrowLeft"?(v.preventDefault(),x(-1)):v.key==="Home"?(v.preventDefault(),f(0)):v.key==="End"?(v.preventDefault(),f(p.length-1)):v.key==="Enter"&&g&&(v.preventDefault(),n(g.path)))};return r.jsxs("section",{className:"knowledge-graph","aria-label":"Wissensgraph",onKeyDown:h,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)"})]})]})]}),m&&r.jsx("p",{className:"notes-conflict",role:"alert",children:m}),!m&&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 _;return r.jsx("li",{children:r.jsxs("button",{type:"button",className:y===u?"on":"",onFocus:()=>f(y),onClick:()=>n(v.path),children:[r.jsx("i",{style:{background:(_=o==null?void 0:o.groups.find(A=>A.name===v.group))==null?void 0:_.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:g?r.jsxs(r.Fragment,{children:[r.jsx("h3",{children:"Inspector"}),r.jsx("strong",{children:g.title}),r.jsx("code",{children:g.path}),r.jsxs("dl",{children:[r.jsxs("div",{children:[r.jsx("dt",{children:"Typ"}),r.jsx("dd",{children:g.properties.typ??"—"})]}),r.jsxs("div",{children:[r.jsx("dt",{children:"Status"}),r.jsx("dd",{children:g.properties.stand??"—"})]}),r.jsxs("div",{children:[r.jsx("dt",{children:"Tags"}),r.jsx("dd",{children:g.properties.tags.map(v=>`#${v}`).join(" ")||"—"})]}),r.jsxs("div",{children:[r.jsx("dt",{children:"Verweise"}),r.jsxs("dd",{children:[g.outLinks," hinaus · ",g.inLinks," herein"]})]})]}),r.jsx("button",{type:"button",className:"primary",onClick:()=>n(g.path),children:"Notiz öffnen"}),b.length>0&&r.jsxs("section",{children:[r.jsx("h4",{children:"Verknüpft"}),b.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 Dv={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 S2(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 M2({code:t,language:e}){const[n,i]=U.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=S2(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,f)=>r.jsx("span",{className:`md-tok md-tok--${u.type}`,children:u.text},f)),c.length===0&&" "]})]},o)})})})]})}function E2(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 qn(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 f=`${i}-${l++}`;if(c[1]){const m=c[2].trim(),d=((u=c[3])==null?void 0:u.trim())||m;a.push(r.jsxs("button",{type:"button",className:"md-wikilink",onClick:()=>e==null?void 0:e(m),title:`Notiz öffnen: ${m}`,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:"↗"})]},f))}else if(c[4]){const m=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(":"),g=d.startsWith("http://")||d.startsWith("https://");p?a.push(r.jsx("a",{href:d,className:"md-link",target:g?"_blank":void 0,rel:g?"noopener noreferrer":void 0,children:m},f)):a.push(r.jsx("span",{className:"md-link-blocked",children:m},f))}else if(c[7]){const m=c[8];E2(m)&&n?a.push(r.jsxs("button",{type:"button",className:"md-code-link",onClick:()=>n(m),title:`Quelle öffnen: ${m}`,children:[r.jsx("code",{children:m}),r.jsx("span",{className:"md-code-link__glyph","aria-hidden":"true",children:"↗"})]},f)):a.push(r.jsx("code",{className:"md-inline-code",children:m},f))}else c[9]?a.push(r.jsx("strong",{children:qn(c[10],e,n,`${f}-b`)},f)):c[11]?a.push(r.jsx("em",{children:qn(c[12],e,n,`${f}-i`)},f)):c[13]?a.push(r.jsx("strong",{children:qn(c[14],e,n,`${f}-b2`)},f)):c[15]?a.push(r.jsx("em",{children:qn(c[16],e,n,`${f}-i2`)},f)):c[17]&&a.push(r.jsx("del",{children:qn(c[18],e,n,`${f}-s`)},f));s=c.index+c[0].length}return s<t.length&&a.push(t.slice(s)),a}function T2({content:t,onNavigateNote:e,onOpenSource:n,className:i=""}){const a=U.useMemo(()=>t?t.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/,""):"",[t]),s=U.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(M2,{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 g=(d[0]||"").match(/^\[!(note|important|warning|tip|caution)\]/i);if(g){const b=g[1].toLowerCase(),x=Dv[b]||Dv.note,h=d.slice(1).join(` +`);o.push(r.jsxs("div",{className:`md-callout md-callout--${x.type}`,style:{borderLeft:`4px solid ${x.border}`,backgroundColor:x.bg},children:[r.jsxs("div",{className:"md-callout-header",style:{color:x.color},children:[r.jsx("span",{className:"md-callout-glyph",children:x.glyph}),r.jsx("span",{className:"md-callout-title",children:x.title})]}),h&&r.jsx("div",{className:"md-callout-content",children:h.split(` +`).map((v,y)=>r.jsx("p",{children:qn(v,e,n,`callout-${o.length}-${y}`)},y))})]},`callout-${o.length}`))}else o.push(r.jsx("blockquote",{className:"md-blockquote",children:d.map((b,x)=>r.jsx("p",{children:qn(b,e,n,`bq-${o.length}-${x}`)},x))},`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(_=>_.trim())},g=p(d[0]),x=p(d[1]).map(v=>{const y=v.startsWith(":"),_=v.endsWith(":");return y&&_?"center":_?"right":"left"}),h=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:g.map((v,y)=>r.jsx("th",{style:{textAlign:x[y]||"left"},children:qn(v,e,n,`th-${o.length}-${y}`)},y))})}),r.jsx("tbody",{children:h.map((v,y)=>r.jsx("tr",{children:v.map((_,A)=>r.jsx("td",{style:{textAlign:x[A]||"left"},children:qn(_,e,n,`td-${o.length}-${y}-${A}`)},A))},y))})]})},`table-${o.length}`));continue}const f=u.match(/^(#{1,6})\s+(.*)$/);if(f){const d=f[1].length,p=f[2],g=qn(p,e,n,`h-${o.length}`),b=`h-${o.length}`;switch(d){case 1:o.push(r.jsx("h1",{className:"md-heading md-heading--1",children:g},b));break;case 2:o.push(r.jsx("h2",{className:"md-heading md-heading--2",children:g},b));break;case 3:o.push(r.jsx("h3",{className:"md-heading md-heading--3",children:g},b));break;case 4:o.push(r.jsx("h4",{className:"md-heading md-heading--4",children:g},b));break;case 5:o.push(r.jsx("h5",{className:"md-heading md-heading--5",children:g},b));break;default:o.push(r.jsx("h6",{className:"md-heading md-heading--6",children:g},b));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,g)=>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:qn(p.text,e,n,`check-${o.length}-${g}`)})]},g))},`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,g)=>r.jsx("li",{children:qn(p,e,n,`ul-${o.length}-${g}`)},g))},`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,g)=>r.jsx("li",{children:qn(p,e,n,`ol-${o.length}-${g}`)},g))},`ol-${o.length}`));continue}const m=[];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]));)m.push(l[c]),c++;m.length>0&&o.push(r.jsx("p",{className:"md-paragraph",children:m.map((d,p)=>r.jsxs(yM.Fragment,{children:[qn(d,e,n,`p-${o.length}-${p}`),p<m.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 A2(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 w2({workspaceId:t,onOpenSource:e,onOpenRevision:n,onOpenAgentRun:i,onNavigateTab:a}){const[s,l]=U.useState([]),[o,c]=U.useState(null),[u,f]=U.useState(""),[m,d]=U.useState(""),[p,g]=U.useState(!1),[b,x]=U.useState(""),[h,v]=U.useState(""),[y,_]=U.useState(""),[A,M]=U.useState([]),[T,S]=U.useState([]),[w,R]=U.useState([]),[D,L]=U.useState(!1),[q,P]=U.useState(""),[O,B]=U.useState(""),[k,W]=U.useState("saved"),[Y,he]=U.useState(!0),[_e,Ce]=U.useState(!0),[X,de]=U.useState(!1),[Ne,ae]=U.useState(null),[j,ee]=U.useState("editor"),[xe,Te]=U.useState("edit"),[Ie,it]=U.useState("notiz"),[re,me]=U.useState("entwurf"),[Ae,se]=U.useState(()=>{try{return localStorage.getItem("plugbrain.notes_meta_collapsed")==="1"}catch{return!1}}),[Le,ke]=U.useState(!1),[Ue,Re]=U.useState(!1),[qe,Se]=U.useState(""),[I,Qe]=U.useState(!1),[Je,C]=U.useState(""),[E,V]=U.useState(0),K=()=>{se(z=>{const ue=!z;try{localStorage.setItem("plugbrain.notes_meta_collapsed",ue?"1":"0")}catch{}return ue})},ne=U.useRef(null),ye=async()=>{const z=await TT(t);return l(z),z},be=async z=>{L(!0),B(""),P(""),de(!1);try{const ue=await Ac(t,z);c(ue),f(ue.content),W("saved"),R(await p0(t,ue.path))}catch(ue){P(ue instanceof Error?ue.message:String(ue))}finally{L(!1)}};U.useEffect(()=>{ye().then(z=>{if(z[0])return be(z[0].path)}).catch(z=>P(String(z)))},[t]);const ie=U.useMemo(()=>[...new Set(s.flatMap(z=>z.tags||[]))].sort(),[s]),Q=U.useMemo(()=>{const z={};for(const ue of s)if(ue.tags)for(const je of ue.tags)z[je]=(z[je]||0)+1;return z},[s]),le=U.useMemo(()=>{if(!b.trim())return ie;const z=b.toLowerCase();return ie.filter(ue=>ue.toLowerCase().includes(z))},[ie,b]),pe=U.useMemo(()=>s.filter(z=>{var ue;return!m||((ue=z.tags)==null?void 0:ue.includes(m))}),[s,m]);U.useEffect(()=>{o&&(u!==o.content?k!=="conflict"&&k!=="saving"&&W("dirty"):k==="dirty"&&W("saved"))},[u,o==null?void 0:o.content,k]);const ve=async(z=!1)=>{if(!(!o||D)){L(!0),B(""),P(""),W("saving");try{const ue=o.content,je=await Wl(t,o.path,u,o.hash||void 0,void 0,o.hash==="");ae({path:o.path,content:ue,savedHash:je.hash});const ht=await Ac(t,o.path);c(ht),f(ht.content),W("saved"),await ye(),P(z?"Automatisch gespeichert und im Brain indiziert.":je.created?"Notiz angelegt und im Brain indiziert.":"Gespeichert und im Brain indiziert.")}catch(ue){ue!=null&&ue.conflict||(ue==null?void 0:ue.status)===409?(B(ue!=null&&ue.conflict?`${ue.message} Bitte neu laden und die Änderungen zusammenführen.`:"Konflikt beim Speichern: Die Notiz wurde extern geändert."),W("conflict")):(P(ue instanceof Error?ue.message:String(ue)),W("dirty"))}finally{L(!1)}}},Me=async()=>{if(!(!o||D)){L(!0),B(""),P(""),W("saving");try{const z=o.content,ue=await Wl(t,o.path,u,void 0,void 0,!1);ae({path:o.path,content:z,savedHash:ue.hash});const je=await Ac(t,o.path);c(je),f(je.content),W("saved"),de(!1),await ye(),P("Änderungen erzwungen und im Brain indiziert.")}catch(z){P(z instanceof Error?z.message:String(z)),W("conflict")}finally{L(!1)}}};U.useEffect(()=>{if(!Y||!o||o.hash===""||D||O||u===o.content)return;const z=setTimeout(()=>{!D&&!O&&u!==o.content&&o&&o.hash!==""&&ve(!0)},600);return()=>clearTimeout(z)},[u,o,Y,D,O]);const Be=async()=>{if(!(!Ne||!o||Ne.path!==o.path||D)){L(!0),B(""),P("");try{const z=await Wl(t,Ne.path,Ne.content,Ne.savedHash),ue=await Ac(t,Ne.path);c(ue),f(ue.content),ae(null),W("saved"),await ye(),P("Wiederhergestellt.")}catch(z){P(z instanceof Error?z.message:String(z))}finally{L(!1)}}},Ve=()=>{const z=document.querySelector(".notes-editor textarea");if(!z)return;const ue=z.selectionStart,je=z.selectionEnd,ht=u.substring(ue,je),qt=`[[${ht||"Notizname"}]]`,an=u.substring(0,ue)+qt+u.substring(je);f(an),setTimeout(()=>{z.focus();const mn=ue+(ht?qt.length:2);z.setSelectionRange(mn,mn+(ht?0:9))},10)},tt=async z=>{if(!o||!z.trim())return;let ue=z.trim();if(ue.endsWith(".md")||(ue+=".md"),ue===o.path){Re(!1);return}L(!0);try{await Wl(t,ue,u,void 0,void 0,!0),await ye(),await be(ue),Re(!1),P(`Notiz umbenannt nach "${ue}".`)}catch(je){P(je instanceof Error?je.message:String(je))}finally{L(!1)}};U.useEffect(()=>{const z=ue=>{(ue.ctrlKey||ue.metaKey)&&ue.key.toLowerCase()==="o"?(ue.preventDefault(),Qe(!0),C(""),V(0)):ue.key==="Escape"&&(I&&Qe(!1),Ue&&Re(!1),Le&&ke(!1))};return window.addEventListener("keydown",z),()=>window.removeEventListener("keydown",z)},[I,Ue,Le]);const H=U.useMemo(()=>{if(!Je.trim())return s.slice(0,15);const z=Je.toLowerCase();return s.filter(ue=>ue.title.toLowerCase().includes(z)||ue.path.toLowerCase().includes(z)||(ue.tags||[]).some(je=>je.toLowerCase().includes(z))).slice(0,20)},[s,Je]),Ee=async()=>{const z=prompt("Dateiname der neuen Notiz (z.B. Architektur.md):");if(!z)return;const ue=z.endsWith(".md")?z:`${z}.md`,je=ue.replace(/\.md$/,"").split("/").pop()||ue,ht=Ie==="notiz"?"":Ie,qt=ht?ht==="entscheidung"?`--- +typ: ${ht} +stand: ${re} +bezug: +code: +revision: +--- + +# ${je} + +## Kontext + +## Entscheidung + +## Konsequenzen +`:`--- +typ: ${ht} +stand: ${re} +bezug: +code: +--- + +# ${je} + +## Befund + +## Ursache + +## Lösungsvorschlag +`:`# ${je} + +`;L(!0);try{await Wl(t,ue,qt,void 0,void 0,!0),await ye(),await be(ue)}catch(an){P(an instanceof Error?an.message:String(an))}finally{L(!1)}},oe=z=>{const ue=(o==null?void 0:o.title)||"Notiz";let je="";z==="entscheidung"?je=`--- +typ: entscheidung +stand: entwurf +bezug: +code: +revision: +--- + +# ${ue} + +## Kontext + +## Entscheidung + +## Konsequenzen + +`:je=`--- +typ: widerspruch +stand: offen +bezug: +code: +--- + +# ${ue} + +## Befund + +## Ursache + +## Lösungsvorschlag + +`,f(ht=>je+ht)},we=z=>{const{path:ue,line:je}=A2(z);e==null||e(ue,je)},De=z=>{const ue=z.trim(),je=s.find(ht=>ht.path===ue||ht.path===`Notizen/${ue}.md`||ht.path.replace(/\.md$/,"").endsWith(ue)||ht.title.toLowerCase()===ue.toLowerCase());be(je?je.path:`Notizen/${ue}.md`)},ge=async z=>{if(z.preventDefault(),!y.trim()){M([]),S([]);return}L(!0);try{const ue=await pp(t,y);M(ue.hits||[]);try{const je=await hp(t,y);S(je)}catch{S([])}}catch(ue){P(ue instanceof Error?ue.message:String(ue))}finally{L(!1)}},We=async z=>{if(!(!z||!o)){L(!0);try{await wT(t,o.path,z),R(await p0(t,o.path)),P(`Anhang ${z.name} hochgeladen.`)}catch(ue){P(ue instanceof Error?ue.message:String(ue))}finally{L(!1)}}},Fe=z=>{(z.ctrlKey||z.metaKey)&&z.key==="s"&&(z.preventDefault(),ve(!1))},bt=z=>{var ue;return((ue=o==null?void 0:o.properties.find(je=>je.key.toLowerCase()===z))==null?void 0:ue.value.trim())||null},rt=bt("bezug"),nn=bt("code"),bn=bt("revision"),Ia=bt("agentenlauf"),_t=U.useMemo(()=>{var ht;if(!u)return null;const z=u.match(/^---\r?\n([\s\S]*?)\r?\n---/);if(!z){if(!((ht=o==null?void 0:o.properties)!=null&&ht.length))return null;const qt=an=>{var mn;return((mn=o.properties.find(Cs=>Cs.key.toLowerCase()===an))==null?void 0:mn.value.trim())||null};return{typ:o.typ||qt("typ"),stand:o.stand||qt("stand"),bezug:qt("bezug"),code:qt("code"),revision:qt("revision"),agentenlauf:qt("agentenlauf")}}const ue=z[1].split(/\r?\n/),je={};for(const qt of ue){const an=qt.match(/^([a-zA-Z0-9_-]+)\s*:\s*(.*)$/);an&&(je[an[1].toLowerCase()]=an[2].trim().replace(/^['"]|['"]$/g,""))}return{typ:je.typ||(o==null?void 0:o.typ)||null,stand:je.stand||(o==null?void 0:o.stand)||null,bezug:je.bezug||bt("bezug"),code:je.code||bt("code"),revision:je.revision||bt("revision"),agentenlauf:je.agentenlauf||bt("agentenlauf")}},[u,o]),Ba=U.useMemo(()=>u?[...u.matchAll(/\[\[([^\]|#]+)(?:#[^\]|]+)?(?:\|([^\]]+))?\]\]/g)].map(ue=>{var an;const je=ue[1].trim(),ht=((an=ue[2])==null?void 0:an.trim())||null,qt=s.find(mn=>mn.path===je||mn.path===`Notizen/${je}.md`||mn.path.replace(/\.md$/,"").endsWith(je)||mn.title.toLowerCase()===je.toLowerCase());return{target:je,alias:ht,resolvedPath:qt?qt.path:null}}):[],[u,s]),Fi=U.useMemo(()=>{if(!(o!=null&&o.backlinks))return[];const z=new Set;for(const ue of o.backlinks){const je=s.find(ht=>ht.path===ue.path);if(je!=null&&je.tags)for(const ht of je.tags)z.add(ht)}return[...z].sort()},[o==null?void 0:o.backlinks,s]),Hi=U.useMemo(()=>o!=null&&o.backlinks?h?o.backlinks.filter(z=>{var je;const ue=s.find(ht=>ht.path===z.path);return(je=ue==null?void 0:ue.tags)==null?void 0:je.includes(h)}):o.backlinks:[],[o==null?void 0:o.backlinks,s,h]);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:j==="editor"?"on":"",onClick:()=>ee("editor"),children:"Editor"}),r.jsx("button",{type:"button",className:j==="graph"?"on":"",onClick:()=>ee("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:Ie,onChange:z=>it(z.target.value),children:[r.jsx("option",{value:"notiz",children:"Notiz"}),r.jsx("option",{value:"entscheidung",children:"Entscheidung"}),r.jsx("option",{value:"widerspruch",children:"Widerspruch"})]}),Ie!=="notiz"&&r.jsxs("select",{"aria-label":"Status der neuen Wissensnotiz",value:re,onChange:z=>me(z.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:Ee,children:"Neu"})]}),r.jsxs("form",{className:"notes-search",onSubmit:ge,children:[r.jsx("input",{value:y,onChange:z=>_(z.target.value),placeholder:"Notizen & Code suchen …"}),r.jsx("button",{disabled:D,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:()=>g(z=>!z),children:r.jsxs("span",{children:[p?"▾":"▸"," Tags (",ie.length,")"]})}),m&&r.jsxs("div",{className:"notes-tag-drawer__active",children:[r.jsxs("span",{className:"notes-tag-chip",children:["#",m]}),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:b,onChange:z=>x(z.target.value)}),r.jsxs("div",{className:"notes-tags notes-tag-drawer__list",children:[r.jsxs("button",{type:"button",className:m?"":"on",onClick:()=>d(""),children:["Alle (",s.length,")"]}),le.map(z=>r.jsxs("button",{type:"button",className:m===z?"on":"",onClick:()=>d(m===z?"":z),children:["#",z," (",Q[z]||0,")"]},z))]})]}),!p&&r.jsxs("div",{className:"notes-tags notes-tags--compact",children:[r.jsx("button",{type:"button",className:m?"":"on",onClick:()=>d(""),children:"Alle"}),ie.slice(0,8).map(z=>r.jsxs("button",{type:"button",className:m===z?"on":"",onClick:()=>d(z),children:["#",z]},z)),ie.length>8&&r.jsxs("button",{type:"button",className:"notes-tags-more",onClick:()=>g(!0),children:["+",ie.length-8," weitere …"]})]})]}),(A.length>0||T.length>0)&&r.jsxs("div",{className:"notes-results",children:[A.length>0&&r.jsxs("div",{children:[r.jsxs("div",{className:"notes-results__section-head",children:["Notizen (",A.length,")"]}),A.map(z=>r.jsxs("button",{type:"button",onClick:()=>void be(z.path),children:[r.jsx("strong",{children:z.title}),r.jsxs("span",{children:[z.path,z.line?`:${z.line}`:""]}),z.snippet&&r.jsx("small",{children:z.snippet})]},`${z.path}:${z.line}`))]}),T.length>0&&r.jsxs("div",{children:[r.jsxs("div",{className:"notes-results__section-head",children:["Code-Symbole (",T.length,")"]}),T.map((z,ue)=>r.jsxs("button",{type:"button",onClick:()=>we(`${z.path}${z.line?`:${z.line}`:""}`),children:[r.jsxs("div",{className:"notes-results__sym-row",children:[r.jsx("strong",{children:z.name}),r.jsx("span",{className:"notes-results__sym-kind",children:z.kind})]}),r.jsxs("span",{children:[z.path,z.line?`:${z.line}`:""]})]},`${z.path}:${z.line}:${ue}`))]})]}),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:Ee,children:"Erste Notiz anlegen"})]}):pe.map(z=>r.jsxs("button",{type:"button",className:(o==null?void 0:o.path)===z.path?"on":"",onClick:()=>void be(z.path),children:[r.jsx("strong",{children:z.title}),r.jsx("span",{children:z.path}),r.jsxs("small",{children:[(z.tags||[]).map(ue=>`#${ue}`).join(" ")," ",z.inLinks?`←${z.inLinks}`:""]})]},z.path))})]}),r.jsxs("section",{className:"notes-editor",children:[j==="graph"&&r.jsx(b2,{workspaceId:t,focus:o==null?void 0:o.path,onOpenNote:z=>{ee("editor"),be(z)}}),j==="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:()=>{Se(o.path),Re(!0)},title:"Notiz umbenennen",children:r.jsx(Et,{path:yt.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 ${xe==="edit"?"on":""}`,onClick:()=>Te("edit"),title:"Reiner Texteditor",children:"Edit"}),r.jsx("button",{type:"button",className:`notes-toggle-btn ${xe==="split"?"on":""}`,onClick:()=>Te("split"),title:"Editor und Live-Vorschau nebeneinander",children:"Split"}),r.jsx("button",{type:"button",className:`notes-toggle-btn ${xe==="lesen"?"on":""}`,onClick:()=>Te("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":k==="saved"?"ok":k==="saving"?"idle":k==="conflict"?"bad":"warn",title:k==="conflict"?"Konflikt beim Speichern":k==="dirty"?"Ungespeicherte Änderungen":"Gespeichert",children:[r.jsx("i",{"aria-hidden":"true"}),r.jsx("span",{children:k==="saving"?"SPEICHERT":k==="dirty"?"GEÄNDERT":k==="conflict"?"KONFLIKT":"GESPEICHERT"})]}),r.jsx("button",{type:"button",className:"pb-button pb-button--primary",disabled:D||k==="saved",onClick:()=>void ve(!1),title:"Notiz speichern (Strg+S)",children:"Speichern"}),r.jsxs("button",{type:"button",className:`pb-tool ${_e?"is-active":""}`,onClick:()=>Ce(z=>!z),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:()=>ke(z=>!z),title:"Weitere Aktionen","aria-expanded":Le,children:"⋯"}),Le&&r.jsxs("div",{className:"notes-overflow-dropdown",onClick:()=>ke(!1),children:[r.jsxs("button",{type:"button",onClick:()=>he(z=>!z),children:["Autosave: ",Y?"Aktiv":"Aus"]}),r.jsx("button",{type:"button",disabled:D||!Ne||Ne.path!==o.path,onClick:()=>void Be(),children:"Rückgängig"}),r.jsx("button",{type:"button",onClick:()=>{Se(o.path),Re(!0)},children:"Notiz umbenennen …"}),r.jsx("a",{href:m0(t,o.path),download:!0,children:"Notiz exportieren"}),r.jsx("a",{href:m0(t),download:!0,children:"Vault exportieren"})]})]})]})]}),_t&&(_t.typ||_t.stand||_t.bezug||_t.code||_t.revision)&&r.jsxs("div",{className:"notes-frontmatter-bar",children:[_t.typ&&r.jsxs("span",{className:`notes-fm-pill notes-fm-pill--${_t.typ.toLowerCase()}`,children:["TYP: ",_t.typ.toUpperCase()]}),_t.stand&&r.jsxs("span",{className:"notes-fm-pill notes-fm-pill--stand",children:["STATUS: ",_t.stand.toUpperCase()]}),_t.bezug&&r.jsxs("button",{type:"button",className:"notes-fm-btn notes-fm-btn--link",onClick:()=>De(_t.bezug.replace(/^\[\[|\]\]$/g,"")),title:`Notiz öffnen: ${_t.bezug}`,children:["BEZUG: ",_t.bezug," ↗"]}),_t.code&&r.jsxs("button",{type:"button",className:"notes-fm-btn notes-fm-btn--code",onClick:()=>we(_t.code),title:`Datei im Editor öffnen: ${_t.code}`,children:["CODE: ",_t.code," ↗"]}),_t.revision&&r.jsxs("button",{type:"button",className:"notes-fm-btn notes-fm-btn--rev",onClick:()=>n==null?void 0:n(_t.revision),title:`Revision prüfen: ${_t.revision}`,children:["GIT: ",_t.revision.slice(0,8)," ↗"]})]}),O&&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:O}),r.jsx("button",{type:"button",style:{fontSize:"11px",padding:"2px 8px"},onClick:()=>o&&void be(o.path),children:"Neu laden"}),r.jsx("button",{type:"button",style:{fontSize:"11px",padding:"2px 8px"},onClick:()=>void Me(),children:"Trotzdem überschreiben"}),r.jsx("button",{type:"button",style:{fontSize:"11px",padding:"2px 8px"},onClick:()=>de(z=>!z),children:X?"Diff schließen":"Diff anzeigen"})]}),X&&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})]})]}),q&&r.jsx("p",{className:"notes-notice",role:"status",children:q}),!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:()=>oe("entscheidung"),children:"+ Vorlage: Entscheidung"}),r.jsx("button",{type:"button",style:{fontSize:"10.5px",padding:"2px 7px"},onClick:()=>oe("widerspruch"),children:"+ Vorlage: Widerspruch"})]}),r.jsxs("div",{className:"notes-content-grid",style:{display:"grid",gridTemplateColumns:_e?xe==="split"?"1fr 1fr 280px":"1fr 280px":xe==="split"?"1fr 1fr":"1fr",height:"100%",minHeight:0,overflow:"hidden"},children:[r.jsx("div",{className:"notes-editor-pane",style:{display:xe==="lesen"?"none":"flex",flexDirection:"column",height:"100%",minHeight:0,overflow:"hidden",borderRight:xe==="split"||_e?"1px solid var(--border)":"none"},children:r.jsx("textarea",{"aria-label":"Notizinhalt",value:u,onChange:z=>f(z.target.value),onKeyDown:Fe,spellCheck:!1,style:{height:"100%",width:"100%",boxSizing:"border-box",resize:"none",border:"none",outline:"none"}})}),xe==="lesen"&&r.jsx("textarea",{"aria-label":"Notizinhalt",value:u,onChange:z=>f(z.target.value),onKeyDown:Fe,spellCheck:!1,style:{position:"absolute",opacity:0,pointerEvents:"none",width:0,height:0}}),(xe==="split"||xe==="lesen")&&r.jsx("div",{className:"notes-preview-pane",style:{height:"100%",minHeight:0,overflowY:"auto",padding:"16px 20px",background:"var(--bg)",borderRight:_e?"1px solid var(--border)":"none"},children:r.jsx(T2,{content:u,onNavigateNote:De,onOpenSource:we})}),_e&&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 (",Hi.length,")"]}),Fi.length>0&&r.jsxs("select",{value:h,onChange:z=>v(z.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"}),Fi.map(z=>r.jsxs("option",{value:z,children:["#",z]},z))]})]}),Hi.length>0?r.jsx("div",{style:{display:"flex",flexDirection:"column",gap:"4px"},children:Hi.map(z=>r.jsxs("button",{type:"button",onClick:()=>void be(z.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:["← ",z.title]}),r.jsxs("div",{style:{fontSize:"11px",color:"var(--faint)",display:"flex",justifyContent:"space-between",marginTop:"2px"},children:[r.jsx("span",{children:z.path}),r.jsxs("span",{children:["Z. ",z.line]})]})]},`${z.path}:${z.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 (",Ba.length,")"]}),Ba.length>0?r.jsx("div",{style:{display:"flex",flexDirection:"column",gap:"4px"},children:Ba.map((z,ue)=>r.jsxs("button",{type:"button",disabled:!z.resolvedPath,onClick:()=>z.resolvedPath&&void be(z.resolvedPath),style:{textAlign:"left",background:"rgba(255,255,255,0.03)",border:"1px solid var(--border)",borderRadius:"4px",padding:"6px 8px",color:z.resolvedPath?"var(--fg)":"var(--faint)",cursor:z.resolvedPath?"pointer":"default"},children:[r.jsxs("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between"},children:[r.jsx("span",{style:{fontWeight:600},children:z.alias||z.target}),r.jsx("span",{style:{fontSize:"10px",color:z.resolvedPath?"var(--pos)":"var(--faint)"},children:z.resolvedPath?"im Vault":"unaufgelöst"})]}),z.resolvedPath&&r.jsx("div",{style:{fontSize:"11px",color:"var(--faint)",marginTop:"2px"},children:z.resolvedPath})]},`${z.target}:${ue}`))}):r.jsx("div",{style:{color:"var(--faint)",fontStyle:"italic",fontSize:"11px"},children:"Keine [[Wiki-Links]] im Text."})]}),nn&&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:()=>we(nn),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:nn}),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":!Ae,title:Ae?"Metadaten und Verknüpfungen aufklappen":"Metadaten und Verknüpfungen einklappen",children:[r.jsxs("span",{children:[Ae?"▸":"▾"," Verknüpfungen & Anhänge"]}),r.jsxs("span",{className:"notes-meta-counts",children:[o.links.length," Links · ",o.backlinks.length," Backlinks",w.length>0?` · ${w.length} Anhänge`:""]})]})}),!Ae&&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(z=>r.jsxs("button",{type:"button",disabled:!z.path,onClick:()=>z.path&&void be(z.path),children:[z.alias||z.target,z.path?"":" (nicht aufgelöst)"]},`${z.target}:${z.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(z=>r.jsxs("button",{type:"button",onClick:()=>void be(z.path),children:["← ",z.title," · Zeile ",z.line]},`${z.path}:${z.line}`)):r.jsx("span",{children:"Keine Rückverweise."})]}),(rt||nn||bn||Ia)&&r.jsxs("section",{children:[r.jsx("h3",{children:"Verknüpfungen"}),rt&&r.jsxs("button",{type:"button",onClick:()=>void be(rt.replace(/^\[\[|\]\]$/g,"")),children:["Entscheidung/Widerspruch: ",rt]}),nn&&r.jsxs("button",{type:"button",disabled:!e,onClick:()=>we(nn),children:["Datei: ",nn]}),bn&&r.jsxs("button",{type:"button",disabled:!n,onClick:()=>n==null?void 0:n(bn),children:["Revision: ",bn]}),Ia&&r.jsxs("button",{type:"button",disabled:!i,onClick:()=>i==null?void 0:i(Ia),children:["Agentenlauf: ",Ia]})]}),r.jsxs("section",{children:[r.jsxs("h3",{children:["Anhänge (",w.length,")"]}),r.jsx("input",{ref:ne,type:"file",hidden:!0,onChange:z=>{var ue;return void We((ue=z.target.files)==null?void 0:ue[0])}}),r.jsx("button",{type:"button",disabled:D||o.hash==="",title:o.hash===""?"Die Notiz zuerst speichern":void 0,onClick:()=>{var z;return(z=ne.current)==null?void 0:z.click()},children:"Datei anhängen"}),o.hash===""&&r.jsx("span",{children:"Notiz zuerst speichern."}),w.map(z=>r.jsxs("a",{href:NT(t,o.path,z.name),children:[z.name," · ",z.bytes," B"]},z.path))]})]})]}):s.length===0?r.jsxs("div",{className:"notes-empty--initial",children:[r.jsx("div",{className:"notes-empty__icon",children:r.jsx(Et,{path:yt.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:Ee,children:"Erste Notiz anlegen"})]}):r.jsxs("div",{className:"notes-empty--initial",children:[r.jsx("div",{className:"notes-empty__icon",children:r.jsx(Et,{path:yt.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:Ee,children:"Neue Notiz anlegen"})]}))]}),I&&r.jsx("div",{className:"brain-modal-backdrop",onClick:()=>Qe(!1),children:r.jsxs("div",{className:"brain-modal brain-quick-switcher",onClick:z=>z.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:()=>Qe(!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:Je,onChange:z=>{C(z.target.value),V(0)},onKeyDown:z=>{z.key==="ArrowDown"?(z.preventDefault(),V(ue=>Math.min(H.length-1,ue+1))):z.key==="ArrowUp"?(z.preventDefault(),V(ue=>Math.max(0,ue-1))):z.key==="Enter"?(z.preventDefault(),H[E]&&(be(H[E].path),Qe(!1))):z.key==="Escape"&&Qe(!1)}})}),r.jsx("div",{className:"quick-switcher-list",role:"listbox",children:H.length===0?r.jsx("p",{className:"quick-switcher-empty",children:"Keine passende Notiz gefunden."}):H.map((z,ue)=>r.jsxs("button",{type:"button",className:`quick-switcher-item ${ue===E?"is-selected":""}`,onClick:()=>{be(z.path),Qe(!1)},onMouseEnter:()=>V(ue),children:[r.jsx("div",{className:"quick-switcher-item__title",children:z.title}),r.jsx("div",{className:"quick-switcher-item__path",children:z.path}),z.tags&&z.tags.length>0&&r.jsx("div",{className:"quick-switcher-item__tags",children:z.tags.map(je=>`#${je}`).join(" ")})]},z.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"]})]})]})}),Ue&&r.jsx("div",{className:"brain-modal-backdrop",onClick:()=>Re(!1),children:r.jsxs("div",{className:"brain-modal",onClick:z=>z.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:z=>{z.preventDefault(),tt(qe)},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:qe,onChange:z=>Se(z.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:D||!qe.trim(),children:D?"Speichert …":"Umbenennen"})]})]})]})})]})}function N2(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 C2({workspaceId:t,onSelectSource:e}){const[n,i]=U.useState(()=>new URLSearchParams(window.location.search).get("goal")||"authKey security tests"),[a,s]=U.useState(!1),[l,o]=U.useState(""),[c,u]=U.useState(null),[f,m]=U.useState(null),[d,p]=U.useState(!1),[g,b]=U.useState(!1);U.useEffect(()=>{const M=new URLSearchParams(window.location.search).get("goal");M&&t&&(s(!0),h0(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 x=async M=>{M&&M.preventDefault();const T=n.trim();if(T){s(!0),o(""),u(null),m(null);try{const S=await h0(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 ST(M);m(T)}catch(T){console.error("Staleness check error:",T)}finally{p(!1)}},[v,y]=U.useState(""),_=(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(()=>{y("Als Kontext kopiert (bereit für ChatGPT, Claude & Co.)"),setTimeout(()=>y(""),3e3)}).catch(()=>{y("Fehler beim Kopieren in die Zwischenablage")})},A=c!=null&&c.body?N2(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(Et,{path:yt.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:x,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:()=>_("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 …"}):f?r.jsxs("span",{className:"pb-status","data-tone":f.stale?"bad":"ok",style:{marginRight:"8px"},children:[r.jsx("i",{"aria-hidden":"true"}),r.jsx("span",{children:f.stale?"VERALTET":"AKTUELL"})]}):null,r.jsx("button",{type:"button",className:"pack-staleness-btn",onClick:()=>h(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}),f&&f.stale&&r.jsxs("div",{className:"pack-stale-warning",children:[r.jsx("strong",{children:"Quellen haben sich geändert:"}),f.changed.length>0&&r.jsxs("div",{children:["Geändert: ",f.changed.join(", ")]}),f.missing.length>0&&r.jsxs("div",{children:["Fehlt: ",f.missing.join(", ")]})]}),r.jsxs("div",{className:"pack-sources-section",children:[r.jsx("h4",{children:"Extrahierte Quellen aus dem Index:"}),A.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:A.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:()=>b(M=>!M),children:g?"Markdown-Text verbergen":"Vollständigen Pack-Markdown anzeigen"})}),g&&r.jsx("div",{className:"pack-raw-markdown mono",children:r.jsx("pre",{children:c.body})})]})})]})}function R2(t){const e=+this._x.call(null,t),n=+this._y.call(null,t);return VS(this.cover(e,n),e,n,t)}function VS(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,f=t._y1,m,d,p,g,b,x,h,v;if(!s)return t._root=l,t;for(;s.length;)if((b=e>=(m=(o+u)/2))?o=m:u=m,(x=n>=(d=(c+f)/2))?c=d:f=d,a=s,!(s=s[h=x<<1|b]))return a[h]=l,t;if(p=+t._x.call(null,s.data),g=+t._y.call(null,s.data),e===p&&n===g)return l.next=s,a?a[h]=l:t._root=l,t;do a=a?a[h]=new Array(4):t._root=new Array(4),(b=e>=(m=(o+u)/2))?o=m:u=m,(x=n>=(d=(c+f)/2))?c=d:f=d;while((h=x<<1|b)===(v=(g>=d)<<1|p>=m));return a[v]=s,a[h]=l,t}function D2(t){var e,n,i=t.length,a,s,l=new Array(i),o=new Array(i),c=1/0,u=1/0,f=-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))||(l[n]=a,o[n]=s,a<c&&(c=a),a>f&&(f=a),s<u&&(u=s),s>m&&(m=s));if(c>f||u>m)return this;for(this.cover(c,u).cover(f,m),n=0;n<i;++n)VS(this,l[n],o[n],t[n]);return this}function L2(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 U2(){var t=[];return this.visit(function(e){if(!e.length)do t.push(e.data);while(e=e.next)}),t}function O2(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 Pn(t,e,n,i,a){this.node=t,this.x0=e,this.y0=n,this.x1=i,this.y1=a}function P2(t,e,n){var i,a=this._x0,s=this._y0,l,o,c,u,f=this._x1,m=this._y1,d=[],p=this._root,g,b;for(p&&d.push(new Pn(p,a,s,f,m)),n==null?n=1/0:(a=t-n,s=e-n,f=t+n,m=e+n,n*=n);g=d.pop();)if(!(!(p=g.node)||(l=g.x0)>f||(o=g.y0)>m||(c=g.x1)<a||(u=g.y1)<s))if(p.length){var x=(l+c)/2,h=(o+u)/2;d.push(new Pn(p[3],x,h,c,u),new Pn(p[2],l,h,x,u),new Pn(p[1],x,o,c,h),new Pn(p[0],l,o,x,h)),(b=(e>=h)<<1|t>=x)&&(g=d[d.length-1],d[d.length-1]=d[d.length-1-b],d[d.length-1-b]=g)}else{var v=t-+this._x.call(null,p.data),y=e-+this._y.call(null,p.data),_=v*v+y*y;if(_<n){var A=Math.sqrt(n=_);a=t-A,s=e-A,f=t+A,m=e+A,i=p.data}}return i}function z2(t){if(isNaN(f=+this._x.call(null,t))||isNaN(m=+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,f,m,d,p,g,b,x,h;if(!n)return this;if(n.length)for(;;){if((g=f>=(d=(l+c)/2))?l=d:c=d,(b=m>=(p=(o+u)/2))?o=p:u=p,e=n,!(n=n[x=b<<1|g]))return this;if(!n.length)break;(e[x+1&3]||e[x+2&3]||e[x+3&3])&&(i=e,h=x)}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[x]=s:delete e[x],(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 k2(t){for(var e=0,n=t.length;e<n;++e)this.remove(t[e]);return this}function I2(){return this._root}function B2(){var t=0;return this.visit(function(e){if(!e.length)do++t;while(e=e.next)}),t}function j2(t){var e=[],n,i=this._root,a,s,l,o,c;for(i&&e.push(new Pn(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,f=(l+c)/2;(a=i[3])&&e.push(new Pn(a,u,f,o,c)),(a=i[2])&&e.push(new Pn(a,s,f,u,c)),(a=i[1])&&e.push(new Pn(a,u,l,o,f)),(a=i[0])&&e.push(new Pn(a,s,l,u,f))}return this}function F2(t){var e=[],n=[],i;for(this._root&&e.push(new Pn(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,f=(l+c)/2,m=(o+u)/2;(s=a[0])&&e.push(new Pn(s,l,o,f,m)),(s=a[1])&&e.push(new Pn(s,f,o,c,m)),(s=a[2])&&e.push(new Pn(s,l,m,f,u)),(s=a[3])&&e.push(new Pn(s,f,m,c,u))}n.push(i)}for(;i=n.pop();)t(i.node,i.x0,i.y0,i.x1,i.y1);return this}function H2(t){return t[0]}function G2(t){return arguments.length?(this._x=t,this):this._x}function V2(t){return t[1]}function W2(t){return arguments.length?(this._y=t,this):this._y}function wg(t,e,n){var i=new Ng(e??H2,n??V2,NaN,NaN,NaN,NaN);return t==null?i:i.addAll(t)}function Ng(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 Lv(t){for(var e={data:t.data},n=e;t=t.next;)n=n.next={data:t.data};return e}var Fn=wg.prototype=Ng.prototype;Fn.copy=function(){var t=new Ng(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=Lv(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]=Lv(i));return t};Fn.add=R2;Fn.addAll=D2;Fn.cover=L2;Fn.data=U2;Fn.extent=O2;Fn.find=P2;Fn.remove=z2;Fn.removeAll=k2;Fn.root=I2;Fn.size=B2;Fn.visit=j2;Fn.visitAfter=F2;Fn.x=G2;Fn.y=W2;function kn(t){return function(){return t}}function ds(t){return(t()-.5)*1e-6}function X2(t){return t.x+t.vx}function q2(t){return t.y+t.vy}function Y2(t){var e,n,i,a=1,s=1;typeof t!="function"&&(t=kn(t==null?1:+t));function l(){for(var u,f=e.length,m,d,p,g,b,x,h=0;h<s;++h)for(m=wg(e,X2,q2).visitAfter(o),u=0;u<f;++u)d=e[u],b=n[d.index],x=b*b,p=d.x+d.vx,g=d.y+d.vy,m.visit(v);function v(y,_,A,M,T){var S=y.data,w=y.r,R=b+w;if(S){if(S.index>d.index){var D=p-S.x-S.vx,L=g-S.y-S.vy,q=D*D+L*L;q<R*R&&(D===0&&(D=ds(i),q+=D*D),L===0&&(L=ds(i),q+=L*L),q=(R-(q=Math.sqrt(q)))/q*a,d.vx+=(D*=q)*(R=(w*=w)/(x+w)),d.vy+=(L*=q)*R,S.vx-=D*(R=1-R),S.vy-=L*R)}return}return _>p+R||M<p-R||A>g+R||T<g-R}}function o(u){if(u.data)return u.r=n[u.data.index];for(var f=u.r=0;f<4;++f)u[f]&&u[f].r>u.r&&(u.r=u[f].r)}function c(){if(e){var u,f=e.length,m;for(n=new Array(f),u=0;u<f;++u)m=e[u],n[m.index]=+t(m,u,e)}}return l.initialize=function(u,f){e=u,i=f,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:kn(+u),c(),l):t},l}function K2(t){return t.index}function Uv(t,e){var n=t.get(e);if(!n)throw new Error("node not found: "+e);return n}function $2(t){var e=K2,n=m,i,a=kn(30),s,l,o,c,u,f=1;t==null&&(t=[]);function m(x){return 1/Math.min(o[x.source.index],o[x.target.index])}function d(x){for(var h=0,v=t.length;h<f;++h)for(var y=0,_,A,M,T,S,w,R;y<v;++y)_=t[y],A=_.source,M=_.target,T=M.x+M.vx-A.x-A.vx||ds(u),S=M.y+M.vy-A.y-A.vy||ds(u),w=Math.sqrt(T*T+S*S),w=(w-s[y])/w*x*i[y],T*=w,S*=w,M.vx-=T*(R=c[y]),M.vy-=S*R,A.vx+=T*(R=1-R),A.vy+=S*R}function p(){if(l){var x,h=l.length,v=t.length,y=new Map(l.map((A,M)=>[e(A,M,l),A])),_;for(x=0,o=new Array(h);x<v;++x)_=t[x],_.index=x,typeof _.source!="object"&&(_.source=Uv(y,_.source)),typeof _.target!="object"&&(_.target=Uv(y,_.target)),o[_.source.index]=(o[_.source.index]||0)+1,o[_.target.index]=(o[_.target.index]||0)+1;for(x=0,c=new Array(v);x<v;++x)_=t[x],c[x]=o[_.source.index]/(o[_.source.index]+o[_.target.index]);i=new Array(v),g(),s=new Array(v),b()}}function g(){if(l)for(var x=0,h=t.length;x<h;++x)i[x]=+n(t[x],x,t)}function b(){if(l)for(var x=0,h=t.length;x<h;++x)s[x]=+a(t[x],x,t)}return d.initialize=function(x,h){l=x,u=h,p()},d.links=function(x){return arguments.length?(t=x,p(),d):t},d.id=function(x){return arguments.length?(e=x,d):e},d.iterations=function(x){return arguments.length?(f=+x,d):f},d.strength=function(x){return arguments.length?(n=typeof x=="function"?x:kn(+x),g(),d):n},d.distance=function(x){return arguments.length?(a=typeof x=="function"?x:kn(+x),b(),d):a},d}var Z2={value:()=>{}};function WS(){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 Lu(n)}function Lu(t){this._=t}function Q2(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}})}Lu.prototype=WS.prototype={constructor:Lu,on:function(t,e){var n=this._,i=Q2(t+"",n),a,s=-1,l=i.length;if(arguments.length<2){for(;++s<l;)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<l;)if(a=(t=i[s]).type)n[a]=Ov(n[a],t.name,e);else if(e==null)for(a in n)n[a]=Ov(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 Lu(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 Ov(t,e,n){for(var i=0,a=t.length;i<a;++i)if(t[i].name===e){t[i]=Z2,t=t.slice(0,i).concat(t.slice(i+1));break}return n!=null&&t.push({name:e,value:n}),t}var Al=0,fo=0,no=0,XS=1e3,gd,ho,xd=0,sr=0,Vd=0,Ko=typeof performance=="object"&&performance.now?performance:Date,qS=typeof window=="object"&&window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(t){setTimeout(t,17)};function YS(){return sr||(qS(eD),sr=Ko.now()+Vd)}function eD(){sr=0}function om(){this._call=this._time=this._next=null}om.prototype=KS.prototype={constructor:om,restart:function(t,e,n){if(typeof t!="function")throw new TypeError("callback is not a function");n=(n==null?YS():+n)+(e==null?0:+e),!this._next&&ho!==this&&(ho?ho._next=this:gd=this,ho=this),this._call=t,this._time=n,cm()},stop:function(){this._call&&(this._call=null,this._time=1/0,cm())}};function KS(t,e,n){var i=new om;return i.restart(t,e,n),i}function tD(){YS(),++Al;for(var t=gd,e;t;)(e=sr-t._time)>=0&&t._call.call(void 0,e),t=t._next;--Al}function Pv(){sr=(xd=Ko.now())+Vd,Al=fo=0;try{tD()}finally{Al=0,iD(),sr=0}}function nD(){var t=Ko.now(),e=t-xd;e>XS&&(Vd-=e,xd=t)}function iD(){for(var t,e=gd,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:gd=n);ho=t,cm(i)}function cm(t){if(!Al){fo&&(fo=clearTimeout(fo));var e=t-sr;e>24?(t<1/0&&(fo=setTimeout(Pv,t-Ko.now()-Vd)),no&&(no=clearInterval(no))):(no||(xd=Ko.now(),no=setInterval(nD,XS)),Al=1,qS(Pv))}}const aD=1664525,sD=1013904223,zv=4294967296;function rD(){let t=1;return()=>(t=(aD*t+sD)%zv)/zv}function lD(t){return t.x}function oD(t){return t.y}var cD=10,uD=Math.PI*(3-Math.sqrt(5));function dD(t){var e,n=1,i=.001,a=1-Math.pow(i,1/300),s=0,l=.6,o=new Map,c=KS(m),u=WS("tick","end"),f=rD();t==null&&(t=[]);function m(){d(),u.call("tick",e),n<i&&(c.stop(),u.call("end",e))}function d(b){var x,h=t.length,v;b===void 0&&(b=1);for(var y=0;y<b;++y)for(n+=(s-n)*a,o.forEach(function(_){_(n)}),x=0;x<h;++x)v=t[x],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 b=0,x=t.length,h;b<x;++b){if(h=t[b],h.index=b,h.fx!=null&&(h.x=h.fx),h.fy!=null&&(h.y=h.fy),isNaN(h.x)||isNaN(h.y)){var v=cD*Math.sqrt(.5+b),y=b*uD;h.x=v*Math.cos(y),h.y=v*Math.sin(y)}(isNaN(h.vx)||isNaN(h.vy))&&(h.vx=h.vy=0)}}function g(b){return b.initialize&&b.initialize(t,f),b}return p(),e={tick:d,restart:function(){return c.restart(m),e},stop:function(){return c.stop(),e},nodes:function(b){return arguments.length?(t=b,p(),o.forEach(g),e):t},alpha:function(b){return arguments.length?(n=+b,e):n},alphaMin:function(b){return arguments.length?(i=+b,e):i},alphaDecay:function(b){return arguments.length?(a=+b,e):+a},alphaTarget:function(b){return arguments.length?(s=+b,e):s},velocityDecay:function(b){return arguments.length?(l=1-b,e):1-l},randomSource:function(b){return arguments.length?(f=b,o.forEach(g),e):f},force:function(b,x){return arguments.length>1?(x==null?o.delete(b):o.set(b,g(x)),e):o.get(b)},find:function(b,x,h){var v=0,y=t.length,_,A,M,T,S;for(h==null?h=1/0:h*=h,v=0;v<y;++v)T=t[v],_=b-T.x,A=x-T.y,M=_*_+A*A,M<h&&(S=T,h=M);return S},on:function(b,x){return arguments.length>1?(u.on(b,x),e):u.on(b)}}}function fD(){var t,e,n,i,a=kn(-30),s,l=1,o=1/0,c=.81;function u(p){var g,b=t.length,x=wg(t,lD,oD).visitAfter(m);for(i=p,g=0;g<b;++g)e=t[g],x.visit(d)}function f(){if(t){var p,g=t.length,b;for(s=new Array(g),p=0;p<g;++p)b=t[p],s[b.index]=+a(b,p,t)}}function m(p){var g=0,b,x,h=0,v,y,_;if(p.length){for(v=y=_=0;_<4;++_)(b=p[_])&&(x=Math.abs(b.value))&&(g+=b.value,h+=x,v+=x*b.x,y+=x*b.y);p.x=v/h,p.y=y/h}else{b=p,b.x=b.data.x,b.y=b.data.y;do g+=s[b.data.index];while(b=b.next)}p.value=g}function d(p,g,b,x){if(!p.value)return!0;var h=p.x-e.x,v=p.y-e.y,y=x-g,_=h*h+v*v;if(y*y/c<_)return _<o&&(h===0&&(h=ds(n),_+=h*h),v===0&&(v=ds(n),_+=v*v),_<l&&(_=Math.sqrt(l*_)),e.vx+=h*p.value*i/_,e.vy+=v*p.value*i/_),!0;if(p.length||_>=o)return;(p.data!==e||p.next)&&(h===0&&(h=ds(n),_+=h*h),v===0&&(v=ds(n),_+=v*v),_<l&&(_=Math.sqrt(l*_)));do p.data!==e&&(y=s[p.data.index]*i/_,e.vx+=h*y,e.vy+=v*y);while(p=p.next)}return u.initialize=function(p,g){t=p,n=g,f()},u.strength=function(p){return arguments.length?(a=typeof p=="function"?p:kn(+p),f(),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 hD(t){var e=kn(.1),n,i,a;typeof t!="function"&&(t=kn(t==null?0:+t));function s(o){for(var c=0,u=n.length,f;c<u;++c)f=n[c],f.vx+=(a[c]-f.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:kn(+o),l(),s):e},s.x=function(o){return arguments.length?(t=typeof o=="function"?o:kn(+o),l(),s):t},s}function pD(t){var e=kn(.1),n,i,a;typeof t!="function"&&(t=kn(t==null?0:+t));function s(o){for(var c=0,u=n.length,f;c<u;++c)f=n[c],f.vy+=(a[c]-f.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:kn(+o),l(),s):e},s.y=function(o){return arguments.length?(t=typeof o=="function"?o:kn(+o),l(),s):t},s}const kv=.04,Iv=6,mD=.0016,gD=4,xD=4,vD=2.6,_D=1.35,yD=13,fh=1.4,bD=.13,SD=.035,MD=.8,ED=.13,TD=.7,AD=1.3,Bv=12,jv=.75,wD=1.25,ND=.06,CD=280,su=3,RD=4,DD=40,Fv=56,LD=1.6,UD=260;var Yv;const Hv=typeof window<"u"&&((Yv=window.matchMedia)==null?void 0:Yv.call(window,"(prefers-reduced-motion: reduce)").matches)===!0;function OD(t){return Math.min(yD,vD+_D*Math.sqrt(t))}function PD(t){return 1-Math.pow(1-t,3)}function Gv(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 zD(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=Gv(t.parentElement??t),s=null,l=[],o=new Map,c=[],u=new Map,f=null,m=()=>!1,d=new Set,p="auto",g=null,b=null;const x={x:0,y:0,k:1};let h=0,v=0,y=1,_=0,A=!0,M=!1,T=!1,S=null,w=-1;const R=(re,me)=>[(re-x.x)/x.k,(me-x.y)/x.k],D=re=>{const me=u.get(re.group)??-1;return me>=0?a.groups[me]??a.other:a.other},L=re=>!m(re.node);function q(re,me){const[Ae,se]=R(re,me);let Le=null,ke=1/0;for(const Ue of l){if(!L(Ue)||Ue.x===void 0||Ue.y===void 0)continue;const Re=Math.max(Ue.r,fh/x.k)+xD/x.k,qe=Ue.x-Ae,Se=Ue.y-se,I=qe*qe+Se*Se;I<=Re*Re&&I<ke&&(Le=Ue,ke=I)}return Le}function P(){const re=b??g;if(re===null&&d.size>0)return new Set(d);if(re===null||s===null)return null;const me=new Set([re]);for(const Ae of s.neighbours.get(re)??[])me.add(Ae);return me}function O(){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*x.k,0,0,y*x.k,y*x.x,y*x.y);const re=P();i.lineCap="round",i.strokeStyle=a.edge,i.globalAlpha=re?SD:bD,i.lineWidth=TD/x.k,i.beginPath();const me=[];for(const se of c){const Le=se.source,ke=se.target;if(!(!L(Le)||!L(ke))){if(re&&(Le.id===(b??g)||ke.id===(b??g))){me.push(se);continue}i.moveTo(Le.x??0,Le.y??0),i.lineTo(ke.x??0,ke.y??0)}}if(i.stroke(),me.length>0){i.strokeStyle=a.accent,i.globalAlpha=MD,i.lineWidth=AD/x.k,i.beginPath();for(const se of me){const Le=se.source,ke=se.target;i.moveTo(Le.x??0,Le.y??0),i.lineTo(ke.x??0,ke.y??0)}i.stroke()}const Ae=fh/x.k;for(const se of l){if(!L(se))continue;const Le=Math.max(se.r,Ae);i.globalAlpha=re&&!re.has(se.id)?ED:1,i.fillStyle=D(se),i.beginPath(),i.arc(se.x??0,se.y??0,Le,0,Math.PI*2),i.fill()}i.globalAlpha=1,i.strokeStyle=a.accent;for(const se of l){if(!L(se))continue;const Le=se.id===g?2.2:se.id===b?1.6:d.has(se.id)?1.4:0;Le!==0&&(i.lineWidth=Le/x.k,i.beginPath(),i.arc(se.x??0,se.y??0,Math.max(se.r,Ae)+3/x.k,0,Math.PI*2),i.stroke())}B(re)}function B(re){const me=b??g,Ae=Ue=>Ue.id===g||Ue.id===b?4:re!=null&&re.has(Ue.id)?3:d.has(Ue.id)?2:0,se=[];for(const Ue of l){if(!L(Ue))continue;const Re=Ae(Ue);let qe=0;if(Re>0)qe=1;else if(re)qe=0;else if(p==="always")qe=1;else if(p==="auto"){const Qe=x.k+Ue.r*ND;qe=Math.max(0,Math.min(1,(Qe-jv)/(wD-jv)))}if(qe<=.02)continue;const Se=(Ue.x??0)*x.k+x.x,I=(Ue.y??0)*x.k+x.y;Se<-200||Se>h+200||I<-40||I>v+40||se.push({node:Ue,alpha:qe,rank:Re*1e5+Ue.node.degree})}se.sort((Ue,Re)=>Re.rank-Ue.rank),i.setTransform(y,0,0,y,0,0),i.font=`500 ${Bv}px ${a.font}`,i.textAlign="center",i.textBaseline="top",i.lineJoin="round";const Le=[];let ke=0;for(const{node:Ue,alpha:Re}of se){if(ke>=CD)break;const qe=Ue.node.label,Se=i.measureText(qe).width,I=Math.max(Ue.r,fh/x.k)*x.k,Qe=(Ue.x??0)*x.k+x.x,Je=(Ue.y??0)*x.k+x.y+I+RD,C={x0:Qe-Se/2-su,y0:Je-su,x1:Qe+Se/2+su,y1:Je+Bv+su};!(Ue.id===me)&&Le.some(V=>C.x0<V.x1&&C.x1>V.x0&&C.y0<V.y1&&C.y1>V.y0)||(Le.push(C),ke+=1,i.globalAlpha=Re,i.lineWidth=3,i.strokeStyle=a.labelHalo,i.strokeText(qe,Qe,Je),i.fillStyle=Ue.id===g||Ue.id===b?a.accent:a.label,i.fillText(qe,Qe,Je))}i.globalAlpha=1}function k(re){_=0;let me=!1;if(S!==null){const Ae=Math.min(1,(re-S.start)/UD),se=PD(Ae);x.x=S.from.x+(S.to.x-S.from.x)*se,x.y=S.from.y+(S.to.y-S.from.y)*se,x.k=S.from.k+(S.to.k-S.from.k)*se,Ae>=1&&(S=null),A=!0,me=!0}f!==null&&f.alpha()>f.alphaMin()?(f.tick(),A=!0,me=!0):f!==null&&!T&&(T=!0,M||_e(),me=!0),A&&(O(),A=!1,x.k!==w&&(w=x.k,e.onZoom(x.k))),me&&W()}function W(){_===0&&(_=requestAnimationFrame(k))}function Y(){A=!0,W()}function he(re){if(Hv){x.x=re.x,x.y=re.y,x.k=re.k,S=null,Y();return}S={from:{...x},to:re,start:performance.now()},W()}function _e(re=!1){let me=1/0,Ae=1/0,se=-1/0,Le=-1/0;for(const Re of l)!L(Re)||Re.x===void 0||Re.y===void 0||(me=Math.min(me,Re.x-Re.r),Ae=Math.min(Ae,Re.y-Re.r),se=Math.max(se,Re.x+Re.r),Le=Math.max(Le,Re.y+Re.r));if(!Number.isFinite(me)||h===0||v===0)return;const ke=Math.max(kv,Math.min(Iv,Math.min((h-Fv*2)/Math.max(1,se-me),(v-Fv*2)/Math.max(1,Le-Ae)))),Ue={k:ke,x:h/2-(me+se)/2*ke,y:v/2-(Ae+Le)/2*ke};if(re){x.x=Ue.x,x.y=Ue.y,x.k=Ue.k,S=null,Y();return}he(Ue)}function Ce(re,me,Ae){const se=Math.max(kv,Math.min(Iv,x.k*Ae)),[Le,ke]=R(re,me);x.x=re-Le*se,x.y=me-ke*se,x.k=se,S=null,M=!0,Y()}let X=null;const de=re=>{const me=t.getBoundingClientRect();return[re.clientX-me.left,re.clientY-me.top]};function Ne(re){if(re.button!==0)return;const[me,Ae]=de(re);X={x:me,y:Ae,node:q(me,Ae),moved:!1,pointerId:re.pointerId},t.setPointerCapture(re.pointerId),S=null}function ae(re){const[me,Ae]=de(re);if(X===null){const ke=q(me,Ae),Ue=(ke==null?void 0:ke.id)??null;t.style.cursor=ke?"pointer":"grab",Ue!==b&&(b=Ue,Y());return}const se=me-X.x,Le=Ae-X.y;if(!(!X.moved&&Math.hypot(se,Le)<gD))if(X.moved=!0,M=!0,t.style.cursor="grabbing",X.node!==null&&f!==null){const[ke,Ue]=R(me,Ae);X.node.fx=ke,X.node.fy=Ue,f.alphaTarget(.25),f.alpha()<.25&&f.alpha(.25),Y()}else x.x+=re.movementX,x.y+=re.movementY,Y()}function j(re){if(X===null)return;const me=X.moved,Ae=X.node;if(t.hasPointerCapture(X.pointerId)&&t.releasePointerCapture(X.pointerId),X=null,t.style.cursor=Ae?"pointer":"grab",Ae!==null&&f!==null&&(Ae.fx=null,Ae.fy=null,f.alphaTarget(0)),!me){const[se,Le]=de(re),ke=q(se,Le);g=(ke==null?void 0:ke.id)??null,e.onSelect(g)}Y()}function ee(re){const[me,Ae]=de(re),se=q(me,Ae);se!==null&&e.onOpen(se.id)}function xe(re){re.preventDefault();const[me,Ae]=de(re);Ce(me,Ae,Math.exp(-re.deltaY*mD))}function Te(){X===null&&b!==null&&(b=null,Y())}t.addEventListener("pointerdown",Ne),t.addEventListener("pointermove",ae),t.addEventListener("pointerup",j),t.addEventListener("pointercancel",j),t.addEventListener("pointerleave",Te),t.addEventListener("dblclick",ee),t.addEventListener("wheel",xe,{passive:!1}),t.style.cursor="grab",t.style.touchAction="none";function Ie(re){s=re,u=new Map(re.groups.map(Re=>[Re.name,Re.slot]));const me=o,Ae=me.size===0,se=re.groups.map(Re=>Re.name),Le=18*Math.sqrt(re.nodes.length),ke=new Map;se.forEach((Re,qe)=>{const Se=qe/Math.max(1,se.length)*Math.PI*2,I=qe===0?0:Le;ke.set(Re,[Math.cos(Se)*I,Math.sin(Se)*I])});const Ue=re.nodes.map(Re=>{const qe=me.get(Re.id),Se={id:Re.id,r:OD(Re.degree),group:Re.group,node:Re};if((qe==null?void 0:qe.x)!==void 0&&qe.y!==void 0)Se.x=qe.x,Se.y=qe.y,Se.vx=qe.vx??0,Se.vy=qe.vy??0;else{const[I,Qe]=ke.get(Re.group)??[0,0],Je=6*Math.sqrt(re.nodes.length/Math.max(1,se.length));Se.x=I+(Math.random()-.5)*Je,Se.y=Qe+(Math.random()-.5)*Je}return Se});if(l=Ue,o=new Map(Ue.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})),f==null||f.stop(),f=dD(Ue).force("link",$2(c).id(Re=>Re.id).distance(30)).force("charge",fD().strength(-42).distanceMax(420).theta(.9)).force("x",hD(0).strength(.04)).force("y",pD(0).strength(.04)).force("collide",Y2(Re=>Re.r+1.5).strength(.7).iterations(1)).stop(),Hv){const Re=Math.ceil(Math.log(f.alphaMin())/Math.log(1-f.alphaDecay()));f.tick(Re)}else Ae?f.tick(DD):f.alpha(.3);g!==null&&!o.has(g)&&(g=null,e.onSelect(null)),Ae&&(M=!1,T=!1,_e(!0)),Y()}function it(){const re=t.getBoundingClientRect();y=window.devicePixelRatio||1;const me=h>0;h=re.width,v=re.height,t.width=Math.max(1,Math.round(h*y)),t.height=Math.max(1,Math.round(v*y)),!me&&l.length>0&&_e(!0),Y()}return{setModel:Ie,setHidden(re){m=re,Y()},setQuery(re){d=re,Y()},setLabelMode(re){p=re,Y()},setSelected(re){g=re,Y()},setHovered(re){b=re,Y()},focusNode(re){const me=o.get(re);if((me==null?void 0:me.x)===void 0||me.y===void 0)return;M=!0;const Ae=Math.max(x.k,LD);he({k:Ae,x:h/2-me.x*Ae,y:v/2-me.y*Ae})},fit(){M=!0,_e()},zoomBy(re){Ce(h/2,v/2,re)},resize:it,refreshColours(){a=Gv(t.parentElement??t),Y()},dispose(){_!==0&&cancelAnimationFrame(_),f==null||f.stop(),t.removeEventListener("pointerdown",Ne),t.removeEventListener("pointermove",ae),t.removeEventListener("pointerup",j),t.removeEventListener("pointercancel",j),t.removeEventListener("pointerleave",Te),t.removeEventListener("dblclick",ee),t.removeEventListener("wheel",xe)}}}const kD=8,cl="Weitere",ID=new Set(["file","class","interface","note"]);function BD(t){return typeof t=="string"&&ID.has(t)?t:"other"}function qi(t){return typeof t=="string"&&t!==""?t:null}function jD(t){return typeof t=="number"&&Number.isFinite(t)?t:null}function FD(t){if(t===null)return cl;const e=t.split("/").filter(Boolean);return e.length>=2&&e[0]==="Code"?e[1]:e.length>=2?e[0]:"Workspace"}function HD(t){var u,f,m;const e=new Map,n={file:0,class:0,interface:0,note:0,other:0};for(const d of t.nodes){const p=qi(d==null?void 0:d.id);if(p===null||e.has(p))continue;const g=qi((u=d.properties)==null?void 0:u.path),b=BD(d.type),x=qi(d.label)??qi(d.name)??(g==null?void 0:g.split("/").pop())??p;e.set(p,{id:p,label:x,kind:b,path:g,group:FD(g),lines:jD((f=d.properties)==null?void 0:f.lines),lang:qi((m=d.properties)==null?void 0:m.lang),degree:0}),n[b]+=1}const i=[],a=new Map,s=new Set;for(const d of t.edges){const p=qi(d==null?void 0:d.sourceId)??qi(d==null?void 0:d.source),g=qi(d==null?void 0:d.targetId)??qi(d==null?void 0:d.target);if(p===null||g===null||p===g)continue;const b=e.get(p),x=e.get(g);if(b===void 0||x===void 0)continue;const h=qi(d.type)??"link",v=`${p}\0${g}\0${h}`;s.has(v)||(s.add(v),i.push({source:p,target:g,kind:h}),b.degree+=1,x.degree+=1,a.has(p)||a.set(p,new Set),a.has(g)||a.set(g,new Set),a.get(p).add(g),a.get(g).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],g)=>({name:d,count:p,slot:g<kD&&d!==cl?g:-1}));return{nodes:[...e.values()],edges:i,groups:c,byId:e,neighbours:a,kindCounts:n}}function GD(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 $S(t){return[...t].sort((e,n)=>n.degree-e.degree||e.label.localeCompare(n.label))}const $a=60;function ZS({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(Et,{path:t.playing?yt.pause:yt.play})}),r.jsx("input",{type:"range",min:0,max:$a,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 hh=120,ru=1.25,Vv={file:"Dateien",class:"Klassen",interface:"Interfaces",note:"Notizen",other:"Sonstige"},VD={file:"Datei",class:"Klasse",interface:"Interface",note:"Notiz",other:"Objekt"},WD={imports:["Importiert","Wird importiert von"],defines:["Definiert","Definiert in"],links:["Verweist auf","Verwiesen von"]},Yi=new Intl.NumberFormat("de-DE");function XD({graph:t,coverage:e,error:n,onRetry:i,onOpenSource:a,timeline:s}){const l=U.useMemo(()=>t?HD(t):null,[t]),o=U.useRef(null),c=U.useRef(null),u=U.useRef(null),f=U.useRef(null),m=U.useRef(null),[d,p]=U.useState(""),[g,b]=U.useState(""),[x,h]=U.useState(new Set),[v,y]=U.useState(new Set),[_,A]=U.useState("auto"),[M,T]=U.useState(null),[S,w]=U.useState(!0),R=U.useCallback(X=>{const de=l==null?void 0:l.byId.get(X);de!=null&&de.path&&a(de.path)},[l,a]),D=U.useRef(R);D.current=R,U.useEffect(()=>{const X=o.current,de=c.current;if(X===null||de===null)return;let Ne;try{Ne=zD(X,{onHover:()=>{},onSelect:ee=>T(ee),onOpen:ee=>D.current(ee),onZoom:ee=>{f.current&&(f.current.textContent=`${Math.round(ee*100)} %`)}})}catch(ee){p(ee instanceof Error?ee.message:String(ee));return}m.current=Ne;const ae=new ResizeObserver(()=>Ne.resize());ae.observe(de),Ne.resize();const j=new MutationObserver(()=>Ne.refreshColours());return j.observe(document.documentElement,{attributes:!0,attributeFilter:["data-theme"]}),()=>{j.disconnect(),ae.disconnect(),Ne.dispose(),m.current=null}},[]),U.useEffect(()=>{var X;l!==null&&((X=m.current)==null||X.setModel(l))},[l]),U.useEffect(()=>{var X;(X=m.current)==null||X.setHidden(de=>x.has(de.kind)||v.has(Wv(l,de)))},[x,v,l]);const L=U.useMemo(()=>l===null||g.trim()===""?new Set:new Set(l.nodes.filter(X=>GD(X,g)).map(X=>X.id)),[l,g]);U.useEffect(()=>{var X;(X=m.current)==null||X.setQuery(L)},[L]),U.useEffect(()=>{var X;(X=m.current)==null||X.setLabelMode(_)},[_]),U.useEffect(()=>{var X;(X=m.current)==null||X.setSelected(M)},[M]);const q=U.useCallback((X,de=!0)=>{var Ne;T(X),de&&((Ne=m.current)==null||Ne.focusNode(X))},[]),P=U.useMemo(()=>{if(l===null)return[];const X=g.trim()===""?l.nodes:l.nodes.filter(de=>L.has(de.id));return $S(X.filter(de=>!x.has(de.kind)&&!v.has(Wv(l,de))))},[l,g,L,x,v]),O=M!==null?(l==null?void 0:l.byId.get(M))??null:null;U.useEffect(()=>{const X=de=>{var j,ee,xe,Te;const Ne=de.target,ae=Ne!==null&&(Ne.tagName==="INPUT"||Ne.tagName==="TEXTAREA"||Ne.isContentEditable);if(de.key==="Escape"){if(ae&&Ne===u.current&&g!==""){b("");return}if(M!==null){T(null);return}ae&&Ne.blur();return}ae||de.metaKey||de.ctrlKey||de.altKey||(de.key==="/"?(de.preventDefault(),(j=u.current)==null||j.focus()):de.key==="f"||de.key==="F"?(ee=m.current)==null||ee.fit():de.key==="+"||de.key==="="?(xe=m.current)==null||xe.zoomBy(ru):de.key==="-"&&((Te=m.current)==null||Te.zoomBy(1/ru)))};return window.addEventListener("keydown",X),()=>window.removeEventListener("keydown",X)},[g,M]);const B=(X,de)=>{const Ne=new Set(X);return Ne.has(de)?Ne.delete(de):Ne.add(de),Ne},k=X=>{if(X.key!=="ArrowDown"&&X.key!=="ArrowUp")return;X.preventDefault();const de=[...X.currentTarget.querySelectorAll("button")],Ne=de.indexOf(document.activeElement),ae=de[Math.max(0,Math.min(de.length-1,Ne+(X.key==="ArrowDown"?1:-1)))];ae==null||ae.focus()},W=X=>{var de;X.key==="Enter"&&P[0]&&q(P[0].id),X.key==="ArrowDown"&&(X.preventDefault(),(de=document.querySelector(".pb-graph__list button"))==null||de.focus())},Y=n?"offline":l===null?"loading":l.nodes.length===0?"empty":"ready",he=(l==null?void 0:l.groups.filter(X=>X.slot>=0))??[],_e=(l==null?void 0:l.groups.filter(X=>X.slot<0))??[],Ce=_e.reduce((X,de)=>X+de.count,0);return r.jsxs("section",{className:"pb-graph","data-inspector":O?"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(Et,{path:yt.search}),r.jsx("input",{ref:u,type:"search",value:g,placeholder:"Datei oder Symbol suchen","aria-label":"Datei oder Symbol im Graph suchen",spellCheck:!1,autoComplete:"off",onChange:X=>b(X.target.value),onKeyDown:W}),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(Vv).filter(X=>l.kindCounts[X]>0).map(X=>r.jsxs("button",{type:"button",className:"pb-chip","aria-pressed":!x.has(X),onClick:()=>h(de=>B(de,X)),children:[Vv[X]," ",r.jsx("span",{className:"pb-num",children:Yi.format(l.kindCounts[X])})]},X))}),r.jsx("h2",{className:"pb-heading",children:"Bereiche"}),r.jsxs("ul",{className:"pb-groups","aria-label":"Bereiche anzeigen oder ausblenden",children:[he.map(X=>r.jsx("li",{children:r.jsxs("button",{type:"button","aria-pressed":!v.has(X.name),onClick:()=>y(de=>B(de,X.name)),children:[r.jsx("i",{className:"pb-dot","data-slot":X.slot,"aria-hidden":"true"}),r.jsx("span",{className:"pb-groups__name",title:X.name,children:X.name}),r.jsx("span",{className:"pb-num",children:Yi.format(X.count)})]})},X.name)),_e.length>0&&r.jsx("li",{children:r.jsxs("button",{type:"button","aria-pressed":!v.has(cl),onClick:()=>y(X=>B(X,cl)),children:[r.jsx("i",{className:"pb-dot","data-slot":"-1","aria-hidden":"true"}),r.jsxs("span",{className:"pb-groups__name",children:[cl," · ",_e.length," Bereiche"]}),r.jsx("span",{className:"pb-num",children:Yi.format(Ce)})]})})]}),r.jsx("h2",{className:"pb-heading",children:g.trim()===""?"Am stärksten vernetzt":`${Yi.format(P.length)} Treffer`}),P.length===0?r.jsx("p",{className:"pb-quiet",children:g.trim()===""?"Alle Objekte sind ausgeblendet.":"Kein Objekt passt zur Suche."}):r.jsxs("ol",{className:"pb-graph__list",onKeyDown:k,children:[P.slice(0,hh).map(X=>r.jsx("li",{children:r.jsxs("button",{type:"button","aria-current":X.id===M?"true":void 0,onClick:()=>q(X.id),onDoubleClick:()=>R(X.id),onMouseEnter:()=>{var de;return(de=m.current)==null?void 0:de.setHovered(X.id)},onMouseLeave:()=>{var de;return(de=m.current)==null?void 0:de.setHovered(null)},onFocus:()=>{var de;return(de=m.current)==null?void 0:de.setHovered(X.id)},onBlur:()=>{var de;return(de=m.current)==null?void 0:de.setHovered(null)},children:[r.jsx("i",{className:"pb-dot","data-slot":Cg(l,X),"aria-hidden":"true"}),r.jsxs("span",{className:"pb-row__text",children:[r.jsx("span",{className:"pb-row__label",children:X.label}),X.path&&r.jsx("span",{className:"pb-row__path",children:YD(X.path)})]}),r.jsx("span",{className:"pb-num",title:`${X.degree} Verbindungen`,children:X.degree})]})},X.id)),P.length>hh&&r.jsxs("li",{className:"pb-quiet",children:[Yi.format(P.length-hh)," 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":Y}),Y==="ready"&&r.jsxs("div",{className:"pb-toolbar",role:"toolbar","aria-label":"Graph-Ansicht",children:[r.jsxs("button",{type:"button",className:"pb-tool",onClick:()=>{var X;return(X=m.current)==null?void 0:X.fit()},title:"Alles einpassen (F)",children:[r.jsx(Et,{path:yt.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 X;return(X=m.current)==null?void 0:X.zoomBy(1/ru)},"aria-label":"Herauszoomen",title:"Herauszoomen (−)",children:r.jsx(Et,{path:yt.minus})}),r.jsx("span",{className:"pb-toolbar__zoom pb-num",ref:f,"aria-live":"off",children:"100 %"}),r.jsx("button",{type:"button",className:"pb-tool pb-tool--icon",onClick:()=>{var X;return(X=m.current)==null?void 0:X.zoomBy(ru)},"aria-label":"Hineinzoomen",title:"Hineinzoomen (+)",children:r.jsx(Et,{path:yt.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(([X,de])=>r.jsx("button",{type:"button",role:"radio","aria-checked":_===X,onClick:()=>A(X),children:de},X))}),r.jsx("span",{className:"pb-toolbar__sep","aria-hidden":"true"}),r.jsx("button",{type:"button",className:`pb-tool ${S?"pb-tool--active":""}`,onClick:()=>w(X=>!X),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(ZS,{timeline:s})]})]}),Y==="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:()=>w(!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)"]})]})]})]}),Y==="ready"&&l&&r.jsxs("p",{className:"pb-graph__status",role:"status",children:[r.jsx("span",{className:"pb-num",children:Yi.format(l.nodes.length)})," Objekte ·"," ",r.jsx("span",{className:"pb-num",children:Yi.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:Yi.format(e.totalFiles)})," Dateien"]}),e&&!e.indexComplete&&r.jsxs("span",{className:"pb-graph__stale",children:[" · ",Yi.format(e.staleFiles??0)," Dateien warten auf den Index"]})]}),Y!=="ready"&&r.jsxs("div",{className:"pb-state",role:Y==="offline"?"alert":"status",children:[Y==="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."})]}),Y==="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."})]}),Y==="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."]})]})]}),O&&l&&r.jsx(qD,{node:O,model:l,onClose:()=>T(null),onSelect:X=>q(X),onOpen:()=>R(O.id),onCentre:()=>{var X;return(X=m.current)==null?void 0:X.focusNode(O.id)}})]})}function qD({node:t,model:e,onClose:n,onSelect:i,onOpen:a,onCentre:s}){const l=U.useMemo(()=>{const c=new Map,u=(f,m)=>{if(m===void 0)return;const d=c.get(f)??[];d.push(m),c.set(f,d)};for(const f of e.edges){const[m,d]=WD[f.kind]??["Verbunden mit","Verbunden mit"];f.source===t.id?u(m,e.byId.get(f.target)):f.target===t.id&&u(d,e.byId.get(f.source))}return[...c.entries()].map(([f,m])=>[f,$S(m)])},[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:VD[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(Et,{path:yt.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:Yi.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(Et,{path:yt.open})," ",t.kind==="note"?"Notiz öffnen":"Quelle öffnen"]}),r.jsxs("button",{type:"button",className:"pb-button",onClick:s,children:[r.jsx(Et,{path:yt.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(f=>r.jsx("li",{children:r.jsxs("button",{type:"button",onClick:()=>i(f.id),children:[r.jsx("i",{className:"pb-dot","data-slot":Cg(e,f),"aria-hidden":"true"}),r.jsx("span",{className:"pb-row__label",children:f.label})]})},f.id)),u.length>40&&r.jsxs("li",{className:"pb-quiet",children:[u.length-40," weitere"]})]})]},c))]})}function Cg(t,e){var n;return((n=t.groups.find(i=>i.name===e.group))==null?void 0:n.slot)??-1}function Wv(t,e){return t===null||Cg(t,e)>=0?e.group:cl}function YD(t){const e=t.split("/");return e.pop(),e.slice(-3).join("/")}function KD({workspaceId:t,workspaceName:e,onOpenFile:n,onOpenNotes:i,initialData:a}){var x,h,v,y,_;const[s,l]=U.useState(a??null),[o,c]=U.useState(!a),[u,f]=U.useState(!1),[m,d]=U.useState(!1),[p,g]=U.useState(null),b=()=>{if(a)return;c(!0),f(!1),d(!1),g(null);let A=!0;return CT(t).then(M=>{A&&(c(!1),"routeMissing"in M?f(!0):"unreachable"in M?d(!0):"serverError"in M?g(`Fehler ${M.status}: ${M.error}`):l(M))}).catch(M=>{A&&(c(!1),g(M instanceof Error?M.message:String(M)))}),()=>{A=!1}};return U.useEffect(()=>{if(a){l(a),c(!1),f(!1),d(!1);return}return b()},[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(Et,{path:yt.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."]})]}),m&&!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:b,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:b,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:((x=s.stats)==null?void 0:x.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:((h=s.stats)==null?void 0:h.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"})]})]}),((_=s.stats)==null?void 0:_.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(A=>r.jsxs("span",{className:"briefing-lang-chip",children:[A.name," (",A.files,")"]},A.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(A=>r.jsxs("li",{className:"briefing-list-item",children:[r.jsxs("button",{type:"button",className:"briefing-link-btn",onClick:()=>n(A.path),children:[r.jsx(Et,{path:yt.file}),r.jsx("span",{className:"briefing-path",children:A.path})]}),A.why&&r.jsx("span",{className:"briefing-desc",children:A.why})]},A.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(A=>r.jsxs("li",{className:"briefing-list-item",children:[r.jsx("button",{type:"button",className:"briefing-link-btn",onClick:()=>n(A.path),children:r.jsx("span",{className:"briefing-path",children:A.path})}),r.jsxs("div",{className:"briefing-change-meta",children:[r.jsx("span",{className:"briefing-kind-badge",children:A.kind}),r.jsx("time",{className:"briefing-time",children:new Date(A.when).toLocaleString()})]})]},A.path+A.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(A=>r.jsxs("li",{className:"briefing-list-item",children:[r.jsxs("button",{type:"button",className:"briefing-link-btn",onClick:()=>n(A.path),children:[r.jsx(Et,{path:yt.code}),r.jsx("span",{className:"briefing-path",children:A.path})]}),r.jsxs("span",{className:"briefing-degree-badge",children:["Grad ",A.degree]})]},A.path))})]})]})]})]})}function Co(t){return t==="risk"?"hygiene-risk":t==="attention"?"hygiene-attention":"hygiene-ok"}function Ro(t){return t==="risk"?"● Risiko":t==="attention"?"◌ Achtung":"● Ok"}function vd({cmd:t,label:e}){const[n,i]=U.useState(!1),a=U.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 $D({co:t}){const[e,n]=U.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 ${Co(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:Ro(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(vd,{cmd:`cd "${t.path}" && git stash push -m "wip-$(date +%Y%m%d-%H%M%S)"`})]})]})]})}function ZD({f:t}){return r.jsxs("div",{className:`hygiene-finding-row ${Co(t.level)}`,children:[r.jsx("span",{className:`hygiene-badge hygiene-badge-${t.level}`,children:Ro(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(vd,{cmd:t.fix})]})]})}function QD({workspaceId:t}){const[e,n]=U.useState("workspace"),[i,a]=U.useState(null),[s,l]=U.useState("loading"),[o,c]=U.useState(""),[u,f]=U.useState(null),[m,d]=U.useState("loading"),[p,g]=U.useState(null),[b,x]=U.useState("loading"),[h,v]=U.useState(!1),y=U.useCallback(()=>{if(!t){l("unavailable");return}l("loading"),DT(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]),_=U.useCallback(()=>{d("loading"),LT().then(M=>{"routeMissing"in M?d("unavailable"):"unreachable"in M?d("unreachable"):"serverError"in M?d("error"):(f(M),d("ready"))}).catch(()=>{d("unreachable")})},[]),A=U.useCallback(()=>{x("loading"),UT().then(M=>{"routeMissing"in M?x("unavailable"):"unreachable"in M?x("unreachable"):"serverError"in M?x("error"):(g(M),x("ready"))}).catch(()=>{x("unreachable")})},[]);return U.useEffect(()=>{y(),_(),A()},[y,_,A]),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(),_(),A()},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"})]}),h&&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(vd,{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 ${Co(i.summary.level)}`,children:[r.jsx("span",{className:`hygiene-badge hygiene-badge-${i.summary.level}`,children:Ro(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,T)=>r.jsx(ZD,{f:M},T))})]}),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,T)=>r.jsx($D,{co:M},T))})]}),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:[m==="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…"})]}),m==="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)."]})]}),m==="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:_,children:"Erneut versuchen"})]}),m==="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:_,children:"Erneut versuchen"})]}),m==="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,T)=>r.jsxs("div",{className:`hygiene-drive-card ${Co(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:Ro(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))}%`}})})]},T))})]}),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,T)=>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,")"]})]},T))})]}),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,T)=>r.jsxs("div",{className:`hygiene-finding-row ${Co(M.level)}`,children:[r.jsx("span",{className:`hygiene-badge hygiene-badge-${M.level}`,children:Ro(M.level)}),r.jsxs("div",{className:"hygiene-finding-body",children:[r.jsx("div",{className:"hygiene-finding-text",children:M.text}),M.fix&&r.jsx(vd,{cmd:M.fix})]})]},T))})]})]})]}),e==="repos"&&r.jsxs("div",{className:"hygiene-tab-content",children:[b==="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…"})]}),b==="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)."]})]}),b==="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:A,children:"Erneut versuchen"})]}),b==="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:A,children:"Erneut versuchen"})]}),b==="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,T)=>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"]})]})]})})]},T))})]})]})]})]})}const Xv=["blocked","still","awaiting-commit","working","needs-task","resting"];function um(t,e,n){const i=t.lastHeartbeat===null?Number.NaN:Date.parse(t.lastHeartbeat);return Number.isFinite(i)&&i>=e&&i<=n}function QS(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 fs(t,e,n,i=Date.now()){return t.retired||t.turnState==="paused"||t.turnState==="offline"||!um(t,e,n)?"resting":t.turnState==="blocked"?"blocked":QS(t,i)?"still":t.turnState==="awaiting-commit"?"awaiting-commit":t.turnState==="working"?"working":t.turnState==="needs-task"?"needs-task":"resting"}function JD(t,e,n,i=Date.now()){return[...t].sort((a,s)=>Xv.indexOf(fs(a,e,n,i))-Xv.indexOf(fs(s,e,n,i))||(a.account??"").localeCompare(s.account??"")||a.name.localeCompare(s.name))}function Or(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 eL(t,e,n,i=Date.now()){return t.reduce((a,s)=>{const l=fs(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 qv(t,e,n,i,a=Date.now()){if(e.size===0)return!0;const s=fs(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 ph(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 tL(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 Ps(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 nL=[{label:"1 Std.",hours:1},{label:"6 Std.",hours:6},{label:"24 Std.",hours:24}],Pr={working:"Läuft","needs-task":"Braucht Aufgabe","awaiting-commit":"Wartet auf Freigabe","commit-approved":"Freigegeben",blocked:"Blockiert",paused:"Pausiert",offline:"Offline"};function iL({workspaceId:t,data:e,onRefresh:n}){var ae;const[i,a]=U.useState(6),[s,l]=U.useState(100),[o,c]=U.useState(Date.now()),[u,f]=U.useState(null),[m,d]=U.useState(null),[p,g]=U.useState(""),[b,x]=U.useState(!1),[h,v]=U.useState(()=>new Set),[y,_]=U.useState(!1);U.useEffect(()=>{const j=window.setInterval(()=>c(Date.now()),3e4);return()=>clearInterval(j)},[]);const A=U.useMemo(()=>(e==null?void 0:e.board.agents)??[],[e]),M=U.useMemo(()=>{var xe;if(!e)return[];const j=e.turns.map(Te=>({...Te})),ee=new Set(j.filter(Te=>Te.endedAt===null).map(Te=>Te.agentId));for(const Te of A)Te.turnState==="working"&&Te.turnStateAt&&!ee.has(Te.id)&&j.push({id:`current-${Te.id}`,agentId:Te.id,taskId:((xe=Te.task)==null?void 0:xe.id)??null,startedAt:Te.turnStateAt,endedAt:null,state:"working",summary:Te.turnSummary,legacy:!0});return j},[e,A]),T=U.useMemo(()=>M.find(j=>j.id===u)??null,[M,u]),S=T?A.find(j=>j.id===T.agentId)??null:null,w=T!=null&&T.taskId?(e==null?void 0:e.tasks.find(j=>j.id===T.taskId))??null:null,R=M.filter(j=>j.legacy).length,D=o-(100-s)/100*i*60*6e4,L=D-i*60*6e4,q=JD(A,L,D,o),P=eL(A,L,D,o),O=q.filter(j=>fs(j,L,D,o)!=="resting"&&qv(j,h,L,D,o)),B=q.filter(j=>fs(j,L,D,o)==="resting"),k=B.filter(j=>qv(j,h,L,D,o)),W=["blocked","still","awaiting-commit","working","needs-task"],Y=new Map(O.map((j,ee)=>[j.id,ee])),he=j=>{v(ee=>{const xe=new Set(ee);return xe.has(j)?xe.delete(j):xe.add(j),xe})},_e=(j,ee)=>{const xe=fs(j,L,D,o),Te=M.filter(se=>se.agentId===j.id&&um(j,L,D)&&Date.parse(se.startedAt)<=D&&(se.endedAt===null||Date.parse(se.endedAt)>=L)),Ie=j.leases.filter(se=>Date.parse(se.createdAt)<=D&&Date.parse(se.expiresAt)>=L),it=X(j.id).filter(se=>Date.parse(se.createdAt)>=L&&Date.parse(se.createdAt)<=D),re=de(j.id).filter(se=>Date.parse(se.approvedAt)>=L&&Date.parse(se.approvedAt)<=D),me=QS(j,o),Ae=j.turnState==="working"&&!um(j,L,D);return r.jsxs("div",{className:"turns-lane","data-priority":xe,children:[r.jsxs("div",{className:"turns-worker",children:[r.jsx("strong",{children:j.name}),r.jsxs("span",{children:[j.surface??"Oberfläche offen"," · ",j.model??"Modell offen"]}),r.jsx("small",{children:Ae?`Zustand veraltet seit ${Or(j.lastHeartbeat,o)}`:me?`still? seit ${Or(j.lastHeartbeat,o)}`:j.turnState?Pr[j.turnState]??j.turnState:"Kein Turn-Status"})]}),r.jsxs("div",{className:"turns-track",style:{"--row":ee},children:[Array.from({length:6},(se,Le)=>r.jsx("i",{className:"turns-gridline",style:{left:`${Le*20}%`}},Le)),Te.map(se=>{const Le=Ps(se.startedAt,L,D),ke=se.endedAt?Ps(se.endedAt,L,D):100,Ue=se.endedAt?se.state:"working";return r.jsx("button",{type:"button",id:`turn-${se.id}`,className:"turns-block","data-state":ph(Ue),"data-still":me?"true":void 0,"data-selected":u===se.id?"true":void 0,style:{left:`${Le}%`,width:`${Math.max(.65,ke-Le)}%`},onClick:()=>{f(se.id),d(null),g("")},"aria-label":`${j.name}, ${Pr[Ue??""]??"laufend"}${me?", still?":""}`,title:`${se.taskId??"Ohne Aufgabe"} · ${new Date(se.startedAt).toLocaleString()}${me?` · still? seit ${Or(j.lastHeartbeat,o)}`:""}`,children:me&&r.jsxs("span",{className:"turns-still",children:["still? seit ",Or(j.lastHeartbeat,o)]})},se.id)}),Ie.map(se=>r.jsx("i",{className:"turns-claim",style:{left:`${Ps(se.createdAt,L,D)}%`,width:`${Math.max(.8,Ps(se.expiresAt,L,D)-Ps(se.createdAt,L,D))}%`},title:`Claim: ${se.paths.join(", ")}`},se.id)),it.map(se=>r.jsx("button",{type:"button",className:"turns-mark turns-mark--message",style:{left:`${Ps(se.createdAt,L,D)}%`},onClick:()=>{const Le=M.find(ke=>ke.agentId===j.id);Le&&f(Le.id)},"aria-label":`Nachricht ${se.fromAgent} an ${se.toAgent??"alle"}: ${se.subject}`,title:`${se.fromAgent} → ${se.toAgent??"alle"}: ${se.subject}`},se.id)),re.map(se=>r.jsx("button",{type:"button",className:"turns-mark turns-mark--approval",style:{left:`${Ps(se.approvedAt,L,D)}%`},onClick:()=>{const Le=M.find(ke=>ke.agentId===j.id);Le&&f(Le.id)},"aria-label":`Freigabe von ${se.byAgent}`,title:`Freigabe von ${se.byAgent}`},se.id))]})]},j.id)};if(U.useEffect(()=>{const j=ee=>{var re;if(ee.target instanceof HTMLElement&&ee.target.matches("input,textarea,select,[contenteditable=true]")||ee.key!=="ArrowLeft"&&ee.key!=="ArrowRight"&&ee.key!=="Enter")return;const xe=M.filter(me=>Date.parse(me.startedAt)<=D&&(me.endedAt===null||Date.parse(me.endedAt)>=L));if(xe.length===0)return;if(ee.key==="Enter"){u&&(ee.preventDefault(),(re=document.getElementById(`turn-${u}`))==null||re.focus());return}ee.preventDefault();const Te=xe.findIndex(me=>me.id===u),Ie=ee.key==="ArrowRight"?1:-1,it=xe[(Te+Ie+xe.length)%xe.length];it&&f(it.id)};return window.addEventListener("keydown",j),()=>window.removeEventListener("keydown",j)},[D,u,M,L]),!e)return r.jsx("section",{className:"turns-empty",role:"status",children:"Fleet-Daten werden geladen oder sind nicht erreichbar."});const Ce=async j=>{if(j.preventDefault(),!S)return;const ee=new FormData(j.currentTarget);x(!0),g("");try{m==="message"?await mT({workspace:t,fromAgent:S.id,toAgent:String(ee.get("toAgent")??""),subject:String(ee.get("subject")??""),body:String(ee.get("body")??"")}):m==="task"&&await gT({workspace:t,requestedBy:S.id,title:String(ee.get("title")??""),body:String(ee.get("body")??"")}),d(null),g("Gespeichert."),n()}catch(xe){g(xe instanceof Error?xe.message:"Aktion fehlgeschlagen.")}finally{x(!1)}},X=j=>e.messages.filter(ee=>ee.fromAgent===j||ee.toAgent===j),de=j=>e.approvals.filter(ee=>ee.agentId===j),Ne=Array.from({length:7},(j,ee)=>new Date(L+i*60*6e4*ee/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:[nL.map(j=>r.jsx("button",{type:"button","aria-pressed":i===j.hours,onClick:()=>{a(j.hours),l(100)},children:j.label},j.hours)),r.jsx("button",{type:"button",onClick:n,children:"Aktualisieren"})]})]}),r.jsxs("div",{className:"turns-summary","aria-label":"Fleet-Zusammenfassung",children:[r.jsxs("button",{type:"button","aria-pressed":h.has("working"),onClick:()=>he("working"),children:[P.working," arbeiten"]}),r.jsxs("button",{type:"button","aria-pressed":h.has("awaiting"),onClick:()=>he("awaiting"),children:[P.awaiting," warten auf Freigabe"]}),r.jsxs("button",{type:"button","aria-pressed":h.has("still"),onClick:()=>he("still"),children:[P.still," still?"]}),r.jsxs("button",{type:"button","aria-pressed":h.has("unread"),onClick:()=>he("unread"),children:[P.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(j=>r.jsxs("span",{children:[r.jsx("i",{"data-state":ph(j)}),Pr[j]]},j)),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:s,onChange:j=>l(Number(j.target.value))}),r.jsx("span",{children:new Date(D).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:Ne.map((j,ee)=>r.jsx("span",{children:j},ee))})]}),O.length===0&&B.length===0&&r.jsx("p",{className:"turns-empty",children:"Keine registrierten Worker in diesem Workspace."}),W.map(j=>{const ee=O.filter(xe=>fs(xe,L,D,o)===j);return ee.length===0?null:r.jsxs("section",{className:"turns-priority","data-priority":j,children:[r.jsx("h3",{children:j==="still"?"still?":Pr[j]??j}),Array.from(new Set(ee.map(xe=>xe.account??"Ohne Konto"))).map(xe=>r.jsxs("section",{className:"turns-account",children:[r.jsx("h4",{children:xe}),ee.filter(Te=>(Te.account??"Ohne Konto")===xe).map(Te=>_e(Te,Y.get(Te.id)??0))]},xe))]},j)}),B.length>0&&r.jsxs("section",{className:"turns-resting",children:[r.jsxs("button",{className:"turns-resting__toggle",type:"button","aria-expanded":y||h.size>0&&k.length>0,onClick:()=>_(j=>!j),children:[B.length," ruhende Worker ",y?"−":"+"]}),(y||h.size>0&&k.length>0)&&r.jsx("div",{children:B.filter(j=>h.size===0||k.includes(j)).map((j,ee)=>r.jsxs("div",{className:"turns-resting__worker",children:[r.jsx("strong",{children:j.name}),r.jsxs("span",{children:[j.turnState==="working"?`Zustand veraltet seit ${Or(j.lastHeartbeat,o)}`:Pr[j.turnState??""]??j.turnState??"Kein Status"," · ",j.unread," ungelesen"]}),j.turnState==="working"&&r.jsx("i",{className:"turns-stale-hatch","aria-label":"Veralteter Status"})]},j.id))})]})]}),r.jsx("aside",{className:"turns-detail","aria-label":"Turn-Details","aria-live":"polite",children:T&&S?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:S.name})]}),r.jsx("button",{type:"button",onClick:()=>f(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":ph(T.endedAt?T.state:"working"),children:T.endedAt?Pr[T.state??""]??T.state:"Läuft"}),tL(S.lastHeartbeat,T.endedAt,o)&&r.jsxs("b",{className:"turns-still-label",children:["still? seit ",Or(S.lastHeartbeat,o)]})]}),r.jsx("dt",{children:"Beginn"}),r.jsx("dd",{children:new Date(T.startedAt).toLocaleString()}),r.jsx("dt",{children:"Ende"}),r.jsx("dd",{children:T.endedAt?new Date(T.endedAt).toLocaleString():"Noch offen"}),r.jsx("dt",{children:"Aufgabe"}),r.jsx("dd",{children:(w==null?void 0:w.title)??((ae=S.task)==null?void 0:ae.title)??T.taskId??"Keine aktuelle Aufgabe"}),(w==null?void 0:w.body)&&r.jsxs(r.Fragment,{children:[r.jsx("dt",{children:"Aufgabenbeschreibung"}),r.jsx("dd",{children:w.body})]}),r.jsx("dt",{children:"Zusammenfassung"}),r.jsx("dd",{children:T.summary??S.turnSummary??"Keine Zusammenfassung gespeichert."})]}),T.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:S.leases.length?S.leases.flatMap(j=>j.paths.map(ee=>r.jsx("li",{children:ee},`${j.id}:${ee}`))):r.jsx("li",{children:"Keine aktiven Claims."})}),r.jsx("h4",{children:"Nachrichten dieses Workers"}),r.jsx("ul",{children:X(T.agentId).slice(-8).map(j=>r.jsxs("li",{children:[r.jsxs("b",{children:[j.fromAgent," → ",j.toAgent??"alle"]}),r.jsxs("span",{children:[j.subject," · ",j.readAt?"quittiert":j.deliveredAt?"zugestellt":"offen"]})]},j.id))}),r.jsx("h4",{children:"Branch / Commit"}),r.jsx("ul",{children:S.worktrees.length?S.worktrees.map(j=>r.jsxs("li",{children:[r.jsxs("b",{children:[j.branch??"Branch nicht lesbar"," · ",j.head??"Commit nicht lesbar"]}),r.jsx("span",{children:j.path})]},j.path)):r.jsx("li",{children:"Kein Worktree registriert."})}),r.jsxs("div",{className:"turns-actions",children:[r.jsx("button",{type:"button",onClick:()=>{d(m==="message"?null:"message"),g("")},children:"Nachricht senden"}),r.jsx("button",{type:"button",onClick:()=>{d(m==="task"?null:"task"),g("")},children:"Aufgabe einreihen"})]}),m&&r.jsxs("form",{className:"turns-composer",onSubmit:Ce,children:[r.jsx("h4",{children:m==="message"?"Nachricht an Worker":"Aufgabe einreihen"}),m==="message"?r.jsxs(r.Fragment,{children:[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("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:b,children:b?"Wird gesendet …":"Senden"})]}),p&&r.jsx("p",{role:"status",children:p})]}):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 · ",R," laufende Profilstatus · ",A.length," Worker · ",e.messages.length," Nachrichten · ",e.approvals.length," Freigaben"]})]})})]})]})}function aL({workspaceId:t,isOpen:e,onClose:n,onOpenSource:i,onNavigateToSearch:a,initialQuestion:s=""}){const[l,o]=U.useState(s),[c,u]=U.useState(!1),[f,m]=U.useState(null),[d,p]=U.useState(!1),[g,b]=U.useState(!1),[x,h]=U.useState(null),v=U.useRef(null);if(U.useEffect(()=>{e&&(o(s),m(null),p(!1),b(!1),h(null),setTimeout(()=>{var T;return(T=v.current)==null?void 0:T.focus()},50))},[e,s]),!e)return null;const y=async T=>{const S=T.trim();if(!(!S||c)){u(!0),h(null),p(!1),b(!1);try{const w=await RT(t,S);"routeMissing"in w?(p(!0),m(null)):"unreachable"in w?(b(!0),m(null)):"serverError"in w?(h(`Fehler ${w.status}: ${w.error}`),m(null)):m(w)}catch(w){h(w instanceof Error?w.message:String(w))}finally{u(!1)}}},_=T=>{o(T),y(T)},A=T=>{i(T.path,T.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:T=>T.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(Et,{path:yt.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:T=>o(T.target.value),onKeyDown:T=>{T.key==="Escape"&&n()}}),l&&r.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:"✕"})]}),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(Et,{path:yt.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(Et,{path:yt.search})," Suche nach „",l,'" öffnen']})]}),g&&!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"}),"?"]})]}),x&&!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:x})]}),!c&&!d&&!g&&!x&&!f&&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 T="Wo ist workspaceIdFor definiert?";o(T),y(T)},children:"„Wo ist workspaceIdFor definiert?“"}),r.jsx("button",{type:"button",className:"ask-modal__suggestion-chip",onClick:()=>{const T="Welche Einstiegspunkte hat das Projekt?";o(T),y(T)},children:"„Welche Einstiegspunkte hat das Projekt?“"}),r.jsx("button",{type:"button",className:"ask-modal__suggestion-chip",onClick:()=>{const T="Was hat sich zuletzt geändert?";o(T),y(T)},children:"„Was hat sich zuletzt geändert?“"})]})]}),f&&!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:f.answer}),r.jsxs("div",{className:"ask-modal__meta",children:[r.jsxs("span",{className:`ask-modal__confidence ask-modal__confidence--${f.confidence}`,title:`Konfidenz: ${f.confidence}`,children:["Konfidenz: ",f.confidence]}),f.intent&&r.jsxs("span",{className:"ask-modal__intent-badge",children:["Fokus: ",f.intent]})]})]}),f.unavailable&&f.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: ",f.unavailable.join(", ")]}),f.sources&&f.sources.length>0&&r.jsxs("div",{className:"ask-modal__sources",children:[r.jsxs("h5",{children:["Gefundene Quellen (",f.sources.length,"):"]}),r.jsx("div",{className:"ask-modal__sources-list",children:f.sources.map((T,S)=>r.jsxs("button",{type:"button",className:"ask-modal__source-card",onClick:()=>A(T),children:[r.jsxs("div",{className:"ask-modal__source-top",children:[r.jsx(Et,{path:yt.file}),r.jsx("strong",{className:"ask-modal__source-path",children:T.path}),r.jsxs("span",{className:"ask-modal__source-line",children:["Zeile ",T.line]})]}),T.symbol&&r.jsxs("span",{className:"ask-modal__source-symbol",children:["Symbol: ",r.jsx("code",{children:T.symbol})]}),T.why&&r.jsx("span",{className:"ask-modal__source-why",children:T.why})]},`${T.path}-${T.line}-${S}`))})]}),f.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"})]}),f.followUps&&f.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:f.followUps.map(T=>r.jsxs("button",{type:"button",className:"ask-modal__followup-btn",onClick:()=>_(T),children:["↳ ",T]},T))})]})]})]}),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:"Strg+K"})," Immer verfügbar"]})]})]})})}const JS=[{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"}],dm=[{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"}],eM=[...JS,...dm],lu=Zb,sL=2e3;function rL(){const t=new URLSearchParams(location.search).get("view"),e=(()=>{try{return localStorage.getItem("plugbrain.view")}catch{return null}})(),n=t||e;return eM.some(i=>i.id===n)?n:"briefing"}function lL(){const t=new URLSearchParams(location.search);return t.has("workspace")?t.get("workspace")??"":t.has("workspaceRoot")?"":tT()}function oL(){var je,ht,qt,an,mn,Cs;const[t,e]=U.useState(null),[n,i]=U.useState(null),[a,s]=U.useState(null),[l,o]=U.useState({depth:0,tasks:[]}),[c,u]=U.useState(""),[f,m]=U.useState(0),[d,p]=U.useState(rL),[g,b]=U.useState(lL),x=U.useMemo(()=>{const F=new URLSearchParams(location.search);return F.has("workspace")?null:F.get("workspaceRoot")},[]),[h,v]=U.useState([]),[y,_]=U.useState(!1),[A,M]=U.useState(""),[T,S]=U.useState(!1),[w,R]=U.useState(""),[D,L]=U.useState(""),[q,P]=U.useState(""),[O,B]=U.useState(null),[k,W]=U.useState(null),[Y,he]=U.useState(!1),[_e,Ce]=U.useState([]),[X,de]=U.useState(()=>{const F=new URLSearchParams(location.search).get("file"),ce=Number(new URLSearchParams(location.search).get("line"));return F?{path:F,line:Number.isFinite(ce)?ce:null}:null}),[Ne,ae]=U.useState(null),[j,ee]=U.useState(null),xe=U.useCallback(()=>{if(!g){s(null);return}f0(g).then(s).catch(()=>s(null))},[g]);U.useEffect(()=>{if(d!=="turns"||!g)return;let F=!0;const ce=()=>{f0(g).then(Ye=>{F&&s(Ye)}).catch(()=>{F&&s(null)})};ce();const Ze=window.setInterval(ce,1e4);return()=>{F=!1,clearInterval(Ze)}},[d,g]);const[Te,Ie]=U.useState(!1),[it,re]=U.useState("appearance"),[me,Ae]=U.useState(()=>{try{const F=localStorage.getItem("plugbrain.theme");if(F==="dark"||F==="light"||F==="system")return F}catch{}return"dark"});U.useEffect(()=>{try{localStorage.setItem("plugbrain.theme",me)}catch{}const F=ce=>{let Ze=ce;ce==="system"&&(Ze=typeof window<"u"&&window.matchMedia&&window.matchMedia("(prefers-color-scheme: light)").matches?"light":"dark"),document.documentElement.dataset.theme=Ze};if(F(me),me==="system"&&typeof window<"u"&&window.matchMedia){const ce=window.matchMedia("(prefers-color-scheme: light)"),Ze=()=>F("system");return ce.addEventListener("change",Ze),()=>ce.removeEventListener("change",Ze)}},[me]);const[se,Le]=U.useState(!1),[ke,Ue]=U.useState(nS()),[Re,qe]=U.useState(ji()),[Se,I]=U.useState(!1),[Qe,Je]=U.useState(null),[C,E]=U.useState([]),[V,K]=U.useState(!1),[ne,ye]=U.useState(""),[be,ie]=U.useState(!1),[Q,le]=U.useState(!1),[pe,ve]=U.useState("");U.useEffect(()=>{const F=ce=>{const Ze=ce.target;Ze!=null&&Ze.matches('input, textarea, select, [contenteditable="true"]')||((ce.ctrlKey||ce.metaKey)&&ce.key.toLowerCase()==="k"||ce.key==="/"&&!ce.shiftKey?(ce.preventDefault(),le(!0)):ce.key==="?"||ce.key==="/"&&ce.shiftKey?(ce.preventDefault(),ie(!0)):ce.key==="Escape"?(ie(!1),Ie(!1),I(!1),le(!1)):ce.key.toLowerCase()==="o"&&!ce.ctrlKey&&!ce.metaKey&&(ce.preventDefault(),_(!0)))};return window.addEventListener("keydown",F),()=>window.removeEventListener("keydown",F)},[]),U.useEffect(()=>{try{localStorage.setItem("plugbrain.view",d)}catch{}},[d]),U.useEffect(()=>{(d==="city"||d==="mesh"||d==="queue")&&de(null),d==="mesh"&&(he(!1),W(null))},[d]);const Me=U.useRef(null);U.useEffect(()=>{Me.current=k},[k]);const Be=F=>{b(F),nT(F);const ce=new URL(location.href);F?ce.searchParams.set("workspace",F):ce.searchParams.delete("workspace"),F&&ce.searchParams.delete("workspaceRoot"),history.replaceState(null,"",ce.toString())};U.useEffect(()=>{if(!g)return;let F=!0;return fetch(`/api/graph?workspace=${encodeURIComponent(g)}&limit=5000`).then(ce=>ce.json()).then(ce=>{if(!F||!(ce!=null&&ce.nodes))return;const Ze=ce.nodes.filter(Ye=>{var Xt;return Ye.type==="file"&&(Ye.path||((Xt=Ye.properties)==null?void 0:Xt.path))}).map(Ye=>{var Xt,hi,Gi,N;return{id:Ye.id,path:Ye.path||((Xt=Ye.properties)==null?void 0:Xt.path),label:Ye.label||Ye.path,lang:Ye.lang||((hi=Ye.properties)==null?void 0:hi.lang),loc:Ye.loc??((Gi=Ye.properties)==null?void 0:Gi.lines)??0,agent:Ye.agent||((N=Ye.properties)!=null&&N.agentId?{id:Ye.properties.agentId,name:Ye.properties.agentName,color:Ye.properties.agentColor}:null)}});Ce(Ze)}).catch(()=>{}),()=>{F=!1}},[g,f]),U.useEffect(()=>{let F=!0;return u0().then(ce=>{if(F){if(v(ce),!g&&x!==null){const Ze=iT(ce,x);Ze&&Be(Ze);return}if(!g&&ce.length>0){const Ze=[...ce].sort((Ye,Xt)=>(Xt.indexedAt??"").localeCompare(Ye.indexedAt??""))[0];Ze&&Be(Ze.id)}}}).catch(()=>{F&&R("Die Galaxie ist nicht erreichbar — läuft plugbrain serve?")}),()=>{F=!1}},[]);const Ve=async F=>{F.preventDefault();const ce=A.trim();if(ce!==""){S(!0),R(""),L(""),P("Vault registriert — Indexlauf wird vorbereitet …");try{const Ze=await aT(ce,void 0,Ye=>P(dp(Ye)));u0().then(Ye=>{Ye.length>0&&v(Ye)}).catch(()=>{}),L("Vault registriert und indiziert."),M(""),_(!1),Be(Ze),p("notes"),m(Ye=>Ye+1)}catch(Ze){const Ye=Ze instanceof Error?Ze.message:String(Ze);let Xt=Ye;/ENOENT|not found|nicht gefunden/i.test(Ye)?Xt=`Ordner nicht gefunden oder nicht lesbar: "${ce}". Bitte überprüfe den Pfad.`:/fetch|network|connection refused|econnrefused/i.test(Ye)&&(Xt='Brain-Kern nicht erreichbar. Bitte prüfe, ob "plugbrain serve" im Terminal läuft.'),R(Xt)}finally{S(!1),P("")}}},tt=async()=>{if(!(!g||T)){S(!0),R(""),L(""),P("Indexlauf wird vorbereitet …");try{const F=await eS(g,ce=>P(dp(ce)));L(`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.`),m(ce=>ce+1)}catch(F){R(F instanceof Error?F.message:String(F))}finally{S(!1),P("")}}};U.useEffect(()=>{if(!g)return;let F=!0,ce;const Ze=async()=>{try{const Ye=await fetch(`/api/index/progress?workspace=${encodeURIComponent(g)}`);if(Ye.ok){const Xt=await Ye.json();if(!F)return;P(hi=>sT(Xt,hi))}}catch{}F&&(ce=setTimeout(()=>void Ze(),1500))};return Ze(),()=>{F=!1,clearTimeout(ce)}},[g]);const H=F=>{F.preventDefault(),iS(ke.trim()),uT(Re.trim()),yT(),m(ce=>ce+1),Le(!1)},Ee=async(F=!1)=>{if(!(!g||V)){K(!0),ye("");try{const ce=await dT(g);Je(ce),E([...ce.indexSelection.checkoutIds]),F&&I(!0)}catch(ce){ye(ce instanceof Error?ce.message:String(ce)),F&&I(!0)}finally{K(!1)}}},oe=F=>{E(ce=>ce.includes(F)?ce.filter(Ze=>Ze!==F):[...ce,F])},we=async F=>{if(F.preventDefault(),!(!g||V)){K(!0),ye("");try{const ce=await fT(g,C);Je(ce),E([...ce.indexSelection.checkoutIds]),L(ce.indexSelection.checkoutIds.length===0?"Code-Auswahl gespeichert: bewusst keine Code-Checkouts aktiv.":`Code-Auswahl gespeichert: ${ce.indexSelection.checkoutIds.length} Checkout(s) aktiv.`),I(!1),m(Ze=>Ze+1)}catch(ce){ye(ce instanceof Error?ce.message:String(ce))}finally{K(!1)}}},De=r.jsxs("form",{className:"brain-vault",onSubmit:Ve,children:[r.jsxs("div",{className:"brain-vault__row",children:[r.jsx("input",{className:"brain-vault__path",value:A,onChange:F=>M(F.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:T||A.trim()==="",children:T?"Indiziere …":"Als Vault öffnen"})]}),g&&r.jsx("div",{className:"brain-vault__row brain-vault__row--tools",children:r.jsx("button",{type:"button",className:"brain-vault__reindex",disabled:T,onClick:()=>void tt(),children:T?"läuft …":"Neu indizieren"})}),q&&r.jsx("p",{className:"brain-vault__progress",role:"status","aria-live":"polite",children:q}),w&&r.jsx("p",{className:"brain-vault__error",role:"alert",children:w}),D&&r.jsx("p",{className:"brain-vault__done",role:"status",children:D})]});U.useEffect(()=>{const F=g||void 0;fetch("/api/timeline"+(F?"?workspace="+encodeURIComponent(F):"")).then(ce=>ce.json()).then(ce=>{var Ze;(Ze=ce==null?void 0:ce.bounds)!=null&&Ze.first&&B(ce.bounds)}).catch(()=>{})},[g]),U.useEffect(()=>{if(!Y||!O)return;const F=new Date(O.first).getTime(),ce=new Date(O.last).getTime(),Ze=Math.max(1,ce-F);let Ye=k?Math.round((new Date(k).getTime()-F)/Ze*$a):0;const Xt=setInterval(()=>{if(Ye+=1,Ye>=$a){W(null),he(!1);return}W(new Date(F+Ze*Ye/$a).toISOString())},220);return()=>clearInterval(Xt)},[Y,O]),U.useEffect(()=>{const F=new AbortController;let ce,Ze="";const Ye=g||void 0;async function Xt(){var hi,Gi,N;try{const G=new URLSearchParams;Ye&&G.set("workspace",Ye),G.set("limit",String(sL)),Me.current&&G.set("until",Me.current);const te=await fetch("/api/atlas/snapshot"+(G.toString()?`?${G}`:""),{signal:F.signal});if(!te.ok)throw new Error(`Brain-Verbindung: HTTP ${te.status}`);const $=await te.json();if(!((hi=$.workspace)!=null&&hi.canonicalPath)||!Array.isArray((Gi=$.graph)==null?void 0:Gi.nodes)||!Array.isArray((N=$.graph)==null?void 0:N.edges))throw new Error("Der Brain-Snapshot ist unvollständig.");const J=`${$.workspace.id}:${$.indexGeneration??""}:${k??""}:${$.graph.nodes.length}:${$.graph.edges.length}`;J!==Ze&&(e($),Ze=J),u("")}catch(G){F.signal.aborted||u(G instanceof Error?G.message:String(G))}if(Ye)try{const G=await hT(Ye);F.signal.aborted||i(G)}catch{F.signal.aborted||i(null)}else F.signal.aborted||i(null);try{const G=await fetch("/api/queue"+(Ye?"?workspace="+encodeURIComponent(Ye):""),{signal:F.signal});if(G.ok){const te=await G.json();(te==null?void 0:te.ok)===!0&&Array.isArray(te.tasks)&&o({depth:Number(te.depth??0),tasks:te.tasks})}}catch{}F.signal.aborted||(ce=setTimeout(Xt,3e3))}return Xt(),()=>{F.abort(),clearTimeout(ce)}},[f,k,g]);const ge=t!=null&&t.coverage?!t.coverage.indexComplete:!1,We=((je=t==null?void 0:t.coverage)==null?void 0:je.staleFiles)??0,Fe=c?{tone:"bad",text:"Offline",title:c}:t?ge?{tone:"warn",text:`${We} warten`,title:`${We} 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."},bt=U.useMemo(()=>{var F;return _e.length>0?_e:(F=t==null?void 0:t.graph)!=null&&F.nodes?t.graph.nodes.filter(ce=>{var Ze;return ce.type==="file"&&(((Ze=ce.properties)==null?void 0:Ze.path)||ce.path)}).map(ce=>{var Ye,Xt,hi,Gi;const Ze=((Ye=ce.properties)==null?void 0:Ye.path)||ce.path||"";return{id:ce.id,path:Ze,label:ce.label||ce.name||Ze,lang:((Xt=ce.properties)==null?void 0:Xt.lang)??ce.lang??null,loc:((hi=ce.properties)==null?void 0:hi.lines)??ce.loc??0,agent:(Gi=ce.properties)!=null&&Gi.agentId?{id:ce.properties.agentId,name:ce.properties.agentName||ce.properties.agentId,color:ce.properties.agentColor||"#60a5fa"}:null}}):[]},[_e,t]),rt=(F,ce)=>{F&&(ae(null),de({path:F,line:ce}))},nn=F=>{rt(F),p("explorer")},bn=F=>{de(null),ae(F),p("explorer")},Ia=F=>{ee(F),p("mesh")},_t=U.useCallback(()=>m(F=>F+1),[]),Ba=U.useCallback(F=>{ae(null),de({path:F,line:null}),F.endsWith(".md")?p("notes"):p("explorer")},[]),Fi=U.useMemo(()=>{if(!O||!(d==="atlas"||d==="city"))return null;const F=new Date(O.first).getTime(),ce=Math.max(1,new Date(O.last).getTime()-F),Ze=k?Math.round((new Date(k).getTime()-F)/ce*$a):$a;return{playing:Y,step:Ze,label:k?new Date(k).toLocaleString("de-DE",{day:"2-digit",month:"2-digit",hour:"2-digit",minute:"2-digit"}):"jetzt",onTogglePlay:()=>he(Ye=>!Ye),onScrub:Ye=>{if(he(!1),Ye>=$a){W(null);return}W(new Date(F+ce*Ye/$a).toISOString())}}},[O,k,Y]),Hi=X&&g?r.jsx("div",{className:"atlas-source-overlay",children:r.jsx(Rv,{workspaceId:g,path:X.path,highlightLine:X.line,onClose:()=>de(null),onNavigateFile:(F,ce)=>rt(F,ce)})}):null,z=F=>X&&g?r.jsx(Rv,{workspaceId:g,path:X.path,highlightLine:X.line,onClose:()=>de(null),onNavigateFile:(ce,Ze)=>rt(ce,Ze)}):F,ue=t?lu(t.workspace.name):((ht=h.find(F=>F.id===g))==null?void 0:ht.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"}),g&&h.length>1?r.jsxs("label",{className:"pb-select",title:"Vault wechseln",children:[r.jsx("span",{className:"pb-sr",children:"Vault"}),r.jsx("select",{value:g,onChange:F=>{const ce=F.target.value;ce&&Be(ce)},children:h.map(F=>r.jsx("option",{value:F.id,children:lu(F.name)},F.id))}),r.jsx(Et,{path:yt.chevron})]}):ue!==""&&r.jsx("span",{className:"pb-topbar__workspace",title:(t==null?void 0:t.workspace.canonicalPath)??"",children:ue})]}),r.jsxs("div",{className:"pb-topbar__actions",children:[g&&r.jsxs("nav",{className:"pb-tabs pb-tabs--desktop","aria-label":"Ansicht",children:[JS.map(F=>r.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:()=>p(F.id),children:F.label},F.id)),r.jsxs("div",{className:"pb-tab-group","data-active":dm.some(F=>F.id===d)?"true":void 0,children:[r.jsx("button",{type:"button",className:"pb-tab","aria-haspopup":"true",children:"Agenten"}),r.jsx("div",{className:"pb-tab-group-menu",children:dm.map(F=>r.jsx("button",{type:"button",className:"pb-tab-menu-item",title:F.hint,"aria-current":F.id===d?"page":void 0,onClick:()=>p(F.id),children:F.label},F.id))})]})]}),g&&r.jsxs("div",{className:"pb-mobile-nav",children:[r.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:r.jsx(Et,{path:yt.burger})}),r.jsxs("div",{id:"mobile-menu",className:"pb-mobile-menu",style:{display:"none"},children:[eM.map(F=>r.jsx("button",{type:"button",className:"pb-tab-menu-item",title:F.hint,"aria-current":F.id===d?"page":void 0,onClick:()=>{p(F.id),document.getElementById("mobile-menu").style.display="none"},children:F.label},F.id)),r.jsx("button",{type:"button",className:"pb-tab-menu-item",onClick:()=>{le(!0),document.getElementById("mobile-menu").style.display="none"},children:"Frag das Projekt (Strg+K)"})]})]}),g&&r.jsxs("span",{className:"pb-status","data-tone":Fe.tone,role:"status",title:Fe.title,children:[r.jsx("i",{"aria-hidden":"true"}),r.jsx("span",{children:Fe.text})]}),g&&r.jsxs("button",{type:"button",className:"pb-tool",onClick:()=>le(!0),title:"Frag das Projekt … (Strg+K / /)","aria-label":"Frag das Projekt",children:[r.jsx(Et,{path:yt.search}),r.jsx("span",{children:"Frag das Projekt"})]}),g&&r.jsxs("button",{type:"button",className:"pb-tool","aria-expanded":y,onClick:()=>{_(F=>!F),R(""),L("")},title:"Einen Ordner als neuen Vault öffnen (O)",children:[r.jsx(Et,{path:yt.folder}),r.jsx("span",{children:y?"Schließen":"Vault öffnen"})]}),g&&r.jsx("button",{type:"button",className:"pb-tool",onClick:()=>{re("repos"),Ie(!0),Ee()},disabled:V,title:"Aktive Code-Checkouts auswählen (Repos wählen)",children:r.jsx("span",{children:V?"Lädt …":"Repos wählen"})}),r.jsx("button",{type:"button",className:"pb-tool pb-tool--icon",onClick:()=>ie(!0),"aria-label":"Tastenkürzel anzeigen",title:"Tastenkürzel (?)",children:r.jsx(Et,{path:yt.keyboard})}),r.jsx("button",{type:"button",className:"pb-tool pb-tool--icon",onClick:()=>{re("appearance"),Ie(!0)},"aria-label":"Einstellungen",title:"Einstellungen",children:r.jsx(Et,{path:yt.gear})})]})]}),c&&r.jsxs("div",{className:"pb-banner",role:"alert",children:[r.jsx("strong",{children:"Brain-Server offline."}),r.jsxs("span",{children:["Der Kern antwortet nicht (",c,"). Bitte prüfe, ob ",r.jsx("code",{children:"plugbrain serve"})," läuft."]}),r.jsxs("button",{type:"button",className:"pb-button pb-button--primary",onClick:_t,children:[r.jsx(Et,{path:yt.refresh})," Erneut verbinden"]})]}),y&&g&&r.jsx("div",{className:"pb-drawer",children:De}),r.jsx("main",{className:"pb-main",children:g?r.jsxs(r.Fragment,{children:[d==="briefing"&&r.jsxs("div",{className:"pb-view",children:[r.jsx(KD,{workspaceId:g,workspaceName:ue,onOpenFile:F=>rt(F),onOpenNotes:()=>p("notes")}),Hi]}),d==="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:_t,children:"Graph aktualisieren"})]}),r.jsx("div",{id:"app",className:"atlas-app",children:r.jsx(XD,{graph:(t==null?void 0:t.graph)??null,coverage:(t==null?void 0:t.coverage)??null,error:t?"":c,onRetry:_t,onOpenSource:Ba,timeline:Fi})})]}),d==="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 F=document.querySelector(".notes-create button, .notes-empty-list button, .notes-empty--initial button");F==null||F.click()},children:"Neue Notiz"})]}),r.jsx(w2,{workspaceId:g,onOpenSource:nn,onOpenRevision:bn,onOpenAgentRun:Ia,onNavigateTab:F=>p(F)})]}),d==="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:_t,children:"Dateien aktualisieren"})]}),r.jsxs("div",{className:"workbench-split",children:[r.jsx("div",{className:"workbench-pane workbench-pane--side",children:r.jsx(_2,{workspaceName:(t==null?void 0:t.workspace.name)??(((qt=h.find(F=>F.id===g))==null?void 0:qt.name)||"Workspace"),files:bt,activePath:X==null?void 0:X.path,onSelectFile:F=>rt(F)})}),r.jsx("div",{className:"workbench-pane workbench-pane--main",children:Ne&&!X?r.jsx(cL,{workspaceId:g,revision:Ne,onClose:()=>ae(null)}):z(r.jsxs("div",{className:"source-placeholder",children:[r.jsx("div",{className:"source-placeholder__icon",children:r.jsx(Et,{path:yt.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."})]}))})]})]}),d==="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 F=document.querySelector(".search-input, .notes-search input");F==null||F.focus()},children:"Suche fokussieren"})]}),r.jsxs("div",{className:"workbench-split",children:[r.jsx("div",{className:"workbench-pane workbench-pane--side",children:r.jsx(y2,{workspaceId:g,onSelectHit:(F,ce)=>rt(F,ce)})}),r.jsx("div",{className:"workbench-pane workbench-pane--main",children:z(r.jsxs("div",{className:"source-placeholder",children:[r.jsx("div",{className:"source-placeholder__icon",children:r.jsx(Et,{path:yt.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."]})]}))})]})]}),d==="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:_t,children:"Pakete aktualisieren"})]}),r.jsxs("div",{className:"workbench-split",children:[r.jsx("div",{className:"workbench-pane workbench-pane--side",children:r.jsx(C2,{workspaceId:g,onSelectSource:F=>rt(F)})}),r.jsx("div",{className:"workbench-pane workbench-pane--main",children:z(r.jsxs("div",{className:"source-placeholder",children:[r.jsx("div",{className:"source-placeholder__icon",children:r.jsx(Et,{path:yt.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."})]}))})]})]}),d==="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:_t,children:"Stadt aktualisieren"})]}),r.jsxs("div",{className:"pb-city",children:[r.jsx(u2,{snapshot:t,onSelectFile:rt}),Fi&&r.jsx("div",{className:"pb-overlay-tools",children:r.jsx(ZS,{timeline:Fi})}),Hi]})]}),d==="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:_t,children:"Aufgaben prüfen"})]}),r.jsx(h2,{tasks:l.tasks,depth:l.depth})]}),d==="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:_t,children:"Netz aktualisieren"})]}),r.jsxs("div",{className:"pb-mesh",children:[r.jsx(f2,{mesh:n,workspaceId:g,onSelectFile:rt,focusAgentId:j}),Hi]})]}),d==="turns"&&r.jsx(iL,{workspaceId:g,data:a,onRefresh:xe}),d==="hygiene"&&r.jsxs("div",{className:"pb-view",children:[r.jsx(QD,{workspaceId:g}),Hi]})]}):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“."})]})]}),De,h.length>0&&r.jsxs("div",{className:"brain-vault__known",children:[r.jsx("span",{children:"Oder einen bekannten Vault öffnen:"}),h.map(F=>r.jsxs("button",{type:"button",className:"brain-vault__known-item",onClick:()=>Be(F.id),children:[lu(F.name)," ",r.jsx("em",{title:F.root,children:F.indexedAt?"indiziert":"nicht indiziert"})]},F.id))]})]})}),Te&&r.jsx("div",{className:"brain-modal-backdrop",onClick:()=>Ie(!1),children:r.jsxs("section",{className:"brain-modal brain-settings-modal",role:"dialog","aria-modal":"true","aria-label":"Einstellungen",onClick:F=>F.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:()=>Ie(!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":it==="appearance"?"true":"false",onClick:()=>re("appearance"),children:"Erscheinungsbild"}),r.jsx("button",{type:"button",className:"brain-settings-nav-item","data-active":it==="vaults"?"true":"false",onClick:()=>re("vaults"),children:"Vault-Verwaltung"}),r.jsx("button",{type:"button",className:"brain-settings-nav-item","data-active":it==="repos"?"true":"false",onClick:()=>{re("repos"),Qe||Ee()},children:"Repos wählen"}),r.jsx("button",{type:"button",className:"brain-settings-nav-item","data-active":it==="shortcuts"?"true":"false",onClick:()=>re("shortcuts"),children:"Tastenkürzel"}),r.jsx("button",{type:"button",className:"brain-settings-nav-item","data-active":it==="advanced"?"true":"false",onClick:()=>re("advanced"),children:"Erweitert"})]}),r.jsxs("div",{className:"brain-settings-content",children:[it==="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":me==="dark"?"true":"false",onClick:()=>Ae("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":me==="light"?"true":"false",onClick:()=>Ae("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":me==="system"?"true":"false",onClick:()=>Ae("system"),children:[r.jsx("strong",{children:"System"}),r.jsx("div",{style:{fontSize:"11px",color:"var(--muted)",marginTop:"4px"},children:"Automatisch"})]})]})]}),it==="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."}),g?r.jsx("div",{className:"brain-settings-vault-card",children:r.jsxs("dl",{children:[r.jsx("dt",{children:"Aktiver Vault:"}),r.jsx("dd",{children:((an=h.find(F=>F.id===g))==null?void 0:an.name)||g}),r.jsx("dt",{children:"Pfad:"}),r.jsx("dd",{children:((mn=h.find(F=>F.id===g))==null?void 0:mn.root)||"—"}),r.jsx("dt",{children:"Status:"}),r.jsx("dd",{children:(Cs=h.find(F=>F.id===g))!=null&&Cs.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:h.map(F=>r.jsxs("button",{type:"button",className:"pb-button",style:{justifyContent:"space-between",background:F.id===g?"var(--panel)":"transparent",borderColor:F.id===g?"var(--accent)":"var(--line)"},onClick:()=>{Be(F.id),Ie(!1)},children:[r.jsx("span",{children:lu(F.name)}),r.jsx("small",{style:{color:"var(--muted)",fontFamily:"var(--mono)"},children:F.root})]},F.id))})]})]}),it==="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 (",Qe?`${Qe.checkouts.filter(F=>C.includes(F.id)).length} von ${Qe.checkouts.length} aktiv`:"lädt …",")."]}),r.jsxs("form",{onSubmit:we,children:[ne&&r.jsx("p",{className:"brain-vault__error",role:"alert",children:ne}),Qe===null?r.jsx("p",{className:"brain-selection-modal__hint",children:"Planet-Inventar wird geladen …"}):Qe.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:V,children:[r.jsx("legend",{children:"Checkout-Inventar"}),Qe.checkouts.map(F=>r.jsxs("label",{className:F.retiredAt?"is-retired":void 0,children:[r.jsx("input",{type:"checkbox",checked:C.includes(F.id),disabled:F.retiredAt!==null,onChange:()=>oe(F.id)}),r.jsxs("span",{children:[r.jsx("strong",{children:F.relPrefix}),r.jsxs("small",{children:[F.id," · ",F.branch??"detached",F.retiredAt?" · retired":""]})]})]},F.id))]}),r.jsx("div",{className:"brain-modal__actions",style:{marginTop:"16px"},children:r.jsx("button",{type:"submit",className:"primary",disabled:V||Qe===null,children:V?"Speichert …":"Auswahl speichern"})})]})]}),it==="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"})]})]})})]}),it==="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:H,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:ke,onChange:F=>Ue(F.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:Re,onChange:F=>qe(F.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"})})]})]})]})]})]})}),be&&r.jsx("div",{className:"brain-modal-backdrop",onClick:()=>ie(!1),children:r.jsxs("section",{className:"brain-modal brain-shortcuts",role:"dialog","aria-modal":"true","aria-labelledby":"shortcut-title",onClick:F=>F.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:()=>ie(!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:"Frag das Projekt"})]}),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 / Suche"})]}),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(aL,{workspaceId:g,isOpen:Q,onClose:()=>le(!1),onOpenSource:(F,ce)=>rt(F,ce),onNavigateToSearch:F=>{p("search")},initialQuestion:pe})]})}function cL({workspaceId:t,revision:e,onClose:n}){var c;const[i,a]=U.useState(null),[s,l]=U.useState("");U.useEffect(()=>{let u=!0;return a(null),l(""),aS(t).then(f=>{u&&a(f)}).catch(f=>{u&&l(f instanceof Error?f.message:String(f))}),()=>{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(Et,{path:yt.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"})]})}J1.createRoot(document.getElementById("root")).render(r.jsx(U.StrictMode,{children:r.jsx(oL,{})})); diff --git a/ui-dist/index.html b/ui-dist/index.html index 9fa5836..f588818 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..7ee9273 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, + fetchMesh, fetchPlanetInventory, fetchSwarmSnapshot, getStoredToken, setStoredToken, getStoredAgentId, setStoredAgentId, resetAgentAttachments, setPlanetCheckoutSelection, fetchGitState, type GitState, type PlanetInventory, } from './lib/brain-client' import CityView from './views/CityView' @@ -19,10 +19,11 @@ 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 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' type Planet = { id: string; name: string; root: string; indexedAt: string | null } @@ -39,6 +40,7 @@ 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' }, ] @@ -66,6 +68,7 @@ function initialWorkspace(): string { export default function App() { const [snapshot, setSnapshot] = useState(null) const [mesh, setMesh] = useState(null) + const [swarm, setSwarm] = useState(null) const [queue, setQueue] = useState<{ depth: number; tasks: QueueTask[] }>({ depth: 0, tasks: [] }) const [error, setError] = useState('') const [attempt, setAttempt] = useState(0) @@ -100,6 +103,20 @@ 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 (view !== 'turns' || !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, 10_000) + return () => { alive = false; clearInterval(timer) } + }, [view, workspaceId]) + // Settings & Theme state const [settingsOpen, setSettingsOpen] = useState(false) const [settingsTab, setSettingsTab] = useState<'appearance' | 'vaults' | 'repos' | 'shortcuts' | 'advanced'>('appearance') @@ -108,7 +125,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(() => { @@ -929,6 +946,8 @@ export default function App() { )} + {view === 'turns' && } + {view === 'hygiene' && (
diff --git a/ui/src/lib/brain-client.test.ts b/ui/src/lib/brain-client.test.ts index 0c0ce1f..578efdf 100644 --- a/ui/src/lib/brain-client.test.ts +++ b/ui/src/lib/brain-client.test.ts @@ -7,8 +7,65 @@ import { fetchHygiene, fetchMachine, fetchRepos, + attachAgent, } from './brain-client.ts' +// While an index run holds the store's writer lock, the daemon refuses every +// store-writing route with 503 and says when to ask again (api.ts, +// refuseWhileStoreIsBusy). A page that opens a note during a reindex must wait +// for that, not give up: a failed attach makes the following read anonymous. +describe('attachAgent waits out a running index (v0.3.1)', () => { + const originalFetch = globalThis.fetch + const busy = (retryAfterMs: number) => ({ + ok: false, + status: 503, + json: async () => ({ ok: false, status: 'busy', busy: true, retryAfterMs, reason: 'an index run for ws-test is already in progress' }), + } as unknown as Response) + const attached = { + ok: true, + status: 200, + json: async () => ({ ok: true, agent: { id: 'portable-ui' } }), + } as unknown as Response + + afterEach(() => { + globalThis.fetch = originalFetch + }) + + test('retries after retryAfterMs and resolves once the index run is done', async () => { + const answers = [busy(5), busy(5), attached] + const fetchMock = mock.fn(async () => answers.shift()!) + globalThis.fetch = fetchMock as unknown as typeof fetch + + const res = await attachAgent('ws-test', 'portable-ui', 'Portable UI') + + assert.equal(fetchMock.mock.callCount(), 3) + assert.equal(res.ok, true) + }) + + test('gives up after its wait budget and reports the daemon\'s reason', async () => { + const fetchMock = mock.fn(async () => busy(5)) + globalThis.fetch = fetchMock as unknown as typeof fetch + + await assert.rejects( + attachAgent('ws-test', 'portable-ui', 'Portable UI', { maxWaitMs: 40 }), + /index run for ws-test is already in progress/, + ) + assert.ok(fetchMock.mock.callCount() >= 2, 'it asked again at least once before giving up') + }) + + test('does not retry a refusal that is not a busy store', async () => { + const fetchMock = mock.fn(async () => ({ + ok: false, + status: 401, + json: async () => ({ ok: false, error: 'missing or wrong token' }), + } as unknown as Response)) + globalThis.fetch = fetchMock as unknown as typeof fetch + + await assert.rejects(attachAgent('ws-test', 'portable-ui'), /missing or wrong token/) + assert.equal(fetchMock.mock.callCount(), 1) + }) +}) + describe('API envelope and discriminator tests (_API-VERTRAG / UX-03)', () => { const originalFetch = globalThis.fetch diff --git a/ui/src/lib/brain-client.ts b/ui/src/lib/brain-client.ts index 7cbffea..e1ebcf4 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 { @@ -273,23 +273,80 @@ 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 async function enqueueSwarmTask(input: { workspace: string; requestedBy: string; title: string; body: string }): Promise { + const response = await fetch('/api/queue', { method: 'POST', headers: authHeaders(), body: JSON.stringify(input) }) + if (!response.ok) throw new Error('Aufgabe konnte nicht eingereiht werden.') +} + 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}`) return res.json() } -export async function attachAgent(workspaceId: string, agentId = getStoredAgentId(), name = 'AGY'): Promise { - const res = await fetch('/api/agent/attach', { - method: 'POST', - headers: authHeaders(), - body: JSON.stringify({ workspace: workspaceId, agentId, name }), - }) - if (!res.ok) { +/** How long an attach waits for a running index run before it gives up. */ +const ATTACH_BUSY_WAIT_MS = 60_000 + +export async function attachAgent( + workspaceId: string, + agentId = getStoredAgentId(), + name = 'AGY', + { maxWaitMs = ATTACH_BUSY_WAIT_MS }: { maxWaitMs?: number } = {}, +): Promise { + const deadline = Date.now() + maxWaitMs + for (;;) { + const res = await fetch('/api/agent/attach', { + method: 'POST', + headers: authHeaders(), + body: JSON.stringify({ workspace: workspaceId, agentId, name }), + }) + if (res.ok) return res.json() const err = await res.json().catch(() => null) - throw new Error(err?.error ?? `Agent Attach HTTP ${res.status}`) + // An index run holds the store's writer lock for its whole length. The + // daemon then answers 503 at once and says when to ask again; giving up + // here would leave the page's following reads without a registered agent. + const remaining = deadline - Date.now() + if (res.status === 503 && err?.busy === true && remaining > 0) { + const wait = Math.min(Math.max(Number(err.retryAfterMs) || 1000, 5), 5000, remaining) + await new Promise(resolve => setTimeout(resolve, wait)) + continue + } + throw new Error(err?.error ?? err?.reason ?? `Agent Attach HTTP ${res.status}`) } - return res.json() } // Reads require a *registered* agent (FO-3): a read must never mint an identity. 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/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..dc47331 100644 --- a/ui/src/types.ts +++ b/ui/src/types.ts @@ -89,7 +89,25 @@ 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' + +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 + 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/TurnsView.tsx b/ui/src/views/TurnsView.tsx new file mode 100644 index 0000000..f553883 --- /dev/null +++ b/ui/src/views/TurnsView.tsx @@ -0,0 +1,219 @@ +import { useEffect, useMemo, useState } from 'react' +import type { SwarmMessage, SwarmSnapshot, SwarmTurn, SwarmWorker } from '../types' +import { enqueueSwarmTask, sendSwarmMessage } 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, onRefresh }: { + workspaceId: string; data: SwarmSnapshot | 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 [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 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: selectedWorker.id, + toAgent: String(form.get('toAgent') ?? ''), subject: String(form.get('subject') ?? ''), body: String(form.get('body') ?? ''), + }) + else if (composer === 'task') await enqueueSwarmTask({ + workspace: workspaceId, requestedBy: selectedWorker.id, + title: String(form.get('title') ?? ''), body: String(form.get('body') ?? ''), + }) + setComposer(null); setFeedback('Gespeichert.'); onRefresh() + } catch (error) { setFeedback(error instanceof Error ? error.message : 'Aktion fehlgeschlagen.') } + finally { setBusy(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' && } +
)} +
} +
} +
+ +